Version Description
- 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.
- Fixed a PHP warning that appeared when using this plugin together with WooCommerce or YITH WooCommerce Gift Cards and running PHP 7.1.
- Minor performance improvements.
- Tested with WP 4.8.3 and 4.9.
Download this release
Release Info
| Developer | whiteshadow |
| Plugin | |
| Version | 1.8.1 |
| Comparing to | |
| See all releases | |
Code changes from version 1.8 to 1.8.1
- css/menu-editor.css +2 -0
- css/menu-editor.scss +4 -0
- includes/editor-page.php +6 -5
- includes/menu-editor-core.php +243 -39
- includes/menu-item.php +21 -14
- includes/menu.php +5 -0
- includes/module.php +16 -5
- includes/shadow_plugin_framework.php +5 -1
- js/actor-manager.js +37 -10
- js/actor-manager.ts +39 -9
- js/menu-editor.js +9 -3
- menu-editor.php +1 -1
- modules/access-editor/access-editor.js +3 -3
- modules/actor-selector/actor-selector.js +1 -2
- modules/plugin-visibility/plugin-visibility-template.php +8 -9
- modules/plugin-visibility/plugin-visibility.php +11 -6
- readme.txt +8 -2
css/menu-editor.css
CHANGED
|
@@ -759,6 +759,8 @@ select.ws_dropdown optgroup option {
|
|
| 759 |
|
| 760 |
.ame-color-option {
|
| 761 |
padding: 10px 0; }
|
|
|
|
|
|
|
| 762 |
|
| 763 |
.ame-advanced-menu-color {
|
| 764 |
display: none; }
|
| 759 |
|
| 760 |
.ame-color-option {
|
| 761 |
padding: 10px 0; }
|
| 762 |
+
.ame-color-option .wp-picker-container {
|
| 763 |
+
display: inline-block; }
|
| 764 |
|
| 765 |
.ame-advanced-menu-color {
|
| 766 |
display: none; }
|
css/menu-editor.scss
CHANGED
|
@@ -1031,6 +1031,10 @@ $activeToolTabBackground: #FDFDFD;
|
|
| 1031 |
|
| 1032 |
.ame-color-option {
|
| 1033 |
padding: 10px 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1034 |
}
|
| 1035 |
|
| 1036 |
.ame-advanced-menu-color {
|
| 1031 |
|
| 1032 |
.ame-color-option {
|
| 1033 |
padding: 10px 0;
|
| 1034 |
+
|
| 1035 |
+
.wp-picker-container {
|
| 1036 |
+
display: inline-block;
|
| 1037 |
+
}
|
| 1038 |
}
|
| 1039 |
|
| 1040 |
.ame-advanced-menu-color {
|
includes/editor-page.php
CHANGED
|
@@ -72,9 +72,7 @@ if ( !apply_filters('admin_menu_editor_is_pro', false) ){
|
|
| 72 |
|
| 73 |
<?php
|
| 74 |
if ( !empty($_GET['message']) ){
|
| 75 |
-
if ( intval($_GET['message']) ==
|
| 76 |
-
echo '<div id="message" class="updated notice is-dismissible"><p><strong>Settings saved.</strong></p></div>';
|
| 77 |
-
} elseif ( intval($_GET['message']) == 2 ) {
|
| 78 |
echo '<div id="message" class="error"><p><strong>Failed to decode input! The menu wasn\'t modified.</strong></p></div>';
|
| 79 |
}
|
| 80 |
}
|
|
@@ -250,9 +248,12 @@ function ame_output_sort_buttons($icons) {
|
|
| 250 |
<div class="ws_basic_container">
|
| 251 |
|
| 252 |
<div class="ws_main_container" id="ws_editor_sidebar">
|
| 253 |
-
<form method="post" action="<?php echo
|
| 254 |
<?php wp_nonce_field('menu-editor-form'); ?>
|
| 255 |
<input type="hidden" name="action" value="save_menu">
|
|
|
|
|
|
|
|
|
|
| 256 |
<input type="hidden" name="data" id="ws_data" value="">
|
| 257 |
<input type="hidden" name="data_length" id="ws_data_length" value="">
|
| 258 |
<input type="hidden" name="selected_actor" id="ws_selected_actor" value="">
|
|
@@ -507,7 +508,7 @@ function ame_output_sort_buttons($icons) {
|
|
| 507 |
}
|
| 508 |
|
| 509 |
$defaultIconImages = array(
|
| 510 |
-
'images/generic.png',
|
| 511 |
);
|
| 512 |
foreach($defaultIconImages as $icon) {
|
| 513 |
printf(
|
| 72 |
|
| 73 |
<?php
|
| 74 |
if ( !empty($_GET['message']) ){
|
| 75 |
+
if ( intval($_GET['message']) == 2 ) {
|
|
|
|
|
|
|
| 76 |
echo '<div id="message" class="error"><p><strong>Failed to decode input! The menu wasn\'t modified.</strong></p></div>';
|
| 77 |
}
|
| 78 |
}
|
| 248 |
<div class="ws_basic_container">
|
| 249 |
|
| 250 |
<div class="ws_main_container" id="ws_editor_sidebar">
|
| 251 |
+
<form method="post" action="<?php echo esc_attr(add_query_arg('noheader', '1', $editor_data['current_tab_url'])); ?>" id='ws_main_form' name='ws_main_form'>
|
| 252 |
<?php wp_nonce_field('menu-editor-form'); ?>
|
| 253 |
<input type="hidden" name="action" value="save_menu">
|
| 254 |
+
<?php
|
| 255 |
+
printf('<input type="hidden" name="config_id" value="%s">', esc_attr($editor_data['menu_config_id']));
|
| 256 |
+
?>
|
| 257 |
<input type="hidden" name="data" id="ws_data" value="">
|
| 258 |
<input type="hidden" name="data_length" id="ws_data_length" value="">
|
| 259 |
<input type="hidden" name="selected_actor" id="ws_selected_actor" value="">
|
| 508 |
}
|
| 509 |
|
| 510 |
$defaultIconImages = array(
|
| 511 |
+
admin_url('images/generic.png'),
|
| 512 |
);
|
| 513 |
foreach($defaultIconImages as $icon) {
|
| 514 |
printf(
|
includes/menu-editor-core.php
CHANGED
|
@@ -25,6 +25,9 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 25 |
const VERBOSITY_NORMAL = 2;
|
| 26 |
const VERBOSITY_VERBOSE = 5;
|
| 27 |
|
|
|
|
|
|
|
|
|
|
| 28 |
/**
|
| 29 |
* @var string The heading tag to use for admin pages.
|
| 30 |
*/
|
|
@@ -80,6 +83,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 80 |
private $relative_template_order = array();
|
| 81 |
|
| 82 |
private $cached_custom_menu = null; //Cached, non-merged version of the custom menu. Used by load_custom_menu().
|
|
|
|
| 83 |
private $cached_virtual_caps = null;//List of virtual caps. Used by get_virtual_caps().
|
| 84 |
|
| 85 |
private $cached_user_caps = array(); //A cache of the current user's capabilities. Used only in very specific places.
|
|
@@ -121,6 +125,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 121 |
'hide_advanced_settings' => true,
|
| 122 |
'show_extra_icons' => false,
|
| 123 |
'custom_menu' => null,
|
|
|
|
| 124 |
'first_install_time' => null,
|
| 125 |
'display_survey_notice' => true,
|
| 126 |
'plugin_db_version' => 0,
|
|
@@ -175,7 +180,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 175 |
//WP 4.3+ uses H1 headings for admin pages. Older versions use H2 instead.
|
| 176 |
self::$admin_heading_tag = version_compare($GLOBALS['wp_version'], '4.3', '<') ? 'h2' : 'h1';
|
| 177 |
|
| 178 |
-
$this->settings_link = 'options-general.php?page=menu_editor';
|
| 179 |
|
| 180 |
$this->magic_hooks = true;
|
| 181 |
//Run our hooks last (almost). Priority is less than PHP_INT_MAX mostly for defensive programming purposes.
|
|
@@ -325,7 +330,12 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 325 |
}
|
| 326 |
|
| 327 |
//Set up the tabs for the menu editor page.
|
| 328 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
//The "Settings" tab is always last.
|
| 330 |
$this->tabs['settings'] = 'Settings';
|
| 331 |
}
|
|
@@ -361,7 +371,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 361 |
//The only way we can see the changes made by the second hook is to do the same thing.
|
| 362 |
static $firstRunSkipped = false;
|
| 363 |
if ( !$firstRunSkipped && class_exists('Flow') ) {
|
| 364 |
-
add_action(
|
| 365 |
$firstRunSkipped = true;
|
| 366 |
return;
|
| 367 |
}
|
|
@@ -378,13 +388,17 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 378 |
$this->log_security_note('Current user can edit the admin menu.');
|
| 379 |
|
| 380 |
//Determine the current menu editor page tab.
|
| 381 |
-
|
|
|
|
| 382 |
$tab_title = '';
|
| 383 |
if ($this->current_tab !== 'editor' && isset($this->tabs[$this->current_tab])) {
|
| 384 |
$tab_title = ' - ' . $this->tabs[$this->current_tab];
|
| 385 |
}
|
| 386 |
|
| 387 |
-
$
|
|
|
|
|
|
|
|
|
|
| 388 |
apply_filters('admin_menu_editor-self_page_title', 'Menu Editor') . $tab_title,
|
| 389 |
apply_filters('admin_menu_editor-self_menu_title', 'Menu Editor'),
|
| 390 |
apply_filters('admin_menu_editor-capability', 'manage_options'),
|
|
@@ -408,7 +422,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 408 |
add_action("admin_print_scripts-$page", array($this, 'remove_ultimate_tinymce_qtags'));
|
| 409 |
|
| 410 |
//Make a placeholder for our screen options (hacky)
|
| 411 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
}
|
| 413 |
|
| 414 |
//Compatibility fix for the WooCommerce order count bubble. Must be run before storing or processing $submenu.
|
|
@@ -444,6 +462,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 444 |
|
| 445 |
//Add extra templates that are not part of the normal menu.
|
| 446 |
$this->item_templates = $this->add_special_templates($this->item_templates);
|
|
|
|
| 447 |
|
| 448 |
//Is there a custom menu to use?
|
| 449 |
$custom_menu = $this->load_custom_menu();
|
|
@@ -740,6 +759,10 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 740 |
//it tracks *all* DOM changes using MutationObserver.
|
| 741 |
remove_action('admin_print_scripts', 'print_emoji_detection_script');
|
| 742 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 743 |
$this->register_base_dependencies();
|
| 744 |
|
| 745 |
//Move admin notices (e.g. "Settings saved") below editor tabs.
|
|
@@ -827,7 +850,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 827 |
'blankMenuItem' => ameMenuItem::blank_menu(),
|
| 828 |
'itemTemplates' => $this->item_templates,
|
| 829 |
'customItemTemplate' => array(
|
| 830 |
-
'name' => '< Custom >',
|
| 831 |
'defaults' => ameMenuItem::custom_item_defaults(),
|
| 832 |
),
|
| 833 |
|
|
@@ -1019,29 +1042,45 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 1019 |
* Set and save a new custom menu for the current site.
|
| 1020 |
*
|
| 1021 |
* @param array|null $custom_menu
|
|
|
|
| 1022 |
*/
|
| 1023 |
-
function set_custom_menu($custom_menu) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1024 |
$custom_menu = apply_filters('ame_pre_set_custom_menu', $custom_menu);
|
| 1025 |
|
| 1026 |
-
$previous_custom_menu = $this->load_custom_menu();
|
| 1027 |
$this->update_wpml_strings($previous_custom_menu, $custom_menu);
|
| 1028 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1029 |
if ( !empty($custom_menu) && $this->options['compress_custom_menu'] ) {
|
| 1030 |
$custom_menu = ameMenu::compress($custom_menu);
|
| 1031 |
}
|
| 1032 |
|
| 1033 |
-
if (
|
| 1034 |
$site_specific_options = get_option($this->option_name);
|
| 1035 |
if ( !is_array($site_specific_options) ) {
|
| 1036 |
$site_specific_options = array();
|
| 1037 |
}
|
| 1038 |
$site_specific_options['custom_menu'] = $custom_menu;
|
| 1039 |
update_option($this->option_name, $site_specific_options);
|
| 1040 |
-
} else {
|
| 1041 |
$this->options['custom_menu'] = $custom_menu;
|
| 1042 |
$this->save_options();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1043 |
}
|
| 1044 |
|
|
|
|
| 1045 |
$this->cached_custom_menu = null;
|
| 1046 |
$this->cached_virtual_caps = null;
|
| 1047 |
$this->cached_user_caps = array();
|
|
@@ -1050,14 +1089,26 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 1050 |
/**
|
| 1051 |
* Load the current custom menu for this site, if any.
|
| 1052 |
*
|
|
|
|
| 1053 |
* @return array|null Either a menu in the internal format, or NULL if there is no custom menu available.
|
| 1054 |
*/
|
| 1055 |
-
public function load_custom_menu() {
|
| 1056 |
-
if ( $
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1057 |
return $this->cached_custom_menu;
|
| 1058 |
}
|
| 1059 |
|
| 1060 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1061 |
$site_specific_options = get_option($this->option_name, null);
|
| 1062 |
if ( is_array($site_specific_options) && isset($site_specific_options['custom_menu']) ) {
|
| 1063 |
$this->cached_custom_menu = ameMenu::load_array($site_specific_options['custom_menu']);
|
|
@@ -1072,6 +1123,23 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 1072 |
return $this->cached_custom_menu;
|
| 1073 |
}
|
| 1074 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1075 |
/**
|
| 1076 |
* Determine if we should use a site-specific admin menu configuration
|
| 1077 |
* for the current site, or fall back to the global config.
|
|
@@ -1378,7 +1446,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 1378 |
unset($following_separator_position[$previous_item]);
|
| 1379 |
}
|
| 1380 |
}
|
| 1381 |
-
$entry['position'] = $entry['position'] + 0.01;
|
| 1382 |
} else if ( $previous_item === '' ) {
|
| 1383 |
//Empty string = this was originally the first item.
|
| 1384 |
$entry['position'] = -1;
|
|
@@ -1628,7 +1696,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 1628 |
$item['page_title'],
|
| 1629 |
$item['css_class'],
|
| 1630 |
$item['hookname'], //ID
|
| 1631 |
-
$item['icon_url']
|
| 1632 |
);
|
| 1633 |
|
| 1634 |
return $wp_item;
|
|
@@ -1825,6 +1893,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 1825 |
return $item;
|
| 1826 |
}
|
| 1827 |
|
|
|
|
| 1828 |
$item = apply_filters('custom_admin_menu_capability', $item);
|
| 1829 |
|
| 1830 |
$item['access_check_log'][] = '-----';
|
|
@@ -1960,7 +2029,9 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 1960 |
$this->display_plugin_settings_ui();
|
| 1961 |
} else if ( $this->current_tab == 'generate-menu-dashicons' ) {
|
| 1962 |
require dirname(__FILE__) . '/generate-menu-dashicons.php';
|
| 1963 |
-
} else if ( $this->current_tab === '
|
|
|
|
|
|
|
| 1964 |
$this->display_editor_ui();
|
| 1965 |
} else {
|
| 1966 |
do_action('admin_menu_editor-section-' . $this->current_tab);
|
|
@@ -1971,6 +2042,67 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 1971 |
do_action('admin_menu_editor-footer-' . $this->current_tab, $action);
|
| 1972 |
}
|
| 1973 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1974 |
private function handle_form_submission($post, $action = '') {
|
| 1975 |
if ( $action == 'save_menu' ) {
|
| 1976 |
//Save the admin menu configuration.
|
|
@@ -2014,8 +2146,14 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2014 |
//Remember if the user has changed any menu icons to different Dashicons.
|
| 2015 |
$menu['has_modified_dashicons'] = ameModifiedIconDetector::detect($menu);
|
| 2016 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2017 |
//Save the custom menu
|
| 2018 |
-
$this->set_custom_menu($menu);
|
| 2019 |
|
| 2020 |
//Redirect back to the editor and display the success message.
|
| 2021 |
$query = array('message' => 1);
|
|
@@ -2144,7 +2282,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2144 |
);
|
| 2145 |
|
| 2146 |
$this->save_options();
|
| 2147 |
-
wp_redirect(add_query_arg('
|
| 2148 |
}
|
| 2149 |
}
|
| 2150 |
|
|
@@ -2155,9 +2293,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2155 |
'images_url' => plugins_url('images', $this->plugin_file),
|
| 2156 |
'hide_advanced_settings' => $this->options['hide_advanced_settings'],
|
| 2157 |
'show_extra_icons' => $this->options['show_extra_icons'],
|
|
|
|
| 2158 |
'settings_page_url' => $this->get_settings_page_url(),
|
| 2159 |
'show_deprecated_hide_button' => $this->options['show_deprecated_hide_button'],
|
| 2160 |
'dashicons_available' => wp_style_is('dashicons', 'done'),
|
|
|
|
| 2161 |
);
|
| 2162 |
|
| 2163 |
//Build a tree struct. for the default menu
|
|
@@ -2257,6 +2397,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2257 |
);
|
| 2258 |
|
| 2259 |
do_action('admin_menu_editor-display_tabs');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2260 |
}
|
| 2261 |
|
| 2262 |
public function display_settings_page_footer() {
|
|
@@ -2271,7 +2416,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2271 |
foreach($this->tabs as $slug => $title) {
|
| 2272 |
printf(
|
| 2273 |
'<a href="%s" id="%s" class="nav-tab%s">%s</a>',
|
| 2274 |
-
esc_attr(add_query_arg('sub_section', $slug,
|
| 2275 |
esc_attr('ws_ame_' . $slug . '_tab'),
|
| 2276 |
$slug === $this->current_tab ? ' nav-tab-active' : '',
|
| 2277 |
$title
|
|
@@ -2298,13 +2443,27 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2298 |
require dirname(__FILE__) . '/settings-page.php';
|
| 2299 |
}
|
| 2300 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2301 |
/**
|
| 2302 |
* Get the fully qualified URL of the "Settings" sub-section of our plugin page.
|
| 2303 |
*
|
| 2304 |
* @return string
|
| 2305 |
*/
|
| 2306 |
private function get_settings_page_url() {
|
| 2307 |
-
return
|
| 2308 |
}
|
| 2309 |
|
| 2310 |
/**
|
|
@@ -2312,8 +2471,8 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2312 |
*
|
| 2313 |
* @return bool
|
| 2314 |
*/
|
| 2315 |
-
|
| 2316 |
-
return $this->is_tab_open('editor');
|
| 2317 |
}
|
| 2318 |
|
| 2319 |
/**
|
|
@@ -2336,46 +2495,73 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2336 |
&& ($this->current_tab === $tab_slug)
|
| 2337 |
&& isset($this->get['page']) && ($this->get['page'] == 'menu_editor');
|
| 2338 |
}
|
| 2339 |
-
|
| 2340 |
/**
|
| 2341 |
-
*
|
| 2342 |
-
*
|
| 2343 |
-
* This is based on grant_access settings for the current custom menu and enables
|
| 2344 |
-
* selected roles and users to access menu items that they ordinarily would not
|
| 2345 |
-
* be able to.
|
| 2346 |
*
|
| 2347 |
-
* @uses self::get_virtual_caps_for() to actually generate the caps.
|
| 2348 |
* @uses self::$cached_virtual_caps to cache the generated list of caps.
|
| 2349 |
*
|
|
|
|
| 2350 |
* @return array A list of capability => [role1 => true, ... roleN => true] assignments.
|
| 2351 |
*/
|
| 2352 |
-
function get_virtual_caps() {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2353 |
if ( $this->cached_virtual_caps !== null ) {
|
| 2354 |
-
return $this->cached_virtual_caps;
|
| 2355 |
}
|
| 2356 |
|
| 2357 |
-
$caps = array();
|
| 2358 |
$custom_menu = $this->load_custom_menu();
|
| 2359 |
if ( $custom_menu === null ){
|
| 2360 |
-
return
|
| 2361 |
}
|
| 2362 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2363 |
//Include directly granted capabilities.
|
|
|
|
| 2364 |
if ( !empty($custom_menu['granted_capabilities']) ) {
|
| 2365 |
foreach ($custom_menu['granted_capabilities'] as $actor => $capabilities) {
|
| 2366 |
foreach ($capabilities as $capability => $allow) {
|
| 2367 |
-
$
|
| 2368 |
}
|
| 2369 |
}
|
| 2370 |
}
|
| 2371 |
|
| 2372 |
-
//
|
|
|
|
| 2373 |
foreach($custom_menu['tree'] as $item) {
|
| 2374 |
-
$
|
| 2375 |
}
|
| 2376 |
|
| 2377 |
-
|
| 2378 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2379 |
}
|
| 2380 |
|
| 2381 |
private function get_virtual_caps_for($item) {
|
|
@@ -2425,6 +2611,22 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2425 |
} elseif ( $capability === 'delete_site' ) {
|
| 2426 |
return 'manage_options';
|
| 2427 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2428 |
return $capability;
|
| 2429 |
}
|
| 2430 |
|
|
@@ -2441,6 +2643,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2441 |
|
| 2442 |
if ($this->options['menu_config_scope'] === 'site') {
|
| 2443 |
$this->cached_custom_menu = null;
|
|
|
|
| 2444 |
}
|
| 2445 |
}
|
| 2446 |
|
|
@@ -2544,6 +2747,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
| 2544 |
'sort_column' => 'post_modified',
|
| 2545 |
'sort_order' => 'DESC',
|
| 2546 |
'hierarchical' => false,
|
|
|
|
| 2547 |
'number' => 50, //Semi-arbitrary. We do need a limit - some users could have thousands of pages.
|
| 2548 |
));
|
| 2549 |
/** @var WP_Post[] $pages */
|
| 25 |
const VERBOSITY_NORMAL = 2;
|
| 26 |
const VERBOSITY_VERBOSE = 5;
|
| 27 |
|
| 28 |
+
const DIRECTLY_GRANTED_VIRTUAL_CAPS = 2;
|
| 29 |
+
const ALL_VIRTUAL_CAPS = 3;
|
| 30 |
+
|
| 31 |
/**
|
| 32 |
* @var string The heading tag to use for admin pages.
|
| 33 |
*/
|
| 83 |
private $relative_template_order = array();
|
| 84 |
|
| 85 |
private $cached_custom_menu = null; //Cached, non-merged version of the custom menu. Used by load_custom_menu().
|
| 86 |
+
private $loaded_menu_config_id = null;
|
| 87 |
private $cached_virtual_caps = null;//List of virtual caps. Used by get_virtual_caps().
|
| 88 |
|
| 89 |
private $cached_user_caps = array(); //A cache of the current user's capabilities. Used only in very specific places.
|
| 125 |
'hide_advanced_settings' => true,
|
| 126 |
'show_extra_icons' => false,
|
| 127 |
'custom_menu' => null,
|
| 128 |
+
'custom_network_menu' => null,
|
| 129 |
'first_install_time' => null,
|
| 130 |
'display_survey_notice' => true,
|
| 131 |
'plugin_db_version' => 0,
|
| 180 |
//WP 4.3+ uses H1 headings for admin pages. Older versions use H2 instead.
|
| 181 |
self::$admin_heading_tag = version_compare($GLOBALS['wp_version'], '4.3', '<') ? 'h2' : 'h1';
|
| 182 |
|
| 183 |
+
$this->settings_link = (is_network_admin() ? 'settings.php' : 'options-general.php') . '?page=menu_editor';
|
| 184 |
|
| 185 |
$this->magic_hooks = true;
|
| 186 |
//Run our hooks last (almost). Priority is less than PHP_INT_MAX mostly for defensive programming purposes.
|
| 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
|
| 336 |
+
$firstTabs = array('network-admin-menu' => 'Network Admin Menu');
|
| 337 |
+
}
|
| 338 |
+
$this->tabs = apply_filters('admin_menu_editor-tabs', $firstTabs);
|
| 339 |
//The "Settings" tab is always last.
|
| 340 |
$this->tabs['settings'] = 'Settings';
|
| 341 |
}
|
| 371 |
//The only way we can see the changes made by the second hook is to do the same thing.
|
| 372 |
static $firstRunSkipped = false;
|
| 373 |
if ( !$firstRunSkipped && class_exists('Flow') ) {
|
| 374 |
+
add_action(current_filter(), array($this, 'hook_admin_menu'), $this->magic_hook_priority + 1);
|
| 375 |
$firstRunSkipped = true;
|
| 376 |
return;
|
| 377 |
}
|
| 388 |
$this->log_security_note('Current user can edit the admin menu.');
|
| 389 |
|
| 390 |
//Determine the current menu editor page tab.
|
| 391 |
+
reset($this->tabs);
|
| 392 |
+
$this->current_tab = isset($this->get['sub_section']) ? strval($this->get['sub_section']) : key($this->tabs);
|
| 393 |
$tab_title = '';
|
| 394 |
if ($this->current_tab !== 'editor' && isset($this->tabs[$this->current_tab])) {
|
| 395 |
$tab_title = ' - ' . $this->tabs[$this->current_tab];
|
| 396 |
}
|
| 397 |
|
| 398 |
+
$parent_slug = is_network_admin() ? 'settings.php' : 'options-general.php';
|
| 399 |
+
|
| 400 |
+
$page = add_submenu_page(
|
| 401 |
+
$parent_slug,
|
| 402 |
apply_filters('admin_menu_editor-self_page_title', 'Menu Editor') . $tab_title,
|
| 403 |
apply_filters('admin_menu_editor-self_menu_title', 'Menu Editor'),
|
| 404 |
apply_filters('admin_menu_editor-capability', 'manage_options'),
|
| 422 |
add_action("admin_print_scripts-$page", array($this, 'remove_ultimate_tinymce_qtags'));
|
| 423 |
|
| 424 |
//Make a placeholder for our screen options (hacky)
|
| 425 |
+
$screen_hook_name = $page;
|
| 426 |
+
if ( is_network_admin() ) {
|
| 427 |
+
$screen_hook_name .= '-network';
|
| 428 |
+
}
|
| 429 |
+
add_meta_box("ws-ame-screen-options", "[AME placeholder]", '__return_false', $screen_hook_name);
|
| 430 |
}
|
| 431 |
|
| 432 |
//Compatibility fix for the WooCommerce order count bubble. Must be run before storing or processing $submenu.
|
| 462 |
|
| 463 |
//Add extra templates that are not part of the normal menu.
|
| 464 |
$this->item_templates = $this->add_special_templates($this->item_templates);
|
| 465 |
+
//TODO: It would be nice to add the "Delete Site" item on multisite when on the main site.
|
| 466 |
|
| 467 |
//Is there a custom menu to use?
|
| 468 |
$custom_menu = $this->load_custom_menu();
|
| 759 |
//it tracks *all* DOM changes using MutationObserver.
|
| 760 |
remove_action('admin_print_scripts', 'print_emoji_detection_script');
|
| 761 |
|
| 762 |
+
//Workaround: Suppress a buggy "lets add a 'defer' attribute to all <script> tags" filter.
|
| 763 |
+
//It's been going around the web and breaking AME installations by producing invalid HTML.
|
| 764 |
+
remove_filter('clean_url', 'defer_parsing_of_js', 11);
|
| 765 |
+
|
| 766 |
$this->register_base_dependencies();
|
| 767 |
|
| 768 |
//Move admin notices (e.g. "Settings saved") below editor tabs.
|
| 850 |
'blankMenuItem' => ameMenuItem::blank_menu(),
|
| 851 |
'itemTemplates' => $this->item_templates,
|
| 852 |
'customItemTemplate' => array(
|
| 853 |
+
'name' => '< Custom URL >',
|
| 854 |
'defaults' => ameMenuItem::custom_item_defaults(),
|
| 855 |
),
|
| 856 |
|
| 1042 |
* Set and save a new custom menu for the current site.
|
| 1043 |
*
|
| 1044 |
* @param array|null $custom_menu
|
| 1045 |
+
* @param string|null $config_id Supported values: 'network-admin', 'global' or 'site'
|
| 1046 |
*/
|
| 1047 |
+
function set_custom_menu($custom_menu, $config_id = null) {
|
| 1048 |
+
if ( $config_id === null ) {
|
| 1049 |
+
$config_id = $this->guess_menu_config_id();
|
| 1050 |
+
}
|
| 1051 |
+
|
| 1052 |
$custom_menu = apply_filters('ame_pre_set_custom_menu', $custom_menu);
|
| 1053 |
|
| 1054 |
+
$previous_custom_menu = $this->load_custom_menu($config_id);
|
| 1055 |
$this->update_wpml_strings($previous_custom_menu, $custom_menu);
|
| 1056 |
|
| 1057 |
+
if ( !empty($custom_menu) ) {
|
| 1058 |
+
$custom_menu['prebuilt_virtual_caps'] = $this->build_virtual_capability_list($custom_menu);
|
| 1059 |
+
}
|
| 1060 |
+
|
| 1061 |
if ( !empty($custom_menu) && $this->options['compress_custom_menu'] ) {
|
| 1062 |
$custom_menu = ameMenu::compress($custom_menu);
|
| 1063 |
}
|
| 1064 |
|
| 1065 |
+
if ($config_id === 'site') {
|
| 1066 |
$site_specific_options = get_option($this->option_name);
|
| 1067 |
if ( !is_array($site_specific_options) ) {
|
| 1068 |
$site_specific_options = array();
|
| 1069 |
}
|
| 1070 |
$site_specific_options['custom_menu'] = $custom_menu;
|
| 1071 |
update_option($this->option_name, $site_specific_options);
|
| 1072 |
+
} else if ($config_id === 'global') {
|
| 1073 |
$this->options['custom_menu'] = $custom_menu;
|
| 1074 |
$this->save_options();
|
| 1075 |
+
|
| 1076 |
+
} else if ($config_id === 'network-admin' ) {
|
| 1077 |
+
$this->options['custom_network_menu'] = $custom_menu;
|
| 1078 |
+
$this->save_options();
|
| 1079 |
+
} else {
|
| 1080 |
+
throw new LogicException(sprintf('Invalid menu configuration ID: "%s"', $config_id));
|
| 1081 |
}
|
| 1082 |
|
| 1083 |
+
$this->loaded_menu_config_id = null;
|
| 1084 |
$this->cached_custom_menu = null;
|
| 1085 |
$this->cached_virtual_caps = null;
|
| 1086 |
$this->cached_user_caps = array();
|
| 1089 |
/**
|
| 1090 |
* Load the current custom menu for this site, if any.
|
| 1091 |
*
|
| 1092 |
+
* @param null $config_id
|
| 1093 |
* @return array|null Either a menu in the internal format, or NULL if there is no custom menu available.
|
| 1094 |
*/
|
| 1095 |
+
public function load_custom_menu($config_id = null) {
|
| 1096 |
+
if ( $config_id === null ) {
|
| 1097 |
+
$config_id = $this->guess_menu_config_id();
|
| 1098 |
+
}
|
| 1099 |
+
|
| 1100 |
+
if ( ($this->cached_custom_menu !== null) && ($this->loaded_menu_config_id === $config_id) ) {
|
| 1101 |
return $this->cached_custom_menu;
|
| 1102 |
}
|
| 1103 |
|
| 1104 |
+
$this->loaded_menu_config_id = $config_id;
|
| 1105 |
+
|
| 1106 |
+
if ( $config_id === 'network-admin' ) {
|
| 1107 |
+
if ( empty($this->options['custom_network_menu']) ) {
|
| 1108 |
+
return null;
|
| 1109 |
+
}
|
| 1110 |
+
$this->cached_custom_menu = ameMenu::load_array($this->options['custom_network_menu']);
|
| 1111 |
+
} else if ( $config_id === 'site' ) {
|
| 1112 |
$site_specific_options = get_option($this->option_name, null);
|
| 1113 |
if ( is_array($site_specific_options) && isset($site_specific_options['custom_menu']) ) {
|
| 1114 |
$this->cached_custom_menu = ameMenu::load_array($site_specific_options['custom_menu']);
|
| 1123 |
return $this->cached_custom_menu;
|
| 1124 |
}
|
| 1125 |
|
| 1126 |
+
private function guess_menu_config_id() {
|
| 1127 |
+
if ( is_network_admin() ) {
|
| 1128 |
+
return 'network-admin';
|
| 1129 |
+
} elseif ( $this->should_use_site_specific_menu() ) {
|
| 1130 |
+
return 'site';
|
| 1131 |
+
} else {
|
| 1132 |
+
return 'global';
|
| 1133 |
+
}
|
| 1134 |
+
}
|
| 1135 |
+
|
| 1136 |
+
/**
|
| 1137 |
+
* @return string|null
|
| 1138 |
+
*/
|
| 1139 |
+
public function get_loaded_menu_config_id() {
|
| 1140 |
+
return $this->loaded_menu_config_id;
|
| 1141 |
+
}
|
| 1142 |
+
|
| 1143 |
/**
|
| 1144 |
* Determine if we should use a site-specific admin menu configuration
|
| 1145 |
* for the current site, or fall back to the global config.
|
| 1446 |
unset($following_separator_position[$previous_item]);
|
| 1447 |
}
|
| 1448 |
}
|
| 1449 |
+
$entry['position'] = strval(floatval($entry['position']) + 0.01);
|
| 1450 |
} else if ( $previous_item === '' ) {
|
| 1451 |
//Empty string = this was originally the first item.
|
| 1452 |
$entry['position'] = -1;
|
| 1696 |
$item['page_title'],
|
| 1697 |
$item['css_class'],
|
| 1698 |
$item['hookname'], //ID
|
| 1699 |
+
isset($item['wp_icon_url']) ? $item['wp_icon_url'] : $item['icon_url'],
|
| 1700 |
);
|
| 1701 |
|
| 1702 |
return $wp_item;
|
| 1893 |
return $item;
|
| 1894 |
}
|
| 1895 |
|
| 1896 |
+
//TODO: A direct call to apply_custom_access would be faster.
|
| 1897 |
$item = apply_filters('custom_admin_menu_capability', $item);
|
| 1898 |
|
| 1899 |
$item['access_check_log'][] = '-----';
|
| 2029 |
$this->display_plugin_settings_ui();
|
| 2030 |
} else if ( $this->current_tab == 'generate-menu-dashicons' ) {
|
| 2031 |
require dirname(__FILE__) . '/generate-menu-dashicons.php';
|
| 2032 |
+
} else if ( $this->current_tab === 'repair-database' ) {
|
| 2033 |
+
$this->repair_database();
|
| 2034 |
+
} else if ( $this->is_editor_page() ) {
|
| 2035 |
$this->display_editor_ui();
|
| 2036 |
} else {
|
| 2037 |
do_action('admin_menu_editor-section-' . $this->current_tab);
|
| 2042 |
do_action('admin_menu_editor-footer-' . $this->current_tab, $action);
|
| 2043 |
}
|
| 2044 |
|
| 2045 |
+
private function repair_database() {
|
| 2046 |
+
global $wpdb; /** @var wpdb $wpdb */
|
| 2047 |
+
|
| 2048 |
+
if ( !is_multisite() ) {
|
| 2049 |
+
echo 'This is not Multisite. The "repair" function does not apply to your site.';
|
| 2050 |
+
return;
|
| 2051 |
+
}
|
| 2052 |
+
|
| 2053 |
+
echo '<div class="wrap"><h1>Repairing database...</h1><p></p>';
|
| 2054 |
+
|
| 2055 |
+
$options_to_repair = array(
|
| 2056 |
+
$this->option_name,
|
| 2057 |
+
'wsh_license_manager-admin-menu-editor-pro',
|
| 2058 |
+
'ws_abe_admin_bar_nodes',
|
| 2059 |
+
'ws_abe_admin_bar_settings',
|
| 2060 |
+
);
|
| 2061 |
+
|
| 2062 |
+
echo "Repair {$wpdb->sitemeta}<br>";
|
| 2063 |
+
$wpdb->query('REPAIR TABLE ' . $wpdb->sitemeta);
|
| 2064 |
+
|
| 2065 |
+
echo "Lock {$wpdb->sitemeta}<br>";
|
| 2066 |
+
$wpdb->query('LOCK TABLES ' . $wpdb->sitemeta);
|
| 2067 |
+
|
| 2068 |
+
foreach($options_to_repair as $option) {
|
| 2069 |
+
if ( empty($option) ) {
|
| 2070 |
+
continue; //Sanity check.
|
| 2071 |
+
}
|
| 2072 |
+
|
| 2073 |
+
echo "Fetch option {$option}<br>";
|
| 2074 |
+
$row = $wpdb->get_row($wpdb->prepare(
|
| 2075 |
+
"SELECT * FROM {$wpdb->sitemeta} WHERE meta_key = %s LIMIT 1",
|
| 2076 |
+
$option
|
| 2077 |
+
));
|
| 2078 |
+
|
| 2079 |
+
if ( empty($row) || empty($row->site_id) ) {
|
| 2080 |
+
echo "Option doesn't exist, skipping it.<br>";
|
| 2081 |
+
continue;
|
| 2082 |
+
}
|
| 2083 |
+
|
| 2084 |
+
echo "Delete all rows where meta_key = {$option}<br>";
|
| 2085 |
+
$wpdb->delete($wpdb->sitemeta, array('meta_key' => $option), '%s');
|
| 2086 |
+
|
| 2087 |
+
echo "Recreate the first copy of {$option}<br>";
|
| 2088 |
+
$wpdb->insert(
|
| 2089 |
+
$wpdb->sitemeta,
|
| 2090 |
+
array(
|
| 2091 |
+
'site_id' => $row->site_id,
|
| 2092 |
+
'meta_key' => $option,
|
| 2093 |
+
'meta_value' => $row->meta_value,
|
| 2094 |
+
),
|
| 2095 |
+
array('%d', '%s', '%s')
|
| 2096 |
+
);
|
| 2097 |
+
}
|
| 2098 |
+
|
| 2099 |
+
echo "Unlock {$wpdb->sitemeta}<br>";
|
| 2100 |
+
$wpdb->query('UNLOCK TABLES');
|
| 2101 |
+
|
| 2102 |
+
echo "Done.<br>";
|
| 2103 |
+
echo '<div>';
|
| 2104 |
+
}
|
| 2105 |
+
|
| 2106 |
private function handle_form_submission($post, $action = '') {
|
| 2107 |
if ( $action == 'save_menu' ) {
|
| 2108 |
//Save the admin menu configuration.
|
| 2146 |
//Remember if the user has changed any menu icons to different Dashicons.
|
| 2147 |
$menu['has_modified_dashicons'] = ameModifiedIconDetector::detect($menu);
|
| 2148 |
|
| 2149 |
+
//Which menu configuration are we changing?
|
| 2150 |
+
$config_id = isset($post['config_id']) ? $post['config_id'] : null;
|
| 2151 |
+
if ( !in_array($config_id, array('site', 'global', 'network-admin')) ) {
|
| 2152 |
+
$config_id = $this->get_loaded_menu_config_id();
|
| 2153 |
+
}
|
| 2154 |
+
|
| 2155 |
//Save the custom menu
|
| 2156 |
+
$this->set_custom_menu($menu, $config_id);
|
| 2157 |
|
| 2158 |
//Redirect back to the editor and display the success message.
|
| 2159 |
$query = array('message' => 1);
|
| 2282 |
);
|
| 2283 |
|
| 2284 |
$this->save_options();
|
| 2285 |
+
wp_redirect(add_query_arg('message', 1, $this->get_settings_page_url()));
|
| 2286 |
}
|
| 2287 |
}
|
| 2288 |
|
| 2293 |
'images_url' => plugins_url('images', $this->plugin_file),
|
| 2294 |
'hide_advanced_settings' => $this->options['hide_advanced_settings'],
|
| 2295 |
'show_extra_icons' => $this->options['show_extra_icons'],
|
| 2296 |
+
'current_tab_url' => $this->get_plugin_page_url(array('sub_section' => $this->current_tab)),
|
| 2297 |
'settings_page_url' => $this->get_settings_page_url(),
|
| 2298 |
'show_deprecated_hide_button' => $this->options['show_deprecated_hide_button'],
|
| 2299 |
'dashicons_available' => wp_style_is('dashicons', 'done'),
|
| 2300 |
+
'menu_config_id' => $this->get_loaded_menu_config_id(),
|
| 2301 |
);
|
| 2302 |
|
| 2303 |
//Build a tree struct. for the default menu
|
| 2397 |
);
|
| 2398 |
|
| 2399 |
do_action('admin_menu_editor-display_tabs');
|
| 2400 |
+
|
| 2401 |
+
if ( isset($_GET['message']) && (intval($_GET['message']) === 1) ) {
|
| 2402 |
+
add_settings_error('ame-settings-page', 'settings_updated', __('Settings saved.'), 'updated');
|
| 2403 |
+
}
|
| 2404 |
+
settings_errors('ame-settings-page');
|
| 2405 |
}
|
| 2406 |
|
| 2407 |
public function display_settings_page_footer() {
|
| 2416 |
foreach($this->tabs as $slug => $title) {
|
| 2417 |
printf(
|
| 2418 |
'<a href="%s" id="%s" class="nav-tab%s">%s</a>',
|
| 2419 |
+
esc_attr(add_query_arg('sub_section', $slug, self_admin_url($this->settings_link))),
|
| 2420 |
esc_attr('ws_ame_' . $slug . '_tab'),
|
| 2421 |
$slug === $this->current_tab ? ' nav-tab-active' : '',
|
| 2422 |
$title
|
| 2443 |
require dirname(__FILE__) . '/settings-page.php';
|
| 2444 |
}
|
| 2445 |
|
| 2446 |
+
/**
|
| 2447 |
+
* Get the fully qualified URL of the plugin page, i.e. "Settings -> Menu Editor [Pro]".
|
| 2448 |
+
*
|
| 2449 |
+
* @param array $extra_query_args List of query arguments to append to the URL. Format: [param => value].
|
| 2450 |
+
* @return string
|
| 2451 |
+
*/
|
| 2452 |
+
public function get_plugin_page_url($extra_query_args = array()) {
|
| 2453 |
+
$url = self_admin_url($this->settings_link);
|
| 2454 |
+
if ( !empty($extra_query_args) ) {
|
| 2455 |
+
$url = add_query_arg($extra_query_args, $url);
|
| 2456 |
+
}
|
| 2457 |
+
return $url;
|
| 2458 |
+
}
|
| 2459 |
+
|
| 2460 |
/**
|
| 2461 |
* Get the fully qualified URL of the "Settings" sub-section of our plugin page.
|
| 2462 |
*
|
| 2463 |
* @return string
|
| 2464 |
*/
|
| 2465 |
private function get_settings_page_url() {
|
| 2466 |
+
return $this->get_plugin_page_url(array('sub_section' => 'settings'));
|
| 2467 |
}
|
| 2468 |
|
| 2469 |
/**
|
| 2471 |
*
|
| 2472 |
* @return bool
|
| 2473 |
*/
|
| 2474 |
+
public function is_editor_page() {
|
| 2475 |
+
return $this->is_tab_open('editor') || $this->is_tab_open('network-admin-menu');
|
| 2476 |
}
|
| 2477 |
|
| 2478 |
/**
|
| 2495 |
&& ($this->current_tab === $tab_slug)
|
| 2496 |
&& isset($this->get['page']) && ($this->get['page'] == 'menu_editor');
|
| 2497 |
}
|
| 2498 |
+
|
| 2499 |
/**
|
| 2500 |
+
* Get the list of virtual capabilities.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2501 |
*
|
|
|
|
| 2502 |
* @uses self::$cached_virtual_caps to cache the generated list of caps.
|
| 2503 |
*
|
| 2504 |
+
* @param int|null $mode
|
| 2505 |
* @return array A list of capability => [role1 => true, ... roleN => true] assignments.
|
| 2506 |
*/
|
| 2507 |
+
function get_virtual_caps($mode = null) {
|
| 2508 |
+
if ( $mode === null ) {
|
| 2509 |
+
$mode = self::ALL_VIRTUAL_CAPS;
|
| 2510 |
+
}
|
| 2511 |
+
|
| 2512 |
if ( $this->cached_virtual_caps !== null ) {
|
| 2513 |
+
return $this->cached_virtual_caps[$mode];
|
| 2514 |
}
|
| 2515 |
|
|
|
|
| 2516 |
$custom_menu = $this->load_custom_menu();
|
| 2517 |
if ( $custom_menu === null ){
|
| 2518 |
+
return array();
|
| 2519 |
}
|
| 2520 |
|
| 2521 |
+
if ( isset($custom_menu['prebuilt_virtual_caps']) ) {
|
| 2522 |
+
$this->cached_virtual_caps = $custom_menu['prebuilt_virtual_caps'];
|
| 2523 |
+
} else {
|
| 2524 |
+
$this->cached_virtual_caps = $this->build_virtual_capability_list($custom_menu);
|
| 2525 |
+
}
|
| 2526 |
+
|
| 2527 |
+
return $this->cached_virtual_caps[$mode];
|
| 2528 |
+
}
|
| 2529 |
+
|
| 2530 |
+
/**
|
| 2531 |
+
* Generate a list of "virtual" capabilities that should be granted to specific actors.
|
| 2532 |
+
*
|
| 2533 |
+
* This is based on grant_access settings for the custom menu and enables selected
|
| 2534 |
+
* roles and users to access menu items that they ordinarily would not be able to.
|
| 2535 |
+
*
|
| 2536 |
+
* @uses self::get_virtual_caps_for() to actually generate the caps.
|
| 2537 |
+
*
|
| 2538 |
+
* @param array $custom_menu
|
| 2539 |
+
* @return array
|
| 2540 |
+
*/
|
| 2541 |
+
private function build_virtual_capability_list($custom_menu) {
|
| 2542 |
//Include directly granted capabilities.
|
| 2543 |
+
$grantedCaps = array();
|
| 2544 |
if ( !empty($custom_menu['granted_capabilities']) ) {
|
| 2545 |
foreach ($custom_menu['granted_capabilities'] as $actor => $capabilities) {
|
| 2546 |
foreach ($capabilities as $capability => $allow) {
|
| 2547 |
+
$grantedCaps[$actor][$capability] = (bool)(is_array($allow) ? $allow[0] : $allow);
|
| 2548 |
}
|
| 2549 |
}
|
| 2550 |
}
|
| 2551 |
|
| 2552 |
+
//Include caps that are required to access menu items (grant_access).
|
| 2553 |
+
$menuCaps = array();
|
| 2554 |
foreach($custom_menu['tree'] as $item) {
|
| 2555 |
+
$menuCaps = self::array_replace_recursive($menuCaps, $this->get_virtual_caps_for($item));
|
| 2556 |
}
|
| 2557 |
|
| 2558 |
+
//grant_access settings on individual items have precedence.
|
| 2559 |
+
$allCaps = self::array_replace_recursive($grantedCaps, $menuCaps);
|
| 2560 |
+
|
| 2561 |
+
return array(
|
| 2562 |
+
self::DIRECTLY_GRANTED_VIRTUAL_CAPS => $grantedCaps,
|
| 2563 |
+
self::ALL_VIRTUAL_CAPS => $allCaps,
|
| 2564 |
+
);
|
| 2565 |
}
|
| 2566 |
|
| 2567 |
private function get_virtual_caps_for($item) {
|
| 2611 |
} elseif ( $capability === 'delete_site' ) {
|
| 2612 |
return 'manage_options';
|
| 2613 |
}
|
| 2614 |
+
|
| 2615 |
+
static $category_caps = array(
|
| 2616 |
+
'manage_post_tags' => true,
|
| 2617 |
+
'edit_categories' => true,
|
| 2618 |
+
'edit_post_tags' => true,
|
| 2619 |
+
'delete_categories' => true,
|
| 2620 |
+
'delete_post_tags' => true,
|
| 2621 |
+
);
|
| 2622 |
+
if ( isset($category_caps[$capability]) ) {
|
| 2623 |
+
return 'manage_categories';
|
| 2624 |
+
}
|
| 2625 |
+
|
| 2626 |
+
if (($capability === 'assign_categories') || ($capability === 'assign_post_tags')) {
|
| 2627 |
+
return 'edit_posts';
|
| 2628 |
+
}
|
| 2629 |
+
|
| 2630 |
return $capability;
|
| 2631 |
}
|
| 2632 |
|
| 2643 |
|
| 2644 |
if ($this->options['menu_config_scope'] === 'site') {
|
| 2645 |
$this->cached_custom_menu = null;
|
| 2646 |
+
$this->loaded_menu_config_id = null;
|
| 2647 |
}
|
| 2648 |
}
|
| 2649 |
|
| 2747 |
'sort_column' => 'post_modified',
|
| 2748 |
'sort_order' => 'DESC',
|
| 2749 |
'hierarchical' => false,
|
| 2750 |
+
'post_status' => array('publish', 'private'),
|
| 2751 |
'number' => 50, //Semi-arbitrary. We do need a limit - some users could have thousands of pages.
|
| 2752 |
));
|
| 2753 |
/** @var WP_Post[] $pages */
|
includes/menu-item.php
CHANGED
|
@@ -34,7 +34,7 @@ abstract class ameMenuItem {
|
|
| 34 |
* Convert a WP menu structure to an associative array.
|
| 35 |
*
|
| 36 |
* @param array $item An menu item.
|
| 37 |
-
* @param int $position The position (index) of the the menu item.
|
| 38 |
* @param string|null $parent The slug of the parent menu that owns this item. Null for top level menus.
|
| 39 |
* @return array
|
| 40 |
*/
|
|
@@ -70,10 +70,11 @@ abstract class ameMenuItem {
|
|
| 70 |
}
|
| 71 |
|
| 72 |
//Flag plugin pages
|
| 73 |
-
$
|
|
|
|
| 74 |
|
| 75 |
if ( !$item['separator'] ) {
|
| 76 |
-
$item['url'] = self::generate_url($item['file'], strval($parent));
|
| 77 |
}
|
| 78 |
|
| 79 |
$item['template_id'] = self::template_id($item, $parent);
|
|
@@ -212,7 +213,7 @@ abstract class ameMenuItem {
|
|
| 212 |
return strval($parent_file) . '>' . $item;
|
| 213 |
}
|
| 214 |
|
| 215 |
-
if (
|
| 216 |
return '';
|
| 217 |
}
|
| 218 |
|
|
@@ -475,7 +476,7 @@ abstract class ameMenuItem {
|
|
| 475 |
* @return int
|
| 476 |
*/
|
| 477 |
public static function compare_position($a, $b){
|
| 478 |
-
$result = self::get($a, 'position', 0) - self::get($b, 'position', 0);
|
| 479 |
//Support for non-integer positions.
|
| 480 |
if ($result > 0) {
|
| 481 |
return 1;
|
|
@@ -490,9 +491,10 @@ abstract class ameMenuItem {
|
|
| 490 |
*
|
| 491 |
* @param string $item_slug
|
| 492 |
* @param string $parent_slug
|
|
|
|
| 493 |
* @return string An URL relative to the /wp-admin/ directory.
|
| 494 |
*/
|
| 495 |
-
public static function generate_url($item_slug, $parent_slug = '') {
|
| 496 |
$menu_url = is_array($item_slug) ? self::get($item_slug, 'file') : $item_slug;
|
| 497 |
$parent_url = !empty($parent_slug) ? $parent_slug : 'admin.php';
|
| 498 |
|
|
@@ -505,32 +507,37 @@ abstract class ameMenuItem {
|
|
| 505 |
return $menu_url;
|
| 506 |
}
|
| 507 |
|
| 508 |
-
if ( self::is_hook_or_plugin_page($menu_url, $parent_url) ) {
|
| 509 |
$parent_file = self::remove_query_from($parent_url);
|
| 510 |
$base_file = self::is_wp_admin_file($parent_file) ? $parent_url : 'admin.php';
|
| 511 |
-
|
|
|
|
|
|
|
|
|
|
| 512 |
} else {
|
| 513 |
$url = $menu_url;
|
| 514 |
}
|
| 515 |
return $url;
|
| 516 |
}
|
| 517 |
|
| 518 |
-
private static function is_hook_or_plugin_page($page_url, $parent_page_url = '') {
|
| 519 |
if ( empty($parent_page_url) ) {
|
| 520 |
$parent_page_url = 'admin.php';
|
| 521 |
}
|
| 522 |
$pageFile = self::remove_query_from($page_url);
|
| 523 |
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
return false;
|
| 527 |
}
|
| 528 |
-
|
| 529 |
-
$hasHook = (get_plugin_page_hook($page_url, $parent_page_url) !== null);
|
| 530 |
if ( $hasHook ) {
|
| 531 |
return true;
|
| 532 |
}
|
| 533 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 534 |
/*
|
| 535 |
* Special case: Absolute paths.
|
| 536 |
*
|
| 34 |
* Convert a WP menu structure to an associative array.
|
| 35 |
*
|
| 36 |
* @param array $item An menu item.
|
| 37 |
+
* @param int|string $position The position (index) of the the menu item.
|
| 38 |
* @param string|null $parent The slug of the parent menu that owns this item. Null for top level menus.
|
| 39 |
* @return array
|
| 40 |
*/
|
| 70 |
}
|
| 71 |
|
| 72 |
//Flag plugin pages
|
| 73 |
+
$has_hook = (get_plugin_page_hook($item['file'], strval($parent)) != null);
|
| 74 |
+
$item['is_plugin_page'] = $has_hook;
|
| 75 |
|
| 76 |
if ( !$item['separator'] ) {
|
| 77 |
+
$item['url'] = self::generate_url($item['file'], strval($parent), $has_hook);
|
| 78 |
}
|
| 79 |
|
| 80 |
$item['template_id'] = self::template_id($item, $parent);
|
| 213 |
return strval($parent_file) . '>' . $item;
|
| 214 |
}
|
| 215 |
|
| 216 |
+
if ( !empty($item['custom']) ) {
|
| 217 |
return '';
|
| 218 |
}
|
| 219 |
|
| 476 |
* @return int
|
| 477 |
*/
|
| 478 |
public static function compare_position($a, $b){
|
| 479 |
+
$result = floatval(self::get($a, 'position', 0)) - floatval(self::get($b, 'position', 0));
|
| 480 |
//Support for non-integer positions.
|
| 481 |
if ($result > 0) {
|
| 482 |
return 1;
|
| 491 |
*
|
| 492 |
* @param string $item_slug
|
| 493 |
* @param string $parent_slug
|
| 494 |
+
* @param bool|null $has_hook
|
| 495 |
* @return string An URL relative to the /wp-admin/ directory.
|
| 496 |
*/
|
| 497 |
+
public static function generate_url($item_slug, $parent_slug = '', $has_hook = null) {
|
| 498 |
$menu_url = is_array($item_slug) ? self::get($item_slug, 'file') : $item_slug;
|
| 499 |
$parent_url = !empty($parent_slug) ? $parent_slug : 'admin.php';
|
| 500 |
|
| 507 |
return $menu_url;
|
| 508 |
}
|
| 509 |
|
| 510 |
+
if ( self::is_hook_or_plugin_page($menu_url, $parent_url, $has_hook) ) {
|
| 511 |
$parent_file = self::remove_query_from($parent_url);
|
| 512 |
$base_file = self::is_wp_admin_file($parent_file) ? $parent_url : 'admin.php';
|
| 513 |
+
//add_query_arg() might be more robust, but it's significantly slower.
|
| 514 |
+
$url = $base_file
|
| 515 |
+
. ((strpos($base_file, '?') === false) ? '?' : '&')
|
| 516 |
+
. 'page=' . urlencode($menu_url);
|
| 517 |
} else {
|
| 518 |
$url = $menu_url;
|
| 519 |
}
|
| 520 |
return $url;
|
| 521 |
}
|
| 522 |
|
| 523 |
+
private static function is_hook_or_plugin_page($page_url, $parent_page_url = '', $hasHook = null) {
|
| 524 |
if ( empty($parent_page_url) ) {
|
| 525 |
$parent_page_url = 'admin.php';
|
| 526 |
}
|
| 527 |
$pageFile = self::remove_query_from($page_url);
|
| 528 |
|
| 529 |
+
if ( $hasHook === null ) {
|
| 530 |
+
$hasHook = (get_plugin_page_hook($page_url, $parent_page_url) !== null);
|
|
|
|
| 531 |
}
|
|
|
|
|
|
|
| 532 |
if ( $hasHook ) {
|
| 533 |
return true;
|
| 534 |
}
|
| 535 |
|
| 536 |
+
//Files in /wp-admin are part of WP core so they're not plugin pages.
|
| 537 |
+
if ( self::is_wp_admin_file($pageFile) ) {
|
| 538 |
+
return false;
|
| 539 |
+
}
|
| 540 |
+
|
| 541 |
/*
|
| 542 |
* Special case: Absolute paths.
|
| 543 |
*
|
includes/menu.php
CHANGED
|
@@ -147,6 +147,11 @@ abstract class ameMenu {
|
|
| 147 |
$menu['has_modified_dashicons'] = (bool)$arr['has_modified_dashicons'];
|
| 148 |
}
|
| 149 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
return $menu;
|
| 151 |
}
|
| 152 |
|
| 147 |
$menu['has_modified_dashicons'] = (bool)$arr['has_modified_dashicons'];
|
| 148 |
}
|
| 149 |
|
| 150 |
+
//Copy the pre-generated list of virtual capabilities.
|
| 151 |
+
if ( isset($arr['prebuilt_virtual_caps']) ) {
|
| 152 |
+
$menu['prebuilt_virtual_caps'] = $arr['prebuilt_virtual_caps'];
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
return $menu;
|
| 156 |
}
|
| 157 |
|
includes/module.php
CHANGED
|
@@ -21,6 +21,10 @@ abstract class ameModule {
|
|
| 21 |
$this->moduleId = basename($this->moduleDir);
|
| 22 |
}
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
add_action('admin_menu_editor-register_scripts', array($this, 'registerScripts'));
|
| 25 |
|
| 26 |
//Register the module tab.
|
|
@@ -33,6 +37,16 @@ abstract class ameModule {
|
|
| 33 |
}
|
| 34 |
}
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
public function addTab($tabs) {
|
| 37 |
$tabs[$this->tabSlug] = !empty($this->tabTitle) ? $this->tabTitle : $this->tabSlug;
|
| 38 |
return $tabs;
|
|
@@ -50,13 +64,10 @@ abstract class ameModule {
|
|
| 50 |
|
| 51 |
protected function getTabUrl($queryParameters = array()) {
|
| 52 |
$queryParameters = array_merge(
|
| 53 |
-
array(
|
| 54 |
-
'page' => 'menu_editor',
|
| 55 |
-
'sub_section' => $this->tabSlug
|
| 56 |
-
),
|
| 57 |
$queryParameters
|
| 58 |
);
|
| 59 |
-
return
|
| 60 |
}
|
| 61 |
|
| 62 |
protected function outputMainTemplate() {
|
| 21 |
$this->moduleId = basename($this->moduleDir);
|
| 22 |
}
|
| 23 |
|
| 24 |
+
if ( !$this->isEnabledForRequest() ) {
|
| 25 |
+
return;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
add_action('admin_menu_editor-register_scripts', array($this, 'registerScripts'));
|
| 29 |
|
| 30 |
//Register the module tab.
|
| 37 |
}
|
| 38 |
}
|
| 39 |
|
| 40 |
+
/**
|
| 41 |
+
* Does this module need to do anything for the current request?
|
| 42 |
+
*
|
| 43 |
+
* For example, some modules work in the normal dashboard but not in the network admin.
|
| 44 |
+
* Other modules don't need to run during AJAX requests or when WP is running Cron jobs.
|
| 45 |
+
*/
|
| 46 |
+
protected function isEnabledForRequest() {
|
| 47 |
+
return true;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
public function addTab($tabs) {
|
| 51 |
$tabs[$this->tabSlug] = !empty($this->tabTitle) ? $this->tabTitle : $this->tabSlug;
|
| 52 |
return $tabs;
|
| 64 |
|
| 65 |
protected function getTabUrl($queryParameters = array()) {
|
| 66 |
$queryParameters = array_merge(
|
| 67 |
+
array('sub_section' => $this->tabSlug),
|
|
|
|
|
|
|
|
|
|
| 68 |
$queryParameters
|
| 69 |
);
|
| 70 |
+
return $this->menuEditor->get_plugin_page_url($queryParameters);
|
| 71 |
}
|
| 72 |
|
| 73 |
protected function outputMainTemplate() {
|
includes/shadow_plugin_framework.php
CHANGED
|
@@ -241,12 +241,16 @@ class MenuEd_ShadowPluginFramework {
|
|
| 241 |
$hook = substr($method->name, 5);
|
| 242 |
//Add the hook. Uses add_filter because add_action is simply a wrapper of the same.
|
| 243 |
add_filter($hook, array(&$this, $method->name),
|
| 244 |
-
$this->
|
| 245 |
}
|
| 246 |
}
|
| 247 |
|
| 248 |
unset($class);
|
| 249 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
|
| 251 |
|
| 252 |
/**
|
| 241 |
$hook = substr($method->name, 5);
|
| 242 |
//Add the hook. Uses add_filter because add_action is simply a wrapper of the same.
|
| 243 |
add_filter($hook, array(&$this, $method->name),
|
| 244 |
+
$this->get_magic_hook_priority(), $method->getNumberOfParameters());
|
| 245 |
}
|
| 246 |
}
|
| 247 |
|
| 248 |
unset($class);
|
| 249 |
}
|
| 250 |
+
|
| 251 |
+
public function get_magic_hook_priority() {
|
| 252 |
+
return $this->magic_hook_priority;
|
| 253 |
+
}
|
| 254 |
|
| 255 |
|
| 256 |
/**
|
js/actor-manager.js
CHANGED
|
@@ -116,9 +116,9 @@ var AmeSuperAdmin = (function (_super) {
|
|
| 116 |
//The Super Admin has all possible capabilities except the special "do_not_allow" flag.
|
| 117 |
return (capability !== 'do_not_allow');
|
| 118 |
};
|
|
|
|
| 119 |
return AmeSuperAdmin;
|
| 120 |
}(AmeBaseActor));
|
| 121 |
-
AmeSuperAdmin.permanentActorId = 'special:super_admin';
|
| 122 |
var AmeActorManager = (function () {
|
| 123 |
function AmeActorManager(roles, users, isMultisite) {
|
| 124 |
if (isMultisite === void 0) { isMultisite = false; }
|
|
@@ -127,6 +127,8 @@ var AmeActorManager = (function () {
|
|
| 127 |
this.users = {};
|
| 128 |
this.grantedCapabilities = {};
|
| 129 |
this.isMultisite = false;
|
|
|
|
|
|
|
| 130 |
this.suggestedCapabilities = [];
|
| 131 |
this.isMultisite = !!isMultisite;
|
| 132 |
AmeActorManager._.forEach(roles, function (roleDetails, id) {
|
|
@@ -140,6 +142,21 @@ var AmeActorManager = (function () {
|
|
| 140 |
if (this.isMultisite) {
|
| 141 |
this.superAdmin = new AmeSuperAdmin();
|
| 142 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
}
|
| 144 |
AmeActorManager.prototype.actorCanAccess = function (actorId, grantAccess, defaultCapability) {
|
| 145 |
if (defaultCapability === void 0) { defaultCapability = null; }
|
|
@@ -194,7 +211,7 @@ var AmeActorManager = (function () {
|
|
| 194 |
if (capability === 'exist') {
|
| 195 |
return true;
|
| 196 |
}
|
| 197 |
-
capability =
|
| 198 |
var result = null;
|
| 199 |
//Step #1: Check temporary context - unsaved caps, etc. Optional.
|
| 200 |
//Step #2: Check granted capabilities. Default on, but can be skipped.
|
|
@@ -238,13 +255,23 @@ var AmeActorManager = (function () {
|
|
| 238 |
}
|
| 239 |
return false;
|
| 240 |
};
|
| 241 |
-
AmeActorManager.mapMetaCap = function (capability) {
|
| 242 |
if (capability === 'customize') {
|
| 243 |
return 'edit_theme_options';
|
| 244 |
}
|
| 245 |
else if (capability === 'delete_site') {
|
| 246 |
return 'manage_options';
|
| 247 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
return capability;
|
| 249 |
};
|
| 250 |
/* -------------------------------
|
|
@@ -291,18 +318,18 @@ var AmeActorManager = (function () {
|
|
| 291 |
* Grant or deny a capability to an actor.
|
| 292 |
*/
|
| 293 |
AmeActorManager.prototype.setCap = function (actor, capability, hasCap, sourceType, sourceName) {
|
| 294 |
-
|
| 295 |
};
|
| 296 |
-
AmeActorManager.setCapInContext = function (context, actor, capability, hasCap, sourceType, sourceName) {
|
| 297 |
-
capability =
|
| 298 |
var grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap;
|
| 299 |
AmeActorManager._.set(context, [actor, capability], grant);
|
| 300 |
};
|
| 301 |
AmeActorManager.prototype.resetCap = function (actor, capability) {
|
| 302 |
-
|
| 303 |
};
|
| 304 |
-
AmeActorManager.resetCapInContext = function (context, actor, capability) {
|
| 305 |
-
capability =
|
| 306 |
if (AmeActorManager._.has(context, [actor, capability])) {
|
| 307 |
delete context[actor][capability];
|
| 308 |
}
|
|
@@ -440,9 +467,9 @@ var AmeActorManager = (function () {
|
|
| 440 |
AmeActorManager.prototype.getSuggestedCapabilities = function () {
|
| 441 |
return this.suggestedCapabilities;
|
| 442 |
};
|
|
|
|
| 443 |
return AmeActorManager;
|
| 444 |
}());
|
| 445 |
-
AmeActorManager._ = wsAmeLodash;
|
| 446 |
if (typeof wsAmeActorData !== 'undefined') {
|
| 447 |
AmeActors = new AmeActorManager(wsAmeActorData.roles, wsAmeActorData.users, wsAmeActorData.isMultisite);
|
| 448 |
if (typeof wsAmeActorData['capPower'] !== 'undefined') {
|
| 116 |
//The Super Admin has all possible capabilities except the special "do_not_allow" flag.
|
| 117 |
return (capability !== 'do_not_allow');
|
| 118 |
};
|
| 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; }
|
| 127 |
this.users = {};
|
| 128 |
this.grantedCapabilities = {};
|
| 129 |
this.isMultisite = false;
|
| 130 |
+
this.exclusiveSuperAdminCapabilities = {};
|
| 131 |
+
this.tagMetaCaps = {};
|
| 132 |
this.suggestedCapabilities = [];
|
| 133 |
this.isMultisite = !!isMultisite;
|
| 134 |
AmeActorManager._.forEach(roles, function (roleDetails, id) {
|
| 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',
|
| 148 |
+
'edit_files', 'edit_plugins', 'edit_themes', 'delete_user', 'delete_users'
|
| 149 |
+
];
|
| 150 |
+
for (var i = 0; i < exclusiveCaps.length; i++) {
|
| 151 |
+
this.exclusiveSuperAdminCapabilities[exclusiveCaps[i]] = true;
|
| 152 |
+
}
|
| 153 |
+
var tagMetaCaps = [
|
| 154 |
+
'manage_post_tags', 'edit_categories', 'edit_post_tags', 'delete_categories',
|
| 155 |
+
'delete_post_tags'
|
| 156 |
+
];
|
| 157 |
+
for (var i = 0; i < tagMetaCaps.length; i++) {
|
| 158 |
+
this.tagMetaCaps[tagMetaCaps[i]] = true;
|
| 159 |
+
}
|
| 160 |
}
|
| 161 |
AmeActorManager.prototype.actorCanAccess = function (actorId, grantAccess, defaultCapability) {
|
| 162 |
if (defaultCapability === void 0) { defaultCapability = null; }
|
| 211 |
if (capability === 'exist') {
|
| 212 |
return true;
|
| 213 |
}
|
| 214 |
+
capability = this.mapMetaCap(capability);
|
| 215 |
var result = null;
|
| 216 |
//Step #1: Check temporary context - unsaved caps, etc. Optional.
|
| 217 |
//Step #2: Check granted capabilities. Default on, but can be skipped.
|
| 255 |
}
|
| 256 |
return false;
|
| 257 |
};
|
| 258 |
+
AmeActorManager.prototype.mapMetaCap = function (capability) {
|
| 259 |
if (capability === 'customize') {
|
| 260 |
return 'edit_theme_options';
|
| 261 |
}
|
| 262 |
else if (capability === 'delete_site') {
|
| 263 |
return 'manage_options';
|
| 264 |
}
|
| 265 |
+
//In Multisite, some capabilities are only available to Super Admins.
|
| 266 |
+
if (this.isMultisite && this.exclusiveSuperAdminCapabilities.hasOwnProperty(capability)) {
|
| 267 |
+
return AmeSuperAdmin.permanentActorId;
|
| 268 |
+
}
|
| 269 |
+
if (this.tagMetaCaps.hasOwnProperty(capability)) {
|
| 270 |
+
return 'manage_categories';
|
| 271 |
+
}
|
| 272 |
+
if ((capability === 'assign_categories') || (capability === 'assign_post_tags')) {
|
| 273 |
+
return 'edit_posts';
|
| 274 |
+
}
|
| 275 |
return capability;
|
| 276 |
};
|
| 277 |
/* -------------------------------
|
| 318 |
* Grant or deny a capability to an actor.
|
| 319 |
*/
|
| 320 |
AmeActorManager.prototype.setCap = function (actor, capability, hasCap, sourceType, sourceName) {
|
| 321 |
+
this.setCapInContext(this.grantedCapabilities, actor, capability, hasCap, sourceType, sourceName);
|
| 322 |
};
|
| 323 |
+
AmeActorManager.prototype.setCapInContext = function (context, actor, capability, hasCap, sourceType, sourceName) {
|
| 324 |
+
capability = this.mapMetaCap(capability);
|
| 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])) {
|
| 334 |
delete context[actor][capability];
|
| 335 |
}
|
| 467 |
AmeActorManager.prototype.getSuggestedCapabilities = function () {
|
| 468 |
return this.suggestedCapabilities;
|
| 469 |
};
|
| 470 |
+
AmeActorManager._ = wsAmeLodash;
|
| 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') {
|
js/actor-manager.ts
CHANGED
|
@@ -177,8 +177,11 @@ class AmeActorManager {
|
|
| 177 |
private users: {[userLogin: string] : AmeUser} = {};
|
| 178 |
private grantedCapabilities: AmeGrantedCapabilityMap = {};
|
| 179 |
|
| 180 |
-
|
| 181 |
private superAdmin: AmeSuperAdmin;
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
private suggestedCapabilities: AmeCapabilitySuggestion[] = [];
|
| 184 |
|
|
@@ -198,6 +201,23 @@ class AmeActorManager {
|
|
| 198 |
if (this.isMultisite) {
|
| 199 |
this.superAdmin = new AmeSuperAdmin();
|
| 200 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
}
|
| 202 |
|
| 203 |
actorCanAccess(
|
|
@@ -265,7 +285,7 @@ class AmeActorManager {
|
|
| 265 |
return true;
|
| 266 |
}
|
| 267 |
|
| 268 |
-
capability =
|
| 269 |
let result = null;
|
| 270 |
|
| 271 |
//Step #1: Check temporary context - unsaved caps, etc. Optional.
|
|
@@ -315,12 +335,22 @@ class AmeActorManager {
|
|
| 315 |
return false;
|
| 316 |
}
|
| 317 |
|
| 318 |
-
private
|
| 319 |
if (capability === 'customize') {
|
| 320 |
return 'edit_theme_options';
|
| 321 |
} else if (capability === 'delete_site') {
|
| 322 |
return 'manage_options';
|
| 323 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
return capability;
|
| 325 |
}
|
| 326 |
|
|
@@ -378,10 +408,10 @@ class AmeActorManager {
|
|
| 378 |
* Grant or deny a capability to an actor.
|
| 379 |
*/
|
| 380 |
setCap(actor: string, capability: string, hasCap: boolean, sourceType?, sourceName?) {
|
| 381 |
-
|
| 382 |
}
|
| 383 |
|
| 384 |
-
|
| 385 |
context: AmeGrantedCapabilityMap,
|
| 386 |
actor: string,
|
| 387 |
capability: string,
|
|
@@ -389,18 +419,18 @@ class AmeActorManager {
|
|
| 389 |
sourceType?: string,
|
| 390 |
sourceName?: string
|
| 391 |
) {
|
| 392 |
-
capability =
|
| 393 |
|
| 394 |
const grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap;
|
| 395 |
AmeActorManager._.set(context, [actor, capability], grant);
|
| 396 |
}
|
| 397 |
|
| 398 |
resetCap(actor: string, capability: string) {
|
| 399 |
-
|
| 400 |
}
|
| 401 |
|
| 402 |
-
|
| 403 |
-
capability =
|
| 404 |
|
| 405 |
if (AmeActorManager._.has(context, [actor, capability])) {
|
| 406 |
delete context[actor][capability];
|
| 177 |
private users: {[userLogin: string] : AmeUser} = {};
|
| 178 |
private grantedCapabilities: AmeGrantedCapabilityMap = {};
|
| 179 |
|
| 180 |
+
public readonly isMultisite: boolean = false;
|
| 181 |
private superAdmin: AmeSuperAdmin;
|
| 182 |
+
private exclusiveSuperAdminCapabilities = {};
|
| 183 |
+
|
| 184 |
+
private tagMetaCaps = {};
|
| 185 |
|
| 186 |
private suggestedCapabilities: AmeCapabilitySuggestion[] = [];
|
| 187 |
|
| 201 |
if (this.isMultisite) {
|
| 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',
|
| 208 |
+
'edit_files', 'edit_plugins', 'edit_themes', 'delete_user', 'delete_users'
|
| 209 |
+
];
|
| 210 |
+
for (let i = 0; i < exclusiveCaps.length; i++) {
|
| 211 |
+
this.exclusiveSuperAdminCapabilities[exclusiveCaps[i]] = true;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
const tagMetaCaps = [
|
| 215 |
+
'manage_post_tags', 'edit_categories', 'edit_post_tags', 'delete_categories',
|
| 216 |
+
'delete_post_tags'
|
| 217 |
+
];
|
| 218 |
+
for (let i = 0; i < tagMetaCaps.length; i++) {
|
| 219 |
+
this.tagMetaCaps[tagMetaCaps[i]] = true;
|
| 220 |
+
}
|
| 221 |
}
|
| 222 |
|
| 223 |
actorCanAccess(
|
| 285 |
return true;
|
| 286 |
}
|
| 287 |
|
| 288 |
+
capability = this.mapMetaCap(capability);
|
| 289 |
let result = null;
|
| 290 |
|
| 291 |
//Step #1: Check temporary context - unsaved caps, etc. Optional.
|
| 335 |
return false;
|
| 336 |
}
|
| 337 |
|
| 338 |
+
private mapMetaCap(capability: string): string {
|
| 339 |
if (capability === 'customize') {
|
| 340 |
return 'edit_theme_options';
|
| 341 |
} else if (capability === 'delete_site') {
|
| 342 |
return 'manage_options';
|
| 343 |
}
|
| 344 |
+
//In Multisite, some capabilities are only available to Super Admins.
|
| 345 |
+
if (this.isMultisite && this.exclusiveSuperAdminCapabilities.hasOwnProperty(capability)) {
|
| 346 |
+
return AmeSuperAdmin.permanentActorId;
|
| 347 |
+
}
|
| 348 |
+
if (this.tagMetaCaps.hasOwnProperty(capability)) {
|
| 349 |
+
return 'manage_categories';
|
| 350 |
+
}
|
| 351 |
+
if ((capability === 'assign_categories') || (capability === 'assign_post_tags')) {
|
| 352 |
+
return 'edit_posts';
|
| 353 |
+
}
|
| 354 |
return capability;
|
| 355 |
}
|
| 356 |
|
| 408 |
* Grant or deny a capability to an actor.
|
| 409 |
*/
|
| 410 |
setCap(actor: string, capability: string, hasCap: boolean, sourceType?, sourceName?) {
|
| 411 |
+
this.setCapInContext(this.grantedCapabilities, actor, capability, hasCap, sourceType, sourceName);
|
| 412 |
}
|
| 413 |
|
| 414 |
+
public setCapInContext(
|
| 415 |
context: AmeGrantedCapabilityMap,
|
| 416 |
actor: string,
|
| 417 |
capability: string,
|
| 419 |
sourceType?: string,
|
| 420 |
sourceName?: string
|
| 421 |
) {
|
| 422 |
+
capability = this.mapMetaCap(capability);
|
| 423 |
|
| 424 |
const grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap;
|
| 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 |
|
| 435 |
if (AmeActorManager._.has(context, [actor, capability])) {
|
| 436 |
delete context[actor][capability];
|
js/menu-editor.js
CHANGED
|
@@ -1226,12 +1226,15 @@ function updateActorAccessUi(containerNode) {
|
|
| 1226 |
var currentUserActor = 'user:' + wsEditorData.currentUserLogin;
|
| 1227 |
var otherActors = _(wsEditorData.actors).keys().without(currentUserActor, 'special:super_admin').value(),
|
| 1228 |
hiddenFromCurrentUser = ! actorCanAccessMenu(menuItem, currentUserActor),
|
| 1229 |
-
hiddenFromOthers = ! _.some(otherActors, _.curry(actorCanAccessMenu, 2)(menuItem))
|
|
|
|
| 1230 |
setMenuFlag(
|
| 1231 |
containerNode,
|
| 1232 |
'hidden_from_others',
|
| 1233 |
hiddenFromOthers,
|
| 1234 |
-
hiddenFromCurrentUser
|
|
|
|
|
|
|
| 1235 |
);
|
| 1236 |
}
|
| 1237 |
|
|
@@ -4834,5 +4837,8 @@ jQuery(function($){
|
|
| 4834 |
});
|
| 4835 |
|
| 4836 |
//Move our options into the screen meta panel
|
| 4837 |
-
$('#adv-settings')
|
|
|
|
|
|
|
|
|
|
| 4838 |
});
|
| 1226 |
var currentUserActor = 'user:' + wsEditorData.currentUserLogin;
|
| 1227 |
var otherActors = _(wsEditorData.actors).keys().without(currentUserActor, 'special:super_admin').value(),
|
| 1228 |
hiddenFromCurrentUser = ! actorCanAccessMenu(menuItem, currentUserActor),
|
| 1229 |
+
hiddenFromOthers = ! _.some(otherActors, _.curry(actorCanAccessMenu, 2)(menuItem)),
|
| 1230 |
+
visibleForSuperAdmin = AmeActors.isMultisite && actorCanAccessMenu(menuItem, 'special:super_admin');
|
| 1231 |
setMenuFlag(
|
| 1232 |
containerNode,
|
| 1233 |
'hidden_from_others',
|
| 1234 |
hiddenFromOthers,
|
| 1235 |
+
hiddenFromCurrentUser
|
| 1236 |
+
? 'Hidden from everyone'
|
| 1237 |
+
: ('Hidden from everyone except you' + (visibleForSuperAdmin ? ' and Super Admins' : ''))
|
| 1238 |
);
|
| 1239 |
}
|
| 1240 |
|
| 4837 |
});
|
| 4838 |
|
| 4839 |
//Move our options into the screen meta panel
|
| 4840 |
+
var advSettings = $('#adv-settings');
|
| 4841 |
+
if (advSettings.length > 0) {
|
| 4842 |
+
advSettings.empty().append(screenOptions.show());
|
| 4843 |
+
}
|
| 4844 |
});
|
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.1
|
| 7 |
Author: Janis Elsts
|
| 8 |
Author URI: http://w-shadow.com/blog/
|
| 9 |
*/
|
modules/access-editor/access-editor.js
CHANGED
|
@@ -242,10 +242,10 @@ window.AmeItemAccessEditor = (function ($) {
|
|
| 242 |
hasCapWhenReset;
|
| 243 |
|
| 244 |
//Don't create custom settings unless necessary.
|
| 245 |
-
|
| 246 |
hasCapWhenReset = AmeCapabilityManager.hasCap(selectedActor, capability, unsavedCapabilities);
|
| 247 |
if (isAllowed !== hasCapWhenReset) {
|
| 248 |
-
|
| 249 |
unsavedCapabilities,
|
| 250 |
selectedActor,
|
| 251 |
capability,
|
|
@@ -277,7 +277,7 @@ window.AmeItemAccessEditor = (function ($) {
|
|
| 277 |
hasCapByDefault = AmeCapabilityManager.hasCapByDefault(selectedActor, itemRequiredCap);
|
| 278 |
|
| 279 |
if (isAllowed && hasCapByDefault && !hasCap) {
|
| 280 |
-
|
| 281 |
unsavedCapabilities,
|
| 282 |
selectedActor,
|
| 283 |
itemRequiredCap,
|
| 242 |
hasCapWhenReset;
|
| 243 |
|
| 244 |
//Don't create custom settings unless necessary.
|
| 245 |
+
AmeCapabilityManager.resetCapInContext(unsavedCapabilities, selectedActor, capability);
|
| 246 |
hasCapWhenReset = AmeCapabilityManager.hasCap(selectedActor, capability, unsavedCapabilities);
|
| 247 |
if (isAllowed !== hasCapWhenReset) {
|
| 248 |
+
AmeCapabilityManager.setCapInContext(
|
| 249 |
unsavedCapabilities,
|
| 250 |
selectedActor,
|
| 251 |
capability,
|
| 277 |
hasCapByDefault = AmeCapabilityManager.hasCapByDefault(selectedActor, itemRequiredCap);
|
| 278 |
|
| 279 |
if (isAllowed && hasCapByDefault && !hasCap) {
|
| 280 |
+
AmeCapabilityManager.setCapInContext(
|
| 281 |
unsavedCapabilities,
|
| 282 |
selectedActor,
|
| 283 |
itemRequiredCap,
|
modules/actor-selector/actor-selector.js
CHANGED
|
@@ -196,7 +196,6 @@ var AmeActorSelector = (function () {
|
|
| 196 |
}
|
| 197 |
return name;
|
| 198 |
};
|
|
|
|
| 199 |
return AmeActorSelector;
|
| 200 |
}());
|
| 201 |
-
AmeActorSelector._ = wsAmeLodash;
|
| 202 |
-
//# sourceMappingURL=actor-selector.js.map
|
| 196 |
}
|
| 197 |
return name;
|
| 198 |
};
|
| 199 |
+
AmeActorSelector._ = wsAmeLodash;
|
| 200 |
return AmeActorSelector;
|
| 201 |
}());
|
|
|
|
|
|
modules/plugin-visibility/plugin-visibility-template.php
CHANGED
|
@@ -1,15 +1,14 @@
|
|
| 1 |
-
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
<div id="ame-plugin-visibility-editor">
|
| 4 |
<form method="post" data-bind="submit: saveChanges" class="ame-pv-save-form" action="<?php
|
| 5 |
-
echo esc_attr(add_query_arg(
|
| 6 |
-
array(
|
| 7 |
-
'page' => 'menu_editor',
|
| 8 |
-
'noheader' => '1',
|
| 9 |
-
'sub_section' => amePluginVisibility::TAB_SLUG,
|
| 10 |
-
),
|
| 11 |
-
admin_url('options-general.php')
|
| 12 |
-
));
|
| 13 |
?>">
|
| 14 |
|
| 15 |
<?php submit_button('Save Changes', 'primary', 'submit', false); ?>
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* @var string $tabUrl Fully qualified URL of the "Plugins" tab.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
do_action('admin_menu_editor-display_header');
|
| 7 |
+
?>
|
| 8 |
|
| 9 |
<div id="ame-plugin-visibility-editor">
|
| 10 |
<form method="post" data-bind="submit: saveChanges" class="ame-pv-save-form" action="<?php
|
| 11 |
+
echo esc_attr(add_query_arg(array('noheader' => '1'), $tabUrl));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
?>">
|
| 13 |
|
| 14 |
<?php submit_button('Save Changes', 'primary', 'submit', false); ?>
|
modules/plugin-visibility/plugin-visibility.php
CHANGED
|
@@ -192,6 +192,11 @@ class amePluginVisibility {
|
|
| 192 |
$user = wp_get_current_user();
|
| 193 |
$settings = $this->getSettings();
|
| 194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
$pluginFileNames = array_keys($plugins);
|
| 196 |
foreach($pluginFileNames as $fileName) {
|
| 197 |
//Remove all hidden plugins.
|
|
@@ -288,6 +293,9 @@ class amePluginVisibility {
|
|
| 288 |
}
|
| 289 |
|
| 290 |
public function displayUi() {
|
|
|
|
|
|
|
|
|
|
| 291 |
require dirname(__FILE__) . '/plugin-visibility-template.php';
|
| 292 |
}
|
| 293 |
|
|
@@ -299,7 +307,7 @@ class amePluginVisibility {
|
|
| 299 |
$this->settings = json_decode($post['settings'], true);
|
| 300 |
$this->saveSettings();
|
| 301 |
|
| 302 |
-
$params = array('
|
| 303 |
|
| 304 |
//Re-select the same actor.
|
| 305 |
if ( !empty($post['selected_actor']) ) {
|
|
@@ -313,13 +321,10 @@ class amePluginVisibility {
|
|
| 313 |
|
| 314 |
private function getTabUrl($queryParameters = array()) {
|
| 315 |
$queryParameters = array_merge(
|
| 316 |
-
array(
|
| 317 |
-
'page' => 'menu_editor',
|
| 318 |
-
'sub_section' => self::TAB_SLUG
|
| 319 |
-
),
|
| 320 |
$queryParameters
|
| 321 |
);
|
| 322 |
-
return
|
| 323 |
}
|
| 324 |
|
| 325 |
public function enqueueScripts() {
|
| 192 |
$user = wp_get_current_user();
|
| 193 |
$settings = $this->getSettings();
|
| 194 |
|
| 195 |
+
//Don't try to hide plugins outside the WP admin. It prevents WP-CLI from seeing all installed plugins.
|
| 196 |
+
if ( !$user->exists() || !is_admin() ) {
|
| 197 |
+
return $plugins;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
$pluginFileNames = array_keys($plugins);
|
| 201 |
foreach($pluginFileNames as $fileName) {
|
| 202 |
//Remove all hidden plugins.
|
| 293 |
}
|
| 294 |
|
| 295 |
public function displayUi() {
|
| 296 |
+
/** @noinspection PhpUnusedLocalVariableInspection Used in the "action" attribute of the settings form. */
|
| 297 |
+
$tabUrl = $this->getTabUrl();
|
| 298 |
+
|
| 299 |
require dirname(__FILE__) . '/plugin-visibility-template.php';
|
| 300 |
}
|
| 301 |
|
| 307 |
$this->settings = json_decode($post['settings'], true);
|
| 308 |
$this->saveSettings();
|
| 309 |
|
| 310 |
+
$params = array('message' => 1);
|
| 311 |
|
| 312 |
//Re-select the same actor.
|
| 313 |
if ( !empty($post['selected_actor']) ) {
|
| 321 |
|
| 322 |
private function getTabUrl($queryParameters = array()) {
|
| 323 |
$queryParameters = array_merge(
|
| 324 |
+
array('sub_section' => self::TAB_SLUG),
|
|
|
|
|
|
|
|
|
|
| 325 |
$queryParameters
|
| 326 |
);
|
| 327 |
+
return $this->menuEditor->get_plugin_page_url($queryParameters);
|
| 328 |
}
|
| 329 |
|
| 330 |
public function enqueueScripts() {
|
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.
|
| 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,12 @@ Plugins installed in the `mu-plugins` directory are treated as "always on", so y
|
|
| 63 |
|
| 64 |
== Changelog ==
|
| 65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
= 1.8 =
|
| 67 |
* You can edit plugin names and descriptions through the "Plugins" tab. This only changes how plugins are displayed on the "Plugins" page. It doesn't affect plugin files on disk.
|
| 68 |
* Added an option to highlight new menu items. This feature is off by default. You can enable it in the "Settings" tab.
|
| 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.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.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.
|
| 69 |
+
* Minor performance improvements.
|
| 70 |
+
* Tested with WP 4.8.3 and 4.9.
|
| 71 |
+
|
| 72 |
= 1.8 =
|
| 73 |
* You can edit plugin names and descriptions through the "Plugins" tab. This only changes how plugins are displayed on the "Plugins" page. It doesn't affect plugin files on disk.
|
| 74 |
* Added an option to highlight new menu items. This feature is off by default. You can enable it in the "Settings" tab.
|
