/** * Create a unique name for a specific field of a specific menu item. * Intended for use with the icl_register_string() function. * * @param array $item Admin menu item in the internal format. * @param string $field Field name. * @return string */ private function get_wpml_name_for($item, $field = '') { $name = ameMenuItem::get($item, 'template_id'); if (empty($name)) { $name = 'custom: ' . ameMenuItem::get($item, 'file'); } if (!empty($field)) { $name = $name . '[' . $field . ']'; } return $name; }
/** * 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; 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; }
/** * Add a menu item as a template. * * @param array $wpItem * @param int $position * @param string|null $parent */ private function addItem($wpItem, $position, $parent = null) { $item = ameMenuItem::fromWpItem($wpItem, $position, $parent); //Skip separators. if ($item['separator']) { $this->wasPreviousItemSeparated = true; return; } //Skip blacklisted menus. if (isset($item['url'], $this->blacklist[$item['url']])) { return; } $name = $this->sanitizeMenuTitle($item['menu_title']); if ($parent === null) { $this->parentNames[$item['file']] = $name; } else { $name = $this->parentNames[$parent] . ' -> ' . $name; } $templateId = ameMenuItem::template_id($item); $this->templates[$templateId] = array('name' => $name, 'used' => false, 'defaults' => $item); //Remember the relative order of menu items. It's a bit like a linked list. $this->templateOrder[$templateId] = array('previous_item' => $this->previousItemId, 'was_previous_item_separated' => $this->wasPreviousItemSeparated); $this->previousItemId = $templateId; $this->wasPreviousItemSeparated = false; }
/** * Compress menu configuration (lossless). * * Reduces data size by storing commonly used properties and defaults in one place * instead of in every menu item. * * @param array $menu * @return array */ public static function compress($menu) { $property_dict = ameMenuItem::blank_menu(); unset($property_dict['defaults']); $common = array('properties' => $property_dict, 'basic_defaults' => ameMenuItem::basic_defaults(), 'custom_item_defaults' => ameMenuItem::custom_item_defaults()); $menu['tree'] = self::map_items($menu['tree'], array(__CLASS__, 'compress_item'), array($common)); $menu = self::add_format_header($menu); $menu['format']['compressed'] = true; $menu['format']['common'] = $common; return $menu; }
/** * Check if a menu contains any items with the "hidden" flag set to true. * * @param array $menu * @return bool */ public static function has_hidden_items($menu) { if (!is_array($menu) || empty($menu) || empty($menu['tree'])) { return false; } foreach ($menu['tree'] as $item) { if (ameMenuItem::get($item, 'hidden')) { return true; } if (!empty($item['items'])) { foreach ($item['items'] as $child) { if (ameMenuItem::get($child, 'hidden')) { return true; } } } } return false; }
private function get_virtual_caps_for($item) { $caps = array(); if ($item['template_id'] !== '') { $required_cap = ameMenuItem::get($item, 'access_level'); foreach ($item['grant_access'] as $grant => $has_access) { if ($has_access) { if (!isset($caps[$grant])) { $caps[$grant] = array(); } $caps[$grant][$required_cap] = true; } } } foreach ($item['items'] as $sub_item) { $caps = array_merge_recursive($caps, $this->get_virtual_caps_for($sub_item)); } return $caps; }
/** * 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; }
/** * 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); }
/** * Convert the WP menu structure to the internal representation. All properties set as defaults. * * @param array $menu * @param array $submenu * @return array Menu in the internal tree format. */ public static function wp2tree($menu, $submenu) { $tree = array(); foreach ($menu as $pos => $item) { $tree_item = ameMenuItem::blank_menu(); $tree_item['defaults'] = ameMenuItem::fromWpItem($item, $pos); $tree_item['separator'] = $tree_item['defaults']['separator']; //Attach sub-menu items $parent = $tree_item['defaults']['file']; if (isset($submenu[$parent])) { foreach ($submenu[$parent] as $position => $subitem) { $tree_item['items'][$subitem[2]] = array_merge(ameMenuItem::blank_menu(), array('defaults' => ameMenuItem::fromWpItem($subitem, $position, $parent))); } } $tree[$parent] = $tree_item; } $tree = self::sort_menu_tree($tree); return $tree; }