Version Description
- Tested with WordPress 3.8.
- Fixed several minor UI/layout issues related to the new 3.8 admin style.
- Fixed a bug where moving an item to a plugin menu and then deactivating that plugin would cause the moved item to disappear.
- Fixed deleted submenus not being restored if their original parent menu is no longer available.
- Fixed a rare glitch where submenu separators added by certain other plugins would sometimes disappear.
- Fixed a conflict with Shopp 1.2.9.
- Made the plugin treat "users.php" and "profile.php" as the same parent menu. This fixes situations where it would be impossible to hide a "Users" submenu item from roles that don't have access to the "Users" menu and instead get a "Profile" menu.
- Added extra logging for situations where a menu item is hidden because a higher-priority item with the same URL is also hidden.
- Minor performance improvements.
Download this release
Release Info
Developer | whiteshadow |
Plugin | Admin Menu Editor |
Version | 1.3.1 |
Comparing to | |
See all releases |
Code changes from version 1.3 to 1.3.1
- css/menu-editor.css +24 -58
- css/screen-meta-old-wp.css +57 -0
- css/screen-meta.css +43 -0
- includes/editor-page.php +45 -4
- includes/menu-editor-core.php +239 -20
- includes/menu-item.php +41 -12
- includes/menu.php +50 -10
- includes/settings-page.php +21 -6
- js/menu-editor.js +67 -3
- menu-editor.php +1 -1
- readme.txt +14 -3
css/menu-editor.css
CHANGED
@@ -102,7 +102,7 @@
|
|
102 |
clear: both;
|
103 |
display: block;
|
104 |
margin: 4px;
|
105 |
-
width:
|
106 |
}
|
107 |
|
108 |
#ws_menu_editor #ws_save_menu {
|
@@ -289,7 +289,7 @@
|
|
289 |
#ws_menu_access_editor .ws_dropdown_button
|
290 |
{
|
291 |
width: 20px;
|
292 |
-
height:
|
293 |
|
294 |
margin: 1px 1px 1px 0;
|
295 |
padding: 0;
|
@@ -561,6 +561,9 @@ select.ws_dropdown optgroup option {
|
|
561 |
margin: 0;
|
562 |
padding: 0;
|
563 |
position: relative;
|
|
|
|
|
|
|
564 |
}
|
565 |
|
566 |
/* Current icon node (CSS class version, for the built-in WP icon sprites) */
|
@@ -611,12 +614,19 @@ select.ws_dropdown optgroup option {
|
|
611 |
|
612 |
.ui-widget-overlay {
|
613 |
background-color: black;
|
614 |
-
position:
|
615 |
left: 0;
|
616 |
top: 0;
|
617 |
opacity: 0.70;
|
618 |
-moz-opacity: 0.70;
|
619 |
-
filter: alpha(opacity=70);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
620 |
}
|
621 |
|
622 |
.ui-dialog {
|
@@ -689,6 +699,8 @@ select.ws_dropdown optgroup option {
|
|
689 |
height: 23px;
|
690 |
text-align: right;
|
691 |
margin-top: 20px;
|
|
|
|
|
692 |
}
|
693 |
|
694 |
.ws_dialog_buttons .button-primary {
|
@@ -718,6 +730,11 @@ select.ws_dropdown optgroup option {
|
|
718 |
padding-top: 25px;
|
719 |
}
|
720 |
|
|
|
|
|
|
|
|
|
|
|
721 |
/************************************
|
722 |
Menu access editor
|
723 |
*************************************/
|
@@ -886,6 +903,8 @@ select.ws_dropdown optgroup option {
|
|
886 |
}
|
887 |
|
888 |
#ws_sidebar_pro_ad {
|
|
|
|
|
889 |
margin-top: 5px;
|
890 |
margin-left: 3px;
|
891 |
|
@@ -893,57 +912,4 @@ select.ws_dropdown optgroup option {
|
|
893 |
right: 20px;
|
894 |
bottom: 40px;
|
895 |
z-index: 100;
|
896 |
-
}
|
897 |
-
|
898 |
-
/************************************
|
899 |
-
Screen meta buttons
|
900 |
-
*************************************/
|
901 |
-
|
902 |
-
/* All buttons */
|
903 |
-
#ws-pro-version-notice {
|
904 |
-
float: right;
|
905 |
-
height: 22px;
|
906 |
-
padding: 0;
|
907 |
-
margin: 0 0 0 6px;
|
908 |
-
font-family: sans-serif;
|
909 |
-
|
910 |
-
-moz-border-radius-bottomleft: 3px;
|
911 |
-
-moz-border-radius-bottomright: 3px;
|
912 |
-
-webkit-border-bottom-left-radius: 3px;
|
913 |
-
-webkit-border-bottom-right-radius: 3px;
|
914 |
-
border-bottom-left-radius: 3px;
|
915 |
-
border-bottom-right-radius: 3px;
|
916 |
-
|
917 |
-
background: #e3e3e3;
|
918 |
-
|
919 |
-
border-right: 1px solid transparent;
|
920 |
-
border-left: 1px solid transparent;
|
921 |
-
border-bottom: 1px solid transparent;
|
922 |
-
background-image: -ms-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* IE10 */
|
923 |
-
background-image: -moz-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Firefox */
|
924 |
-
background-image: -o-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Opera */
|
925 |
-
background-image: -webkit-gradient(linear, left bottom, left top, from(#dfdfdf), to(#f1f1f1)); /* old Webkit */
|
926 |
-
background-image: -webkit-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* new Webkit */
|
927 |
-
background-image: linear-gradient(bottom, #dfdfdf, #f1f1f1); /* proposed W3C Markup */
|
928 |
-
}
|
929 |
-
|
930 |
-
#ws-pro-version-notice a.show-settings {
|
931 |
-
background-image: none;
|
932 |
-
padding:0 6px 0 6px;
|
933 |
-
}
|
934 |
-
|
935 |
-
/* "Upgrade to Pro" */
|
936 |
-
#ws-pro-version-notice {
|
937 |
-
background: #00C31F none;
|
938 |
-
}
|
939 |
-
|
940 |
-
|
941 |
-
#ws-pro-version-notice a.show-settings {
|
942 |
-
font-weight: bold;
|
943 |
-
color: #DEFFD8;
|
944 |
-
text-shadow: none;
|
945 |
-
}
|
946 |
-
|
947 |
-
#ws-pro-version-notice a.show-settings:hover {
|
948 |
-
color: white;
|
949 |
-
}
|
102 |
clear: both;
|
103 |
display: block;
|
104 |
margin: 4px;
|
105 |
+
width: 130px;
|
106 |
}
|
107 |
|
108 |
#ws_menu_editor #ws_save_menu {
|
289 |
#ws_menu_access_editor .ws_dropdown_button
|
290 |
{
|
291 |
width: 20px;
|
292 |
+
height: 23px;
|
293 |
|
294 |
margin: 1px 1px 1px 0;
|
295 |
padding: 0;
|
561 |
margin: 0;
|
562 |
padding: 0;
|
563 |
position: relative;
|
564 |
+
|
565 |
+
box-sizing: border-box;
|
566 |
+
height: 25px;
|
567 |
}
|
568 |
|
569 |
/* Current icon node (CSS class version, for the built-in WP icon sprites) */
|
614 |
|
615 |
.ui-widget-overlay {
|
616 |
background-color: black;
|
617 |
+
position: fixed;
|
618 |
left: 0;
|
619 |
top: 0;
|
620 |
opacity: 0.70;
|
621 |
-moz-opacity: 0.70;
|
622 |
+
filter: alpha(opacity=70);
|
623 |
+
|
624 |
+
width: 100%;
|
625 |
+
height: 100%;
|
626 |
+
}
|
627 |
+
|
628 |
+
.ui-front {
|
629 |
+
z-index: 100;
|
630 |
}
|
631 |
|
632 |
.ui-dialog {
|
699 |
height: 23px;
|
700 |
text-align: right;
|
701 |
margin-top: 20px;
|
702 |
+
margin-bottom: 1px;
|
703 |
+
clear: both;
|
704 |
}
|
705 |
|
706 |
.ws_dialog_buttons .button-primary {
|
730 |
padding-top: 25px;
|
731 |
}
|
732 |
|
733 |
+
.ws_dont_show_again {
|
734 |
+
display: inline-block;
|
735 |
+
margin-top: 1em;
|
736 |
+
}
|
737 |
+
|
738 |
/************************************
|
739 |
Menu access editor
|
740 |
*************************************/
|
903 |
}
|
904 |
|
905 |
#ws_sidebar_pro_ad {
|
906 |
+
min-width: 225px;
|
907 |
+
|
908 |
margin-top: 5px;
|
909 |
margin-left: 3px;
|
910 |
|
912 |
right: 20px;
|
913 |
bottom: 40px;
|
914 |
z-index: 100;
|
915 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
css/screen-meta-old-wp.css
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/************************************
|
2 |
+
Screen meta buttons
|
3 |
+
for WP 3.7 and below
|
4 |
+
*************************************/
|
5 |
+
|
6 |
+
/* All buttons */
|
7 |
+
.custom-screen-meta-link-wrap {
|
8 |
+
float: right;
|
9 |
+
height: 22px;
|
10 |
+
padding: 0;
|
11 |
+
margin: 0 0 0 6px;
|
12 |
+
font-family: sans-serif;
|
13 |
+
-moz-border-radius-bottomleft: 3px;
|
14 |
+
-moz-border-radius-bottomright: 3px;
|
15 |
+
-webkit-border-bottom-left-radius: 3px;
|
16 |
+
-webkit-border-bottom-right-radius: 3px;
|
17 |
+
border-bottom-left-radius: 3px;
|
18 |
+
border-bottom-right-radius: 3px;
|
19 |
+
|
20 |
+
background: #e3e3e3;
|
21 |
+
|
22 |
+
border-right: 1px solid transparent;
|
23 |
+
border-left: 1px solid transparent;
|
24 |
+
border-bottom: 1px solid transparent;
|
25 |
+
background-image: -ms-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* IE10 */
|
26 |
+
background-image: -moz-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Firefox */
|
27 |
+
background-image: -o-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Opera */
|
28 |
+
background-image: -webkit-gradient(linear, left bottom, left top, from(#dfdfdf), to(#f1f1f1)); /* old Webkit */
|
29 |
+
background-image: -webkit-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* new Webkit */
|
30 |
+
background-image: linear-gradient(bottom, #dfdfdf, #f1f1f1); /* proposed W3C Markup */
|
31 |
+
}
|
32 |
+
|
33 |
+
#screen-meta .custom-screen-meta-link-wrap a.custom-screen-meta-link,
|
34 |
+
#screen-meta-links .custom-screen-meta-link-wrap a.custom-screen-meta-link
|
35 |
+
{
|
36 |
+
background-image: none;
|
37 |
+
padding: 0 6px 0 6px;
|
38 |
+
}
|
39 |
+
|
40 |
+
#screen-meta-links a.custom-screen-meta-link::after {
|
41 |
+
display: none;
|
42 |
+
}
|
43 |
+
|
44 |
+
/* "Upgrade to Pro" */
|
45 |
+
#ws-pro-version-notice {
|
46 |
+
background: #00C31F none;
|
47 |
+
}
|
48 |
+
|
49 |
+
#ws-pro-version-notice a.show-settings {
|
50 |
+
font-weight: bold;
|
51 |
+
color: #DEFFD8;
|
52 |
+
text-shadow: none;
|
53 |
+
}
|
54 |
+
|
55 |
+
#ws-pro-version-notice a.show-settings:hover {
|
56 |
+
color: white;
|
57 |
+
}
|
css/screen-meta.css
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/************************************
|
2 |
+
Screen meta buttons
|
3 |
+
for WP 3.8+
|
4 |
+
*************************************/
|
5 |
+
|
6 |
+
/* All buttons */
|
7 |
+
.custom-screen-meta-link-wrap {
|
8 |
+
float: right;
|
9 |
+
height: 28px;
|
10 |
+
margin: 0 0 0 6px;
|
11 |
+
|
12 |
+
border: 1px solid #ddd;
|
13 |
+
border-top: none;
|
14 |
+
background: #fff;
|
15 |
+
-webkit-box-shadow: 0px 1px 1px -1px rgba(0,0,0,0.1);
|
16 |
+
box-shadow: 0px 1px 1px -1px rgba(0,0,0,0.1);
|
17 |
+
}
|
18 |
+
|
19 |
+
#screen-meta .custom-screen-meta-link-wrap a.custom-screen-meta-link,
|
20 |
+
#screen-meta-links .custom-screen-meta-link-wrap a.custom-screen-meta-link
|
21 |
+
{
|
22 |
+
padding: 3px 16px 3px 16px;
|
23 |
+
}
|
24 |
+
|
25 |
+
#screen-meta-links a.custom-screen-meta-link::after {
|
26 |
+
display: none;
|
27 |
+
}
|
28 |
+
|
29 |
+
/* "Upgrade to Pro" */
|
30 |
+
#ws-pro-version-notice {
|
31 |
+
background-color: #00C31F;
|
32 |
+
border-color: #0a0;
|
33 |
+
}
|
34 |
+
|
35 |
+
#ws-pro-version-notice a.show-settings {
|
36 |
+
font-weight: bold;
|
37 |
+
color: #DEFFD8;
|
38 |
+
text-shadow: none;
|
39 |
+
}
|
40 |
+
|
41 |
+
#ws-pro-version-notice a.show-settings:hover {
|
42 |
+
color: white;
|
43 |
+
}
|
includes/editor-page.php
CHANGED
@@ -24,8 +24,8 @@ if ( !apply_filters('admin_menu_editor_is_pro', false) ){
|
|
24 |
<script type="text/javascript">
|
25 |
(function($){
|
26 |
$('#screen-meta-links').append(
|
27 |
-
'<div id="ws-pro-version-notice">' +
|
28 |
-
'<a href="http://adminmenueditor.com/upgrade-to-pro/?utm_source=Admin%2BMenu%2BEditor%2Bfree&utm_medium=text_link&utm_content=top_upgrade_link&utm_campaign=Plugins" id="ws-pro-version-notice-link" class="show-settings" target="_blank" title="View Pro version details">Upgrade to Pro</a>' +
|
29 |
'</div>'
|
30 |
);
|
31 |
})(jQuery);
|
@@ -104,7 +104,11 @@ endif;
|
|
104 |
<div class="ws_separator"> </div>
|
105 |
|
106 |
<a id='ws_new_menu' class='ws_button' href='javascript:void(0)' title='New menu'><img src='<?php echo $icons['new']; ?>' alt="New menu" /></a>
|
107 |
-
|
|
|
|
|
|
|
|
|
108 |
<a id='ws_delete_menu' class='ws_button' href='javascript:void(0)' title='Delete menu'><img src='<?php echo $icons['delete']; ?>' alt="Delete menu" /></a>
|
109 |
|
110 |
<div class="ws_separator"> </div>
|
@@ -136,7 +140,9 @@ endif;
|
|
136 |
<div class="ws_separator"> </div>
|
137 |
|
138 |
<a id='ws_new_item' class='ws_button' href='javascript:void(0)' title='New menu item'><img src='<?php echo $icons['new']; ?>' alt="New menu item" /></a>
|
139 |
-
|
|
|
|
|
140 |
<a id='ws_delete_item' class='ws_button' href='javascript:void(0)' title='Delete menu item'><img src='<?php echo $icons['delete']; ?>' alt="Delete menu item" /></a>
|
141 |
|
142 |
<div class="ws_separator"> </div>
|
@@ -316,6 +322,41 @@ endif;
|
|
316 |
</label>
|
317 |
</span>
|
318 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
<script type='text/javascript'>
|
320 |
var defaultMenu = <?php echo $editor_data['default_menu_js']; ?>;
|
321 |
var customMenu = <?php echo $editor_data['custom_menu_js']; ?>;
|
24 |
<script type="text/javascript">
|
25 |
(function($){
|
26 |
$('#screen-meta-links').append(
|
27 |
+
'<div id="ws-pro-version-notice" class="custom-screen-meta-link-wrap">' +
|
28 |
+
'<a href="http://adminmenueditor.com/upgrade-to-pro/?utm_source=Admin%2BMenu%2BEditor%2Bfree&utm_medium=text_link&utm_content=top_upgrade_link&utm_campaign=Plugins" id="ws-pro-version-notice-link" class="show-settings custom-screen-meta-link" target="_blank" title="View Pro version details">Upgrade to Pro</a>' +
|
29 |
'</div>'
|
30 |
);
|
31 |
})(jQuery);
|
104 |
<div class="ws_separator"> </div>
|
105 |
|
106 |
<a id='ws_new_menu' class='ws_button' href='javascript:void(0)' title='New menu'><img src='<?php echo $icons['new']; ?>' alt="New menu" /></a>
|
107 |
+
|
108 |
+
<?php if ( $editor_data['show_deprecated_hide_button'] ): ?>
|
109 |
+
<a id='ws_hide_menu' class='ws_button' href='javascript:void(0)' title='Show/Hide'><img src='<?php echo $icons['hide']; ?>' alt="Show/Hide" /></a>
|
110 |
+
<?php endif; ?>
|
111 |
+
|
112 |
<a id='ws_delete_menu' class='ws_button' href='javascript:void(0)' title='Delete menu'><img src='<?php echo $icons['delete']; ?>' alt="Delete menu" /></a>
|
113 |
|
114 |
<div class="ws_separator"> </div>
|
140 |
<div class="ws_separator"> </div>
|
141 |
|
142 |
<a id='ws_new_item' class='ws_button' href='javascript:void(0)' title='New menu item'><img src='<?php echo $icons['new']; ?>' alt="New menu item" /></a>
|
143 |
+
<?php if ( $editor_data['show_deprecated_hide_button'] ): ?>
|
144 |
+
<a id='ws_hide_item' class='ws_button' href='javascript:void(0)' title='Show/Hide'><img src='<?php echo $icons['hide']; ?>' alt="Show/Hide" /></a>
|
145 |
+
<?php endif; ?>
|
146 |
<a id='ws_delete_item' class='ws_button' href='javascript:void(0)' title='Delete menu item'><img src='<?php echo $icons['delete']; ?>' alt="Delete menu item" /></a>
|
147 |
|
148 |
<div class="ws_separator"> </div>
|
322 |
</label>
|
323 |
</span>
|
324 |
|
325 |
+
|
326 |
+
<!-- Confirmation dialog when hiding "Dashboard -> Home" -->
|
327 |
+
<div id="ws-ame-dashboard-hide-confirmation" style="display: none;">
|
328 |
+
<span>
|
329 |
+
Hiding <em>Dashboard -> Home</em> may prevent users with the selected role from logging in!
|
330 |
+
Are you sure you want to do it?
|
331 |
+
</span>
|
332 |
+
|
333 |
+
<h4>Explanation</h4>
|
334 |
+
<p>
|
335 |
+
WordPress automatically redirects users to the <em>Dashboard -> Home</em> page upon successful login.
|
336 |
+
If you hide this page, users will get an "insufficient permissions" error when they log in
|
337 |
+
due to being redirected to a hidden page. As a result, it will look like their login failed.
|
338 |
+
</p>
|
339 |
+
|
340 |
+
<h4>Recommendations</h4>
|
341 |
+
<p>
|
342 |
+
You can use a plugin like <a href="http://wordpress.org/plugins/peters-login-redirect/">Peter's Login Redirect</a>
|
343 |
+
to redirect specific roles to different pages.
|
344 |
+
</p>
|
345 |
+
|
346 |
+
<div class="ws_dialog_buttons">
|
347 |
+
<?php
|
348 |
+
submit_button('Hide the menu', 'primary', 'ws_confirm_menu_hiding', false);
|
349 |
+
submit_button('Leave it visible', 'secondary', 'ws_cancel_menu_hiding', false);
|
350 |
+
?>
|
351 |
+
</div>
|
352 |
+
|
353 |
+
<label class="ws_dont_show_again">
|
354 |
+
<input type="checkbox" id="ws-ame-disable-dashboard-hide-confirmation">
|
355 |
+
Don't show this message again
|
356 |
+
</label>
|
357 |
+
</div>
|
358 |
+
|
359 |
+
|
360 |
<script type='text/javascript'>
|
361 |
var defaultMenu = <?php echo $editor_data['default_menu_js']; ?>;
|
362 |
var customMenu = <?php echo $editor_data['custom_menu_js']; ?>;
|
includes/menu-editor-core.php
CHANGED
@@ -69,6 +69,9 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
69 |
private $cached_custom_menu = null; //Cached, non-merged version of the custom menu. Used by load_custom_menu().
|
70 |
private $cached_virtual_caps = null;//List of virtual caps. Used by get_virtual_caps().
|
71 |
|
|
|
|
|
|
|
72 |
//Our personal copy of the request vars, without any "magic quotes".
|
73 |
private $post = array();
|
74 |
private $get = array();
|
@@ -96,6 +99,12 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
96 |
'allowed_user_id' => null,
|
97 |
//The user who can see this plugin on the "Plugins" page. By default all admins can see it.
|
98 |
'plugins_page_allowed_user_id' => null,
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
);
|
100 |
$this->serialize_with_json = false; //(Don't) store the options in JSON format
|
101 |
|
@@ -105,10 +114,14 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
105 |
$this->magic_hook_priority = 99999;
|
106 |
|
107 |
//AJAXify screen options
|
108 |
-
add_action('wp_ajax_ws_ame_save_screen_options', array(
|
109 |
|
110 |
-
//AJAXify hints
|
111 |
add_action('wp_ajax_ws_ame_hide_hint', array($this, 'ajax_hide_hint'));
|
|
|
|
|
|
|
|
|
112 |
|
113 |
//Make sure we have access to the original, un-mangled request data.
|
114 |
//This is necessary because WordPress will stupidly apply "magic quotes"
|
@@ -123,6 +136,9 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
123 |
|
124 |
//User survey
|
125 |
add_action('admin_notices', array($this, 'display_survey_notice'));
|
|
|
|
|
|
|
126 |
}
|
127 |
|
128 |
function init_finish() {
|
@@ -182,7 +198,20 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
182 |
function hook_admin_menu(){
|
183 |
global $menu, $submenu;
|
184 |
|
185 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
186 |
$reset_requested = isset($this->get['reset_admin_menu']) && $this->get['reset_admin_menu'];
|
187 |
if ( $reset_requested && $this->current_user_can_edit_menu() ){
|
188 |
$this->set_custom_menu(null);
|
@@ -235,7 +264,9 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
235 |
|
236 |
//Convert our custom menu to the $menu + $submenu structure used by WP.
|
237 |
//Note: This method sets up multiple internal fields and may cause side-effects.
|
|
|
238 |
$this->build_custom_wp_menu($this->merged_custom_menu['tree']);
|
|
|
239 |
|
240 |
if ( !$this->user_can_access_current_page() ) {
|
241 |
$this->log_security_note('DENY access.');
|
@@ -278,7 +309,10 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
278 |
|
279 |
$menu = $this->custom_wp_menu;
|
280 |
$submenu = $this->custom_wp_submenu;
|
|
|
|
|
281 |
list($menu, $submenu) = $this->filter_menu($menu, $submenu);
|
|
|
282 |
|
283 |
return $parent_file;
|
284 |
}
|
@@ -496,6 +530,8 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
496 |
'selectedActor' => isset($this->get['selected_actor']) ? strval($this->get['selected_actor']) : null,
|
497 |
|
498 |
'showHints' => $this->get_hint_visibility(),
|
|
|
|
|
499 |
|
500 |
'isDemoMode' => defined('IS_DEMO_MODE'),
|
501 |
'isMasterMode' => defined('IS_MASTER_MODE'),
|
@@ -559,6 +595,17 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
559 |
array('menu-editor-base-style')
|
560 |
);
|
561 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
562 |
wp_enqueue_style('menu-editor-colours-classic');
|
563 |
}
|
564 |
|
@@ -585,6 +632,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
585 |
|
586 |
$this->cached_custom_menu = null;
|
587 |
$this->cached_virtual_caps = null;
|
|
|
588 |
}
|
589 |
|
590 |
/**
|
@@ -746,6 +794,8 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
746 |
*/
|
747 |
function menu_merge($tree){
|
748 |
//Iterate over all menus and submenus and look up default values
|
|
|
|
|
749 |
foreach ($tree as &$topmenu){
|
750 |
|
751 |
if ( !ameMenuItem::get($topmenu, 'custom') ) {
|
@@ -779,6 +829,10 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
779 |
//Yes, load defaults from that item
|
780 |
$item['defaults'] = $this->item_templates[$template_id]['defaults'];
|
781 |
$this->item_templates[$template_id]['used'] = true;
|
|
|
|
|
|
|
|
|
782 |
} else if ( empty($item['separator']) ) {
|
783 |
//Record as missing, unless it's a menu separator
|
784 |
$item['missing'] = true;
|
@@ -787,6 +841,9 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
787 |
$temp = $this->set_final_menu_capability($temp);
|
788 |
$this->add_access_lookup($temp, 'submenu', true);
|
789 |
}
|
|
|
|
|
|
|
790 |
}
|
791 |
}
|
792 |
}
|
@@ -838,13 +895,25 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
838 |
$tree[$template['defaults']['parent']]['items'][] = $entry;
|
839 |
} else {
|
840 |
//This can happen if the original parent menu has been moved to a submenu.
|
841 |
-
|
842 |
}
|
843 |
} else {
|
844 |
$tree[$template['defaults']['file']] = $entry;
|
845 |
}
|
846 |
}
|
847 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
848 |
//Resort the tree to ensure the found items are in the right spots
|
849 |
$tree = ameMenu::sort_menu_tree($tree);
|
850 |
|
@@ -976,9 +1045,19 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
976 |
//Convert the prepared tree to the internal WordPress format.
|
977 |
foreach($new_tree as $topmenu) {
|
978 |
$trueAccess = isset($this->page_access_lookup[$topmenu['url']]) ? $this->page_access_lookup[$topmenu['url']] : null;
|
979 |
-
if ( $trueAccess === 'do_not_allow' ) {
|
980 |
$topmenu['access_level'] = $trueAccess;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
981 |
}
|
|
|
982 |
if ( !isset($this->reverse_item_lookup[$topmenu['url']]) ) { //Prefer sub-menus.
|
983 |
$this->reverse_item_lookup[$topmenu['url']] = $topmenu;
|
984 |
}
|
@@ -987,9 +1066,19 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
987 |
|
988 |
foreach($topmenu['items'] as $item) {
|
989 |
$trueAccess = isset($this->page_access_lookup[$item['url']]) ? $this->page_access_lookup[$item['url']] : null;
|
990 |
-
if ( $trueAccess === 'do_not_allow' ) {
|
991 |
$item['access_level'] = $trueAccess;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
992 |
}
|
|
|
993 |
$this->reverse_item_lookup[$item['url']] = $item;
|
994 |
$new_submenu[$topmenu['file']][] = $this->convert_to_wp_format($item);
|
995 |
}
|
@@ -1083,7 +1172,10 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1083 |
}
|
1084 |
|
1085 |
//Used later to determine the current page based on URL.
|
1086 |
-
|
|
|
|
|
|
|
1087 |
|
1088 |
//Convert relative URls to fully qualified ones. This prevents problems with WordPress
|
1089 |
//incorrectly converting "index.php?page=xyz" to, say, "tools.php?page=index.php?page=xyz"
|
@@ -1136,15 +1228,30 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1136 |
$user_has_access = true;
|
1137 |
$cap_to_use = '';
|
1138 |
if ( !empty($item['access_level']) ) {
|
1139 |
-
$user_has_cap = $this->current_user_can($item['access_level']);
|
1140 |
-
$user_has_access = $user_has_access && $user_has_cap;
|
1141 |
$cap_to_use = $item['access_level'];
|
1142 |
|
1143 |
-
$item['
|
1144 |
-
|
1145 |
-
|
1146 |
-
|
1147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1148 |
} else {
|
1149 |
$item['access_check_log'][] = '- No required capability set.';
|
1150 |
}
|
@@ -1191,6 +1298,18 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1191 |
$this->handle_form_submission($this->post, $action);
|
1192 |
}
|
1193 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1194 |
$sub_section = isset($this->get['sub_section']) ? $this->get['sub_section'] : null;
|
1195 |
if ( $sub_section === 'settings' ) {
|
1196 |
$this->display_plugin_settings_ui();
|
@@ -1281,6 +1400,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1281 |
//Hide some menu options by default.
|
1282 |
$this->options['hide_advanced_settings'] = !empty($this->post['hide_advanced_settings']);
|
1283 |
|
|
|
|
|
|
|
|
|
|
|
1284 |
$this->save_options();
|
1285 |
wp_redirect(add_query_arg('updated', 1, $this->get_settings_page_url()));
|
1286 |
}
|
@@ -1293,6 +1417,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1293 |
'images_url' => plugins_url('images', $this->plugin_file),
|
1294 |
'hide_advanced_settings' => $this->options['hide_advanced_settings'],
|
1295 |
'settings_page_url' => $this->get_settings_page_url(),
|
|
|
1296 |
);
|
1297 |
|
1298 |
//Build a tree struct. for the default menu
|
@@ -1346,8 +1471,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1346 |
*/
|
1347 |
private function display_plugin_settings_ui() {
|
1348 |
//These variables are used by settings-page.php.
|
|
|
1349 |
$settings = $this->options;
|
|
|
1350 |
$settings_page_url = $this->get_settings_page_url();
|
|
|
1351 |
$editor_page_url = admin_url($this->settings_link);
|
1352 |
|
1353 |
require dirname(__FILE__) . '/settings-page.php';
|
@@ -1408,7 +1536,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1408 |
}
|
1409 |
|
1410 |
foreach($custom_menu['tree'] as $item) {
|
1411 |
-
$caps =
|
1412 |
}
|
1413 |
|
1414 |
$this->cached_virtual_caps = $caps;
|
@@ -1431,12 +1559,25 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1431 |
}
|
1432 |
|
1433 |
foreach($item['items'] as $sub_item) {
|
1434 |
-
$caps =
|
1435 |
}
|
1436 |
|
1437 |
return $caps;
|
1438 |
}
|
1439 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1440 |
/**
|
1441 |
* Create a virtual 'super_admin' capability that only super admins have.
|
1442 |
* This function accomplishes that by by filtering 'user_has_cap' calls.
|
@@ -1508,6 +1649,17 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1508 |
update_user_meta($user->ID, 'ame_show_hints', $show_hints);
|
1509 |
}
|
1510 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1511 |
/**
|
1512 |
* Enqueue a script that fixes a bug where pages moved to a different menu
|
1513 |
* would not be highlighted properly when the user visits them.
|
@@ -1561,7 +1713,19 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1561 |
* @return bool
|
1562 |
*/
|
1563 |
private function current_user_can($capability) {
|
1564 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1565 |
}
|
1566 |
|
1567 |
/**
|
@@ -1600,6 +1764,12 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1600 |
$base_site_url = $matches[1];
|
1601 |
}
|
1602 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1603 |
$current_url = $base_site_url . remove_query_arg('___ame_dummy_param___');
|
1604 |
$this->log_security_note(sprintf('Current URL: "%s"', htmlentities($current_url)));
|
1605 |
|
@@ -1612,7 +1782,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1612 |
if ( substr($item_url, 0, 1) == '/' ) {
|
1613 |
$item_url = $base_site_url . $item_url;
|
1614 |
} else {
|
1615 |
-
|
|
|
|
|
|
|
|
|
1616 |
}
|
1617 |
}
|
1618 |
$item_url = $this->parse_url($item_url);
|
@@ -1735,7 +1909,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1735 |
|
1736 |
$display_notice = $this->options['display_survey_notice'] && $this->current_user_can_edit_menu();
|
1737 |
if ( isset($this->options['first_install_time']) ) {
|
1738 |
-
$minimum_usage_period =
|
1739 |
$display_notice = $display_notice && ((time() - $this->options['first_install_time']) > $minimum_usage_period);
|
1740 |
}
|
1741 |
|
@@ -1753,7 +1927,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1753 |
$free_survey_url = 'https://docs.google.com/spreadsheet/viewform?formkey=dERyeDk0OWhlbkxYcEY4QTNaMnlTQUE6MQ';
|
1754 |
$pro_survey_url = 'https://docs.google.com/spreadsheet/viewform?formkey=dHl4MnlHaVI3NE5JdVFDWG01SkRKTWc6MA';
|
1755 |
|
1756 |
-
if (
|
1757 |
$survey_url = $pro_survey_url;
|
1758 |
} else {
|
1759 |
$survey_url = $free_survey_url;
|
@@ -1952,4 +2126,49 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1952 |
return $name;
|
1953 |
}
|
1954 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1955 |
} //class
|
69 |
private $cached_custom_menu = null; //Cached, non-merged version of the custom menu. Used by load_custom_menu().
|
70 |
private $cached_virtual_caps = null;//List of virtual caps. Used by get_virtual_caps().
|
71 |
|
72 |
+
private $cached_user_caps = array(); //A cache of the current user's capabilities. Used only in very specific places.
|
73 |
+
private $user_cap_cache_enabled = false;
|
74 |
+
|
75 |
//Our personal copy of the request vars, without any "magic quotes".
|
76 |
private $post = array();
|
77 |
private $get = array();
|
99 |
'allowed_user_id' => null,
|
100 |
//The user who can see this plugin on the "Plugins" page. By default all admins can see it.
|
101 |
'plugins_page_allowed_user_id' => null,
|
102 |
+
|
103 |
+
'show_deprecated_hide_button' => null,
|
104 |
+
'dashboard_hiding_confirmation_enabled' => true,
|
105 |
+
|
106 |
+
//Enable/disable the admin notice that tells the user where the plugin settings menu is.
|
107 |
+
'show_plugin_menu_notice' => true,
|
108 |
);
|
109 |
$this->serialize_with_json = false; //(Don't) store the options in JSON format
|
110 |
|
114 |
$this->magic_hook_priority = 99999;
|
115 |
|
116 |
//AJAXify screen options
|
117 |
+
add_action('wp_ajax_ws_ame_save_screen_options', array($this,'ajax_save_screen_options'));
|
118 |
|
119 |
+
//AJAXify hints and warnings
|
120 |
add_action('wp_ajax_ws_ame_hide_hint', array($this, 'ajax_hide_hint'));
|
121 |
+
add_action(
|
122 |
+
'wp_ajax_ws_ame_disable_dashboard_hiding_confirmation',
|
123 |
+
array($this, 'ajax_disable_dashboard_hiding_confirmation')
|
124 |
+
);
|
125 |
|
126 |
//Make sure we have access to the original, un-mangled request data.
|
127 |
//This is necessary because WordPress will stupidly apply "magic quotes"
|
136 |
|
137 |
//User survey
|
138 |
add_action('admin_notices', array($this, 'display_survey_notice'));
|
139 |
+
|
140 |
+
//Tell first-time users where they can find the plugin settings page.
|
141 |
+
add_action('all_admin_notices', array($this, 'display_plugin_menu_notice'));
|
142 |
}
|
143 |
|
144 |
function init_finish() {
|
198 |
function hook_admin_menu(){
|
199 |
global $menu, $submenu;
|
200 |
|
201 |
+
//Compatibility fix for Shopp 1.2.9. This plugin has an "admin_menu" hook (Flow::menu) that adds another
|
202 |
+
//"admin_menu" hook (AdminFlow::taxonomies) when it runs. Basically, it indirectly modifies the global
|
203 |
+
//$wp_filters['admin_menu'] array while WordPress is iterating it (nasty!). Due to how PHP arrays are
|
204 |
+
//implemented and how do_action() works, this second hook is the very last one to run, even after hooks
|
205 |
+
//with a lower priority.
|
206 |
+
//The only way we can see the changes made by the second hook is to do the same thing.
|
207 |
+
static $firstRunSkipped = false;
|
208 |
+
if ( !$firstRunSkipped && class_exists('Flow') ) {
|
209 |
+
add_action('admin_menu', array($this, 'hook_admin_menu'), $this->magic_hook_priority + 1);
|
210 |
+
$firstRunSkipped = true;
|
211 |
+
return;
|
212 |
+
}
|
213 |
+
|
214 |
+
//Menu reset (for emergencies). Executed by accessing http://example.com/wp-admin/?reset_admin_menu=1
|
215 |
$reset_requested = isset($this->get['reset_admin_menu']) && $this->get['reset_admin_menu'];
|
216 |
if ( $reset_requested && $this->current_user_can_edit_menu() ){
|
217 |
$this->set_custom_menu(null);
|
264 |
|
265 |
//Convert our custom menu to the $menu + $submenu structure used by WP.
|
266 |
//Note: This method sets up multiple internal fields and may cause side-effects.
|
267 |
+
$this->user_cap_cache_enabled = true;
|
268 |
$this->build_custom_wp_menu($this->merged_custom_menu['tree']);
|
269 |
+
$this->user_cap_cache_enabled = false;
|
270 |
|
271 |
if ( !$this->user_can_access_current_page() ) {
|
272 |
$this->log_security_note('DENY access.');
|
309 |
|
310 |
$menu = $this->custom_wp_menu;
|
311 |
$submenu = $this->custom_wp_submenu;
|
312 |
+
|
313 |
+
$this->user_cap_cache_enabled = true;
|
314 |
list($menu, $submenu) = $this->filter_menu($menu, $submenu);
|
315 |
+
$this->user_cap_cache_enabled = false;
|
316 |
|
317 |
return $parent_file;
|
318 |
}
|
530 |
'selectedActor' => isset($this->get['selected_actor']) ? strval($this->get['selected_actor']) : null,
|
531 |
|
532 |
'showHints' => $this->get_hint_visibility(),
|
533 |
+
'dashboardHidingConfirmationEnabled' => $this->options['dashboard_hiding_confirmation_enabled'],
|
534 |
+
'disableDashboardConfirmationNonce' => wp_create_nonce('ws_ame_disable_dashboard_hiding_confirmation'),
|
535 |
|
536 |
'isDemoMode' => defined('IS_DEMO_MODE'),
|
537 |
'isMasterMode' => defined('IS_MASTER_MODE'),
|
595 |
array('menu-editor-base-style')
|
596 |
);
|
597 |
|
598 |
+
//WordPress introduced a new screen meta button style in WP 3.8.
|
599 |
+
//We have two different stylesheets - one for 3.8+ and one for backwards compatibility.
|
600 |
+
wp_register_auto_versioned_style('menu-editor-screen-meta', plugins_url('css/screen-meta.css', $this->plugin_file));
|
601 |
+
wp_register_auto_versioned_style('menu-editor-screen-meta-old', plugins_url('css/screen-meta-old-wp.css', $this->plugin_file));
|
602 |
+
|
603 |
+
if ( isset($GLOBALS['wp_version']) && version_compare($GLOBALS['wp_version'], '3.8-RC1', '<') ) {
|
604 |
+
wp_enqueue_style('menu-editor-screen-meta-old');
|
605 |
+
} else {
|
606 |
+
wp_enqueue_style('menu-editor-screen-meta');
|
607 |
+
}
|
608 |
+
|
609 |
wp_enqueue_style('menu-editor-colours-classic');
|
610 |
}
|
611 |
|
632 |
|
633 |
$this->cached_custom_menu = null;
|
634 |
$this->cached_virtual_caps = null;
|
635 |
+
$this->cached_user_caps = array();
|
636 |
}
|
637 |
|
638 |
/**
|
794 |
*/
|
795 |
function menu_merge($tree){
|
796 |
//Iterate over all menus and submenus and look up default values
|
797 |
+
//Also flag used and missing items.
|
798 |
+
$orphans = array();
|
799 |
foreach ($tree as &$topmenu){
|
800 |
|
801 |
if ( !ameMenuItem::get($topmenu, 'custom') ) {
|
829 |
//Yes, load defaults from that item
|
830 |
$item['defaults'] = $this->item_templates[$template_id]['defaults'];
|
831 |
$this->item_templates[$template_id]['used'] = true;
|
832 |
+
//We must move orphaned items elsewhere. Use the default location if possible.
|
833 |
+
if ( isset($topmenu['missing']) && $topmenu['missing'] ) {
|
834 |
+
$orphans[] = $item;
|
835 |
+
}
|
836 |
} else if ( empty($item['separator']) ) {
|
837 |
//Record as missing, unless it's a menu separator
|
838 |
$item['missing'] = true;
|
841 |
$temp = $this->set_final_menu_capability($temp);
|
842 |
$this->add_access_lookup($temp, 'submenu', true);
|
843 |
}
|
844 |
+
} else {
|
845 |
+
//What if the parent of this custom item is missing?
|
846 |
+
//Right now the custom item will just disappear.
|
847 |
}
|
848 |
}
|
849 |
}
|
895 |
$tree[$template['defaults']['parent']]['items'][] = $entry;
|
896 |
} else {
|
897 |
//This can happen if the original parent menu has been moved to a submenu.
|
898 |
+
$tree[$template['defaults']['file']] = $entry;
|
899 |
}
|
900 |
} else {
|
901 |
$tree[$template['defaults']['file']] = $entry;
|
902 |
}
|
903 |
}
|
904 |
|
905 |
+
//Move orphaned items back to their original parents.
|
906 |
+
foreach($orphans as $item) {
|
907 |
+
$defaultParent = !empty($item['defaults']['parent']) ? $item['defaults']['parent'] : null;
|
908 |
+
if ( isset($defaultParent) && isset($tree[$defaultParent]) ) {
|
909 |
+
$tree[$defaultParent]['items'][] = $item;
|
910 |
+
} else {
|
911 |
+
//This can happen if the parent has been moved to a submenu.
|
912 |
+
//Just put the orphan at the bottom of the menu.
|
913 |
+
$tree[$item['defaults']['file']] = $item;
|
914 |
+
}
|
915 |
+
}
|
916 |
+
|
917 |
//Resort the tree to ensure the found items are in the right spots
|
918 |
$tree = ameMenu::sort_menu_tree($tree);
|
919 |
|
1045 |
//Convert the prepared tree to the internal WordPress format.
|
1046 |
foreach($new_tree as $topmenu) {
|
1047 |
$trueAccess = isset($this->page_access_lookup[$topmenu['url']]) ? $this->page_access_lookup[$topmenu['url']] : null;
|
1048 |
+
if ( ($trueAccess === 'do_not_allow') && ($topmenu['access_level'] !== $trueAccess) ) {
|
1049 |
$topmenu['access_level'] = $trueAccess;
|
1050 |
+
if ( isset($topmenu['access_check_log']) ) {
|
1051 |
+
$topmenu['access_check_log'][] = sprintf(
|
1052 |
+
'+ Override: There is a hidden menu item with the same URL (%1$s) but a higher priority.' .
|
1053 |
+
' Setting the capability to "%2$s".',
|
1054 |
+
$topmenu['url'],
|
1055 |
+
$trueAccess
|
1056 |
+
);
|
1057 |
+
$topmenu['access_check_log'][] = str_repeat('=', 79);
|
1058 |
+
}
|
1059 |
}
|
1060 |
+
|
1061 |
if ( !isset($this->reverse_item_lookup[$topmenu['url']]) ) { //Prefer sub-menus.
|
1062 |
$this->reverse_item_lookup[$topmenu['url']] = $topmenu;
|
1063 |
}
|
1066 |
|
1067 |
foreach($topmenu['items'] as $item) {
|
1068 |
$trueAccess = isset($this->page_access_lookup[$item['url']]) ? $this->page_access_lookup[$item['url']] : null;
|
1069 |
+
if ( ($trueAccess === 'do_not_allow') && ($item['access_level'] !== $trueAccess) ) {
|
1070 |
$item['access_level'] = $trueAccess;
|
1071 |
+
if ( isset($item['access_check_log']) ) {
|
1072 |
+
$item['access_check_log'][] = sprintf(
|
1073 |
+
'+ Override: There is a hidden menu item with the same URL (%1$s) but a higher priority.' .
|
1074 |
+
' Setting the capability to "%2$s".',
|
1075 |
+
$item['url'],
|
1076 |
+
$trueAccess
|
1077 |
+
);
|
1078 |
+
$item['access_check_log'][] = str_repeat('=', 79);
|
1079 |
+
}
|
1080 |
}
|
1081 |
+
|
1082 |
$this->reverse_item_lookup[$item['url']] = $item;
|
1083 |
$new_submenu[$topmenu['file']][] = $this->convert_to_wp_format($item);
|
1084 |
}
|
1172 |
}
|
1173 |
|
1174 |
//Used later to determine the current page based on URL.
|
1175 |
+
if ( empty($item['url']) ) {
|
1176 |
+
$original_parent = !empty($item['defaults']['parent']) ? $item['defaults']['parent'] : $parent;
|
1177 |
+
$item['url'] = ameMenuItem::generate_url($item['file'], $original_parent);
|
1178 |
+
}
|
1179 |
|
1180 |
//Convert relative URls to fully qualified ones. This prevents problems with WordPress
|
1181 |
//incorrectly converting "index.php?page=xyz" to, say, "tools.php?page=index.php?page=xyz"
|
1228 |
$user_has_access = true;
|
1229 |
$cap_to_use = '';
|
1230 |
if ( !empty($item['access_level']) ) {
|
|
|
|
|
1231 |
$cap_to_use = $item['access_level'];
|
1232 |
|
1233 |
+
if ( isset($item['user_has_access_level']) ) {
|
1234 |
+
//The "custom_admin_menu_capability" filter has already determined whether this user should
|
1235 |
+
//have the required capability, so checking it again would be redundant. This usually only
|
1236 |
+
//applies to the Pro version which uses that filter in extras.php.
|
1237 |
+
$user_has_cap = $item['user_has_access_level'];
|
1238 |
+
|
1239 |
+
$item['access_check_log'][] = sprintf(
|
1240 |
+
'Skipping a "%1$s" capability check because we\'ve already determined that the current user %2$s access.',
|
1241 |
+
htmlentities($cap_to_use),
|
1242 |
+
$user_has_cap ? 'should have' : 'should not have'
|
1243 |
+
);
|
1244 |
+
} else {
|
1245 |
+
$user_has_cap = $this->current_user_can($cap_to_use);
|
1246 |
+
$item['access_check_log'][] = sprintf(
|
1247 |
+
'Required capability: %1$s. User %2$s this capability.',
|
1248 |
+
htmlentities($cap_to_use),
|
1249 |
+
$user_has_cap ? 'HAS' : 'DOES NOT have'
|
1250 |
+
);
|
1251 |
+
}
|
1252 |
+
|
1253 |
+
$user_has_access = $user_has_access && $user_has_cap;
|
1254 |
+
|
1255 |
} else {
|
1256 |
$item['access_check_log'][] = '- No required capability set.';
|
1257 |
}
|
1298 |
$this->handle_form_submission($this->post, $action);
|
1299 |
}
|
1300 |
|
1301 |
+
//By default, show the "Hide" button only if the user has already hidden something with it,
|
1302 |
+
//or if they're using the free version. Pro users should use role permissions instead, but can
|
1303 |
+
//explicitly enable the button if they want.
|
1304 |
+
if ( !isset($this->options['show_deprecated_hide_button']) ) {
|
1305 |
+
if ( $this->is_pro_version() ) {
|
1306 |
+
$this->options['show_deprecated_hide_button'] = ameMenu::has_hidden_items($this->merged_custom_menu);
|
1307 |
+
$this->save_options();
|
1308 |
+
} else {
|
1309 |
+
$this->options['show_deprecated_hide_button'] = true;
|
1310 |
+
}
|
1311 |
+
}
|
1312 |
+
|
1313 |
$sub_section = isset($this->get['sub_section']) ? $this->get['sub_section'] : null;
|
1314 |
if ( $sub_section === 'settings' ) {
|
1315 |
$this->display_plugin_settings_ui();
|
1400 |
//Hide some menu options by default.
|
1401 |
$this->options['hide_advanced_settings'] = !empty($this->post['hide_advanced_settings']);
|
1402 |
|
1403 |
+
//Enable the now-obsolete "Hide" button.
|
1404 |
+
if ( $this->is_pro_version() ) {
|
1405 |
+
$this->options['show_deprecated_hide_button'] = !empty($this->post['show_deprecated_hide_button']);
|
1406 |
+
}
|
1407 |
+
|
1408 |
$this->save_options();
|
1409 |
wp_redirect(add_query_arg('updated', 1, $this->get_settings_page_url()));
|
1410 |
}
|
1417 |
'images_url' => plugins_url('images', $this->plugin_file),
|
1418 |
'hide_advanced_settings' => $this->options['hide_advanced_settings'],
|
1419 |
'settings_page_url' => $this->get_settings_page_url(),
|
1420 |
+
'show_deprecated_hide_button' => $this->options['show_deprecated_hide_button'],
|
1421 |
);
|
1422 |
|
1423 |
//Build a tree struct. for the default menu
|
1471 |
*/
|
1472 |
private function display_plugin_settings_ui() {
|
1473 |
//These variables are used by settings-page.php.
|
1474 |
+
/** @noinspection PhpUnusedLocalVariableInspection */
|
1475 |
$settings = $this->options;
|
1476 |
+
/** @noinspection PhpUnusedLocalVariableInspection */
|
1477 |
$settings_page_url = $this->get_settings_page_url();
|
1478 |
+
/** @noinspection PhpUnusedLocalVariableInspection */
|
1479 |
$editor_page_url = admin_url($this->settings_link);
|
1480 |
|
1481 |
require dirname(__FILE__) . '/settings-page.php';
|
1536 |
}
|
1537 |
|
1538 |
foreach($custom_menu['tree'] as $item) {
|
1539 |
+
$caps = self::array_replace_recursive($caps, $this->get_virtual_caps_for($item));
|
1540 |
}
|
1541 |
|
1542 |
$this->cached_virtual_caps = $caps;
|
1559 |
}
|
1560 |
|
1561 |
foreach($item['items'] as $sub_item) {
|
1562 |
+
$caps = self::array_replace_recursive($caps, $this->get_virtual_caps_for($sub_item));
|
1563 |
}
|
1564 |
|
1565 |
return $caps;
|
1566 |
}
|
1567 |
|
1568 |
+
private static function array_replace_recursive($array1, $array2) {
|
1569 |
+
if ( function_exists('array_replace_recursive') ) {
|
1570 |
+
return array_replace_recursive($array1, $array2);
|
1571 |
+
}
|
1572 |
+
foreach($array2 as $key => $value) {
|
1573 |
+
if ( is_array($value) && isset($array1[$key]) && is_array($array1[$key]) ) {
|
1574 |
+
$value = self::array_replace_recursive($array1[$key], $value);
|
1575 |
+
}
|
1576 |
+
$array1[$key] = $value;
|
1577 |
+
}
|
1578 |
+
return $array1;
|
1579 |
+
}
|
1580 |
+
|
1581 |
/**
|
1582 |
* Create a virtual 'super_admin' capability that only super admins have.
|
1583 |
* This function accomplishes that by by filtering 'user_has_cap' calls.
|
1649 |
update_user_meta($user->ID, 'ame_show_hints', $show_hints);
|
1650 |
}
|
1651 |
|
1652 |
+
/**
|
1653 |
+
* AJAX callback for permanently hiding the "are you sure you want to hide the Dashboard?" warning.
|
1654 |
+
*/
|
1655 |
+
public function ajax_disable_dashboard_hiding_confirmation() {
|
1656 |
+
if (!check_ajax_referer('ws_ame_disable_dashboard_hiding_confirmation', false, false) || !$this->current_user_can_edit_menu()){
|
1657 |
+
die("You don't have sufficient permissions to do that.");
|
1658 |
+
}
|
1659 |
+
$this->options['dashboard_hiding_confirmation_enabled'] = false;
|
1660 |
+
$this->save_options();
|
1661 |
+
}
|
1662 |
+
|
1663 |
/**
|
1664 |
* Enqueue a script that fixes a bug where pages moved to a different menu
|
1665 |
* would not be highlighted properly when the user visits them.
|
1713 |
* @return bool
|
1714 |
*/
|
1715 |
private function current_user_can($capability) {
|
1716 |
+
//WP core uses a special "do_not_allow" capability in a dozen or so places to explicitly deny access.
|
1717 |
+
//Even multisite super admins do not have this cap. We can return early here.
|
1718 |
+
if ( $capability === 'do_not_allow' ) {
|
1719 |
+
return false;
|
1720 |
+
}
|
1721 |
+
|
1722 |
+
if ( $this->user_cap_cache_enabled && isset($this->cached_user_caps[$capability]) ) {
|
1723 |
+
return $this->cached_user_caps[$capability];
|
1724 |
+
}
|
1725 |
+
|
1726 |
+
$user_can = apply_filters('admin_menu_editor-current_user_can', current_user_can($capability), $capability);
|
1727 |
+
$this->cached_user_caps[$capability] = $user_can;
|
1728 |
+
return $user_can;
|
1729 |
}
|
1730 |
|
1731 |
/**
|
1764 |
$base_site_url = $matches[1];
|
1765 |
}
|
1766 |
|
1767 |
+
//Calling admin_url() once and then manually appending each page's path is measurably faster than calling it
|
1768 |
+
//for each menu, but it means the "admin_url" filter is only called once. If there is a plugin that changes
|
1769 |
+
//the admin_url for some pages but not others, this could lead to bugs (no such plugins are known at this time).
|
1770 |
+
$base_admin_url = admin_url();
|
1771 |
+
$admin_url_is_filtered = has_filter('admin_url');
|
1772 |
+
|
1773 |
$current_url = $base_site_url . remove_query_arg('___ame_dummy_param___');
|
1774 |
$this->log_security_note(sprintf('Current URL: "%s"', htmlentities($current_url)));
|
1775 |
|
1782 |
if ( substr($item_url, 0, 1) == '/' ) {
|
1783 |
$item_url = $base_site_url . $item_url;
|
1784 |
} else {
|
1785 |
+
if ( $admin_url_is_filtered ) {
|
1786 |
+
$item_url = admin_url($item_url);
|
1787 |
+
} else {
|
1788 |
+
$item_url = $base_admin_url . ltrim( $item_url, '/' );
|
1789 |
+
}
|
1790 |
}
|
1791 |
}
|
1792 |
$item_url = $this->parse_url($item_url);
|
1909 |
|
1910 |
$display_notice = $this->options['display_survey_notice'] && $this->current_user_can_edit_menu();
|
1911 |
if ( isset($this->options['first_install_time']) ) {
|
1912 |
+
$minimum_usage_period = 7*24*3600;
|
1913 |
$display_notice = $display_notice && ((time() - $this->options['first_install_time']) > $minimum_usage_period);
|
1914 |
}
|
1915 |
|
1927 |
$free_survey_url = 'https://docs.google.com/spreadsheet/viewform?formkey=dERyeDk0OWhlbkxYcEY4QTNaMnlTQUE6MQ';
|
1928 |
$pro_survey_url = 'https://docs.google.com/spreadsheet/viewform?formkey=dHl4MnlHaVI3NE5JdVFDWG01SkRKTWc6MA';
|
1929 |
|
1930 |
+
if ( $this->is_pro_version() ) {
|
1931 |
$survey_url = $pro_survey_url;
|
1932 |
} else {
|
1933 |
$survey_url = $free_survey_url;
|
2126 |
return $name;
|
2127 |
}
|
2128 |
|
2129 |
+
/**
|
2130 |
+
* Tell new users how to access the plugin settings page.
|
2131 |
+
*/
|
2132 |
+
public function display_plugin_menu_notice() {
|
2133 |
+
//Display the notice only if it's enabled, the current user can access our settings page,
|
2134 |
+
//and there is no custom menu (if a custom menu already exists, chances are the user knows
|
2135 |
+
//where the settings page is).
|
2136 |
+
$showNotice = $this->options['show_plugin_menu_notice'] && ($this->load_custom_menu() === null);
|
2137 |
+
$showNotice = $showNotice && $this->current_user_can_edit_menu();
|
2138 |
+
if ( !$showNotice ) {
|
2139 |
+
return;
|
2140 |
+
}
|
2141 |
+
|
2142 |
+
//Disable the notice when the user hides it or visits any of our admin pages.
|
2143 |
+
$hideNoticeParameter = 'ame-plugin-menu-notice';
|
2144 |
+
if ( !empty($_GET[$hideNoticeParameter]) || $this->is_editor_page() || $this->is_settings_page() ) {
|
2145 |
+
$this->options['show_plugin_menu_notice'] = false;
|
2146 |
+
$this->save_options();
|
2147 |
+
return;
|
2148 |
+
}
|
2149 |
+
|
2150 |
+
$dismissUrl = add_query_arg($hideNoticeParameter, 'hide');
|
2151 |
+
$dismissUrl = remove_query_arg(array('message', 'activate'), $dismissUrl);
|
2152 |
+
|
2153 |
+
if ( is_multisite() && is_network_admin() ) {
|
2154 |
+
$message = 'Tip: Go to any subsite to access Admin Menu Editor. It will not show up in the network admin.';
|
2155 |
+
} else {
|
2156 |
+
$message = 'Tip: Go to <a href="%1$s">Settings -> %2$s</a> to start customizing the admin menu.';
|
2157 |
+
}
|
2158 |
+
printf(
|
2159 |
+
'<div class="updated" id="ame-plugin-menu-notice">
|
2160 |
+
<p>' . $message . '</p>
|
2161 |
+
<p><a href="%3$s" id="ame-hide-plugin-menu-notice">Hide this message</a></p>
|
2162 |
+
</div>',
|
2163 |
+
esc_attr(admin_url($this->settings_link)),
|
2164 |
+
apply_filters('admin_menu_editor-self_menu_title', 'Menu Editor'),
|
2165 |
+
esc_attr($dismissUrl)
|
2166 |
+
);
|
2167 |
+
|
2168 |
+
}
|
2169 |
+
|
2170 |
+
private function is_pro_version() {
|
2171 |
+
return apply_filters('admin_menu_editor_is_pro', false);
|
2172 |
+
}
|
2173 |
+
|
2174 |
} //class
|
includes/menu-item.php
CHANGED
@@ -8,6 +8,22 @@
|
|
8 |
* currently registered hooks and the presence of specific files in admin/plugin folders.
|
9 |
*/
|
10 |
abstract class ameMenuItem {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
/**
|
12 |
* Convert a WP menu structure to an associative array.
|
13 |
*
|
@@ -193,6 +209,10 @@ abstract class ameMenuItem {
|
|
193 |
}
|
194 |
}
|
195 |
|
|
|
|
|
|
|
|
|
196 |
return $parent_file . '>' . $item_file;
|
197 |
}
|
198 |
|
@@ -218,10 +238,7 @@ abstract class ameMenuItem {
|
|
218 |
/**
|
219 |
* Apply custom menu filters to an item of the custom menu.
|
220 |
*
|
221 |
-
* Calls
|
222 |
-
* 'custom_admin_$item_type' with the entire $item passed as the argument.
|
223 |
-
* 'custom_admin_$item_type-$field' with the value of a single field of $item as the argument.
|
224 |
-
*
|
225 |
* Used when converting the current custom menu to a WP-format menu.
|
226 |
*
|
227 |
* @param array $item Associative array representing one menu item (either top-level or submenu).
|
@@ -230,12 +247,7 @@ abstract class ameMenuItem {
|
|
230 |
* @return array Filtered menu item.
|
231 |
*/
|
232 |
public static function apply_filters($item, $item_type, $extra = null){
|
233 |
-
|
234 |
-
foreach($item as $field => $value){
|
235 |
-
$item[$field] = apply_filters("custom_admin_{$item_type}-$field", $value, $extra);
|
236 |
-
}
|
237 |
-
|
238 |
-
return $item;
|
239 |
}
|
240 |
|
241 |
/**
|
@@ -376,11 +388,28 @@ abstract class ameMenuItem {
|
|
376 |
}
|
377 |
$pageFile = self::remove_query_from($page_url);
|
378 |
|
379 |
-
|
|
|
|
|
|
|
|
|
|
|
380 |
$adminFileExists = is_file(ABSPATH . '/wp-admin/' . $pageFile);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
381 |
$pluginFileExists = ($page_url != 'index.php') && is_file(WP_PLUGIN_DIR . '/' . $pageFile);
|
|
|
|
|
|
|
382 |
|
383 |
-
return
|
384 |
}
|
385 |
|
386 |
/**
|
8 |
* currently registered hooks and the presence of specific files in admin/plugin folders.
|
9 |
*/
|
10 |
abstract class ameMenuItem {
|
11 |
+
/**
|
12 |
+
* @var array A partial list of files in /wp-admin/. Correct as of WP 3.8-RC1, 2013.12.04.
|
13 |
+
* When trying to determine if a menu links to one of the default WP admin pages, it's faster
|
14 |
+
* to check this list than to hit the disk.
|
15 |
+
*/
|
16 |
+
private static $known_wp_admin_files = array(
|
17 |
+
'customize.php' => true, 'edit-comments.php' => true, 'edit-tags.php' => true, 'edit.php' => true,
|
18 |
+
'export.php' => true, 'import.php' => true, 'index.php' => true, 'link-add.php' => true,
|
19 |
+
'link-manager.php' => true, 'media-new.php' => true, 'nav-menus.php' => true, 'options-discussion.php' => true,
|
20 |
+
'options-general.php' => true, 'options-media.php' => true, 'options-permalink.php' => true,
|
21 |
+
'options-reading.php' => true, 'options-writing.php' => true, 'plugin-editor.php' => true,
|
22 |
+
'plugin-install.php' => true, 'plugins.php' => true, 'post-new.php' => true, 'profile.php' => true,
|
23 |
+
'theme-editor.php' => true, 'themes.php' => true, 'tools.php' => true, 'update-core.php' => true,
|
24 |
+
'upload.php' => true, 'user-new.php' => true, 'users.php' => true, 'widgets.php' => true,
|
25 |
+
);
|
26 |
+
|
27 |
/**
|
28 |
* Convert a WP menu structure to an associative array.
|
29 |
*
|
209 |
}
|
210 |
}
|
211 |
|
212 |
+
if ($parent_file === 'profile.php') {
|
213 |
+
$parent_file = 'users.php';
|
214 |
+
}
|
215 |
+
|
216 |
return $parent_file . '>' . $item_file;
|
217 |
}
|
218 |
|
238 |
/**
|
239 |
* Apply custom menu filters to an item of the custom menu.
|
240 |
*
|
241 |
+
* Calls a 'custom_admin_$item_type' filter with the entire $item passed as the argument.
|
|
|
|
|
|
|
242 |
* Used when converting the current custom menu to a WP-format menu.
|
243 |
*
|
244 |
* @param array $item Associative array representing one menu item (either top-level or submenu).
|
247 |
* @return array Filtered menu item.
|
248 |
*/
|
249 |
public static function apply_filters($item, $item_type, $extra = null){
|
250 |
+
return apply_filters("custom_admin_{$item_type}", $item, $extra);
|
|
|
|
|
|
|
|
|
|
|
251 |
}
|
252 |
|
253 |
/**
|
388 |
}
|
389 |
$pageFile = self::remove_query_from($page_url);
|
390 |
|
391 |
+
//Check our hard-coded list of admin pages first. It's measurably faster than
|
392 |
+
//hitting the disk with is_file().
|
393 |
+
if ( isset(self::$known_wp_admin_files[$pageFile]) ) {
|
394 |
+
return false;
|
395 |
+
}
|
396 |
+
//Now actually check the filesystem.
|
397 |
$adminFileExists = is_file(ABSPATH . '/wp-admin/' . $pageFile);
|
398 |
+
if ( $adminFileExists ) {
|
399 |
+
return false;
|
400 |
+
}
|
401 |
+
|
402 |
+
$hasHook = (get_plugin_page_hook($page_url, $parent_page_url) !== null);
|
403 |
+
if ( $hasHook ) {
|
404 |
+
return true;
|
405 |
+
}
|
406 |
+
|
407 |
$pluginFileExists = ($page_url != 'index.php') && is_file(WP_PLUGIN_DIR . '/' . $pageFile);
|
408 |
+
if ( $pluginFileExists ) {
|
409 |
+
return true;
|
410 |
+
}
|
411 |
|
412 |
+
return false;
|
413 |
}
|
414 |
|
415 |
/**
|
includes/menu.php
CHANGED
@@ -1,42 +1,50 @@
|
|
1 |
<?php
|
2 |
abstract class ameMenu {
|
3 |
const format_name = 'Admin Menu Editor menu';
|
4 |
-
const format_version = '5.
|
5 |
|
6 |
/**
|
7 |
* Load an admin menu from a JSON string.
|
8 |
*
|
9 |
* @static
|
10 |
-
* @throws InvalidMenuException when the supplied input is not a valid menu.
|
11 |
*
|
12 |
* @param string $json A JSON-encoded menu structure.
|
13 |
* @param bool $assume_correct_format Skip the format header check and assume everything is fine. Defaults to false.
|
|
|
|
|
14 |
* @return array
|
15 |
*/
|
16 |
-
public static function load_json($json, $assume_correct_format = false) {
|
17 |
$arr = json_decode($json, true);
|
18 |
if ( !is_array($arr) ) {
|
19 |
throw new InvalidMenuException('The input is not a valid JSON-encoded admin menu.');
|
20 |
}
|
21 |
-
return self::load_array($arr, $assume_correct_format);
|
22 |
}
|
23 |
|
24 |
/**
|
25 |
* Load an admin menu structure from an associative array.
|
26 |
*
|
27 |
* @static
|
28 |
-
* @throws InvalidMenuException when the supplied input is not a valid menu.
|
29 |
*
|
30 |
* @param array $arr
|
31 |
* @param bool $assume_correct_format
|
|
|
|
|
32 |
* @return array
|
33 |
*/
|
34 |
-
public static function load_array($arr, $assume_correct_format = false){
|
|
|
35 |
if ( !$assume_correct_format ) {
|
36 |
if ( isset($arr['format']) && ($arr['format']['name'] == self::format_name) ) {
|
37 |
-
|
|
|
38 |
throw new InvalidMenuException("Can't load a menu created by a newer version of the plugin.");
|
39 |
}
|
|
|
|
|
|
|
|
|
40 |
} else {
|
41 |
return self::load_menu_40($arr);
|
42 |
}
|
@@ -49,8 +57,13 @@ abstract class ameMenu {
|
|
49 |
$menu = array('tree' => array());
|
50 |
$menu = self::add_format_header($menu);
|
51 |
|
52 |
-
|
53 |
-
$menu['tree']
|
|
|
|
|
|
|
|
|
|
|
54 |
}
|
55 |
|
56 |
return $menu;
|
@@ -135,7 +148,7 @@ abstract class ameMenu {
|
|
135 |
$parent = $tree_item['defaults']['file'];
|
136 |
if ( isset($submenu[$parent]) ){
|
137 |
foreach($submenu[$parent] as $position => $subitem){
|
138 |
-
$tree_item['items'][
|
139 |
ameMenuItem::blank_menu(),
|
140 |
array('defaults' => ameMenuItem::fromWpItem($subitem, $position, $parent))
|
141 |
);
|
@@ -149,6 +162,33 @@ abstract class ameMenu {
|
|
149 |
|
150 |
return $tree;
|
151 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
}
|
153 |
|
154 |
|
1 |
<?php
|
2 |
abstract class ameMenu {
|
3 |
const format_name = 'Admin Menu Editor menu';
|
4 |
+
const format_version = '5.1';
|
5 |
|
6 |
/**
|
7 |
* Load an admin menu from a JSON string.
|
8 |
*
|
9 |
* @static
|
|
|
10 |
*
|
11 |
* @param string $json A JSON-encoded menu structure.
|
12 |
* @param bool $assume_correct_format Skip the format header check and assume everything is fine. Defaults to false.
|
13 |
+
* @param bool $always_normalize Always normalize the menu structure, even if format.is_normalized is true.
|
14 |
+
* @throws InvalidMenuException
|
15 |
* @return array
|
16 |
*/
|
17 |
+
public static function load_json($json, $assume_correct_format = false, $always_normalize = false) {
|
18 |
$arr = json_decode($json, true);
|
19 |
if ( !is_array($arr) ) {
|
20 |
throw new InvalidMenuException('The input is not a valid JSON-encoded admin menu.');
|
21 |
}
|
22 |
+
return self::load_array($arr, $assume_correct_format, $always_normalize);
|
23 |
}
|
24 |
|
25 |
/**
|
26 |
* Load an admin menu structure from an associative array.
|
27 |
*
|
28 |
* @static
|
|
|
29 |
*
|
30 |
* @param array $arr
|
31 |
* @param bool $assume_correct_format
|
32 |
+
* @param bool $always_normalize
|
33 |
+
* @throws InvalidMenuException
|
34 |
* @return array
|
35 |
*/
|
36 |
+
public static function load_array($arr, $assume_correct_format = false, $always_normalize = false){
|
37 |
+
$is_normalized = false;
|
38 |
if ( !$assume_correct_format ) {
|
39 |
if ( isset($arr['format']) && ($arr['format']['name'] == self::format_name) ) {
|
40 |
+
$compared = version_compare($arr['format']['version'], self::format_version);
|
41 |
+
if ( $compared > 0 ) {
|
42 |
throw new InvalidMenuException("Can't load a menu created by a newer version of the plugin.");
|
43 |
}
|
44 |
+
//We can skip normalization if the version number matches exactly and the menu is already normalized.
|
45 |
+
if ( ($compared === 0) && isset($arr['format']['is_normalized']) ) {
|
46 |
+
$is_normalized = $arr['format']['is_normalized'];
|
47 |
+
}
|
48 |
} else {
|
49 |
return self::load_menu_40($arr);
|
50 |
}
|
57 |
$menu = array('tree' => array());
|
58 |
$menu = self::add_format_header($menu);
|
59 |
|
60 |
+
if ( $is_normalized && !$always_normalize ) {
|
61 |
+
$menu['tree'] = $arr['tree'];
|
62 |
+
} else {
|
63 |
+
foreach($arr['tree'] as $file => $item) {
|
64 |
+
$menu['tree'][$file] = ameMenuItem::normalize($item);
|
65 |
+
}
|
66 |
+
$menu['format']['is_normalized'] = true;
|
67 |
}
|
68 |
|
69 |
return $menu;
|
148 |
$parent = $tree_item['defaults']['file'];
|
149 |
if ( isset($submenu[$parent]) ){
|
150 |
foreach($submenu[$parent] as $position => $subitem){
|
151 |
+
$tree_item['items'][] = array_merge(
|
152 |
ameMenuItem::blank_menu(),
|
153 |
array('defaults' => ameMenuItem::fromWpItem($subitem, $position, $parent))
|
154 |
);
|
162 |
|
163 |
return $tree;
|
164 |
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Check if a menu contains any items with the "hidden" flag set to true.
|
168 |
+
*
|
169 |
+
* @param array $menu
|
170 |
+
* @return bool
|
171 |
+
*/
|
172 |
+
public static function has_hidden_items($menu) {
|
173 |
+
if ( !is_array($menu) || empty($menu) || empty($menu['tree']) ) {
|
174 |
+
return false;
|
175 |
+
}
|
176 |
+
|
177 |
+
foreach($menu['tree'] as $item) {
|
178 |
+
if ( ameMenuItem::get($item, 'hidden') ) {
|
179 |
+
return true;
|
180 |
+
}
|
181 |
+
if ( !empty($item['items']) ) {
|
182 |
+
foreach($item['items'] as $child) {
|
183 |
+
if ( ameMenuItem::get($child, 'hidden') ) {
|
184 |
+
return true;
|
185 |
+
}
|
186 |
+
}
|
187 |
+
}
|
188 |
+
}
|
189 |
+
|
190 |
+
return false;
|
191 |
+
}
|
192 |
}
|
193 |
|
194 |
|
includes/settings-page.php
CHANGED
@@ -125,11 +125,26 @@ $isProVersion = apply_filters('admin_menu_editor_is_pro', false);
|
|
125 |
<tr>
|
126 |
<th scope="row">Interface</th>
|
127 |
<td>
|
128 |
-
<
|
129 |
-
<
|
130 |
-
|
131 |
-
|
132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
</td>
|
134 |
</tr>
|
135 |
|
@@ -138,7 +153,7 @@ $isProVersion = apply_filters('admin_menu_editor_is_pro', false);
|
|
138 |
<td>
|
139 |
<label>
|
140 |
<input type="checkbox" name="security_logging_enabled"
|
141 |
-
<?php checked($
|
142 |
Show menu access checks performed by the plugin on every admin page
|
143 |
</label>
|
144 |
<br><span class="description">
|
125 |
<tr>
|
126 |
<th scope="row">Interface</th>
|
127 |
<td>
|
128 |
+
<p>
|
129 |
+
<label>
|
130 |
+
<input type="checkbox" name="hide_advanced_settings"
|
131 |
+
<?php checked($settings['hide_advanced_settings']); ?>>
|
132 |
+
Hide advanced menu options by default
|
133 |
+
</label>
|
134 |
+
</p>
|
135 |
+
|
136 |
+
<?php if ($isProVersion): ?>
|
137 |
+
<p>
|
138 |
+
<label>
|
139 |
+
<input type="checkbox" name="show_deprecated_hide_button"
|
140 |
+
<?php checked($settings['show_deprecated_hide_button']); ?>>
|
141 |
+
Enable the "Show/Hide" toolbar button (not recommended)
|
142 |
+
</label>
|
143 |
+
<br><span class="description">
|
144 |
+
This feature is deprecated and is only kept for backwards compatibility purposes.
|
145 |
+
</span>
|
146 |
+
</p>
|
147 |
+
<?php endif; ?>
|
148 |
</td>
|
149 |
</tr>
|
150 |
|
153 |
<td>
|
154 |
<label>
|
155 |
<input type="checkbox" name="security_logging_enabled"
|
156 |
+
<?php checked($settings['security_logging_enabled']); ?>>
|
157 |
Show menu access checks performed by the plugin on every admin page
|
158 |
</label>
|
159 |
<br><span class="description">
|
js/menu-editor.js
CHANGED
@@ -1358,7 +1358,18 @@ $(document).ready(function(){
|
|
1358 |
var checked = $(this).is(':checked');
|
1359 |
var containerNode = $(this).closest('.ws_container');
|
1360 |
|
1361 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1362 |
});
|
1363 |
|
1364 |
/**
|
@@ -1388,6 +1399,58 @@ $(document).ready(function(){
|
|
1388 |
updateParentAccessUi(containerNode);
|
1389 |
}
|
1390 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1391 |
/*************************************************************************
|
1392 |
Access editor dialog
|
1393 |
*************************************************************************/
|
@@ -1460,7 +1523,7 @@ $(document).ready(function(){
|
|
1460 |
accessEditorState.menuItem = menuItem;
|
1461 |
|
1462 |
//Show/hide the hint about sub menus overriding menu permissions.
|
1463 |
-
var itemHasSubmenus = containerNode.data('submenu_id') &&
|
1464 |
$('#' + containerNode.data('submenu_id')).find('.ws_item').length > 0;
|
1465 |
var hintIsEnabled = !wsEditorData.showHints.hasOwnProperty('ws_hint_menu_permissions') || wsEditorData.showHints['ws_hint_menu_permissions'];
|
1466 |
$('#ws_hint_menu_permissions').toggle(hintIsEnabled && itemHasSubmenus);
|
@@ -1479,7 +1542,8 @@ $(document).ready(function(){
|
|
1479 |
|
1480 |
$('#ws_save_access_settings').click(function() {
|
1481 |
//Save the new settings.
|
1482 |
-
|
|
|
1483 |
|
1484 |
var grantAccess = accessEditorState.menuItem.grant_access;
|
1485 |
if (!$.isPlainObject(grantAccess)) {
|
1358 |
var checked = $(this).is(':checked');
|
1359 |
var containerNode = $(this).closest('.ws_container');
|
1360 |
|
1361 |
+
var menu = containerNode.data('menu_item');
|
1362 |
+
//Ask for confirmation if the user tries to hide Dashboard -> Home.
|
1363 |
+
if ( !checked && ((menu.template_id == 'index.php>index.php') || (menu.template_id == '>index.php')) ) {
|
1364 |
+
updateItemEditor(containerNode); //Resets the checkbox back to the old value.
|
1365 |
+
confirmDashboardHiding(function(ok) {
|
1366 |
+
if (ok) {
|
1367 |
+
setActorAccessForTreeAndUpdateUi(containerNode, selectedActor, checked);
|
1368 |
+
}
|
1369 |
+
});
|
1370 |
+
} else {
|
1371 |
+
setActorAccessForTreeAndUpdateUi(containerNode, selectedActor, checked);
|
1372 |
+
}
|
1373 |
});
|
1374 |
|
1375 |
/**
|
1399 |
updateParentAccessUi(containerNode);
|
1400 |
}
|
1401 |
|
1402 |
+
/**
|
1403 |
+
* Confirm with the user that they want to hide "Dashboard -> Home".
|
1404 |
+
*
|
1405 |
+
* This particular menu is important because hiding it can cause an "insufficient permissions" error
|
1406 |
+
* to be displayed right when someone logs in, making it look like login failed.
|
1407 |
+
*/
|
1408 |
+
var permissionConfirmationDialog = $('#ws-ame-dashboard-hide-confirmation').dialog({
|
1409 |
+
autoOpen: false,
|
1410 |
+
modal: true,
|
1411 |
+
closeText: ' ',
|
1412 |
+
width: 380,
|
1413 |
+
title: 'Warning'
|
1414 |
+
});
|
1415 |
+
var currentConfirmationCallback = function(ok) {};
|
1416 |
+
|
1417 |
+
/**
|
1418 |
+
* Confirm hiding "Dashboard -> Home".
|
1419 |
+
*
|
1420 |
+
* @param callback Called when the user selects an option. True = confirmed.
|
1421 |
+
*/
|
1422 |
+
function confirmDashboardHiding(callback) {
|
1423 |
+
//The user can disable the confirmation dialog.
|
1424 |
+
if (!wsEditorData.dashboardHidingConfirmationEnabled) {
|
1425 |
+
callback(true);
|
1426 |
+
return;
|
1427 |
+
}
|
1428 |
+
|
1429 |
+
currentConfirmationCallback = callback;
|
1430 |
+
permissionConfirmationDialog.dialog('open');
|
1431 |
+
}
|
1432 |
+
|
1433 |
+
$('#ws_confirm_menu_hiding, #ws_cancel_menu_hiding').click(function() {
|
1434 |
+
var confirmed = $(this).is('#ws_confirm_menu_hiding');
|
1435 |
+
var dontShowAgain = permissionConfirmationDialog.find('.ws_dont_show_again input[type="checkbox"]').is(':checked');
|
1436 |
+
|
1437 |
+
currentConfirmationCallback(confirmed);
|
1438 |
+
permissionConfirmationDialog.dialog('close');
|
1439 |
+
|
1440 |
+
if (dontShowAgain) {
|
1441 |
+
wsEditorData.dashboardHidingConfirmationEnabled = false;
|
1442 |
+
//Run an AJAX request to disable the dialog for this user.
|
1443 |
+
$.post(
|
1444 |
+
wsEditorData.adminAjaxUrl,
|
1445 |
+
{
|
1446 |
+
'action' : 'ws_ame_disable_dashboard_hiding_confirmation',
|
1447 |
+
'_ajax_nonce' : wsEditorData.disableDashboardConfirmationNonce
|
1448 |
+
}
|
1449 |
+
);
|
1450 |
+
}
|
1451 |
+
});
|
1452 |
+
|
1453 |
+
|
1454 |
/*************************************************************************
|
1455 |
Access editor dialog
|
1456 |
*************************************************************************/
|
1523 |
accessEditorState.menuItem = menuItem;
|
1524 |
|
1525 |
//Show/hide the hint about sub menus overriding menu permissions.
|
1526 |
+
var itemHasSubmenus = !!(containerNode.data('submenu_id')) &&
|
1527 |
$('#' + containerNode.data('submenu_id')).find('.ws_item').length > 0;
|
1528 |
var hintIsEnabled = !wsEditorData.showHints.hasOwnProperty('ws_hint_menu_permissions') || wsEditorData.showHints['ws_hint_menu_permissions'];
|
1529 |
$('#ws_hint_menu_permissions').toggle(hintIsEnabled && itemHasSubmenus);
|
1542 |
|
1543 |
$('#ws_save_access_settings').click(function() {
|
1544 |
//Save the new settings.
|
1545 |
+
var extraCapability = jsTrim($('#ws_extra_capability').val());
|
1546 |
+
accessEditorState.menuItem.extra_capability = (extraCapability === '') ? null : extraCapability;
|
1547 |
|
1548 |
var grantAccess = accessEditorState.menuItem.grant_access;
|
1549 |
if (!$.isPlainObject(grantAccess)) {
|
menu-editor.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
Plugin Name: Admin Menu Editor
|
4 |
Plugin URI: http://w-shadow.com/blog/2008/12/20/admin-menu-editor-for-wordpress/
|
5 |
Description: Lets you directly edit the WordPress admin menu. You can re-order, hide or rename existing menus, add custom menus and more.
|
6 |
-
Version: 1.3
|
7 |
Author: Janis Elsts
|
8 |
Author URI: http://w-shadow.com/blog/
|
9 |
*/
|
3 |
Plugin Name: Admin Menu Editor
|
4 |
Plugin URI: http://w-shadow.com/blog/2008/12/20/admin-menu-editor-for-wordpress/
|
5 |
Description: Lets you directly edit the WordPress admin menu. You can re-order, hide or rename existing menus, add custom menus and more.
|
6 |
+
Version: 1.3.1
|
7 |
Author: Janis Elsts
|
8 |
Author URI: http://w-shadow.com/blog/
|
9 |
*/
|
readme.txt
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
Contributors: whiteshadow
|
3 |
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A6P9S6CE3SRSW
|
4 |
Tags: admin, dashboard, menu, security, wpmu
|
5 |
-
Requires at least: 3.
|
6 |
-
Tested up to: 3.
|
7 |
-
Stable tag: 1.3
|
8 |
|
9 |
Lets you edit the WordPress admin menu. You can re-order, hide or rename menus, add custom menus and more.
|
10 |
|
@@ -63,6 +63,17 @@ Plugins installed in the `mu-plugins` directory are treated as "always on", so y
|
|
63 |
|
64 |
== Changelog ==
|
65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
= 1.3 =
|
67 |
* Added a new settings page that lets you choose whether admin menu settings are per-site or network-wide, as well as specify who can access the plugin. To access this page, go to "Settings -> Menu Editor Pro" and click the small "Settings" link next to the page title.
|
68 |
* Added a way to show/hide advanced menu options through the settings page in addition to the "Screen Options" panel.
|
2 |
Contributors: whiteshadow
|
3 |
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A6P9S6CE3SRSW
|
4 |
Tags: admin, dashboard, menu, security, wpmu
|
5 |
+
Requires at least: 3.3
|
6 |
+
Tested up to: 3.8
|
7 |
+
Stable tag: 1.3.1
|
8 |
|
9 |
Lets you edit the WordPress admin menu. You can re-order, hide or rename menus, add custom menus and more.
|
10 |
|
63 |
|
64 |
== Changelog ==
|
65 |
|
66 |
+
= 1.3.1 =
|
67 |
+
* Tested with WordPress 3.8.
|
68 |
+
* Fixed several minor UI/layout issues related to the new 3.8 admin style.
|
69 |
+
* Fixed a bug where moving an item to a plugin menu and then deactivating that plugin would cause the moved item to disappear.
|
70 |
+
* Fixed deleted submenus not being restored if their original parent menu is no longer available.
|
71 |
+
* Fixed a rare glitch where submenu separators added by certain other plugins would sometimes disappear.
|
72 |
+
* Fixed a conflict with Shopp 1.2.9.
|
73 |
+
* Made the plugin treat "users.php" and "profile.php" as the same parent menu. This fixes situations where it would be impossible to hide a "Users" submenu item from roles that don't have access to the "Users" menu and instead get a "Profile" menu.
|
74 |
+
* Added extra logging for situations where a menu item is hidden because a higher-priority item with the same URL is also hidden.
|
75 |
+
* Minor performance improvements.
|
76 |
+
|
77 |
= 1.3 =
|
78 |
* Added a new settings page that lets you choose whether admin menu settings are per-site or network-wide, as well as specify who can access the plugin. To access this page, go to "Settings -> Menu Editor Pro" and click the small "Settings" link next to the page title.
|
79 |
* Added a way to show/hide advanced menu options through the settings page in addition to the "Screen Options" panel.
|