/** * Prepare a menu item to be converted to the WordPress format and added to the current * WordPress admin menu. This function applies menu defaults and templates, calls filters * that allow other components to tweak the menu, decides on what capability/-ies to use, * and so on. * * Caution: The filters called by this function may cause side-effects. Specifically, the Pro-only feature * for displaying menu pages in a frame does this. See wsMenuEditorExtras::create_framed_menu(). * Therefore, it is not safe to call this function more than once for the same item. * * @param array $item Menu item in the internal format. * @param string $item_type Either 'menu' or 'submenu'. * @param string $parent Optional. The parent of this sub-menu item. An empty string for top-level menus. * @return array Menu item in the internal format. */ private function prepare_for_output($item, $item_type = 'menu', $parent = '') { // Special case : plugin pages that have been moved from a sub-menu to a different // menu or the top level. We'll need to adjust the file field to point to the correct URL. // This is required because WP identifies plugin pages using *both* the plugin file // and the parent file. if ($item['template_id'] !== '' && !$item['separator']) { $template = $this->item_templates[$item['template_id']]; if ($template['defaults']['is_plugin_page']) { $default_parent = $template['defaults']['parent']; if ($parent != $default_parent) { $item['file'] = $template['defaults']['url']; } } } //Give each unclickable item a unique URL. if ($item['template_id'] === ameMenuItem::unclickableTemplateId) { static $unclickableCounter = 0; $unclickableCounter++; $unclickableUrl = '#' . ameMenuItem::unclickableTemplateClass . '-' . $unclickableCounter; $item['file'] = $item['url'] = $unclickableUrl; //The item must have the special "unclickable" class even if the user overrides the class. $cssClass = ameMenuItem::get($item, 'css_class', ''); if (strpos($cssClass, ameMenuItem::unclickableTemplateClass) === false) { $item['css_class'] = ameMenuItem::unclickableTemplateClass . ' ' . $cssClass; } } //Menus that have both a custom icon URL and a "menu-icon-*" class will get two overlapping icons. //Fix this by automatically removing the class. The user can set a custom class attr. to override. $hasCustomIconUrl = !ameMenuItem::is_default($item, 'icon_url'); $hasIcon = !in_array(ameMenuItem::get($item, 'icon_url'), array('', 'none', 'div')); if (ameMenuItem::is_default($item, 'css_class') && $hasCustomIconUrl && $hasIcon) { $new_classes = preg_replace('@\\bmenu-icon-[^\\s]+\\b@', '', $item['defaults']['css_class']); if ($new_classes !== $item['defaults']['css_class']) { $item['css_class'] = $new_classes; } } //Apply defaults & filters $item = ameMenuItem::apply_defaults($item); $item = ameMenuItem::apply_filters($item, $item_type, $parent); //may cause side-effects $item = $this->set_final_menu_capability($item); if (!$this->options['security_logging_enabled']) { unset($item['access_check_log']); //Throw away the log to conserve memory. } $this->add_access_lookup($item, $item_type); //Menus without a custom icon image should have it set to "none" (or "div" in older WP versions). //See /wp-admin/menu-header.php for details on how this works. if ($item['icon_url'] === '') { $item['icon_url'] = 'none'; } //Submenus must not have the "menu-top" class(-es). In WP versions that support submenu CSS classes, //it can break menu display. if (!empty($item['css_class']) && $item_type === 'submenu') { $item['css_class'] = preg_replace('@\\bmenu-top(?:-[\\w\\-]+)?\\b@', '', $item['css_class']); } elseif ($item_type === 'menu' && !$item['separator'] && !preg_match('@\\bmenu-top\\b@', $item['css_class'])) { //Top-level menus should always have the "menu-top" class. $item['css_class'] = 'menu-top ' . $item['css_class']; } //Add submenu icons if necessary. if ($item_type === 'submenu' && $hasIcon) { $item = apply_filters('admin_menu_editor-submenu_with_icon', $item, $hasCustomIconUrl); } //Used later to determine the current page based on URL. if (empty($item['url'])) { $original_parent = !empty($item['defaults']['parent']) ? $item['defaults']['parent'] : $parent; $item['url'] = ameMenuItem::generate_url($item['file'], $original_parent); } //Convert relative URls to fully qualified ones. This prevents problems with WordPress //incorrectly converting "index.php?page=xyz" to, say, "tools.php?page=index.php?page=xyz" //if the menu item was moved from "Dashboard" to "Tools". $itemFile = ameMenuItem::remove_query_from($item['file']); $shouldMakeAbsolute = strpos($item['file'], '://') === false && substr($item['file'], 0, 1) != '/' && $itemFile == 'index.php' && strpos($item['file'], '?') !== false; if ($shouldMakeAbsolute) { $item['file'] = admin_url($item['url']); } //WPML support: Use translated menu titles where available. if (!$item['separator'] && function_exists('icl_t')) { $item['menu_title'] = icl_t(self::WPML_CONTEXT, $this->get_wpml_name_for($item, 'menu_title'), $item['menu_title']); } return $item; }
/** * Convert internal menu representation to the form used by WP. * * Note : While this function doesn't cause any side effects of its own, * it executes several filters that may modify global state. Specifically, * IFrame-handling callbacks in 'extras.php' may insert items into the * global $menu and $submenu arrays. * * @param array $tree * @return array $menu and $submenu */ function tree2wp($tree) { $menu = array(); $submenu = array(); $title_lookup = array(); //Sort the menu by position uasort($tree, array(&$this, 'compare_position')); //Prepare the top menu $first_nonseparator_found = false; foreach ($tree as $topmenu) { //Skip missing menus, unless they're user-created and thus might point to a non-standard file $custom = $this->get_menu_field($topmenu, 'custom', false); if (!empty($topmenu['missing']) && !$custom) { continue; } //Skip leading menu separators. Fixes a superfluous separator showing up //in WP 3.0 (multisite mode) when there's a custom menu and the current user //can't access its first item ("Super Admin"). if (!empty($topmenu['separator']) && !$first_nonseparator_found) { continue; } $first_nonseparator_found = true; //Menus that have both a custom icon URL and a "menu-icon-*" class will get two overlapping icons. //Fix this by automatically removing the class. The user can set a custom class attr. to override. if (ameMenuItem::is_default($topmenu, 'css_class') && !ameMenuItem::is_default($topmenu, 'icon_url') && !in_array($topmenu['icon_url'], array('', 'none', 'div'))) { $new_classes = preg_replace('@\\bmenu-icon-[^\\s]+\\b@', '', $topmenu['defaults']['css_class']); if ($new_classes !== $topmenu['defaults']['css_class']) { $topmenu['css_class'] = $new_classes; } } //Apply defaults & filters $topmenu = $this->apply_defaults($topmenu); $topmenu = $this->apply_menu_filters($topmenu, 'menu'); //Skip hidden entries if (!empty($topmenu['hidden'])) { continue; } //Build the menu structure that WP expects $menu[] = array($topmenu['menu_title'], $topmenu['access_level'], $topmenu['file'], $topmenu['page_title'], $topmenu['css_class'], $topmenu['hookname'], $topmenu['icon_url']); //Prepare the submenu of this menu if (!empty($topmenu['items'])) { $items = $topmenu['items']; //Sort by position uasort($items, array(&$this, 'compare_position')); foreach ($items as $item) { //Skip missing items, unless they're user-created $custom = $this->get_menu_field($item, 'custom', false); if (!empty($item['missing']) && !$custom) { continue; } //Special case : plugin pages that have been moved to a different menu. //If the file field hasn't already been modified, we'll need to adjust it //to point to the old parent. This is required because WP identifies //plugin pages using *both* the plugin file and the parent file. if ($this->get_menu_field($item, 'is_plugin_page', false) && $item['file'] === null) { $default_parent = ''; if (isset($item['defaults']) && isset($item['defaults']['parent'])) { $default_parent = $item['defaults']['parent']; } if ($topmenu['file'] != $default_parent) { $item['file'] = $default_parent . '?page=' . $item['defaults']['file']; } } $item = $this->apply_defaults($item); $item = $this->apply_menu_filters($item, 'submenu', $topmenu['file']); //Skip hidden items if (!empty($item['hidden'])) { continue; } $submenu[$topmenu['file']][] = array($item['menu_title'], $item['access_level'], $item['file'], $item['page_title']); //Make a note of the page's correct title so we can fix it later //if necessary. $title_lookup[$item['file']] = $item['menu_title']; } } } return array($menu, $submenu, $title_lookup); }