Version Description
- Fixed the PHP warning "count(): Parameter must be an array or an object that implements Countable in menu-editor-core.php".
- Fixed a bug that could cause some network admin menus to be highlighted in green as if they were new.
- Fixed a conflict with WP Courseware 4.1.2 where activating AME would cause many extra menu items to show up unexpectedly.
- Fixed a conflict with Ultra WordPress Admin 7.4 that made it impossible to hide plugins.
- Replaced the "this is a new item" icon with a different one.
- Tested with WP 4.9.4.
Download this release
Release Info
Developer | whiteshadow |
Plugin | Admin Menu Editor |
Version | 1.8.2 |
Comparing to | |
See all releases |
Code changes from version 1.8.1 to 1.8.2
- css/menu-editor.css +28 -2
- css/menu-editor.scss +41 -2
- images/new-menu-badge.png +0 -0
- includes/editor-page.php +60 -3
- includes/menu-editor-core.php +154 -14
- includes/module.php +33 -0
- includes/role-utils.php +7 -1
- js/actor-manager.js +41 -28
- js/actor-manager.ts +47 -26
- js/menu-editor.js +46 -12
- menu-editor.php +1 -1
- modules/actor-selector/actor-selector.js +9 -1
- modules/actor-selector/actor-selector.ts +7 -0
- modules/highlight-new-menus/wsNewMenuHighlighter.php +8 -2
- modules/plugin-visibility/plugin-visibility.js +2 -2
- modules/plugin-visibility/plugin-visibility.php +1 -1
- readme.txt +10 -2
css/menu-editor.css
CHANGED
@@ -422,7 +422,8 @@ to accommodate the drop-down button.
|
|
422 |
|
423 |
/* unused items - those that are in the default menu but not in the custom one */
|
424 |
.ws_unused_flag {
|
425 |
-
background-image: url("../images/
|
|
|
426 |
|
427 |
/* hidden items */
|
428 |
.ws_hidden_flag {
|
@@ -446,6 +447,15 @@ to accommodate the drop-down button.
|
|
446 |
.ws_hidden_from_others_flag {
|
447 |
background-image: url("../images/font-awesome/eye-slash.png"); }
|
448 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
449 |
/* These classes could be used to apply different styles to items depending on their flags */
|
450 |
/************************************
|
451 |
Toolbars
|
@@ -802,7 +812,8 @@ a#ws-ame-delete-color-preset:hover {
|
|
802 |
font-size: 12px;
|
803 |
border: 1px solid #ddd;
|
804 |
background: white;
|
805 |
-
cursor: pointer;
|
|
|
806 |
|
807 |
.ws_color_display_item {
|
808 |
display: inline-block;
|
@@ -1320,6 +1331,8 @@ a#ws-ame-delete-color-preset:hover {
|
|
1320 |
|
1321 |
.ws_ame_doc_box .hndle, .ws_ame_custom_postbox .hndle {
|
1322 |
cursor: default !important; }
|
|
|
|
|
1323 |
.ws_ame_doc_box ul, .ws_ame_custom_postbox ul {
|
1324 |
list-style: disc outside;
|
1325 |
margin-left: 1em; }
|
@@ -1344,6 +1357,19 @@ a#ws-ame-delete-color-preset:hover {
|
|
1344 |
margin-left: 2px;
|
1345 |
margin-right: 2px; }
|
1346 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1347 |
/************************************
|
1348 |
Copy Permissions dialog
|
1349 |
*************************************/
|
422 |
|
423 |
/* unused items - those that are in the default menu but not in the custom one */
|
424 |
.ws_unused_flag {
|
425 |
+
background-image: url("../images/new-menu-badge.png");
|
426 |
+
width: 31px; }
|
427 |
|
428 |
/* hidden items */
|
429 |
.ws_hidden_flag {
|
447 |
.ws_hidden_from_others_flag {
|
448 |
background-image: url("../images/font-awesome/eye-slash.png"); }
|
449 |
|
450 |
+
/* Item visibility can't be determined because it depends on a meta capability. */
|
451 |
+
.ws_uncertain_meta_cap_flag::before {
|
452 |
+
font: 16px/1 'dashicons';
|
453 |
+
content: "\f348";
|
454 |
+
color: black;
|
455 |
+
filter: alpha(opacity=25);
|
456 |
+
/*IE 5-7*/
|
457 |
+
opacity: 0.25; }
|
458 |
+
|
459 |
/* These classes could be used to apply different styles to items depending on their flags */
|
460 |
/************************************
|
461 |
Toolbars
|
812 |
font-size: 12px;
|
813 |
border: 1px solid #ddd;
|
814 |
background: white;
|
815 |
+
cursor: pointer;
|
816 |
+
line-height: 20px; }
|
817 |
|
818 |
.ws_color_display_item {
|
819 |
display: inline-block;
|
1331 |
|
1332 |
.ws_ame_doc_box .hndle, .ws_ame_custom_postbox .hndle {
|
1333 |
cursor: default !important; }
|
1334 |
+
.ws_ame_doc_box .inside, .ws_ame_custom_postbox .inside {
|
1335 |
+
margin-bottom: 0; }
|
1336 |
.ws_ame_doc_box ul, .ws_ame_custom_postbox ul {
|
1337 |
list-style: disc outside;
|
1338 |
margin-left: 1em; }
|
1357 |
margin-left: 2px;
|
1358 |
margin-right: 2px; }
|
1359 |
|
1360 |
+
.ws_ame_custom_postbox .ame-tutorial-list {
|
1361 |
+
margin: 0; }
|
1362 |
+
.ws_ame_custom_postbox .ame-tutorial-list a {
|
1363 |
+
text-decoration: none;
|
1364 |
+
display: block;
|
1365 |
+
padding: 4px; }
|
1366 |
+
.ws_ame_custom_postbox .ame-tutorial-list ul {
|
1367 |
+
margin-left: 1em; }
|
1368 |
+
.ws_ame_custom_postbox .ame-tutorial-list li {
|
1369 |
+
display: block;
|
1370 |
+
margin: 0;
|
1371 |
+
list-style: none; }
|
1372 |
+
|
1373 |
/************************************
|
1374 |
Copy Permissions dialog
|
1375 |
*************************************/
|
css/menu-editor.scss
CHANGED
@@ -582,7 +582,8 @@ to accommodate the drop-down button.
|
|
582 |
|
583 |
/* unused items - those that are in the default menu but not in the custom one */
|
584 |
.ws_unused_flag {
|
585 |
-
background-image: url('../images/
|
|
|
586 |
}
|
587 |
|
588 |
/* hidden items */
|
@@ -608,6 +609,16 @@ to accommodate the drop-down button.
|
|
608 |
background-image: url('../images/font-awesome/eye-slash.png');
|
609 |
}
|
610 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
611 |
/* These classes could be used to apply different styles to items depending on their flags */
|
612 |
.ws_custom { }
|
613 |
.ws_hidden { }
|
@@ -1078,8 +1089,10 @@ a#ws-ame-delete-color-preset:hover {
|
|
1078 |
/* Color scheme display in the editor widget. */
|
1079 |
|
1080 |
.ws_color_scheme_display {
|
|
|
|
|
1081 |
display: inline-block;
|
1082 |
-
height:
|
1083 |
width: 186px;
|
1084 |
|
1085 |
margin-right: 5px;
|
@@ -1089,6 +1102,8 @@ a#ws-ame-delete-color-preset:hover {
|
|
1089 |
border: 1px solid #ddd;
|
1090 |
background: white;
|
1091 |
cursor: pointer;
|
|
|
|
|
1092 |
}
|
1093 |
|
1094 |
.ws_color_display_item {
|
@@ -1860,6 +1875,10 @@ $userSelectionPanelPadding: 10px;
|
|
1860 |
cursor: default !important;
|
1861 |
}
|
1862 |
|
|
|
|
|
|
|
|
|
1863 |
ul {
|
1864 |
list-style: disc outside;
|
1865 |
margin-left: 1em;
|
@@ -1896,6 +1915,26 @@ $userSelectionPanelPadding: 10px;
|
|
1896 |
margin-right: 2px;
|
1897 |
}
|
1898 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1899 |
/************************************
|
1900 |
Copy Permissions dialog
|
1901 |
*************************************/
|
582 |
|
583 |
/* unused items - those that are in the default menu but not in the custom one */
|
584 |
.ws_unused_flag {
|
585 |
+
background-image: url('../images/new-menu-badge.png');
|
586 |
+
width: 31px;
|
587 |
}
|
588 |
|
589 |
/* hidden items */
|
609 |
background-image: url('../images/font-awesome/eye-slash.png');
|
610 |
}
|
611 |
|
612 |
+
/* Item visibility can't be determined because it depends on a meta capability. */
|
613 |
+
.ws_uncertain_meta_cap_flag::before {
|
614 |
+
font: 16px/1 'dashicons';
|
615 |
+
content: "\f348";
|
616 |
+
color: black;
|
617 |
+
|
618 |
+
filter: alpha(opacity=25); /*IE 5-7*/
|
619 |
+
opacity: 0.25;
|
620 |
+
}
|
621 |
+
|
622 |
/* These classes could be used to apply different styles to items depending on their flags */
|
623 |
.ws_custom { }
|
624 |
.ws_hidden { }
|
1089 |
/* Color scheme display in the editor widget. */
|
1090 |
|
1091 |
.ws_color_scheme_display {
|
1092 |
+
$colorFieldHeight: 20px;
|
1093 |
+
|
1094 |
display: inline-block;
|
1095 |
+
height: $colorFieldHeight;
|
1096 |
width: 186px;
|
1097 |
|
1098 |
margin-right: 5px;
|
1102 |
border: 1px solid #ddd;
|
1103 |
background: white;
|
1104 |
cursor: pointer;
|
1105 |
+
|
1106 |
+
line-height: $colorFieldHeight;
|
1107 |
}
|
1108 |
|
1109 |
.ws_color_display_item {
|
1875 |
cursor: default !important;
|
1876 |
}
|
1877 |
|
1878 |
+
.inside {
|
1879 |
+
margin-bottom: 0;
|
1880 |
+
}
|
1881 |
+
|
1882 |
ul {
|
1883 |
list-style: disc outside;
|
1884 |
margin-left: 1em;
|
1915 |
margin-right: 2px;
|
1916 |
}
|
1917 |
|
1918 |
+
.ws_ame_custom_postbox .ame-tutorial-list {
|
1919 |
+
margin: 0;
|
1920 |
+
|
1921 |
+
a {
|
1922 |
+
text-decoration: none;
|
1923 |
+
display: block;
|
1924 |
+
padding: 4px;
|
1925 |
+
}
|
1926 |
+
|
1927 |
+
ul {
|
1928 |
+
margin-left: 1em;
|
1929 |
+
}
|
1930 |
+
|
1931 |
+
li {
|
1932 |
+
display: block;
|
1933 |
+
margin: 0;
|
1934 |
+
list-style: none;
|
1935 |
+
}
|
1936 |
+
}
|
1937 |
+
|
1938 |
/************************************
|
1939 |
Copy Permissions dialog
|
1940 |
*************************************/
|
images/new-menu-badge.png
ADDED
Binary file
|
includes/editor-page.php
CHANGED
@@ -286,6 +286,8 @@ function ame_output_sort_buttons($icons) {
|
|
286 |
?>
|
287 |
</div>
|
288 |
|
|
|
|
|
289 |
<?php
|
290 |
if ( apply_filters('admin_menu_editor-show_general_box', false) ) :
|
291 |
$is_general_box_open = true;
|
@@ -295,8 +297,6 @@ function ame_output_sort_buttons($icons) {
|
|
295 |
$box_class = $is_general_box_open ? '' : 'closed';
|
296 |
|
297 |
?>
|
298 |
-
<div class="clear"></div>
|
299 |
-
<div class="metabox-holder">
|
300 |
<div class="postbox ws_ame_custom_postbox <?php echo $box_class; ?>" id="ws_ame_general_vis_box">
|
301 |
<button type="button" class="handlediv button-link">
|
302 |
<span class="toggle-indicator"></span>
|
@@ -306,16 +306,73 @@ function ame_output_sort_buttons($icons) {
|
|
306 |
<?php do_action('admin_menu_editor-general_box'); ?>
|
307 |
</div>
|
308 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
309 |
</div>
|
310 |
<?php
|
311 |
endif;
|
|
|
|
|
312 |
|
|
|
313 |
$hint_id = 'ws_sidebar_pro_ad';
|
314 |
$show_pro_benefits = !apply_filters('admin_menu_editor_is_pro', false) && (!isset($editor_data['show_hints'][$hint_id]) || $editor_data['show_hints'][$hint_id]);
|
315 |
|
316 |
if ( $show_pro_benefits ):
|
317 |
$benefit_variations = array(
|
318 |
-
'
|
319 |
'More menu icons.',
|
320 |
'Make menus open in a new tab or an iframe.',
|
321 |
'Prevent users from deleting a specific user.',
|
286 |
?>
|
287 |
</div>
|
288 |
|
289 |
+
<div class="clear"></div>
|
290 |
+
<div class="metabox-holder">
|
291 |
<?php
|
292 |
if ( apply_filters('admin_menu_editor-show_general_box', false) ) :
|
293 |
$is_general_box_open = true;
|
297 |
$box_class = $is_general_box_open ? '' : 'closed';
|
298 |
|
299 |
?>
|
|
|
|
|
300 |
<div class="postbox ws_ame_custom_postbox <?php echo $box_class; ?>" id="ws_ame_general_vis_box">
|
301 |
<button type="button" class="handlediv button-link">
|
302 |
<span class="toggle-indicator"></span>
|
306 |
<?php do_action('admin_menu_editor-general_box'); ?>
|
307 |
</div>
|
308 |
</div>
|
309 |
+
<?php
|
310 |
+
endif;
|
311 |
+
|
312 |
+
if ( $is_pro_version ) :
|
313 |
+
$is_how_to_box_open = true;
|
314 |
+
if ( isset($_COOKIE['ame_how_to_box_open']) ) {
|
315 |
+
$is_how_to_box_open = ($_COOKIE['ame_how_to_box_open'] === '1');
|
316 |
+
}
|
317 |
+
$box_class = $is_how_to_box_open ? '' : 'closed';
|
318 |
+
|
319 |
+
$how_to_link_template = '<a href="https://adminmenueditor.com/documentation/%1$s" target="_blank" title="Opens in a new tab">%2$s</a>';
|
320 |
+
$how_to_item_template = '<li>' . $how_to_link_template . '</li>';
|
321 |
+
|
322 |
+
?>
|
323 |
+
<div class="postbox ws_ame_custom_postbox <?php echo $box_class; ?>" id="ws_ame_how_to_box">
|
324 |
+
<button type="button" class="handlediv button-link">
|
325 |
+
<span class="toggle-indicator"></span>
|
326 |
+
</button>
|
327 |
+
<h2 class="hndle">How To</h2>
|
328 |
+
<div class="inside">
|
329 |
+
<ul class="ame-tutorial-list">
|
330 |
+
<li><?php
|
331 |
+
printf($how_to_link_template, 'how-to-hide-a-menu-item/', 'Hide a Menu...');
|
332 |
+
?>
|
333 |
+
<ul class="ame-tutorial-list">
|
334 |
+
<?php
|
335 |
+
foreach (
|
336 |
+
array(
|
337 |
+
'how-to-hide-a-menu-item/#how-to-hide-a-menu-from-a-role' => 'From a Role',
|
338 |
+
'how-to-hide-a-menu-item/#how-to-hide-a-menu-from-a-user' => 'From a User',
|
339 |
+
'how-to-hide-a-menu-item/#how-to-hide-a-menu-from-everyone-except-yourself' => 'From Everyone Except You',
|
340 |
+
'how-to-hide-menu-without-preventing-access/' => 'Without Preventing Access',
|
341 |
+
)
|
342 |
+
as $how_to_url => $how_to_title
|
343 |
+
) {
|
344 |
+
printf($how_to_item_template, esc_attr($how_to_url), $how_to_title);
|
345 |
+
}
|
346 |
+
?>
|
347 |
+
</ul>
|
348 |
+
</li>
|
349 |
+
<?php
|
350 |
+
foreach (
|
351 |
+
array(
|
352 |
+
'how-to-give-access-to-menu/' => 'Show a Menu',
|
353 |
+
'how-to-move-and-sort-menus/' => 'Move and Sort Menus',
|
354 |
+
'how-to-add-a-new-menu-item/' => 'Add a New Menu',
|
355 |
+
)
|
356 |
+
as $how_to_url => $how_to_title
|
357 |
+
) {
|
358 |
+
printf($how_to_item_template, esc_attr($how_to_url), $how_to_title);
|
359 |
+
}
|
360 |
+
?>
|
361 |
+
</ul>
|
362 |
+
</div>
|
363 |
</div>
|
364 |
<?php
|
365 |
endif;
|
366 |
+
?>
|
367 |
+
</div> <!-- / .metabox-holder -->
|
368 |
|
369 |
+
<?php
|
370 |
$hint_id = 'ws_sidebar_pro_ad';
|
371 |
$show_pro_benefits = !apply_filters('admin_menu_editor_is_pro', false) && (!isset($editor_data['show_hints'][$hint_id]) || $editor_data['show_hints'][$hint_id]);
|
372 |
|
373 |
if ( $show_pro_benefits ):
|
374 |
$benefit_variations = array(
|
375 |
+
'Hide dashboard widgets.',
|
376 |
'More menu icons.',
|
377 |
'Make menus open in a new tab or an iframe.',
|
378 |
'Prevent users from deleting a specific user.',
|
includes/menu-editor-core.php
CHANGED
@@ -114,6 +114,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
114 |
*/
|
115 |
private $current_tab = '';
|
116 |
|
|
|
|
|
|
|
|
|
|
|
117 |
function init(){
|
118 |
$this->sitewide_options = true;
|
119 |
|
@@ -221,6 +226,22 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
221 |
'index.php?page=nf-credits' => true,
|
222 |
//All in One SEO Pack 2.3.9.2
|
223 |
'index.php?page=aioseop-about' => true,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
);
|
225 |
|
226 |
//AJAXify screen options
|
@@ -320,6 +341,14 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
320 |
add_action('admin_notices', array($this, 'display_security_log'));
|
321 |
}
|
322 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
323 |
//Modules
|
324 |
foreach($this->get_active_modules() as $module) {
|
325 |
/** @noinspection PhpIncludeInspection */
|
@@ -329,7 +358,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
329 |
}
|
330 |
}
|
331 |
|
332 |
-
//Set up the tabs for the menu editor page.
|
333 |
$firstTabs = array('editor' => 'Admin Menu');
|
334 |
if ( is_network_admin() ) {
|
335 |
//TODO: This could be in extras.php
|
@@ -676,20 +705,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
676 |
}
|
677 |
$done = true;
|
678 |
|
|
|
|
|
679 |
//Lodash library
|
680 |
wp_register_auto_versioned_script('ame-lodash', plugins_url('js/lodash.min.js', $this->plugin_file));
|
681 |
|
682 |
-
//jQuery JSON plugin
|
683 |
-
wp_register_auto_versioned_script('jquery-json', plugins_url('js/jquery.json.js', $this->plugin_file), array('jquery'));
|
684 |
-
//jQuery sort plugin
|
685 |
-
wp_register_auto_versioned_script('jquery-sort', plugins_url('js/jquery.sort.js', $this->plugin_file), array('jquery'));
|
686 |
-
//qTip2 - jQuery tooltip plugin
|
687 |
-
wp_register_auto_versioned_script('jquery-qtip', plugins_url('js/jquery.qtip.min.js', $this->plugin_file), array('jquery'));
|
688 |
-
//jQuery Form plugin. This is a more recent version than the one included with WP.
|
689 |
-
wp_register_auto_versioned_script('ame-jquery-form', plugins_url('js/jquery.form.js', $this->plugin_file), array('jquery'));
|
690 |
-
//jQuery cookie plugin
|
691 |
-
wp_register_auto_versioned_script('jquery-cookie', plugins_url('js/jquery.biscuit.js', $this->plugin_file), array('jquery'));
|
692 |
-
|
693 |
//Knockout
|
694 |
wp_register_auto_versioned_script('knockout', plugins_url('js/knockout.js', $this->plugin_file));
|
695 |
|
@@ -728,12 +748,19 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
728 |
//Compatibility workaround: Get the real roles of the current user even if other plugins corrupt the list.
|
729 |
$users[$current_user->get('user_login')]['roles'] = array_values($this->get_user_roles($current_user));
|
730 |
|
|
|
|
|
|
|
|
|
|
|
|
|
731 |
//TODO: Include currentUserLogin
|
732 |
$actor_data = array(
|
733 |
'roles' => $roles,
|
734 |
'users' => $users,
|
735 |
'isMultisite' => is_multisite(),
|
736 |
'capPower' => $this->load_cap_power(),
|
|
|
737 |
);
|
738 |
wp_localize_script('ame-actor-manager', 'wsAmeActorData', $actor_data);
|
739 |
|
@@ -748,6 +775,115 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
748 |
do_action('admin_menu_editor-register_scripts');
|
749 |
}
|
750 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
751 |
|
752 |
/**
|
753 |
* Add the JS required by the editor to the page header
|
@@ -898,6 +1034,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
898 |
'id' => $user->ID,
|
899 |
'roles' => !empty($user->roles) ? (array)($user->roles) : array(),
|
900 |
'capabilities' => $this->castValuesToBool($user->caps),
|
|
|
901 |
'display_name' => $user->display_name,
|
902 |
'is_super_admin' => is_multisite() && is_super_admin($user->ID),
|
903 |
);
|
@@ -1185,7 +1322,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1185 |
* @param string $login
|
1186 |
* @param WP_User $current_user
|
1187 |
*/
|
1188 |
-
public function maybe_reset_plugin_access(/** @noinspection PhpUnusedParameterInspection */ $login, $current_user) {
|
1189 |
if ( ($this->options['plugin_access'] !== 'specific_user') || !$current_user || !$current_user->exists() ) {
|
1190 |
return;
|
1191 |
}
|
@@ -3849,7 +3986,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
3849 |
continue;
|
3850 |
}
|
3851 |
|
3852 |
-
if ( count($line) >= 2 ) {
|
3853 |
$cap_power[strval($line[0])] = floatval(str_replace(',', '.', $line[1]));
|
3854 |
}
|
3855 |
}
|
@@ -3938,6 +4075,8 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
3938 |
}
|
3939 |
unset($module);
|
3940 |
|
|
|
|
|
3941 |
$modules = array_filter($modules, array($this, 'module_path_exists'));
|
3942 |
|
3943 |
return $modules;
|
@@ -4037,6 +4176,7 @@ class ameMenuTemplateBuilder {
|
|
4037 |
}
|
4038 |
|
4039 |
//Skip blacklisted menus.
|
|
|
4040 |
if ( isset($item['url'], $this->blacklist[$item['url']]) ) {
|
4041 |
return;
|
4042 |
}
|
114 |
*/
|
115 |
private $current_tab = '';
|
116 |
|
117 |
+
/**
|
118 |
+
* @var array List of capabilities that are used in the default admin menu. Used to detect meta capabilities.
|
119 |
+
*/
|
120 |
+
private $caps_used_in_menu = array();
|
121 |
+
|
122 |
function init(){
|
123 |
$this->sitewide_options = true;
|
124 |
|
226 |
'index.php?page=nf-credits' => true,
|
227 |
//All in One SEO Pack 2.3.9.2
|
228 |
'index.php?page=aioseop-about' => true,
|
229 |
+
//WP Courseware 4.1.2
|
230 |
+
//'wpcw' => true, //This is commented out due to a bug. The Courseware top level menu and its first submenu
|
231 |
+
//both have the URL "wpcw", but the top level menu also has some visible, non-blacklisted items. AME would
|
232 |
+
//still hide the entire menu because the template builder doesn't check if a menu has submenu items.
|
233 |
+
'admin.php?page=wpcw-course-classroom' => true,
|
234 |
+
'admin.php?page=wpcw-student' => true,
|
235 |
+
'admin.php?page=WPCW_showPage_ConvertPage' => true,
|
236 |
+
'admin.php?page=WPCW_showPage_CourseOrdering' => true,
|
237 |
+
'admin.php?page=WPCW_showPage_GradeBook' => true,
|
238 |
+
'admin.php?page=WPCW_showPage_ModifyCourse' => true,
|
239 |
+
'admin.php?page=WPCW_showPage_ModifyModule' => true,
|
240 |
+
'admin.php?page=WPCW_showPage_ModifyQuestion' => true,
|
241 |
+
'admin.php?page=WPCW_showPage_ModifyQuiz' => true,
|
242 |
+
'admin.php?page=WPCW_showPage_UserCourseAccess' => true,
|
243 |
+
'admin.php?page=WPCW_showPage_UserProgess' => true,
|
244 |
+
'admin.php?page=WPCW_showPage_UserProgess_quizAnswers' => true,
|
245 |
);
|
246 |
|
247 |
//AJAXify screen options
|
341 |
add_action('admin_notices', array($this, 'display_security_log'));
|
342 |
}
|
343 |
|
344 |
+
if ( did_action('plugins_loaded') ) {
|
345 |
+
$this->load_modules();
|
346 |
+
} else {
|
347 |
+
add_action('plugins_loaded', array($this, 'load_modules'), 11);
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
public function load_modules() {
|
352 |
//Modules
|
353 |
foreach($this->get_active_modules() as $module) {
|
354 |
/** @noinspection PhpIncludeInspection */
|
358 |
}
|
359 |
}
|
360 |
|
361 |
+
//Set up the tabs for the menu editor page. Many tabs are provided by modules.
|
362 |
$firstTabs = array('editor' => 'Admin Menu');
|
363 |
if ( is_network_admin() ) {
|
364 |
//TODO: This could be in extras.php
|
705 |
}
|
706 |
$done = true;
|
707 |
|
708 |
+
$this->register_jquery_plugins();
|
709 |
+
|
710 |
//Lodash library
|
711 |
wp_register_auto_versioned_script('ame-lodash', plugins_url('js/lodash.min.js', $this->plugin_file));
|
712 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
713 |
//Knockout
|
714 |
wp_register_auto_versioned_script('knockout', plugins_url('js/knockout.js', $this->plugin_file));
|
715 |
|
748 |
//Compatibility workaround: Get the real roles of the current user even if other plugins corrupt the list.
|
749 |
$users[$current_user->get('user_login')]['roles'] = array_values($this->get_user_roles($current_user));
|
750 |
|
751 |
+
$suspected_meta_caps = $this->detect_meta_caps($roles, $users);
|
752 |
+
|
753 |
+
//The current user has all of the meta caps. That's how we know they're meta caps and not just regular
|
754 |
+
//capabilities that simply haven't been granted to anyone.
|
755 |
+
$users[$current_user->get('user_login')]['meta_capabilities'] = $suspected_meta_caps;
|
756 |
+
|
757 |
//TODO: Include currentUserLogin
|
758 |
$actor_data = array(
|
759 |
'roles' => $roles,
|
760 |
'users' => $users,
|
761 |
'isMultisite' => is_multisite(),
|
762 |
'capPower' => $this->load_cap_power(),
|
763 |
+
'suspectedMetaCaps' => $suspected_meta_caps,
|
764 |
);
|
765 |
wp_localize_script('ame-actor-manager', 'wsAmeActorData', $actor_data);
|
766 |
|
775 |
do_action('admin_menu_editor-register_scripts');
|
776 |
}
|
777 |
|
778 |
+
/**
|
779 |
+
* @access private
|
780 |
+
*/
|
781 |
+
public function register_jquery_plugins() {
|
782 |
+
//jQuery JSON plugin
|
783 |
+
wp_register_auto_versioned_script('jquery-json', plugins_url('js/jquery.json.js', $this->plugin_file), array('jquery'));
|
784 |
+
//jQuery sort plugin
|
785 |
+
wp_register_auto_versioned_script('jquery-sort', plugins_url('js/jquery.sort.js', $this->plugin_file), array('jquery'));
|
786 |
+
//qTip2 - jQuery tooltip plugin
|
787 |
+
wp_register_auto_versioned_script('jquery-qtip', plugins_url('js/jquery.qtip.min.js', $this->plugin_file), array('jquery'));
|
788 |
+
//jQuery Form plugin. This is a more recent version than the one included with WP.
|
789 |
+
wp_register_auto_versioned_script('ame-jquery-form', plugins_url('js/jquery.form.js', $this->plugin_file), array('jquery'));
|
790 |
+
//jQuery cookie plugin
|
791 |
+
wp_register_auto_versioned_script('jquery-cookie', plugins_url('js/jquery.biscuit.js', $this->plugin_file), array('jquery'));
|
792 |
+
}
|
793 |
+
|
794 |
+
/**
|
795 |
+
* Detect meta capabilities.
|
796 |
+
* This only works if the current user is an admin. In Multisite, they must be a Super Admin.
|
797 |
+
*
|
798 |
+
* @param array $roles
|
799 |
+
* @param array $users
|
800 |
+
* @return array [capability => true]
|
801 |
+
*/
|
802 |
+
private function detect_meta_caps($roles, $users) {
|
803 |
+
if ( !$this->current_user_can_edit_menu() || !is_super_admin() ) {
|
804 |
+
return array();
|
805 |
+
}
|
806 |
+
|
807 |
+
//Any capability that's assigned to a role probably isn't a meta capability.
|
808 |
+
$allRealCaps = ameRoleUtils::get_all_capabilities();
|
809 |
+
//Similarly, capabilities that are directly assigned to users are probably real.
|
810 |
+
foreach($users as $user) {
|
811 |
+
$allRealCaps = array_merge($allRealCaps, $user['capabilities']);
|
812 |
+
}
|
813 |
+
//Role IDs can also be used as capabilities.
|
814 |
+
foreach($roles as $roleId => $role) {
|
815 |
+
$allRealCaps[$roleId] = true;
|
816 |
+
}
|
817 |
+
|
818 |
+
//Collect all of the required capabilities from the admin menu.
|
819 |
+
$menu = $this->get_default_menu();
|
820 |
+
ameMenu::for_each($menu['tree'], array($this, 'collect_menu_cap'));
|
821 |
+
|
822 |
+
//Any capability that's part of the admin menu but not assigned to any role or user
|
823 |
+
//is probably a meta capability.
|
824 |
+
$suspectedMetaCaps = array_diff_key($this->caps_used_in_menu, $allRealCaps);
|
825 |
+
|
826 |
+
//The current user is an admin and should have access to everything. If they don't have a cap,
|
827 |
+
//that's probably a non-meta cap that isn't enabled for *anyone*.
|
828 |
+
$suspectedMetaCaps = array_filter(array_keys($suspectedMetaCaps), 'current_user_can');
|
829 |
+
|
830 |
+
return array_fill_keys($suspectedMetaCaps, true);
|
831 |
+
}
|
832 |
+
|
833 |
+
/**
|
834 |
+
* @access private
|
835 |
+
* @param array $item
|
836 |
+
*/
|
837 |
+
public function collect_menu_cap($item) {
|
838 |
+
if ( isset($item['defaults'], $item['defaults']['access_level']) ) {
|
839 |
+
$this->caps_used_in_menu[$item['defaults']['access_level']] = true;
|
840 |
+
}
|
841 |
+
}
|
842 |
+
|
843 |
+
/** @noinspection PhpUnusedPrivateMethodInspection */
|
844 |
+
/**
|
845 |
+
* Unfinished feature: Detect which roles have which meta capabilities.
|
846 |
+
*
|
847 |
+
* Create a temp. user for each role, test which meta caps they have, then cache the results in a site option.
|
848 |
+
* Put this part in an AJAX request to avoid a massive slowdown (takes several seconds even on a fast PC).
|
849 |
+
*
|
850 |
+
* @param array $suspected_meta_caps
|
851 |
+
* @param string[] $roleIds
|
852 |
+
* @return array
|
853 |
+
*/
|
854 |
+
private function analyse_role_meta_caps($suspected_meta_caps, $roleIds) {
|
855 |
+
//$start = microtime(true);
|
856 |
+
$results = array();
|
857 |
+
$real_current_user = wp_get_current_user();
|
858 |
+
|
859 |
+
foreach($roleIds as $role_id) {
|
860 |
+
$id = wp_insert_user(array(
|
861 |
+
'role' => $role_id,
|
862 |
+
'user_login' => wp_slash('ametemp_' . wp_generate_password(14)),
|
863 |
+
'user_pass' => wp_generate_password(20),
|
864 |
+
'display_name' => 'Temporary user created by AME',
|
865 |
+
));
|
866 |
+
$user = new WP_User($id);
|
867 |
+
|
868 |
+
//Some plugins only check the current user and ignore the user ID passed to the "user_has_cap" filter.
|
869 |
+
//To account for cases like that, we need to also change the current user.
|
870 |
+
wp_set_current_user($user->ID);
|
871 |
+
|
872 |
+
$results[$role_id] = array();
|
873 |
+
foreach($suspected_meta_caps as $meta_cap => $ignored) {
|
874 |
+
$results[$role_id][$meta_cap] = $user->has_cap($meta_cap);
|
875 |
+
}
|
876 |
+
|
877 |
+
wp_delete_user($id);
|
878 |
+
}
|
879 |
+
|
880 |
+
//Restore the original user.
|
881 |
+
wp_set_current_user($real_current_user->ID);
|
882 |
+
|
883 |
+
/*$elapsed = microtime(true) - $start;
|
884 |
+
printf('Meta cap analysis: %.2f ms<br>', $elapsed * 1000);*/
|
885 |
+
return $results;
|
886 |
+
}
|
887 |
|
888 |
/**
|
889 |
* Add the JS required by the editor to the page header
|
1034 |
'id' => $user->ID,
|
1035 |
'roles' => !empty($user->roles) ? (array)($user->roles) : array(),
|
1036 |
'capabilities' => $this->castValuesToBool($user->caps),
|
1037 |
+
'meta_capabilities' => array(),
|
1038 |
'display_name' => $user->display_name,
|
1039 |
'is_super_admin' => is_multisite() && is_super_admin($user->ID),
|
1040 |
);
|
1322 |
* @param string $login
|
1323 |
* @param WP_User $current_user
|
1324 |
*/
|
1325 |
+
public function maybe_reset_plugin_access(/** @noinspection PhpUnusedParameterInspection */ $login = null, $current_user = null) {
|
1326 |
if ( ($this->options['plugin_access'] !== 'specific_user') || !$current_user || !$current_user->exists() ) {
|
1327 |
return;
|
1328 |
}
|
3986 |
continue;
|
3987 |
}
|
3988 |
|
3989 |
+
if ( is_array($line) && (count($line) >= 2) ) {
|
3990 |
$cap_power[strval($line[0])] = floatval(str_replace(',', '.', $line[1]));
|
3991 |
}
|
3992 |
}
|
4075 |
}
|
4076 |
unset($module);
|
4077 |
|
4078 |
+
$modules = apply_filters('admin_menu_editor-available_modules', $modules);
|
4079 |
+
|
4080 |
$modules = array_filter($modules, array($this, 'module_path_exists'));
|
4081 |
|
4082 |
return $modules;
|
4176 |
}
|
4177 |
|
4178 |
//Skip blacklisted menus.
|
4179 |
+
//BUG: We shouldn't skip top level menus that have non-blacklisted submenu items.
|
4180 |
if ( isset($item['url'], $this->blacklist[$item['url']]) ) {
|
4181 |
return;
|
4182 |
}
|
includes/module.php
CHANGED
@@ -7,6 +7,8 @@ abstract class ameModule {
|
|
7 |
protected $moduleId = '';
|
8 |
protected $moduleDir = '';
|
9 |
|
|
|
|
|
10 |
/**
|
11 |
* @var WPMenuEditor
|
12 |
*/
|
@@ -34,6 +36,11 @@ abstract class ameModule {
|
|
34 |
|
35 |
add_action('admin_menu_editor-enqueue_scripts-' . $this->tabSlug, array($this, 'enqueueTabScripts'));
|
36 |
add_action('admin_menu_editor-enqueue_styles-' . $this->tabSlug, array($this, 'enqueueTabStyles'));
|
|
|
|
|
|
|
|
|
|
|
37 |
}
|
38 |
}
|
39 |
|
@@ -80,6 +87,11 @@ abstract class ameModule {
|
|
80 |
/** @noinspection PhpUnusedLocalVariableInspection Used in some templates. */
|
81 |
$moduleTabUrl = $this->getTabUrl();
|
82 |
|
|
|
|
|
|
|
|
|
|
|
83 |
/** @noinspection PhpIncludeInspection */
|
84 |
require $templateFile;
|
85 |
return true;
|
@@ -87,6 +99,11 @@ abstract class ameModule {
|
|
87 |
return false;
|
88 |
}
|
89 |
|
|
|
|
|
|
|
|
|
|
|
90 |
public function registerScripts() {
|
91 |
//Override this method to register scripts.
|
92 |
}
|
@@ -98,4 +115,20 @@ abstract class ameModule {
|
|
98 |
public function enqueueTabStyles() {
|
99 |
//Override this method to add stylesheets to the $this->tabSlug tab.
|
100 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
}
|
7 |
protected $moduleId = '';
|
8 |
protected $moduleDir = '';
|
9 |
|
10 |
+
protected $settingsFormAction = '';
|
11 |
+
|
12 |
/**
|
13 |
* @var WPMenuEditor
|
14 |
*/
|
36 |
|
37 |
add_action('admin_menu_editor-enqueue_scripts-' . $this->tabSlug, array($this, 'enqueueTabScripts'));
|
38 |
add_action('admin_menu_editor-enqueue_styles-' . $this->tabSlug, array($this, 'enqueueTabStyles'));
|
39 |
+
|
40 |
+
//Optionally, handle settings form submission.
|
41 |
+
if ( $this->settingsFormAction !== '' ) {
|
42 |
+
add_action('admin_menu_editor-header', array($this, '_processAction'), 10, 2);
|
43 |
+
}
|
44 |
}
|
45 |
}
|
46 |
|
87 |
/** @noinspection PhpUnusedLocalVariableInspection Used in some templates. */
|
88 |
$moduleTabUrl = $this->getTabUrl();
|
89 |
|
90 |
+
$templateVariables = $this->getTemplateVariables($name);
|
91 |
+
if ( !empty($templateVariables) ) {
|
92 |
+
extract($templateVariables, EXTR_SKIP);
|
93 |
+
}
|
94 |
+
|
95 |
/** @noinspection PhpIncludeInspection */
|
96 |
require $templateFile;
|
97 |
return true;
|
99 |
return false;
|
100 |
}
|
101 |
|
102 |
+
protected function getTemplateVariables(/** @noinspection PhpUnusedParameterInspection */ $templateName) {
|
103 |
+
//Override this method to pass variables to a template.
|
104 |
+
return array();
|
105 |
+
}
|
106 |
+
|
107 |
public function registerScripts() {
|
108 |
//Override this method to register scripts.
|
109 |
}
|
115 |
public function enqueueTabStyles() {
|
116 |
//Override this method to add stylesheets to the $this->tabSlug tab.
|
117 |
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* @access private
|
121 |
+
* @param string $action
|
122 |
+
* @param array $post
|
123 |
+
*/
|
124 |
+
public function _processAction($action, $post = array()) {
|
125 |
+
if ( $action === $this->settingsFormAction ) {
|
126 |
+
check_admin_referer($action);
|
127 |
+
$this->handleSettingsForm($post);
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
public function handleSettingsForm($post = array()) {
|
132 |
+
//Override this method to process a form submitted from the module's tab.
|
133 |
+
}
|
134 |
}
|
includes/role-utils.php
CHANGED
@@ -1,11 +1,17 @@
|
|
1 |
<?php
|
2 |
class ameRoleUtils {
|
3 |
/**
|
4 |
-
* Retrieve a list of all known capabilities of all roles
|
5 |
*
|
6 |
* @return array Associative array with capability names as keys
|
7 |
*/
|
8 |
public static function get_all_capabilities(){
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
$wp_roles = self::get_roles();
|
10 |
$capabilities = array();
|
11 |
|
1 |
<?php
|
2 |
class ameRoleUtils {
|
3 |
/**
|
4 |
+
* Retrieve a list of all known, non-meta capabilities of all roles.
|
5 |
*
|
6 |
* @return array Associative array with capability names as keys
|
7 |
*/
|
8 |
public static function get_all_capabilities(){
|
9 |
+
//Cache the results.
|
10 |
+
static $capabilities = null;
|
11 |
+
if ( isset($capabilities) ) {
|
12 |
+
return $capabilities;
|
13 |
+
}
|
14 |
+
|
15 |
$wp_roles = self::get_roles();
|
16 |
$capabilities = array();
|
17 |
|
js/actor-manager.js
CHANGED
@@ -10,14 +10,15 @@ var __extends = (this && this.__extends) || (function () {
|
|
10 |
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
11 |
};
|
12 |
})();
|
13 |
-
var AmeBaseActor = (function () {
|
14 |
-
function AmeBaseActor(id, displayName, capabilities) {
|
|
|
15 |
this.displayName = '[Error: No displayName set]';
|
16 |
this.groupActors = [];
|
17 |
-
this.actorTypeSpecificity = 0;
|
18 |
this.id = id;
|
19 |
this.displayName = displayName;
|
20 |
this.capabilities = capabilities;
|
|
|
21 |
}
|
22 |
/**
|
23 |
* Get the capability setting directly from this actor, ignoring capabilities
|
@@ -32,6 +33,9 @@ var AmeBaseActor = (function () {
|
|
32 |
if (this.capabilities.hasOwnProperty(capability)) {
|
33 |
return this.capabilities[capability];
|
34 |
}
|
|
|
|
|
|
|
35 |
return null;
|
36 |
};
|
37 |
AmeBaseActor.getActorSpecificity = function (actorId) {
|
@@ -56,11 +60,11 @@ var AmeBaseActor = (function () {
|
|
56 |
};
|
57 |
return AmeBaseActor;
|
58 |
}());
|
59 |
-
var AmeRole = (function (_super) {
|
60 |
__extends(AmeRole, _super);
|
61 |
-
function AmeRole(roleId, displayName, capabilities) {
|
62 |
-
|
63 |
-
_this
|
64 |
_this.name = roleId;
|
65 |
return _this;
|
66 |
}
|
@@ -75,15 +79,15 @@ var AmeRole = (function (_super) {
|
|
75 |
};
|
76 |
return AmeRole;
|
77 |
}(AmeBaseActor));
|
78 |
-
var AmeUser = (function (_super) {
|
79 |
__extends(AmeUser, _super);
|
80 |
-
function AmeUser(userLogin, displayName, capabilities, roles, isSuperAdmin, userId) {
|
81 |
if (isSuperAdmin === void 0) { isSuperAdmin = false; }
|
82 |
-
|
|
|
83 |
_this.userId = 0;
|
84 |
_this.isSuperAdmin = false;
|
85 |
_this.avatarHTML = '';
|
86 |
-
_this.actorTypeSpecificity = 10;
|
87 |
_this.userLogin = userLogin;
|
88 |
_this.roles = roles;
|
89 |
_this.isSuperAdmin = isSuperAdmin;
|
@@ -97,7 +101,7 @@ var AmeUser = (function (_super) {
|
|
97 |
return _this;
|
98 |
}
|
99 |
AmeUser.createFromProperties = function (properties) {
|
100 |
-
var user = new AmeUser(properties.user_login, properties.display_name, properties.capabilities, properties.roles, properties.is_super_admin, properties.hasOwnProperty('id') ? properties.id : null);
|
101 |
if (properties.avatar_html) {
|
102 |
user.avatarHTML = properties.avatar_html;
|
103 |
}
|
@@ -105,12 +109,10 @@ var AmeUser = (function (_super) {
|
|
105 |
};
|
106 |
return AmeUser;
|
107 |
}(AmeBaseActor));
|
108 |
-
var AmeSuperAdmin = (function (_super) {
|
109 |
__extends(AmeSuperAdmin, _super);
|
110 |
function AmeSuperAdmin() {
|
111 |
-
|
112 |
-
_this.actorTypeSpecificity = 2;
|
113 |
-
return _this;
|
114 |
}
|
115 |
AmeSuperAdmin.prototype.hasOwnCap = function (capability) {
|
116 |
//The Super Admin has all possible capabilities except the special "do_not_allow" flag.
|
@@ -119,9 +121,10 @@ var AmeSuperAdmin = (function (_super) {
|
|
119 |
AmeSuperAdmin.permanentActorId = 'special:super_admin';
|
120 |
return AmeSuperAdmin;
|
121 |
}(AmeBaseActor));
|
122 |
-
var AmeActorManager = (function () {
|
123 |
-
function AmeActorManager(roles, users, isMultisite) {
|
124 |
if (isMultisite === void 0) { isMultisite = false; }
|
|
|
125 |
var _this = this;
|
126 |
this.roles = {};
|
127 |
this.users = {};
|
@@ -132,7 +135,7 @@ var AmeActorManager = (function () {
|
|
132 |
this.suggestedCapabilities = [];
|
133 |
this.isMultisite = !!isMultisite;
|
134 |
AmeActorManager._.forEach(roles, function (roleDetails, id) {
|
135 |
-
var role = new AmeRole(id, roleDetails.name, roleDetails.capabilities);
|
136 |
_this.roles[role.name] = role;
|
137 |
});
|
138 |
AmeActorManager._.forEach(users, function (userDetails) {
|
@@ -142,6 +145,7 @@ var AmeActorManager = (function () {
|
|
142 |
if (this.isMultisite) {
|
143 |
this.superAdmin = new AmeSuperAdmin();
|
144 |
}
|
|
|
145 |
var exclusiveCaps = [
|
146 |
'update_core', 'update_plugins', 'delete_plugins', 'install_plugins', 'upload_plugins', 'update_themes',
|
147 |
'delete_themes', 'install_themes', 'upload_themes', 'update_core', 'edit_css', 'unfiltered_html',
|
@@ -235,7 +239,11 @@ var AmeActorManager = (function () {
|
|
235 |
}
|
236 |
}
|
237 |
//Step #3: Check owned/default capabilities. Always checked.
|
238 |
-
var actor = this.getActor(actorId)
|
|
|
|
|
|
|
|
|
239 |
if (hasOwnCap !== null) {
|
240 |
return hasOwnCap;
|
241 |
}
|
@@ -247,11 +255,19 @@ var AmeActorManager = (function () {
|
|
247 |
return this.actorHasCap('special:super_admin', capability, contextList);
|
248 |
}
|
249 |
//Check if any of the user's roles have the capability.
|
250 |
-
result =
|
251 |
for (var index = 0; index < actor.roles.length; index++) {
|
252 |
-
|
|
|
|
|
|
|
253 |
}
|
254 |
-
|
|
|
|
|
|
|
|
|
|
|
255 |
}
|
256 |
return false;
|
257 |
};
|
@@ -325,9 +341,6 @@ var AmeActorManager = (function () {
|
|
325 |
var grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap;
|
326 |
AmeActorManager._.set(context, [actor, capability], grant);
|
327 |
};
|
328 |
-
AmeActorManager.prototype.resetCap = function (actor, capability) {
|
329 |
-
this.resetCapInContext(this.grantedCapabilities, actor, capability);
|
330 |
-
};
|
331 |
AmeActorManager.prototype.resetCapInContext = function (context, actor, capability) {
|
332 |
capability = this.mapMetaCap(capability);
|
333 |
if (AmeActorManager._.has(context, [actor, capability])) {
|
@@ -355,7 +368,7 @@ var AmeActorManager = (function () {
|
|
355 |
_.forEach(_.keys(pruned[actor]), function (capability) {
|
356 |
var grant = pruned[actor][capability];
|
357 |
delete pruned[actor][capability];
|
358 |
-
var hasCap = _.isArray(grant) ? grant[0] : grant, hasCapWhenPruned = _this.actorHasCap(actor, capability, context);
|
359 |
if (hasCap !== hasCapWhenPruned) {
|
360 |
pruned[actor][capability] = grant; //Restore.
|
361 |
}
|
@@ -471,7 +484,7 @@ var AmeActorManager = (function () {
|
|
471 |
return AmeActorManager;
|
472 |
}());
|
473 |
if (typeof wsAmeActorData !== 'undefined') {
|
474 |
-
AmeActors = new AmeActorManager(wsAmeActorData.roles, wsAmeActorData.users, wsAmeActorData.isMultisite);
|
475 |
if (typeof wsAmeActorData['capPower'] !== 'undefined') {
|
476 |
AmeActors.generateCapabilitySuggestions(wsAmeActorData['capPower']);
|
477 |
}
|
10 |
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
11 |
};
|
12 |
})();
|
13 |
+
var AmeBaseActor = /** @class */ (function () {
|
14 |
+
function AmeBaseActor(id, displayName, capabilities, metaCapabilities) {
|
15 |
+
if (metaCapabilities === void 0) { metaCapabilities = {}; }
|
16 |
this.displayName = '[Error: No displayName set]';
|
17 |
this.groupActors = [];
|
|
|
18 |
this.id = id;
|
19 |
this.displayName = displayName;
|
20 |
this.capabilities = capabilities;
|
21 |
+
this.metaCapabilities = metaCapabilities;
|
22 |
}
|
23 |
/**
|
24 |
* Get the capability setting directly from this actor, ignoring capabilities
|
33 |
if (this.capabilities.hasOwnProperty(capability)) {
|
34 |
return this.capabilities[capability];
|
35 |
}
|
36 |
+
if (this.metaCapabilities.hasOwnProperty(capability)) {
|
37 |
+
return this.metaCapabilities[capability];
|
38 |
+
}
|
39 |
return null;
|
40 |
};
|
41 |
AmeBaseActor.getActorSpecificity = function (actorId) {
|
60 |
};
|
61 |
return AmeBaseActor;
|
62 |
}());
|
63 |
+
var AmeRole = /** @class */ (function (_super) {
|
64 |
__extends(AmeRole, _super);
|
65 |
+
function AmeRole(roleId, displayName, capabilities, metaCapabilities) {
|
66 |
+
if (metaCapabilities === void 0) { metaCapabilities = {}; }
|
67 |
+
var _this = _super.call(this, 'role:' + roleId, displayName, capabilities, metaCapabilities) || this;
|
68 |
_this.name = roleId;
|
69 |
return _this;
|
70 |
}
|
79 |
};
|
80 |
return AmeRole;
|
81 |
}(AmeBaseActor));
|
82 |
+
var AmeUser = /** @class */ (function (_super) {
|
83 |
__extends(AmeUser, _super);
|
84 |
+
function AmeUser(userLogin, displayName, capabilities, roles, isSuperAdmin, userId, metaCapabilities) {
|
85 |
if (isSuperAdmin === void 0) { isSuperAdmin = false; }
|
86 |
+
if (metaCapabilities === void 0) { metaCapabilities = {}; }
|
87 |
+
var _this = _super.call(this, 'user:' + userLogin, displayName, capabilities, metaCapabilities) || this;
|
88 |
_this.userId = 0;
|
89 |
_this.isSuperAdmin = false;
|
90 |
_this.avatarHTML = '';
|
|
|
91 |
_this.userLogin = userLogin;
|
92 |
_this.roles = roles;
|
93 |
_this.isSuperAdmin = isSuperAdmin;
|
101 |
return _this;
|
102 |
}
|
103 |
AmeUser.createFromProperties = function (properties) {
|
104 |
+
var user = new AmeUser(properties.user_login, properties.display_name, properties.capabilities, properties.roles, properties.is_super_admin, properties.hasOwnProperty('id') ? properties.id : null, properties.meta_capabilities);
|
105 |
if (properties.avatar_html) {
|
106 |
user.avatarHTML = properties.avatar_html;
|
107 |
}
|
109 |
};
|
110 |
return AmeUser;
|
111 |
}(AmeBaseActor));
|
112 |
+
var AmeSuperAdmin = /** @class */ (function (_super) {
|
113 |
__extends(AmeSuperAdmin, _super);
|
114 |
function AmeSuperAdmin() {
|
115 |
+
return _super.call(this, AmeSuperAdmin.permanentActorId, 'Super Admin', {}) || this;
|
|
|
|
|
116 |
}
|
117 |
AmeSuperAdmin.prototype.hasOwnCap = function (capability) {
|
118 |
//The Super Admin has all possible capabilities except the special "do_not_allow" flag.
|
121 |
AmeSuperAdmin.permanentActorId = 'special:super_admin';
|
122 |
return AmeSuperAdmin;
|
123 |
}(AmeBaseActor));
|
124 |
+
var AmeActorManager = /** @class */ (function () {
|
125 |
+
function AmeActorManager(roles, users, isMultisite, suspectedMetaCaps) {
|
126 |
if (isMultisite === void 0) { isMultisite = false; }
|
127 |
+
if (suspectedMetaCaps === void 0) { suspectedMetaCaps = {}; }
|
128 |
var _this = this;
|
129 |
this.roles = {};
|
130 |
this.users = {};
|
135 |
this.suggestedCapabilities = [];
|
136 |
this.isMultisite = !!isMultisite;
|
137 |
AmeActorManager._.forEach(roles, function (roleDetails, id) {
|
138 |
+
var role = new AmeRole(id, roleDetails.name, roleDetails.capabilities, AmeActorManager._.get(roleDetails, 'meta_capabilities', {}));
|
139 |
_this.roles[role.name] = role;
|
140 |
});
|
141 |
AmeActorManager._.forEach(users, function (userDetails) {
|
145 |
if (this.isMultisite) {
|
146 |
this.superAdmin = new AmeSuperAdmin();
|
147 |
}
|
148 |
+
this.suspectedMetaCaps = suspectedMetaCaps;
|
149 |
var exclusiveCaps = [
|
150 |
'update_core', 'update_plugins', 'delete_plugins', 'install_plugins', 'upload_plugins', 'update_themes',
|
151 |
'delete_themes', 'install_themes', 'upload_themes', 'update_core', 'edit_css', 'unfiltered_html',
|
239 |
}
|
240 |
}
|
241 |
//Step #3: Check owned/default capabilities. Always checked.
|
242 |
+
var actor = this.getActor(actorId);
|
243 |
+
if (actor === null) {
|
244 |
+
return false;
|
245 |
+
}
|
246 |
+
var hasOwnCap = actor.hasOwnCap(capability);
|
247 |
if (hasOwnCap !== null) {
|
248 |
return hasOwnCap;
|
249 |
}
|
255 |
return this.actorHasCap('special:super_admin', capability, contextList);
|
256 |
}
|
257 |
//Check if any of the user's roles have the capability.
|
258 |
+
result = null;
|
259 |
for (var index = 0; index < actor.roles.length; index++) {
|
260 |
+
var roleHasCap = this.actorHasCap('role:' + actor.roles[index], capability, contextList);
|
261 |
+
if (roleHasCap !== null) {
|
262 |
+
result = result || roleHasCap;
|
263 |
+
}
|
264 |
}
|
265 |
+
if (result !== null) {
|
266 |
+
return result;
|
267 |
+
}
|
268 |
+
}
|
269 |
+
if (this.suspectedMetaCaps.hasOwnProperty(capability)) {
|
270 |
+
return null;
|
271 |
}
|
272 |
return false;
|
273 |
};
|
341 |
var grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap;
|
342 |
AmeActorManager._.set(context, [actor, capability], grant);
|
343 |
};
|
|
|
|
|
|
|
344 |
AmeActorManager.prototype.resetCapInContext = function (context, actor, capability) {
|
345 |
capability = this.mapMetaCap(capability);
|
346 |
if (AmeActorManager._.has(context, [actor, capability])) {
|
368 |
_.forEach(_.keys(pruned[actor]), function (capability) {
|
369 |
var grant = pruned[actor][capability];
|
370 |
delete pruned[actor][capability];
|
371 |
+
var hasCap = _.isArray(grant) ? grant[0] : grant, hasCapWhenPruned = !!_this.actorHasCap(actor, capability, context);
|
372 |
if (hasCap !== hasCapWhenPruned) {
|
373 |
pruned[actor][capability] = grant; //Restore.
|
374 |
}
|
484 |
return AmeActorManager;
|
485 |
}());
|
486 |
if (typeof wsAmeActorData !== 'undefined') {
|
487 |
+
AmeActors = new AmeActorManager(wsAmeActorData.roles, wsAmeActorData.users, wsAmeActorData.isMultisite, wsAmeActorData.suspectedMetaCaps);
|
488 |
if (typeof wsAmeActorData['capPower'] !== 'undefined') {
|
489 |
AmeActors.generateCapabilitySuggestions(wsAmeActorData['capPower']);
|
490 |
}
|
js/actor-manager.ts
CHANGED
@@ -5,6 +5,9 @@ declare let wsAmeActorData: any;
|
|
5 |
declare var wsAmeLodash: _.LoDashStatic;
|
6 |
declare let AmeActors: AmeActorManager;
|
7 |
|
|
|
|
|
|
|
8 |
interface CapabilityMap {
|
9 |
[capabilityName: string] : boolean;
|
10 |
}
|
@@ -13,15 +16,15 @@ abstract class AmeBaseActor {
|
|
13 |
public id: string;
|
14 |
public displayName: string = '[Error: No displayName set]';
|
15 |
public capabilities: CapabilityMap;
|
|
|
16 |
|
17 |
groupActors: string[] = [];
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
constructor(id: string, displayName: string, capabilities: CapabilityMap) {
|
22 |
this.id = id;
|
23 |
this.displayName = displayName;
|
24 |
this.capabilities = capabilities;
|
|
|
25 |
}
|
26 |
|
27 |
/**
|
@@ -37,6 +40,9 @@ abstract class AmeBaseActor {
|
|
37 |
if (this.capabilities.hasOwnProperty(capability)) {
|
38 |
return this.capabilities[capability];
|
39 |
}
|
|
|
|
|
|
|
40 |
return null;
|
41 |
}
|
42 |
|
@@ -66,10 +72,9 @@ abstract class AmeBaseActor {
|
|
66 |
|
67 |
class AmeRole extends AmeBaseActor {
|
68 |
name: string;
|
69 |
-
protected actorTypeSpecificity = 1;
|
70 |
|
71 |
-
constructor(roleId: string, displayName: string, capabilities: CapabilityMap) {
|
72 |
-
super('role:' + roleId, displayName, capabilities);
|
73 |
this.name = roleId;
|
74 |
}
|
75 |
|
@@ -88,6 +93,7 @@ interface AmeUserPropertyMap {
|
|
88 |
user_login: string;
|
89 |
display_name: string;
|
90 |
capabilities: CapabilityMap;
|
|
|
91 |
roles : string[];
|
92 |
is_super_admin: boolean;
|
93 |
id?: number;
|
@@ -102,17 +108,16 @@ class AmeUser extends AmeBaseActor {
|
|
102 |
groupActors: string[];
|
103 |
avatarHTML: string = '';
|
104 |
|
105 |
-
protected actorTypeSpecificity = 10;
|
106 |
-
|
107 |
constructor(
|
108 |
userLogin: string,
|
109 |
displayName: string,
|
110 |
capabilities: CapabilityMap,
|
111 |
roles: string[],
|
112 |
isSuperAdmin: boolean = false,
|
113 |
-
userId?: number
|
|
|
114 |
) {
|
115 |
-
super('user:' + userLogin, displayName, capabilities);
|
116 |
|
117 |
this.userLogin = userLogin;
|
118 |
this.roles = roles;
|
@@ -134,7 +139,8 @@ class AmeUser extends AmeBaseActor {
|
|
134 |
properties.capabilities,
|
135 |
properties.roles,
|
136 |
properties.is_super_admin,
|
137 |
-
properties.hasOwnProperty('id') ? properties.id : null
|
|
|
138 |
);
|
139 |
|
140 |
if (properties.avatar_html) {
|
@@ -147,7 +153,6 @@ class AmeUser extends AmeBaseActor {
|
|
147 |
|
148 |
class AmeSuperAdmin extends AmeBaseActor {
|
149 |
static permanentActorId = 'special:super_admin';
|
150 |
-
protected actorTypeSpecificity = 2;
|
151 |
|
152 |
constructor() {
|
153 |
super(AmeSuperAdmin.permanentActorId, 'Super Admin', {});
|
@@ -182,14 +187,20 @@ class AmeActorManager {
|
|
182 |
private exclusiveSuperAdminCapabilities = {};
|
183 |
|
184 |
private tagMetaCaps = {};
|
|
|
185 |
|
186 |
private suggestedCapabilities: AmeCapabilitySuggestion[] = [];
|
187 |
|
188 |
-
constructor(roles, users, isMultisite:
|
189 |
this.isMultisite = !!isMultisite;
|
190 |
|
191 |
AmeActorManager._.forEach(roles, (roleDetails, id) => {
|
192 |
-
const role = new AmeRole(
|
|
|
|
|
|
|
|
|
|
|
193 |
this.roles[role.name] = role;
|
194 |
});
|
195 |
|
@@ -202,6 +213,8 @@ class AmeActorManager {
|
|
202 |
this.superAdmin = new AmeSuperAdmin();
|
203 |
}
|
204 |
|
|
|
|
|
205 |
const exclusiveCaps: string[] = [
|
206 |
'update_core', 'update_plugins', 'delete_plugins', 'install_plugins', 'upload_plugins', 'update_themes',
|
207 |
'delete_themes', 'install_themes', 'upload_themes', 'update_core', 'edit_css', 'unfiltered_html',
|
@@ -277,7 +290,7 @@ class AmeActorManager {
|
|
277 |
return this.actorHasCap(actorId, capability);
|
278 |
}
|
279 |
|
280 |
-
private actorHasCap(actorId: string, capability: string, contextList?: Array<Object>): boolean {
|
281 |
//It's like the chain-of-responsibility pattern.
|
282 |
|
283 |
//Everybody has the "exist" cap and it can't be removed or overridden by plugins.
|
@@ -310,8 +323,11 @@ class AmeActorManager {
|
|
310 |
}
|
311 |
|
312 |
//Step #3: Check owned/default capabilities. Always checked.
|
313 |
-
let actor = this.getActor(actorId)
|
314 |
-
|
|
|
|
|
|
|
315 |
if (hasOwnCap !== null) {
|
316 |
return hasOwnCap;
|
317 |
}
|
@@ -325,13 +341,21 @@ class AmeActorManager {
|
|
325 |
}
|
326 |
|
327 |
//Check if any of the user's roles have the capability.
|
328 |
-
result =
|
329 |
for (let index = 0; index < actor.roles.length; index++) {
|
330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
331 |
}
|
332 |
-
return result;
|
333 |
}
|
334 |
|
|
|
|
|
|
|
335 |
return false;
|
336 |
}
|
337 |
|
@@ -425,10 +449,6 @@ class AmeActorManager {
|
|
425 |
AmeActorManager._.set(context, [actor, capability], grant);
|
426 |
}
|
427 |
|
428 |
-
resetCap(actor: string, capability: string) {
|
429 |
-
this.resetCapInContext(this.grantedCapabilities, actor, capability);
|
430 |
-
}
|
431 |
-
|
432 |
public resetCapInContext(context: AmeGrantedCapabilityMap, actor: string, capability: string) {
|
433 |
capability = this.mapMetaCap(capability);
|
434 |
|
@@ -463,7 +483,7 @@ class AmeActorManager {
|
|
463 |
delete pruned[actor][capability];
|
464 |
|
465 |
const hasCap = _.isArray(grant) ? grant[0] : grant,
|
466 |
-
hasCapWhenPruned = this.actorHasCap(actor, capability, context);
|
467 |
|
468 |
if (hasCap !== hasCapWhenPruned) {
|
469 |
pruned[actor][capability] = grant; //Restore.
|
@@ -605,7 +625,8 @@ if (typeof wsAmeActorData !== 'undefined') {
|
|
605 |
AmeActors = new AmeActorManager(
|
606 |
wsAmeActorData.roles,
|
607 |
wsAmeActorData.users,
|
608 |
-
wsAmeActorData.isMultisite
|
|
|
609 |
);
|
610 |
|
611 |
if (typeof wsAmeActorData['capPower'] !== 'undefined') {
|
5 |
declare var wsAmeLodash: _.LoDashStatic;
|
6 |
declare let AmeActors: AmeActorManager;
|
7 |
|
8 |
+
type Falsy = false | null | '' | undefined | 0;
|
9 |
+
type Truthy = true | string | 1;
|
10 |
+
|
11 |
interface CapabilityMap {
|
12 |
[capabilityName: string] : boolean;
|
13 |
}
|
16 |
public id: string;
|
17 |
public displayName: string = '[Error: No displayName set]';
|
18 |
public capabilities: CapabilityMap;
|
19 |
+
public metaCapabilities: CapabilityMap;
|
20 |
|
21 |
groupActors: string[] = [];
|
22 |
|
23 |
+
constructor(id: string, displayName: string, capabilities: CapabilityMap, metaCapabilities: CapabilityMap = {}) {
|
|
|
|
|
24 |
this.id = id;
|
25 |
this.displayName = displayName;
|
26 |
this.capabilities = capabilities;
|
27 |
+
this.metaCapabilities = metaCapabilities;
|
28 |
}
|
29 |
|
30 |
/**
|
40 |
if (this.capabilities.hasOwnProperty(capability)) {
|
41 |
return this.capabilities[capability];
|
42 |
}
|
43 |
+
if (this.metaCapabilities.hasOwnProperty(capability)) {
|
44 |
+
return this.metaCapabilities[capability];
|
45 |
+
}
|
46 |
return null;
|
47 |
}
|
48 |
|
72 |
|
73 |
class AmeRole extends AmeBaseActor {
|
74 |
name: string;
|
|
|
75 |
|
76 |
+
constructor(roleId: string, displayName: string, capabilities: CapabilityMap, metaCapabilities: CapabilityMap = {}) {
|
77 |
+
super('role:' + roleId, displayName, capabilities, metaCapabilities);
|
78 |
this.name = roleId;
|
79 |
}
|
80 |
|
93 |
user_login: string;
|
94 |
display_name: string;
|
95 |
capabilities: CapabilityMap;
|
96 |
+
meta_capabilities: CapabilityMap;
|
97 |
roles : string[];
|
98 |
is_super_admin: boolean;
|
99 |
id?: number;
|
108 |
groupActors: string[];
|
109 |
avatarHTML: string = '';
|
110 |
|
|
|
|
|
111 |
constructor(
|
112 |
userLogin: string,
|
113 |
displayName: string,
|
114 |
capabilities: CapabilityMap,
|
115 |
roles: string[],
|
116 |
isSuperAdmin: boolean = false,
|
117 |
+
userId?: number,
|
118 |
+
metaCapabilities: CapabilityMap = {}
|
119 |
) {
|
120 |
+
super('user:' + userLogin, displayName, capabilities, metaCapabilities);
|
121 |
|
122 |
this.userLogin = userLogin;
|
123 |
this.roles = roles;
|
139 |
properties.capabilities,
|
140 |
properties.roles,
|
141 |
properties.is_super_admin,
|
142 |
+
properties.hasOwnProperty('id') ? properties.id : null,
|
143 |
+
properties.meta_capabilities
|
144 |
);
|
145 |
|
146 |
if (properties.avatar_html) {
|
153 |
|
154 |
class AmeSuperAdmin extends AmeBaseActor {
|
155 |
static permanentActorId = 'special:super_admin';
|
|
|
156 |
|
157 |
constructor() {
|
158 |
super(AmeSuperAdmin.permanentActorId, 'Super Admin', {});
|
187 |
private exclusiveSuperAdminCapabilities = {};
|
188 |
|
189 |
private tagMetaCaps = {};
|
190 |
+
private suspectedMetaCaps: CapabilityMap;
|
191 |
|
192 |
private suggestedCapabilities: AmeCapabilitySuggestion[] = [];
|
193 |
|
194 |
+
constructor(roles, users, isMultisite: Truthy | Falsy = false, suspectedMetaCaps: CapabilityMap = {}) {
|
195 |
this.isMultisite = !!isMultisite;
|
196 |
|
197 |
AmeActorManager._.forEach(roles, (roleDetails, id) => {
|
198 |
+
const role = new AmeRole(
|
199 |
+
id,
|
200 |
+
roleDetails.name,
|
201 |
+
roleDetails.capabilities,
|
202 |
+
AmeActorManager._.get(roleDetails, 'meta_capabilities', {})
|
203 |
+
);
|
204 |
this.roles[role.name] = role;
|
205 |
});
|
206 |
|
213 |
this.superAdmin = new AmeSuperAdmin();
|
214 |
}
|
215 |
|
216 |
+
this.suspectedMetaCaps = suspectedMetaCaps;
|
217 |
+
|
218 |
const exclusiveCaps: string[] = [
|
219 |
'update_core', 'update_plugins', 'delete_plugins', 'install_plugins', 'upload_plugins', 'update_themes',
|
220 |
'delete_themes', 'install_themes', 'upload_themes', 'update_core', 'edit_css', 'unfiltered_html',
|
290 |
return this.actorHasCap(actorId, capability);
|
291 |
}
|
292 |
|
293 |
+
private actorHasCap(actorId: string, capability: string, contextList?: Array<Object>): (boolean | null) {
|
294 |
//It's like the chain-of-responsibility pattern.
|
295 |
|
296 |
//Everybody has the "exist" cap and it can't be removed or overridden by plugins.
|
323 |
}
|
324 |
|
325 |
//Step #3: Check owned/default capabilities. Always checked.
|
326 |
+
let actor = this.getActor(actorId);
|
327 |
+
if (actor === null) {
|
328 |
+
return false;
|
329 |
+
}
|
330 |
+
let hasOwnCap = actor.hasOwnCap(capability);
|
331 |
if (hasOwnCap !== null) {
|
332 |
return hasOwnCap;
|
333 |
}
|
341 |
}
|
342 |
|
343 |
//Check if any of the user's roles have the capability.
|
344 |
+
result = null;
|
345 |
for (let index = 0; index < actor.roles.length; index++) {
|
346 |
+
let roleHasCap = this.actorHasCap('role:' + actor.roles[index], capability, contextList);
|
347 |
+
if (roleHasCap !== null) {
|
348 |
+
result = result || roleHasCap;
|
349 |
+
}
|
350 |
+
}
|
351 |
+
if (result !== null) {
|
352 |
+
return result;
|
353 |
}
|
|
|
354 |
}
|
355 |
|
356 |
+
if (this.suspectedMetaCaps.hasOwnProperty(capability)) {
|
357 |
+
return null;
|
358 |
+
}
|
359 |
return false;
|
360 |
}
|
361 |
|
449 |
AmeActorManager._.set(context, [actor, capability], grant);
|
450 |
}
|
451 |
|
|
|
|
|
|
|
|
|
452 |
public resetCapInContext(context: AmeGrantedCapabilityMap, actor: string, capability: string) {
|
453 |
capability = this.mapMetaCap(capability);
|
454 |
|
483 |
delete pruned[actor][capability];
|
484 |
|
485 |
const hasCap = _.isArray(grant) ? grant[0] : grant,
|
486 |
+
hasCapWhenPruned = !!this.actorHasCap(actor, capability, context);
|
487 |
|
488 |
if (hasCap !== hasCapWhenPruned) {
|
489 |
pruned[actor][capability] = grant; //Restore.
|
625 |
AmeActors = new AmeActorManager(
|
626 |
wsAmeActorData.roles,
|
627 |
wsAmeActorData.users,
|
628 |
+
wsAmeActorData.isMultisite,
|
629 |
+
wsAmeActorData.suspectedMetaCaps
|
630 |
);
|
631 |
|
632 |
if (typeof wsAmeActorData['capPower'] !== 'undefined') {
|
js/menu-editor.js
CHANGED
@@ -1191,13 +1191,13 @@ function updateActorAccessUi(containerNode) {
|
|
1191 |
var checkbox = containerNode.find('.ws_actor_access_checkbox');
|
1192 |
checkbox.prop('checked', hasAccess);
|
1193 |
|
1194 |
-
//Display the checkbox
|
|
|
|
|
|
|
1195 |
//or if their permissions don't match this menu's permissions.
|
1196 |
var submenuItems = getSubmenuItemNodes(containerNode);
|
1197 |
-
if ((submenuItems.length
|
1198 |
-
//Either this menu doesn't contain any items, or their permissions don't matter because they're overridden.
|
1199 |
-
checkbox.prop('indeterminate', false);
|
1200 |
-
} else {
|
1201 |
var differentPermissions = false;
|
1202 |
submenuItems.each(function() {
|
1203 |
var item = $(this).data('menu_item');
|
@@ -1212,7 +1212,24 @@ function updateActorAccessUi(containerNode) {
|
|
1212 |
return true;
|
1213 |
});
|
1214 |
|
1215 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1216 |
}
|
1217 |
|
1218 |
containerNode.toggleClass('ws_is_hidden_for_actor', !hasAccess);
|
@@ -1222,12 +1239,17 @@ function updateActorAccessUi(containerNode) {
|
|
1222 |
} else {
|
1223 |
containerNode.removeClass('ws_is_hidden_for_actor ws_has_custom_permissions_for_actor');
|
1224 |
setMenuFlag(containerNode, 'custom_actor_permissions', false);
|
|
|
1225 |
|
1226 |
var currentUserActor = 'user:' + wsEditorData.currentUserLogin;
|
1227 |
var otherActors = _(wsEditorData.actors).keys().without(currentUserActor, 'special:super_admin').value(),
|
1228 |
hiddenFromCurrentUser = ! actorCanAccessMenu(menuItem, currentUserActor),
|
1229 |
-
|
|
|
|
|
|
|
1230 |
visibleForSuperAdmin = AmeActors.isMultisite && actorCanAccessMenu(menuItem, 'special:super_admin');
|
|
|
1231 |
setMenuFlag(
|
1232 |
containerNode,
|
1233 |
'hidden_from_others',
|
@@ -1729,11 +1751,12 @@ function readAllFields(container){
|
|
1729 |
***************************************************************************/
|
1730 |
|
1731 |
var item_flags = {
|
1732 |
-
'custom':'This is a custom menu item',
|
1733 |
-
'unused':'This item was
|
1734 |
-
'hidden':'Cosmetically hidden',
|
1735 |
-
'custom_actor_permissions'
|
1736 |
-
'hidden_from_others'
|
|
|
1737 |
};
|
1738 |
|
1739 |
function setMenuFlag(item, flag, state, title) {
|
@@ -4729,6 +4752,17 @@ function ameOnDomReady() {
|
|
4729 |
);
|
4730 |
});
|
4731 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4732 |
|
4733 |
/******************************************************************
|
4734 |
Actor views
|
1191 |
var checkbox = containerNode.find('.ws_actor_access_checkbox');
|
1192 |
checkbox.prop('checked', hasAccess);
|
1193 |
|
1194 |
+
//Display the checkbox in an indeterminate state if the actual menu permissions are unknown
|
1195 |
+
//because it uses meta capabilities.
|
1196 |
+
var isIndeterminate = (hasAccess === null);
|
1197 |
+
//Also show it as indeterminate if some items of this menu are hidden and some are visible,
|
1198 |
//or if their permissions don't match this menu's permissions.
|
1199 |
var submenuItems = getSubmenuItemNodes(containerNode);
|
1200 |
+
if ((submenuItems.length > 0) && !isOverrideActive) {
|
|
|
|
|
|
|
1201 |
var differentPermissions = false;
|
1202 |
submenuItems.each(function() {
|
1203 |
var item = $(this).data('menu_item');
|
1212 |
return true;
|
1213 |
});
|
1214 |
|
1215 |
+
if (differentPermissions) {
|
1216 |
+
isIndeterminate = true;
|
1217 |
+
}
|
1218 |
+
}
|
1219 |
+
checkbox.prop('indeterminate', isIndeterminate);
|
1220 |
+
|
1221 |
+
if (isIndeterminate && (hasAccess === null)) {
|
1222 |
+
setMenuFlag(
|
1223 |
+
containerNode,
|
1224 |
+
'uncertain_meta_cap',
|
1225 |
+
true,
|
1226 |
+
"This item might be visible.\n"
|
1227 |
+
+ "The plugin cannot reliably detect if \"" + actorSelectorWidget.selectedDisplayName
|
1228 |
+
+ "\" has the \"" + getFieldValue(menuItem, 'access_level', '[No capability]')
|
1229 |
+
+ "\" capability. If you need to hide the item, try checking and then unchecking it."
|
1230 |
+
);
|
1231 |
+
} else {
|
1232 |
+
setMenuFlag(containerNode, 'uncertain_meta_cap', false);
|
1233 |
}
|
1234 |
|
1235 |
containerNode.toggleClass('ws_is_hidden_for_actor', !hasAccess);
|
1239 |
} else {
|
1240 |
containerNode.removeClass('ws_is_hidden_for_actor ws_has_custom_permissions_for_actor');
|
1241 |
setMenuFlag(containerNode, 'custom_actor_permissions', false);
|
1242 |
+
setMenuFlag(containerNode, 'uncertain_meta_cap', false);
|
1243 |
|
1244 |
var currentUserActor = 'user:' + wsEditorData.currentUserLogin;
|
1245 |
var otherActors = _(wsEditorData.actors).keys().without(currentUserActor, 'special:super_admin').value(),
|
1246 |
hiddenFromCurrentUser = ! actorCanAccessMenu(menuItem, currentUserActor),
|
1247 |
+
hasAccessToThisItem = _.curry(actorCanAccessMenu, 2)(menuItem),
|
1248 |
+
hiddenFromOthers = _.every(otherActors, function(actorId) {
|
1249 |
+
return (hasAccessToThisItem(actorId) === false);
|
1250 |
+
}),
|
1251 |
visibleForSuperAdmin = AmeActors.isMultisite && actorCanAccessMenu(menuItem, 'special:super_admin');
|
1252 |
+
|
1253 |
setMenuFlag(
|
1254 |
containerNode,
|
1255 |
'hidden_from_others',
|
1751 |
***************************************************************************/
|
1752 |
|
1753 |
var item_flags = {
|
1754 |
+
'custom': 'This is a custom menu item',
|
1755 |
+
'unused': 'This item was added since the last time you saved menu settings.',
|
1756 |
+
'hidden': 'Cosmetically hidden',
|
1757 |
+
'custom_actor_permissions': "The selected role has custom permissions for this item.",
|
1758 |
+
'hidden_from_others': 'Hidden from everyone except you.',
|
1759 |
+
'uncertain_meta_cap': 'The plugin cannot detect if this item is visible by default.'
|
1760 |
};
|
1761 |
|
1762 |
function setMenuFlag(item, flag, state, title) {
|
4752 |
);
|
4753 |
});
|
4754 |
|
4755 |
+
//Expand/collapse the "How To" box.
|
4756 |
+
var $howToBox = $("#ws_ame_how_to_box");
|
4757 |
+
$howToBox.find(".handlediv").click(function() {
|
4758 |
+
$howToBox.toggleClass('closed');
|
4759 |
+
$.cookie(
|
4760 |
+
'ame_how_to_box_open',
|
4761 |
+
($howToBox.hasClass('closed') ? '0' : '1'),
|
4762 |
+
{ expires: 180 }
|
4763 |
+
);
|
4764 |
+
});
|
4765 |
+
|
4766 |
|
4767 |
/******************************************************************
|
4768 |
Actor views
|
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.8.
|
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.8.2
|
7 |
Author: Janis Elsts
|
8 |
Author URI: http://w-shadow.com/blog/
|
9 |
*/
|
modules/actor-selector/actor-selector.js
CHANGED
@@ -1,10 +1,11 @@
|
|
1 |
/// <reference path="../../js/jquery.d.ts" />
|
2 |
/// <reference path="../../js/jquery-json.d.ts" />
|
3 |
/// <reference path="../../js/actor-manager.ts" />
|
4 |
-
var AmeActorSelector = (function () {
|
5 |
function AmeActorSelector(actorManager, isProVersion, currentUserLogin, visibleUsers, ajaxParams) {
|
6 |
var _this = this;
|
7 |
this.selectedActor = null;
|
|
|
8 |
this.visibleUsers = [];
|
9 |
this.subscribers = [];
|
10 |
this.isProVersion = false;
|
@@ -88,6 +89,12 @@ var AmeActorSelector = (function () {
|
|
88 |
var previousSelection = this.selectedActor;
|
89 |
this.selectedActor = actorId;
|
90 |
this.highlightSelectedActor();
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
//Notify subscribers that the selection has changed.
|
92 |
if (this.selectedActor !== previousSelection) {
|
93 |
for (var i = 0; i < this.subscribers.length; i++) {
|
@@ -199,3 +206,4 @@ var AmeActorSelector = (function () {
|
|
199 |
AmeActorSelector._ = wsAmeLodash;
|
200 |
return AmeActorSelector;
|
201 |
}());
|
|
1 |
/// <reference path="../../js/jquery.d.ts" />
|
2 |
/// <reference path="../../js/jquery-json.d.ts" />
|
3 |
/// <reference path="../../js/actor-manager.ts" />
|
4 |
+
var AmeActorSelector = /** @class */ (function () {
|
5 |
function AmeActorSelector(actorManager, isProVersion, currentUserLogin, visibleUsers, ajaxParams) {
|
6 |
var _this = this;
|
7 |
this.selectedActor = null;
|
8 |
+
this.selectedDisplayName = 'All';
|
9 |
this.visibleUsers = [];
|
10 |
this.subscribers = [];
|
11 |
this.isProVersion = false;
|
89 |
var previousSelection = this.selectedActor;
|
90 |
this.selectedActor = actorId;
|
91 |
this.highlightSelectedActor();
|
92 |
+
if (actorId !== null) {
|
93 |
+
this.selectedDisplayName = this.actorManager.getActor(actorId).displayName;
|
94 |
+
}
|
95 |
+
else {
|
96 |
+
this.selectedDisplayName = 'All';
|
97 |
+
}
|
98 |
//Notify subscribers that the selection has changed.
|
99 |
if (this.selectedActor !== previousSelection) {
|
100 |
for (var i = 0; i < this.subscribers.length; i++) {
|
206 |
AmeActorSelector._ = wsAmeLodash;
|
207 |
return AmeActorSelector;
|
208 |
}());
|
209 |
+
//# sourceMappingURL=actor-selector.js.map
|
modules/actor-selector/actor-selector.ts
CHANGED
@@ -27,6 +27,7 @@ class AmeActorSelector {
|
|
27 |
private static _ = wsAmeLodash;
|
28 |
|
29 |
public selectedActor: string = null;
|
|
|
30 |
|
31 |
private visibleUsers: string[] = [];
|
32 |
private subscribers: SelectedActorChangedCallback[] = [];
|
@@ -128,6 +129,12 @@ class AmeActorSelector {
|
|
128 |
this.selectedActor = actorId;
|
129 |
this.highlightSelectedActor();
|
130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
//Notify subscribers that the selection has changed.
|
132 |
if (this.selectedActor !== previousSelection) {
|
133 |
for (let i = 0; i < this.subscribers.length; i++) {
|
27 |
private static _ = wsAmeLodash;
|
28 |
|
29 |
public selectedActor: string = null;
|
30 |
+
public selectedDisplayName: string = 'All';
|
31 |
|
32 |
private visibleUsers: string[] = [];
|
33 |
private subscribers: SelectedActorChangedCallback[] = [];
|
129 |
this.selectedActor = actorId;
|
130 |
this.highlightSelectedActor();
|
131 |
|
132 |
+
if (actorId !== null) {
|
133 |
+
this.selectedDisplayName = this.actorManager.getActor(actorId).displayName;
|
134 |
+
} else {
|
135 |
+
this.selectedDisplayName = 'All';
|
136 |
+
}
|
137 |
+
|
138 |
//Notify subscribers that the selection has changed.
|
139 |
if (this.selectedActor !== previousSelection) {
|
140 |
for (let i = 0; i < this.subscribers.length; i++) {
|
modules/highlight-new-menus/wsNewMenuHighlighter.php
CHANGED
@@ -44,6 +44,12 @@ class wsNewMenuHighlighter {
|
|
44 |
'user-new.php' => true,
|
45 |
'users.php' => true,
|
46 |
'widgets.php' => true,
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
);
|
48 |
|
49 |
private $menusWithNewSubmenus = array();
|
@@ -180,10 +186,10 @@ class wsNewMenuHighlighter {
|
|
180 |
|
181 |
if ( isset($GLOBALS['wp_menu_editor']) && is_callable(array(
|
182 |
$GLOBALS['wp_menu_editor'],
|
183 |
-
'
|
184 |
))
|
185 |
) {
|
186 |
-
$GLOBALS['wp_menu_editor']->
|
187 |
$dependencies[] = 'jquery-cookie';
|
188 |
}
|
189 |
|
44 |
'user-new.php' => true,
|
45 |
'users.php' => true,
|
46 |
'widgets.php' => true,
|
47 |
+
//Network admin items.
|
48 |
+
'settings.php' => true,
|
49 |
+
'site-new.php' => true,
|
50 |
+
'sites.php' => true,
|
51 |
+
'theme-install.php' => true,
|
52 |
+
'upgrade.php' => true,
|
53 |
);
|
54 |
|
55 |
private $menusWithNewSubmenus = array();
|
186 |
|
187 |
if ( isset($GLOBALS['wp_menu_editor']) && is_callable(array(
|
188 |
$GLOBALS['wp_menu_editor'],
|
189 |
+
'register_jquery_plugins',
|
190 |
))
|
191 |
) {
|
192 |
+
$GLOBALS['wp_menu_editor']->register_jquery_plugins();
|
193 |
$dependencies[] = 'jquery-cookie';
|
194 |
}
|
195 |
|
modules/plugin-visibility/plugin-visibility.js
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
/// <reference path="../../js/lodash-3.10.d.ts" />
|
5 |
/// <reference path="../../modules/actor-selector/actor-selector.ts" />
|
6 |
/// <reference path="../../ajax-wrapper/ajax-action-wrapper.d.ts" />
|
7 |
-
var AmePluginVisibilityModule = (function () {
|
8 |
function AmePluginVisibilityModule(scriptData) {
|
9 |
var _this = this;
|
10 |
var _ = AmePluginVisibilityModule._;
|
@@ -159,7 +159,7 @@ var AmePluginVisibilityModule = (function () {
|
|
159 |
AmePluginVisibilityModule._ = wsAmeLodash;
|
160 |
return AmePluginVisibilityModule;
|
161 |
}());
|
162 |
-
var AmePlugin = (function () {
|
163 |
function AmePlugin(details, settings, module) {
|
164 |
var _this = this;
|
165 |
var _ = AmePluginVisibilityModule._;
|
4 |
/// <reference path="../../js/lodash-3.10.d.ts" />
|
5 |
/// <reference path="../../modules/actor-selector/actor-selector.ts" />
|
6 |
/// <reference path="../../ajax-wrapper/ajax-action-wrapper.d.ts" />
|
7 |
+
var AmePluginVisibilityModule = /** @class */ (function () {
|
8 |
function AmePluginVisibilityModule(scriptData) {
|
9 |
var _this = this;
|
10 |
var _ = AmePluginVisibilityModule._;
|
159 |
AmePluginVisibilityModule._ = wsAmeLodash;
|
160 |
return AmePluginVisibilityModule;
|
161 |
}());
|
162 |
+
var AmePlugin = /** @class */ (function () {
|
163 |
function AmePlugin(details, settings, module) {
|
164 |
var _this = this;
|
165 |
var _ = AmePluginVisibilityModule._;
|
modules/plugin-visibility/plugin-visibility.php
CHANGED
@@ -20,7 +20,7 @@ class amePluginVisibility {
|
|
20 |
self::$lastInstance = $this;
|
21 |
|
22 |
//Remove "hidden" plugins from the list on the "Plugins -> Installed Plugins" page.
|
23 |
-
add_filter('all_plugins', array($this, 'filterPluginList'));
|
24 |
|
25 |
//It's not possible to completely prevent a user from (de)activating "hidden" plugins because plugin API
|
26 |
//functions like activate_plugin() and deactivate_plugins() don't provide a way to abort (de)activation.
|
20 |
self::$lastInstance = $this;
|
21 |
|
22 |
//Remove "hidden" plugins from the list on the "Plugins -> Installed Plugins" page.
|
23 |
+
add_filter('all_plugins', array($this, 'filterPluginList'), 15);
|
24 |
|
25 |
//It's not possible to completely prevent a user from (de)activating "hidden" plugins because plugin API
|
26 |
//functions like activate_plugin() and deactivate_plugins() don't provide a way to abort (de)activation.
|
readme.txt
CHANGED
@@ -3,8 +3,8 @@ 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: 4.1
|
6 |
-
Tested up to: 4.9
|
7 |
-
Stable tag: 1.8.
|
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,14 @@ Plugins installed in the `mu-plugins` directory are treated as "always on", so y
|
|
63 |
|
64 |
== Changelog ==
|
65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
= 1.8.1 =
|
67 |
* Added a workaround for a buggy "defer_parsing_of_js" code snippet that some users have added to their functions.php. This snippet produces invalid HTML code, which used to break the menu editor.
|
68 |
* Fixed a PHP warning that appeared when using this plugin together with WooCommerce or YITH WooCommerce Gift Cards and running PHP 7.1.
|
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: 4.1
|
6 |
+
Tested up to: 4.9.4
|
7 |
+
Stable tag: 1.8.2
|
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.8.2 =
|
67 |
+
* Fixed the PHP warning "count(): Parameter must be an array or an object that implements Countable in menu-editor-core.php".
|
68 |
+
* Fixed a bug that could cause some network admin menus to be highlighted in green as if they were new.
|
69 |
+
* Fixed a conflict with WP Courseware 4.1.2 where activating AME would cause many extra menu items to show up unexpectedly.
|
70 |
+
* Fixed a conflict with Ultra WordPress Admin 7.4 that made it impossible to hide plugins.
|
71 |
+
* Replaced the "this is a new item" icon with a different one.
|
72 |
+
* Tested with WP 4.9.4.
|
73 |
+
|
74 |
= 1.8.1 =
|
75 |
* Added a workaround for a buggy "defer_parsing_of_js" code snippet that some users have added to their functions.php. This snippet produces invalid HTML code, which used to break the menu editor.
|
76 |
* Fixed a PHP warning that appeared when using this plugin together with WooCommerce or YITH WooCommerce Gift Cards and running PHP 7.1.
|