PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
| Dir : /home/antonkerr/www/daltonstours.com/wp-content/pluginsX/admin-menu-editor-pro/ |
| Server: Linux premium47.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64 IP: 68.65.123.244 |
| Dir : /home/antonkerr/www/daltonstours.com/wp-content/pluginsX/admin-menu-editor-pro/extras.php |
<?php
/**
* These flags control how the {@link wsMenuEditorExtras::check_current_user_access} method
* deals with users that have multiple roles with different permissions. "RC" stands for "role combination".
*/
/**
* Use only custom role permissions. Roles that don't have explicit settings will be ignored.
*/
define('AME_RC_ONLY_CUSTOM', 1);
/**
* When a role has no custom settings, use the $default_access argument instead.
*/
define('AME_RC_USE_DEFAULT_ACCESS', 2);
class wsMenuEditorExtras {
/** @var WPMenuEditor */
private $wp_menu_editor;
private $framed_pages = array();
private $embedded_wp_pages = array();
//A list of IDs for menu items output by Ozh's Admin Drop Down Menu
//(those can't be modified the usual way because Ozh's plugin strips tags
//from submenu titles).
private $ozhs_new_window_menus;
protected $export_settings;
private $disable_virtual_caps = false;
private $username_cache = array();
private $cached_user_caps = array();
private $cached_virtual_user_caps = array();
private $fields_supporting_shortcodes = array('page_title', 'menu_title', 'file', 'css_class', 'hookname', 'icon_url');
/**
* Class constructor.
*
* @param WPMenuEditor $wp_menu_editor
*/
function __construct($wp_menu_editor){
$this->wp_menu_editor = $wp_menu_editor;
//Apply most Pro version menu customizations all in one go. This reduces apply_filters() overhead
//and is slightly faster than adding a separate filter for each feature.
add_filter('custom_admin_menu', array($this, 'apply_admin_menu_filters'));
add_filter('custom_admin_submenu', array($this, 'apply_admin_menu_filters'), 10, 2);
//Add some extra shortcodes of our own
$shortcode_callback = array($this, 'handle_shortcode');
$info_shortcodes = array(
'wp-wpurl', //WordPress address (URI), as returned by get_bloginfo()
'wp-siteurl', //Blog address (URI)
'wp-admin', //Admin area URL (with a trailing slash)
'wp-name', //Weblog title
'wp-version', //Current WP version
'wp-user-display-name', //Current user's display name,
'wp-logout-url', //A URL that lets the current user log out.
);
foreach($info_shortcodes as $tag){
add_shortcode($tag, $shortcode_callback);
}
//Output the menu-modification JS after the menu has been generated.
//'admin_notices' is, AFAIK, the action that fires the soonest after menu
//output has been completed, so we use that.
add_action('admin_notices', array($this, 'fix_flagged_menus'));
//Import/export settings
$this->export_settings = array(
'max_file_size' => 5*1024*1024,
'file_extension' => 'dat',
'old_format_string' => 'wsMenuEditor_ExportFile',
);
//Insert the import and export dialog HTML into the editor's page
add_action('admin_menu_editor-footer-editor', array($this, 'menu_editor_footer'));
//Handle menu downloads and uploads
add_action('admin_menu_editor-header', array($this, 'menu_editor_header'));
//Handle export requests
add_action( 'wp_ajax_export_custom_menu', array($this,'ajax_export_custom_menu') );
//Add the "Import" and "Export" buttons
add_action('admin_menu_editor-sidebar', array($this, 'add_extra_buttons'));
add_filter('admin_menu_editor-self_page_title', array($this, 'pro_page_title'), 10, 0);
add_filter('admin_menu_editor-self_menu_title', array($this, 'pro_menu_title'), 10, 0);
//Let other components know we're Pro.
add_filter('admin_menu_editor_is_pro', array($this, 'is_pro_version'), 10, 0);
//Add menu item drop zones to the top-level and sub-menu containers.
add_action('admin_menu_editor-container', array($this, 'output_menu_dropzone'), 10, 1);
//Add submenu icons.
add_filter('admin_menu_editor-submenu_with_icon', array($this, 'add_submenu_icon_html'), 10, 2);
/**
* Access management extensions.
*/
//Allow usernames to be used in capability checks. Syntax : "user:user_login"
add_filter('user_has_cap', array($this, 'hook_user_has_cap'), 10, 3);
//Enable advanced capability operations (OR, AND, NOT) for internal use.
add_filter('admin_menu_editor-current_user_can', array($this, 'grant_computed_caps_to_current_user'), 10, 2);
//Custom per-role and per-user access settings (distinct from the "extra capability" field.
add_filter('custom_admin_menu_capability', array($this, 'apply_custom_access'));
//Role access: Grant virtual capabilities to roles/users that need them to access certain menus.
add_filter('user_has_cap', array($this, 'grant_virtual_caps_to_user'), 9, 3);
add_filter('user_has_cap', array($this, 'regrant_virtual_caps_to_user'), 200, 1);
add_filter('role_has_cap', array($this, 'grant_virtual_caps_to_role'), 200, 3);
//Remove the plugin from the "Plugins" page for users who're not allowed to see it.
if ( $this->wp_menu_editor->get_plugin_option('plugins_page_allowed_user_id') !== null ) {
add_filter('all_plugins', array($this, 'filter_plugin_list'));
}
/**
* Menu color scheme generation.
*/
add_filter('ame_pre_set_custom_menu', array($this, 'add_menu_color_css'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_menu_color_style'));
add_action('wp_ajax_ame_output_menu_color_css', array($this,'ajax_output_menu_color_css') );
//FontAwesome icons.
add_action('admin_enqueue_scripts', array($this, 'enqueue_fontawesome'));
add_filter('custom_admin_menu', array($this, 'add_menu_fa_icon'), 10, 1);
add_filter('admin_menu_editor-icon_selector_tabs', array($this, 'add_fa_selector_tab'), 10, 1);
add_action('admin_menu_editor-icon_selector', array($this, 'output_fa_selector_tab'));
/**
* Modules.
*/
include dirname(__FILE__) . '/extras/modules/visible-users/visible-users.php';
new ameVisibleUsers($this->wp_menu_editor);
include dirname(__FILE__) . '/extras/modules/hide-admin-bar/hide-admin-bar.php';
new ameAdminBarHider($this->wp_menu_editor, $this);
include dirname(__FILE__) . '/extras/modules/hide-admin-menu/hide-admin-menu.php';
new ameAdminMenuHider($this->wp_menu_editor, $this);
//License management
add_filter('wslm_license_ui_title-admin-menu-editor-pro', array($this, 'license_ui_title'), 10, 0);
add_action('wslm_license_ui_logo-admin-menu-editor-pro', array($this, 'license_ui_logo'));
add_action('wslm_license_ui_details-admin-menu-editor-pro', array($this, 'license_ui_upgrade_link'), 10, 3);
add_filter('wslm_product_name-admin-menu-editor-pro', array($this, 'license_ui_product_name'), 10, 0);
}
/**
* Process shortcodes in menu fields
*
* @param array $item
* @return array
*/
function do_shortcodes($item){
foreach($this->fields_supporting_shortcodes as $field){
if ( isset($item[$field]) ) {
$value = $item[$field];
if ( strpos($value, '[') !== false ){
$item[$field] = do_shortcode($value);
}
}
}
return $item;
}
/**
* Get the value of one of our extra shortcodes
*
* @param array $atts Shortcode attributes (ignored)
* @param string $content Content enclosed by the shortcode (ignored)
* @param string $code
* @return string Shortcode will be replaced with this value
*/
function handle_shortcode($atts, /** @noinspection PhpUnusedParameterInspection */ $content = null, $code = ''){
//The shortcode tag can be either $code or the zeroth member of the $atts array.
if ( empty($code) ){
$code = isset($atts[0]) ? $atts[0] : '';
}
$info = '['.$code.']'; //Default value
switch($code){
case 'wp-wpurl':
$info = get_bloginfo('wpurl');
break;
case 'wp-siteurl':
$info = get_bloginfo('url');
break;
case 'wp-admin':
$info = admin_url();
break;
case 'wp-name':
$info = get_bloginfo('name');
break;
case 'wp-version':
$info = get_bloginfo('version');
break;
case 'wp-user-display-name':
$user = wp_get_current_user();
$info = is_object($user) ? strval($user->get('display_name')) : '';
break;
case 'wp-logout-url':
$info = wp_logout_url();
break;
}
return $info;
}
/**
* Flag menus (and menu items) that are set to open in a new window
* so that they can be identified later.
*
* Adds a <span class="ws-new-window-please"></span> element to the title
* of each detected menu.
*
* @param array $item
* @return array
*/
function flag_new_window_menus($item){
$open_in = ameMenuItem::get($item, 'open_in', 'same_window');
if ( $open_in == 'new_window' ){
$old_title = ameMenuItem::get($item, 'menu_title', '');
$item['menu_title'] = $old_title . '<span class="ws-new-window-please" style="display:none;"></span>';
//For compatibility with Ozh's Admin Drop Down menu, record the link ID that will be
//assigned to this item. This lets us modify it later.
if ( function_exists('wp_ozh_adminmenu_sanitize_id') ){
$subid = 'oamsub_'.wp_ozh_adminmenu_sanitize_id(
ameMenuItem::get($item, 'file', '')
);
$this->ozhs_new_window_menus[] = '#' . str_replace(
array(':', '&'),
array('\\\\:', '\\\\&'),
$subid
);
}
}
return $item;
}
/**
* Output a piece of JS that will find flagged menu links and make them
* open in a new window.
*
* @return void
*/
function fix_flagged_menus(){
?>
<script type="text/javascript">
(function($){
$('#adminmenu span.ws-new-window-please, #ozhmenu span.ws-new-window-please').each(function(){
var marker = $(this);
//Add target="_blank" to the enclosing link
marker.parents('a').first().attr('target', '_blank');
//And to the menu image link, too (only for top-level menus)
marker.parent().parent().find('> .wp-menu-image a').attr('target', '_blank');
//Get rid of the marker
marker.remove();
});
<?php if ( !empty($this->ozhs_new_window_menus) ): ?>
$('<?php echo implode(', ', $this->ozhs_new_window_menus); ?>').each(function(){
//Add target="_blank" to the link
$(this).find('a').attr('target', '_blank');
});
<?php endif; ?>
})(jQuery);
</script>
<?php
}
/**
* Intercept menus that need to be displayed in an IFrame.
*
* Here's how this works : each item that needs to be displayed in an IFrame
* gets added as a new menu (or submenu) using the standard WP plugin API.
* This ensures that the myriad undocumented data structures that WP employs
* for menu generation get populated correctly.
*
* The reason why this doesn't lead to menu duplication is that the global $menu
* and $submenu arrays are thrown away and replaced with custom-generated ones
* shortly afterwards. The modified menu entry returned by this function becomes
* part of that custom menu.
*
* All items added in this way have the same callback function - wsMenuEditorExtras::display_framed_page()
*
* @param array $item
* @return array
*/
function create_framed_menu($item){
if ( $item['open_in'] == 'iframe' ){
$slug = 'framed-menu-' . md5($item['file']);//MD5 should be unique enough
$this->framed_pages[$slug] = $item; //Used by the callback function
//Default to using menu title for page title, if no custom title specified
if ( empty($item['page_title']) ) {
$item['page_title'] = $item['menu_title'];
}
//Add a virtual menu. The menu record created by add_menu_page will be
//thrown away; what matters is that this populates other structures
//like $_registered_pages.
add_menu_page(
$item['page_title'],
$item['menu_title'],
$item['access_level'],
$slug,
array($this, 'display_framed_page')
);
//Change the slug to our newly created page.
$item['file'] = $slug;
}
return $item;
}
/**
* Intercept menu items that need to be displayed in an IFrame.
*
* @see wsMenuEditorExtras::create_framed_menu()
*
* @param array $item
* @param string $parent_file
* @return array
*/
function create_framed_item($item, $parent_file = null){
if ( ($item['open_in'] == 'iframe') && !empty($parent_file) ){
$slug = 'framed-menu-item-' . md5($item['file'] . '|' . $parent_file);
$this->framed_pages[$slug] = $item;
if ( empty($item['page_title']) ) {
$item['page_title'] = $item['menu_title'];
}
add_submenu_page(
$parent_file,
$item['page_title'],
$item['menu_title'],
$item['access_level'],
$slug,
array($this, 'display_framed_page')
);
$item['file'] = $slug;
}
return $item;
}
/**
* Display a page in an IFrame.
* This callback is used by all menu items that are set to open in a frame.
*
* @return void
*/
function display_framed_page(){
global $plugin_page;
if ( isset($this->framed_pages[$plugin_page]) ){
$item = $this->framed_pages[$plugin_page];
} else {
return;
}
if ( !current_user_can($item['access_level']) ){
echo "You do not have sufficient permissions to view this page.";
return;
}
$styles = array(
'border' => 'none',
'width' => '100%',
'min-height' => '300px',
);
//The user can set the frame height manually or let the plugin calculate it automatically (the default).
$height = !empty($item['iframe_height']) ? intval($item['iframe_height']) : 0;
$height = min(max($height, 0), 10000);
if ( !empty($height) ) {
$styles['height'] = $height . 'px';
unset($styles['min-height']);
}
$style_attr = '';
foreach($styles as $property => $value) {
$style_attr .= $property . ': ' . $value . ';';
}
$heading = !empty($item['page_title'])?$item['page_title']:$item['menu_title'];
$heading = sprintf('<%1$s>%2$s</%1$s>', WPMenuEditor::$admin_heading_tag, $heading);
?>
<div class="wrap">
<?php echo $heading; ?>
<!--suppress HtmlUnknownAttribute "frameborder" is in fact allowed here -->
<iframe
src="<?php echo esc_attr($item['file']); ?>"
style="<?php echo esc_attr($style_attr); ?>>"
id="ws-framed-page"
frameborder="0"
></iframe>
</div>
<?php
if ( empty($height) ) :
?>
<script type="text/javascript">
function wsResizeFrame(){
var $ = jQuery;
var footer = $('#footer, #wpfooter');
var frame = $('#ws-framed-page');
var containerPadding = parseInt(frame.closest('#wpbody-content').css('padding-bottom'), 10) || 0;
//Automagically calculate a frame height that fills the entire page without creating a vertical scrollbar.
var maxHeight = footer.offset().top - frame.offset().top;
var minHeight = maxHeight - containerPadding;
var empiricalFudgeFactor = 29; //Based on the default admin theme in WP 4.1 (without the test helper output).
var initialHeight = maxHeight - empiricalFudgeFactor;
frame.height(initialHeight);
setTimeout(function() {
//Check if there's a scroll bar and reduce the height just enough to get rid of it.
//Sometimes it's not possible to avoid scrolling because another part of the page is too tall,
//so we have a minimum height limit.
var scrollDelta = $(document).height() - $(window).height();
if (scrollDelta > 0) {
frame.height(Math.max(initialHeight - scrollDelta, minHeight));
}
}, 1)
}
jQuery(function(){
wsResizeFrame();
});
</script>
<?php
endif;
}
/**
* Set up menu items that display the content of a normal page (as in the post type) as an admin page.
*
* @param array $item Menu item.
* @param string|null $parent_file Parent menu slug or URL.
* @return array Modified menu item.
*/
public function create_embedded_wp_page($item, $parent_file = null) {
if ( ameMenuItem::get($item, 'template_id') !== ameMenuItem::embeddedPageTemplateId ) {
return $item;
}
$page_id = ameMenuItem::get($item, 'embedded_page_id', 0);
$blog_id = ameMenuItem::get($item, 'embedded_page_blog_id', get_current_blog_id());
//Default to using the menu title as the window title.
if ( empty($item['page_title']) ) {
$item['page_title'] = strip_tags($item['menu_title']);
}
$slug = 'embedded-page-' . md5($page_id . '|' . $blog_id . '|' . count($this->embedded_wp_pages));
$this->embedded_wp_pages[$slug] = $item; //Used by the callback function.
//Add a virtual menu.
if ( empty($parent_file) ) {
add_menu_page(
$item['page_title'],
$item['menu_title'],
$item['access_level'],
$slug,
array($this, 'display_embedded_wp_page')
);
} else {
add_submenu_page(
$parent_file,
$item['menu_title'],
$item['menu_title'],
$item['access_level'],
$slug,
array($this, 'display_embedded_wp_page')
);
}
//Change the slug to our newly created page.
$item['file'] = $slug;
//Force automatic URL generation.
$item['url'] = '';
//Make sure admin-helpers.js won't replace the real heading with the placeholder.
if ($item['page_heading'] === ameMenuItem::embeddedPagePlaceholderHeading) {
$item['page_heading'] = null;
}
return $item;
}
/**
* A callback for menu items that embed a page or CPT item in the admin panel. Displays the content of the page.
*/
public function display_embedded_wp_page() {
$slug = isset($_GET['page']) ? strval($_GET['page']) : null;
if ( empty($slug) || !isset($this->embedded_wp_pages[$slug]) ) {
echo '<h1>Error: Invalid page. How did you get here?</h1>';
return;
}
$item = $this->embedded_wp_pages[$slug];
$page_id = ameMenuItem::get($item, 'embedded_page_id', 0);
$page_blog_id = ameMenuItem::get($item, 'embedded_page_blog_id', get_current_blog_id());
$should_switch = ($page_blog_id !== get_current_blog_id());
if ( $should_switch ) {
switch_to_blog($page_blog_id);
}
$page = get_post($page_id);
$expected_post_status = 'publish';
if ( empty($page) ) {
printf(
'Error: Page not found. Post ID %1$d does not exist on blog ID %2$d.',
$page_id,
$page_blog_id
);
} else if ( $page->post_status !== $expected_post_status ) {
printf(
'Error: This page is not published. Post ID: %1$d, expected status: "%2$s", actual status: "%3$s".',
$page_id,
esc_html($expected_post_status),
esc_html($page->post_status)
);
} else {
$heading = $item['page_heading'];
if ( $heading === ameMenuItem::embeddedPagePlaceholderHeading ) {
//Note that this means the user can't set the heading to the same text as the placeholder.
//That's poor design, but it probably won't matter in practice.
$heading = strip_tags($item['menu_title']);
}
echo '<div class="wrap">';
if ( !empty($heading) ) {
printf('<%2$s>%1$s</%2$s>', $heading, WPMenuEditor::$admin_heading_tag);
}
echo apply_filters('the_content', $page->post_content);
echo '</div>';
}
if ( $should_switch ) {
restore_current_blog();
}
}
private function set_final_hidden_flag($item) {
//Globally hidden items stay hidden regardless of who is currently logged in.
if ( !empty($item['hidden']) ) {
return $item;
}
static $user = null, $user_login = '';
if ( $user === null ) {
$user = wp_get_current_user();
$user_login = $user->get('user_login');
}
//User-specific settings take precedence.
$user_actor = 'user:' . $user_login;
if ( isset($item['hidden_from_actor'][$user_actor]) ) {
$item['hidden'] = $item['hidden_from_actor'][$user_actor];
return $item;
}
//The item will be hidden only if *all* of the user's roles have it hidden.
//Unlike with capabilities and permissions, there are no defaults to worry about.
$actors = array();
if ( is_multisite() && is_super_admin($user->ID) ) {
$actors[] = 'special:super_admin';
}
foreach($this->wp_menu_editor->get_user_roles($user) as $role) {
$actors[] = 'role:' . $role;
}
$is_hidden = null;
foreach($actors as $actor) {
if ( !isset($is_hidden) ) {
$is_hidden = !empty($item['hidden_from_actor'][$actor]);
} else {
$is_hidden = $is_hidden && !empty($item['hidden_from_actor'][$actor]);
}
}
if ( $is_hidden ) {
$item['hidden'] = true;
}
return $item;
}
/**
* Output the HTML for import and export dialogs.
* Callback for the 'menu_editor_footer' action.
*
* @return void
*/
function menu_editor_footer(){
?>
<div id="export_dialog" title="Export">
<div class="ws_dialog_panel">
<div id="export_progress_notice">
<img src="<?php echo plugins_url('images/spinner.gif', __FILE__); ?>" alt="wait">
Creating export file...
</div>
<div id="export_complete_notice">
Click the "Download" button below to download the exported admin menu to your computer.
</div>
</div>
<div class="ws_dialog_buttons">
<a class="button-primary" id="download_menu_button" href="#">Download Export File</a>
<input type="button" name="cancel" class="button" value="Close" id="ws_cancel_export">
</div>
</div>
<div id="import_dialog" title="Import">
<form id="import_menu_form" action="<?php echo esc_attr(admin_url('options-general.php?page=menu_editor&noheader=1')); ?>" method="post">
<input type="hidden" name="action" value="upload_menu">
<div class="ws_dialog_panel" id="ws_import_panel">
<div id="import_progress_notice">
<img src="<?php echo plugins_url('images/spinner.gif', __FILE__); ?>" alt="wait">
Uploading file...
</div>
<div id="import_progress_notice2">
<img src="<?php echo plugins_url('images/spinner.gif', __FILE__); ?>" alt="wait">
Importing menu...
</div>
<div id="import_complete_notice">
Import Complete!
</div>
<div class="hide-when-uploading">
Choose an exported menu file (.<?php echo $this->export_settings['file_extension']; ?>)
to import:
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo intval($this->export_settings['max_file_size']); ?>">
<input type="file" name="menu" id="import_file_selector" size="35">
</div>
</div>
<div id="ws_import_error" style="display: none">
<div class="ws_dialog_subpanel">
<strong>Error:</strong><br>
<span id="ws_import_error_message">N/A</span>
</div>
<div class="ws_dialog_subpanel">
<strong>HTTP code:</strong><br>
<span id="ws_import_error_http_code">N/A</span>
</div>
<div class="ws_dialog_subpanel">
<label for="ws_import_error_response"><strong>Server response:</strong></label><br>
<textarea id="ws_import_error_response" rows="8"></textarea>
</div>
</div>
<div class="ws_dialog_buttons">
<input type="submit" name="upload" class="button-primary hide-when-uploading" value="Upload File" id="ws_start_import">
<input type="button" name="cancel" class="button" value="Close" id="ws_cancel_import">
</div>
</form>
</div>
<script type="text/javascript">
/** @namespace wsEditorData */
wsEditorData.wsMenuEditorPro = true;
wsEditorData.exportMenuNonce = "<?php echo esc_js(wp_create_nonce('export_custom_menu')); ?>";
wsEditorData.menuUploadHandler = "<?php echo ('options-general.php?page=menu_editor&noheader=1'); ?>";
wsEditorData.importMenuNonce = "<?php echo esc_js(wp_create_nonce('import_custom_menu')); ?>";
</script>
<?php
}
/**
* Prepare a custom menu for export.
*
* Expects menu data to be in $_POST['data'].
* Outputs a JSON-encoded object with three fields :
* download_url - the URL that can be used to download the exported menu.
* filename - export file name.
* filesize - export file size (in bytes).
*
* If something goes wrong, the response object will contain an 'error' field with an error message.
*
* @return void
*/
function ajax_export_custom_menu(){
$wp_menu_editor = $this->wp_menu_editor;
if (!$wp_menu_editor->current_user_can_edit_menu() || !check_ajax_referer('export_custom_menu', false, false)){
die( $wp_menu_editor->json_encode( array(
'error' => __("You're not allowed to do that!", 'admin-menu-editor')
)));
}
//Prepare the export record.
$export = $this->get_exported_menu();
$export['total']++; //Export counter. Could be used to make download URLs unique.
//Compress menu data to make export files smaller.
$menu_data = wp_unslash($_POST['data']); //WordPress simulates magic quotes, so we must strip slashes.
$menu = ameMenu::load_json($menu_data);
$menu_data = ameMenu::to_json(ameMenu::compress($menu));
//Save the menu structure.
$export['menu'] = $menu_data;
//Include the blog's domain name in the export filename to make it easier to
//distinguish between multiple export files.
$siteurl = get_bloginfo('url');
$domain = @parse_url($siteurl);
$domain = isset($domain['host']) ? ($domain['host'] . ' ') : '';
$export['filename'] = sprintf(
'%sadmin menu (%s).dat',
$domain,
date('Y-m-d')
);
//Store the modified export record. The plugin will need it when the user
//actually tries to download the menu.
$this->set_exported_menu($export);
$download_url = sprintf(
'options-general.php?page=menu_editor&noheader=1&action=download_menu&export_num=%d',
$export['total']
);
$download_url = admin_url($download_url);
$result = array(
'download_url' => $download_url,
'filename' => $export['filename'],
'filesize' => strlen($export['menu']),
);
die($wp_menu_editor->json_encode($result));
}
/**
* Get the current exported record
*
* @return array
*/
function get_exported_menu(){
$user = wp_get_current_user();
$exports = get_metadata('user', $user->ID, 'custom_menu_export', true);
$defaults = array(
'total' => 0,
'menu' => '',
'filename' => '',
);
if ( !is_array($exports) ){
$exports = array();
}
return array_merge($defaults, $exports);
}
/**
* Store the export record.
*
* @param array $export
* @return bool
*/
function set_exported_menu($export){
//Caution: update_metadata expects slashed data.
$export = wp_slash($export);
$user = wp_get_current_user();
return update_metadata('user', $user->ID, 'custom_menu_export', $export);
}
/**
* Handle menu uploads and downloads.
* This is a callback for the 'admin_menu_editor_header' action.
*
* @param string $action
* @return void
*/
function menu_editor_header($action = ''){
$wp_menu_editor = $this->wp_menu_editor;
//Handle menu download requests
if ( $action == 'download_menu' ){
$export = $this->get_exported_menu();
if ( empty($export['menu']) || empty($export['filename']) ){
die("Exported data not found");
}
//Force file download
header("Content-Description: File Transfer");
header('Content-Disposition: attachment; filename="' . $export['filename'] . '"');
header("Content-Type: application/force-download");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . strlen($export['menu']));
/* The three lines below basically make the download non-cacheable */
header("Cache-control: private");
header("Pragma: private");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
echo $export['menu'];
die();
//Handle menu uploads
} elseif ( $action == 'upload_menu' ) {
header('Content-Type: text/html');
if ( empty($_FILES['menu']) ){
echo $wp_menu_editor->json_encode(array('error' => "No file specified"));
die();
}
$file_data = $_FILES['menu'];
if ( filesize($file_data['tmp_name']) > $this->export_settings['max_file_size'] ){
$this->output_for_jquery_form( $wp_menu_editor->json_encode(array('error' => "File too big")) );
die();
}
//Check for general upload errors.
if ($file_data['error'] != UPLOAD_ERR_OK) {
switch($file_data['error']) {
case UPLOAD_ERR_INI_SIZE:
$message = sprintf(
'The uploaded file exceeds the upload_max_filesize directive in php.ini. Limit: %s',
strval(ini_get('upload_max_filesize'))
);
break;
case UPLOAD_ERR_FORM_SIZE:
$message = "The uploaded file exceeds the internal file size limit. Please contact the developer.";
break;
case UPLOAD_ERR_PARTIAL:
$message = "The file was only partially uploaded";
break;
case UPLOAD_ERR_NO_FILE:
$message = "No file was uploaded";
break;
case UPLOAD_ERR_NO_TMP_DIR:
$message = "Missing a temporary folder";
break;
case UPLOAD_ERR_CANT_WRITE:
$message = "Failed to write file to disk";
break;
case UPLOAD_ERR_EXTENSION:
$message = "File upload stopped by a PHP extension";
break;
default:
$message = 'Unknown upload error #' . $file_data['error'];
break;
}
$this->output_for_jquery_form( $wp_menu_editor->json_encode(array('error' => $message)) );
die();
}
$file_contents = file_get_contents($file_data['tmp_name']);
//Check if this file could plausibly contain an exported menu
if ( strpos($file_contents, $this->export_settings['old_format_string']) !== false ){
//This is an exported menu in the old format.
$data = $wp_menu_editor->json_decode($file_contents, true);
if ( !(isset($data['menu']) && is_array($data['menu'])) ) {
$this->output_for_jquery_form( $wp_menu_editor->json_encode(array('error' => "Unknown or corrupted file format")) );
die();
}
try {
$menu = ameMenu::load_array($data['menu'], false, true);
} catch (InvalidMenuException $ex) {
$this->output_for_jquery_form( $wp_menu_editor->json_encode(array('error' => $ex->getMessage())) );
die();
}
} else {
if (strpos($file_contents, ameMenu::format_name) !== false) {
//This is an export file in the new format.
try {
$menu = ameMenu::load_json($file_contents, false, true);
} catch (InvalidMenuException $ex) {
$this->output_for_jquery_form( $wp_menu_editor->json_encode(array('error' => $ex->getMessage())) );
die();
}
} else {
//This is an unknown file.
$this->output_for_jquery_form($wp_menu_editor->json_encode(array('error' => "Unknown file format")));
die();
}
}
//Merge the imported menu with the current one.
$menu['tree'] = $wp_menu_editor->menu_merge($menu['tree']);
//Everything looks okay, send back the menu data
$this->output_for_jquery_form( ameMenu::to_json($menu) );
die ();
}
}
/**
* Utility method that outputs data in a format suitable to the jQuery Form plugin.
*
* Specifically, the docs recommend enclosing JSON data in a <textarea> element if
* the request was not sent by XMLHttpRequest. This is because the plugin uses IFrames
* in older browsers, which supposedly causes problems with JSON responses.
*
* @param string $string
*/
private function output_for_jquery_form($string) {
$xhr = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest');
if (!$xhr) {
echo '<textarea>';
}
echo $string;
if (!$xhr) {
echo '</textarea>';
}
}
/**
* Output the "Import" and "Export" buttons.
* Callback for the 'admin_menu_editor_sidebar' action.
*
* @return void
*/
function add_extra_buttons(){
?>
<div class="ws_sidebar_button_separator"></div>
<input type="button" id='ws_edit_global_colors' value="Colors" class="button ws_main_button" title="Edit default menu colors" />
<input type="button" id='ws_export_menu' value="Export" class="button ws_main_button" title="Export current menu" />
<input type="button" id='ws_import_menu' value="Import" class="button ws_main_button" />
<?php
}
function hook_user_has_cap($allcaps, /** @noinspection PhpUnusedParameterInspection */ $caps, $args){
//Add "user:user_login" to the user's capabilities. This makes it possible to restrict
//menu access on a per-user basis.
//The second entry of the $args array should be the user ID
if ( count($args) < 2 ){
return $allcaps;
}
$user_id = intval($args[1]);
//Get the username & add it as a valid cap
$username = $this->get_username_by_id($user_id);
if ( $username !== null ){
$allcaps['user:' . $username] = true;
}
return $allcaps;
}
private function get_username_by_id($user_id) {
if ( !array_key_exists($user_id, $this->username_cache) ) {
$user = get_userdata($user_id);
if ( $user && isset($user->user_login) && is_string($user->user_login) ){
$this->username_cache[$user_id] = $user->user_login;
} else {
$this->username_cache[$user_id] = null;
}
}
return $this->username_cache[$user_id];
}
/**
* Apply custom per-role and per-user access settings to a menu item.
*
* If the user can't access this menu, this method will change the required
* capability to "do_not_allow". Otherwise, it will be left unmodified.
*
* Callback for the 'custom_admin_menu_capability' filter.
*
* @param array $item
* @return array Modified item.
*/
public function apply_custom_access($item) {
if ( !isset($item['access_check_log']) ) {
$item['access_check_log'] = array();
}
if ( ! $this->current_user_is_granted_access($item) ) {
$item['access_check_log'][] = '! Changing the required capability to "do_not_allow".';
$item['access_level'] = 'do_not_allow';
$item['user_has_access_level'] = false;
} else {
$item['user_has_access_level'] = true;
}
return $item;
}
/**
* Check if the current user should be granted access to the specified menu item.
*
* Applies explicit per-role and per-user settings from $item['grant_access'].
* DOES NOT apply the extra capability. That should be done elsewhere.
*
* If there are no custom permissions set that match the current user, this method
* will check if the user would normally be able to access the menu (i.e. without
* virtual caps).
*
* @param array $item Menu item.
* @return bool
*/
private function current_user_is_granted_access(&$item) {
static $is_multisite = null;
static $user = null, $user_login = '';
if ( $is_multisite === null ) {
$is_multisite = is_multisite();
}
if ( $user === null ) {
$user = wp_get_current_user();
$user_login = $user->get('user_login');
}
$has_access = null;
$log = array();
$reason = null;
$debug_title = ameMenuItem::get($item, 'full_title', ameMenuItem::get($item, 'menu_title', '[untitled menu]'));
$log[] = sprintf('Checking "%s" permissions:', ameMenuItem::get($item, 'menu_title', '[untitled menu item]'));
if ( isset($item['grant_access']) ) {
$grants = $item['grant_access'];
//If this user is specifically allowed/forbidden, use that setting.
if ( isset($grants['user:' . $user_login]) ) {
$has_access = $grants['user:' . $user_login];
$log[] = sprintf(
'+ Custom permissions for user "%s": %s.',
$user_login,
$has_access ? 'ALLOW' : 'DENY'
);
$reason = sprintf(
'The "%1$s" menu item is explicitly %2$s for the user "%3$s".',
$debug_title,
$has_access ? 'enabled' : 'disabled',
$user_login
);
} else {
$log[] = sprintf(
'- No custom permissions for the "%s" username.',
$user_login
);
}
//Or if they're a super admin, allow *everything* unless explicitly denied.
$this->disable_virtual_caps = true;
if ( is_null($has_access) && $is_multisite && is_super_admin($user->ID) ) {
$log[] = '+ The current user is a Super Admin.';
if ( isset($grants['special:super_admin']) ) {
$has_access = $grants['special:super_admin'];
$log[] = sprintf("+ Custom permissions for Super Admin: %s.", $has_access ? 'ALLOW' : 'DENY');
$reason = sprintf(
'The user "%3$s" is a Super Admin. The "%1$s" menu item is explicitly %2$s for Super Admin.',
$debug_title,
$has_access ? 'enabled' : 'disabled',
$user_login
);
} else {
$has_access = true;
$log[] = '+ ALLOW access to everything by default.';
$reason = sprintf(
'As a Super Admin, the user "%2$s" has access to the "%1$s" menu item by default.',
$debug_title,
$user_login
);
}
} else if ( is_null($has_access) ) {
$log[] = '- The current user is not a Super Admin, or this is not a Multisite install.';
}
$this->disable_virtual_caps = false;
if ( is_null($has_access) ) {
//Allow the user if at least one of their roles is allowed,
//or disallow if all their roles are forbidden.
$roles = $this->wp_menu_editor->get_user_roles($user);
$log[] = sprintf(
'- Current user\'s role: %s',
!empty($roles) ? implode(', ', $roles) : 'N/A (not logged in?)'
);
if ( empty($roles) ) {
$log[] = sprintf(
'- Current user\'s capabilities: %s',
implode(', ', array_keys(array_filter($user->allcaps)))
);
}
foreach ($roles as $role_id) {
if ( isset($grants['role:' . $role_id]) ) {
$role_has_access = $grants['role:' . $role_id];
$log[] = sprintf(
'+ Permissions for the "%1$s" role: %2$s',
$role_id,
$role_has_access ? 'ALLOW' : 'DENY'
);
if ( is_null($has_access) ){
$has_access = $role_has_access;
$reason = sprintf(
'The "%1$s" menu item is %2$s for the "%3$s" role.',
$debug_title,
$has_access ? 'enabled' : 'disabled',
$role_id
);
} else {
$has_access = $has_access || $role_has_access; //Allow access if at least one role has access.
if ( $has_access ) {
$reason = sprintf(
'The "%1$s" menu item is enabled for the "%2$s" role.',
$debug_title,
$user_login
);
}
}
} else {
$log[] = sprintf('- No custom permissions for the "%s" role.', $role_id);
}
}
}
}
if ( is_null($has_access) ) {
//There are no custom settings for this user. Check if
//they would be able to access the menu by default.
$required_capability = $item['access_level'];
$log[] = '- There are no custom permissions for the current user or any of their roles.';
$log[] = '- Checking the default required capability: ' . $required_capability;
$this->disable_virtual_caps = true;
//Cache capability checks because they're relatively slow (determined by profiling).
//Right now we can rely on capabilities not changing when this method is called, but that's not a safe
//assumption in general so we only use the cache in this specific case.
if ( isset($this->cached_user_caps[$required_capability]) ) {
$has_access = $this->cached_user_caps[$required_capability];
} else {
$has_access = $user && $user->has_cap($required_capability);
$this->cached_user_caps[$required_capability] = $has_access;
}
$log[] = sprintf(
'+ The current user %1$s the "%2$s" capability.',
$has_access ? 'HAS' : 'does not have',
htmlentities($required_capability)
);
$this->disable_virtual_caps = false;
$reason = sprintf(
'The user "%1$s" %2$s the "%3$s" capability that is required to access the "%4$s" menu item.',
$user_login,
$has_access ? 'has' : 'doesn\'t have',
$required_capability,
$debug_title
);
}
$log[] = '= Result: ' . ($has_access ? 'ALLOW' : 'DENY');
//Store the log in the item for debugging and configuration analysis.
if ( !isset($item['access_check_log']) ) {
$item['access_check_log'] = $log;
} else {
$item['access_check_log'] = array_merge($item['access_check_log'], $log);
}
//Store the decision summary as well.
$item['access_decision_reason'] = $reason;
return $has_access;
}
/**
* Check if the current user has access to something.
*
* This is a general method for checking authorization based on a combination of
* actor-specific and capability-based permissions.
*
* @param array $grants List of grants as an [actorId => boolean] map.
* @param string|null $default_cap
* @param string|null $extra_cap
* @param bool $default_access
* @param int $flags
* @return bool
*/
public function check_current_user_access(
$grants = array(),
$default_cap = null,
$extra_cap = null,
$default_access = false,
$flags = AME_RC_ONLY_CUSTOM
) {
static $is_multisite = null, $user = null, $user_login = '';
if ( $is_multisite === null ) {
$is_multisite = is_multisite();
}
if ( $user === null ) {
$user = wp_get_current_user();
$user_login = $user->get('user_login');
}
//User-specific settings have the highest priority.
if ( isset($grants['user:' . $user_login]) ) {
return $grants['user:' . $user_login];
}
//Super Admins have access to *everything* unless explicitly denied.
$this->disable_virtual_caps = true;
$is_super_admin = $is_multisite && is_super_admin($user->ID);
$this->disable_virtual_caps = false;
if ( $is_super_admin ) {
if ( isset($grants['special:super_admin']) ) {
return $grants['special:super_admin'];
} else {
return true;
}
}
//Allow the user if at least one of their roles is allowed,
//or disallow if all their roles are forbidden.
$has_access = null;
$roles = $this->wp_menu_editor->get_user_roles($user);
foreach ($roles as $role_id) {
if ( !isset($grants['role:' . $role_id]) && ($flags & AME_RC_ONLY_CUSTOM) ) {
continue;
}
if ( isset($grants['role:' . $role_id]) ) {
$role_has_access = $grants['role:' . $role_id];
} else if ($flags & AME_RC_USE_DEFAULT_ACCESS) {
$role_has_access = $default_access;
} else {
throw new RuntimeException(sprintf(
"Can't determine default permissions for role \"%s\". Check the flags passed to %s().",
$role_id,
__FUNCTION__
));
}
if ( is_null($has_access) ) {
$has_access = $role_has_access;
} else {
$has_access = $has_access || $role_has_access;
}
}
if ( $has_access !== null ) {
return $has_access;
}
//There are no custom settings for this user. Check if they have the capabilities.
if ( isset($default_cap) ) {
$this->disable_virtual_caps = true;
//Cache capability checks because they're relatively slow.
if ( isset($this->cached_user_caps[$default_cap]) ) {
$has_access = $this->cached_user_caps[$default_cap];
} else {
$has_access = $user && $user->has_cap($default_cap);
$this->cached_user_caps[$default_cap] = $has_access;
}
$this->disable_virtual_caps = false;
}
//The extra capability is an optional filter that's applied on top of other settings.
if ( isset($extra_cap) && $has_access ) {
$this->disable_virtual_caps = true;
if ( isset($this->cached_user_caps[$extra_cap]) ) {
$has_extra_cap = $this->cached_user_caps[$extra_cap];
} else {
$has_extra_cap = $user && $user->has_cap($extra_cap);
$this->cached_user_caps[$extra_cap] = $has_extra_cap;
}
$this->disable_virtual_caps = false;
$has_access = $has_access && $has_extra_cap;
}
if ( $has_access !== null ) {
return $has_access;
}
return $default_access;
}
/**
* Grant a user virtual caps they'll need to access certain menu items.
*
* @param array $capabilities All capabilities belonging to the current user, cap => true/false.
* @param array $required_caps The required capabilities.
* @param array $args The capability passed to current_user_can, the current user's ID, and other args.
* @return array Filtered list of capabilities.
*/
function grant_virtual_caps_to_user($capabilities, /** @noinspection PhpUnusedParameterInspection */ $required_caps, $args){
$wp_menu_editor = $this->wp_menu_editor;
$this->cached_virtual_user_caps = array();
if ( $this->disable_virtual_caps ) {
return $capabilities;
}
$virtual_caps = $wp_menu_editor->get_virtual_caps();
//The second entry of the $args array should be the user ID
if ( count($args) < 2 ){
return $capabilities;
}
$user_id = intval($args[1]);
//We can avoid a potentially costly call chain and object initialization
//by retrieving the current user directly if the ID matches (as it usually will).
$current_user = wp_get_current_user();
if ( $user_id == intval($current_user->ID) ) {
$user = $current_user;
} else {
$user = get_user_by('id', $user_id);
}
$grant_keys = array();
if ( $user ) {
if ( isset($user->user_login) ) {
$grant_keys[] = 'user:' . $user->user_login;
}
$roles = $this->wp_menu_editor->get_user_roles($user);
if ( !empty($roles) ) {
foreach($roles as $role_id) {
$grant_keys[] = 'role:' . $role_id;
}
}
}
//is_super_admin() will call has_cap on single-site installs.
$this->disable_virtual_caps = true;
if ( is_multisite() && is_super_admin($user->ID) ) {
$grant_keys[] = 'special:super_admin';
}
$this->disable_virtual_caps = false;
$caps_to_grant = array();
foreach($grant_keys as $grant) {
if ( isset($virtual_caps[$grant]) ) {
$caps_to_grant = array_merge($caps_to_grant, $virtual_caps[$grant]);
}
}
$this->cached_virtual_user_caps = $caps_to_grant;
$capabilities = array_merge($capabilities, $this->cached_virtual_user_caps);
return $capabilities;
}
/**
* Set the capabilities that were already set by grant_virtual_caps_to_user() again.
*
* The goal of granting the same capabilities twice at different hook priorities is to:
* 1) Make sure meta caps that rely on the granted caps are enabled.
* 2) Reduce the risk that the granted caps will be overridden by other plugins.
*
* @param array $capabilities
* @return array
*/
public function regrant_virtual_caps_to_user($capabilities) {
if ( !empty($this->cached_virtual_user_caps) ) {
$capabilities = array_merge($capabilities, $this->cached_virtual_user_caps);
$this->cached_virtual_user_caps = array();
}
return $capabilities;
}
/**
* Grant a role virtual caps it'll need to access certain menu items.
*
* @param array $capabilities Current role capabilities.
* @param string $required_cap The required capability.
* @param string $role_id Role name/slug.
* @return array Filtered capability list.
*/
function grant_virtual_caps_to_role($capabilities, /** @noinspection PhpUnusedParameterInspection */ $required_cap, $role_id){
$wp_menu_editor = $this->wp_menu_editor;
if ( $this->disable_virtual_caps ) {
return $capabilities;
}
$virtual_caps = $wp_menu_editor->get_virtual_caps();
$grant_key = 'role:' . $role_id;
if ( isset($virtual_caps[$grant_key]) ) {
$capabilities = array_merge($capabilities, $virtual_caps[$grant_key]);
}
return $capabilities;
}
/**
* Hook for the internal current_user_can() function used by Admin Menu Editor.
* Enables us to use computed capabilities.
*
* @uses wsMenuEditorExtras::current_user_can_computed()
*
* @param bool $allow The return value of current_user_can($capablity).
* @param string $capability The capability to check for.
* @return bool Whether the user has the specified capability.
*/
function grant_computed_caps_to_current_user($allow, $capability) {
return $this->current_user_can_computed($capability, $allow);
}
/**
* Check if the current user has the specified computed capability. Basically, this method
* implements a very limited subset of Boolean logic for use in capability checks.
*
* Supported operations:
* "capX" - Normal capability check. Returns true if the user has the capability "capX".
* "not:capX" - Logical NOT. Returns true if the user *doesn't* have "capX".
* "capX,capY" - Logical OR. Returns true if the user has at least one of "capX" or "capY".
* "capX+capY" - Logical AND. Returns true if the user has all the listed capabilities.
*
* Operator precedence: NOT, AND, OR.
*
* @uses current_user_can() Uses the capability checking function from WordPress core.
*
* @param string $capability
* @param bool $default
* @return bool
*/
private function current_user_can_computed($capability, $default = null) {
$or_operator = ',';
if ( strpos($capability, $or_operator) !== false ) {
$allow = false;
foreach(explode($or_operator, $capability) as $term) {
$allow = $allow || $this->current_user_can_computed($term);
}
return $allow;
}
$and_operator = '+';
if ( strpos($capability, $and_operator) !== false ) {
$allow = true;
foreach(explode($and_operator, $capability) as $term) {
$allow = $allow && $this->current_user_can_computed($term);
}
return $allow;
}
$not_operator = 'not:';
$length = strlen($not_operator);
if ( substr($capability, 0, $length) == $not_operator ) {
return ! $this->current_user_can_computed(substr($capability, $length));
}
$capability = trim($capability);
//Special case to handle weird input like "capability+" and " ,capability".
if ($capability == '') {
return true;
}
return isset($default) ? $default : current_user_can($capability);
}
function output_menu_dropzone($type = 'menu') {
printf(
'<div id="ws_%s_dropzone" class="ws_dropzone"> </div>',
($type == 'menu') ? 'top_menu' : 'sub_menu'
);
}
function pro_page_title(){
return 'Menu Editor Pro';
}
function pro_menu_title(){
return 'Menu Editor Pro';
}
/**
* Callback for the 'admin_menu_editor_is_pro' hook. Always returns True to indicate that
* the Pro version extras are installed.
*
* @return bool True
*/
function is_pro_version(){
return true;
}
function license_ui_title() {
$title = 'Admin Menu Editor Pro License';
return $title;
}
function license_ui_logo() {
printf(
'<p style="text-align: center; margin: 30px 0;"><img src="%s" alt="Logo"></p>',
esc_attr(plugins_url('images/logo-medium.png', __FILE__))
);
}
/**
* @param string|null $currentKey
* @param string|null $currentToken
* @param Wslm_ProductLicense $currentLicense
*/
public function license_ui_upgrade_link($currentKey = null, $currentToken = null, $currentLicense = null) {
if ( empty($currentKey) && empty($currentToken) ) {
return;
}
$upgradeLink = 'http://adminmenueditor.com/upgrade-license/';
$upgradeText = 'Upgrade or renew license';
if ( $currentLicense && ($currentLicense->getStatus() === 'expired') ) {
$upgradeLink = 'http://adminmenueditor.com/renew-license/';
$upgradeText = 'Renew license';
}
if ( !empty($currentKey) ) {
$upgradeLink = add_query_arg('license_key', $currentKey, $upgradeLink);
}
$externalIcon = plugins_url('/images/external.png', $this->wp_menu_editor->plugin_file);
?><p>
<label>Actions:</label>
<a href="<?php echo esc_attr($upgradeLink); ?>"
rel="external"
target="_blank"
title="Opens in a new window"
>
<?php echo $upgradeText; ?>
<img src="<?php echo esc_attr($externalIcon); ?>" alt="External link icon" width="10" height="10">
</a>
</p><?php
}
public function license_ui_product_name() {
return 'Admin Menu Editor Pro';
}
/**
* Format separator items located in sub-menus.
* See /css/admin.css for the relevant styles.
*
* @param array $item Submenu item.
* @return array
*/
public function create_submenu_separator($item) {
static $separator_num = 1;
if ( $item['separator'] ) {
$item['menu_title'] = '<hr class="ws-submenu-separator">';
$item['file'] = '#submenu-separator-' . ($separator_num++);
}
return $item;
}
/**
* Generate the HTML for submenu icons.
*
* @param array $item A submenu item in the internal format.
* @param boolean $hasCustomIconUrl Whether the item has a custom icon URL.
* @return array Modified $item.
*/
public function add_submenu_icon_html($item, $hasCustomIconUrl) {
$enabled = $this->wp_menu_editor->get_plugin_option('submenu_icons_enabled');
if ( empty($enabled) || ($enabled === 'never') ) {
//Icons are disabled.
return $item;
}
if ( ($enabled == 'if_custom') && !$hasCustomIconUrl ) {
//Only enabled for icons with custom icons.
return $item;
}
if ( !empty($item['separator']) ) {
//Separators can't have icons.
return $item;
}
if (strpos($item['icon_url'], 'dashicons-') === 0) {
$item['menu_title'] = sprintf(
'<div class="ame-submenu-icon"><div class="dashicons %1$s"></div></div>%2$s',
esc_attr($item['icon_url']),
$item['menu_title']
);
$item['has_submenu_icon'] = true;
} elseif (strpos($item['icon_url'], 'ame-fa-') === 0) {
$item['menu_title'] = sprintf(
'<div class="ame-submenu-icon"><div class="ame-fa %1$s"></div></div>%2$s',
esc_attr($item['icon_url']),
$item['menu_title']
);
$item['has_submenu_icon'] = true;
} elseif ( !empty($item['icon_url']) ) {
$item['menu_title'] = sprintf(
'<div class="ame-submenu-icon"><img src="%1$s"></div>%2$s',
esc_attr($item['icon_url']),
$item['menu_title']
);
$item['has_submenu_icon'] = true;
}
return $item;
}
/**
* Remove Admin Menu Editor Pro from the list of plugins unless the current user
* is explicitly allowed to see it.
*
* @param array $plugins List of installed plugins.
* @return array Filtered list of plugins.
*/
public function filter_plugin_list($plugins) {
$allowed_user_id = $this->wp_menu_editor->get_plugin_option('plugins_page_allowed_user_id');
if ( get_current_user_id() != $allowed_user_id ) {
unset($plugins[$this->wp_menu_editor->plugin_basename]);
}
return $plugins;
}
/**
* Apply Pro version menu customizations like shortcode support, "open in new window" support,
* submenu separators and so on.
*
* @param array $item Admin menu item in the internal format.
* @param string|null $parent_file
* @return array Modified admin menu item.
*/
public function apply_admin_menu_filters($item, $parent_file = null) {
//Allow the usage of shortcodes in the admin menu
$item = $this->do_shortcodes($item);
//Flag menus that are set to open in a new window so that we can later find
//and modify them with JS. This is necessary because there is no practical
//way to intercept and modify the menu HTML with PHP alone.
$item = $this->flag_new_window_menus($item);
//Handle pages that need to be displayed in a frame.
if ( !empty($parent_file) ) {
$item = $this->create_framed_item($item, $parent_file);
} else {
$item = $this->create_framed_menu($item);
}
//Handle submenu separators.
if ( current_filter() == 'custom_admin_submenu' ) {
$item = $this->create_submenu_separator($item);
}
//Handle menus that display a WP page in the admin.
if ( $item['template_id'] === ameMenuItem::embeddedPageTemplateId ) {
$item = $this->create_embedded_wp_page($item, $parent_file);
}
//Apply per-role visibility (cosmetic, not permissions).
if ( !empty($item['hidden_from_actor']) ) {
$item = $this->set_final_hidden_flag($item);
}
return $item;
}
/**
* Generate CSS rules for menu items that have user-defined colors.
*
* This method stores the CSS at the "color_css" key in the menu structure and returns a modified menu.
* By storing the color scheme CSS in the menu itself we avoid having to regenerate it on every page load.
* We also don't have to worry about cache lifetime - when the menu is modified the old CSS will be
* overwritten automatically.
*
* @param array $custom_menu Admin menu in the internal format.
* @return array Modified menu.
*/
public function add_menu_color_css($custom_menu) {
if ( empty($custom_menu) || !is_array($custom_menu) || !isset($custom_menu['tree']) ) {
return $custom_menu;
}
if (!class_exists('ameMenuColorGenerator')) {
require_once dirname(__FILE__) . '/extras/menu-color-generator.php';
}
$generator = new ameMenuColorGenerator();
$css = array();
$used_ids = array();
$colorized_menu_count = 0;
//Include global colors, if any.
if ( isset($custom_menu['color_presets']['[global]']) ) {
$base_css = $generator->getCss(
'',
$custom_menu['color_presets']['[global]'],
dirname(__FILE__) . '/extras/global-menu-color-template.txt'
);
$css[] = $base_css;
}
foreach($custom_menu['tree'] as &$item) {
if ( !isset($item['colors']) || empty($item['colors']) ) {
continue;
}
$colorized_menu_count++;
//Each item needs to have a unique ID so we can target it in CSS. Using a class would be cleaner,
//but the selectors wouldn't have enough specificity to override WP defaults.
$id = ameMenuItem::get($item, 'hookname');
if ( empty($id) || isset($used_ids[$id]) ) {
$id = (empty($id) ? 'ame-colorized-item' : $id) . '-';
$id .= $colorized_menu_count . '-t' . time();
$item['hookname'] = $id;
}
$used_ids[$id] = true;
$item_css = $generator->getCss($id, $item['colors']);
if ( !empty($item_css) ) {
$css[] = sprintf(
'/* %1$s (%2$s) */',
str_replace('*/', ' ', ameMenuItem::get($item, 'menu_title', 'Untitled menu')),
str_replace('*/', ' ', ameMenuItem::get($item, 'file', '(no URL)'))
);
$css[] = $item_css;
}
}
if ( !empty($css) ) {
$css = implode("\n", $css);
$custom_menu['color_css'] = $css;
$custom_menu['color_css_modified'] = time();
} else {
$custom_menu['color_css'] = '';
$custom_menu['color_css_modified'] = 0;
}
return $custom_menu;
}
/**
* Enqueue the user-defined menu color scheme, if any.
*/
public function enqueue_menu_color_style() {
$custom_menu = $this->wp_menu_editor->load_custom_menu();
if ( empty($custom_menu) || empty($custom_menu['color_css']) ) {
return;
}
wp_enqueue_style(
'ame-custom-menu-colors',
admin_url('admin-ajax.php?action=ame_output_menu_color_css'),
array(),
$custom_menu['color_css_modified']
);
}
/**
* Output menu color CSS for the current custom menu.
*/
public function ajax_output_menu_color_css() {
$custom_menu = $this->wp_menu_editor->load_custom_menu();
if ( empty($custom_menu) || empty($custom_menu['color_css']) ) {
return;
}
header('Content-Type: text/css');
header('X-Content-Type-Options: nosniff'); //No really IE, it's CSS. Honest.
//Enable browser caching.
header('Cache-Control: public');
header('Expires: Thu, 31 Dec ' . date('Y', strtotime('+1 year')) . ' 23:59:59 GMT');
header('Pragma: cache');
echo $custom_menu['color_css'];
exit();
}
/**
* Enqueue the Font Awesome icon font & CSS.
*/
public function enqueue_fontawesome() {
wp_enqueue_auto_versioned_style(
'ame-font-awesome',
plugins_url('extras/font-awesome/scss/font-awesome.css', __FILE__)
);
}
/**
* Add FA icons to top level menus.
*
* @param array $menu
* @return array
*/
public function add_menu_fa_icon($menu) {
$fa_prefix = 'ame-fa-';
if ( strpos($menu['icon_url'], $fa_prefix) !== 0 ) {
return $menu;
}
$icon = substr($menu['icon_url'], strlen($fa_prefix));
//Add a placeholder icon to force WP to generate a .wp-menu-image node.
$menu['icon_url'] = 'dashicons-warning';
//Override the icon using CSS.
$menu['css_class'] .= ' ame-menu-fa ame-menu-fa-' . $icon;
return $menu;
}
public function add_fa_selector_tab($tabs) {
$tabs['ws_fontawesome_icons_tab'] = 'Font Awesome';
return $tabs;
}
public function output_fa_selector_tab() {
echo '<div class="ws_tool_tab" id="ws_fontawesome_icons_tab" style="display: none">';
$icons = $this->get_available_fa_icons();
foreach($icons as $icon_name) {
printf(
'<div class="ws_icon_option" title="%1$s" data-icon-url="ame-fa-%2$s">
<div class="ws_icon_image ame-fa ame-fa-%2$s"></div>
</div>',
esc_attr(ucwords(str_replace('-', ' ', $icon_name))),
$icon_name
);
}
echo '<div class="clear"></div></div>';
}
private function get_available_fa_icons() {
$icon_list = dirname(__FILE__) . '/extras/font-awesome/scss/_ame-icons.scss';
if ( !is_readable($icon_list) ) {
return array();
}
$scss = file_get_contents($icon_list);
if ( preg_match_all('@^#\{[^}]+?\}-(?P<name>[\w\d\-]+)\s[^{]*?[^#]\{@m', $scss, $matches) ) {
return $matches['name'];
}
return array();
}
}
if ( isset($wp_menu_editor) && !defined('WP_UNINSTALL_PLUGIN') ) {
//Initialize extras
$wsMenuEditorExtras = new wsMenuEditorExtras($wp_menu_editor);
}
if ( !defined('IS_DEMO_MODE') && !defined('IS_MASTER_MODE') ) {
//Load the custom update checker (requires PHP 5)
if ( (version_compare(PHP_VERSION, '5.0.0', '>=')) && isset($wp_menu_editor) ){
require dirname(__FILE__) . '/plugin-updates/plugin-update-checker.php';
$ameProUpdateChecker = PucFactory::buildUpdateChecker(
'http://adminmenueditor.com/?get_metadata_for=admin-menu-editor-pro',
$wp_menu_editor->plugin_file, //Note: This variable is set in the framework constructor
'admin-menu-editor-pro',
12, //check every 12 hours
'ame_pro_external_updates', //store book-keeping info in this WP option
'admin-menu-editor-mu.php'
);
//Hack. See PluginUpdateChecker::installHooks().
function wsDisableAmeCron(){
wp_clear_scheduled_hook('check_plugin_updates-admin-menu-editor-pro');
}
register_deactivation_hook($wp_menu_editor->plugin_file, 'wsDisableAmeCron');
}
//Load the license manager.
require dirname(__FILE__) . '/license-manager/LicenseManager.php';
global $ameProLicenseManager;
$ameProLicenseManager = new Wslm_LicenseManagerClient(array(
'api_url' => 'http://adminmenueditor.com/licensing_api/',
'product_slug' => 'admin-menu-editor-pro',
'license_scope' => Wslm_LicenseManagerClient::LICENSE_SCOPE_NETWORK,
'update_checker' => isset($ameProUpdateChecker) ? $ameProUpdateChecker : null,
'token_history_size' => 5,
));
if ( isset($wp_menu_editor) ) {
$ameLicensingUi = new Wslm_BasicPluginLicensingUI(
$ameProLicenseManager,
$wp_menu_editor->plugin_file,
isset($ameProUpdateChecker) ? $ameProUpdateChecker : null,
'AME_LICENSE_KEY'
);
}
//Load WP-CLI commands.
if ( defined('WP_CLI') && WP_CLI && isset($wp_menu_editor) ) {
include dirname(__FILE__) . '/extras/wp-cli-integration.php';
}
}