From c77851c47358136d0244bbe151cea5c6c4df5e2c Mon Sep 17 00:00:00 2001 From: Dries <dries@buytaert.net> Date: Wed, 30 Jul 2014 15:54:09 -0400 Subject: [PATCH] Issue #2301319 by pwolanin, effulgentsia: MenuLinkNG part5: Remove dead code and party! --- core/includes/menu.inc | 426 ----------- core/modules/menu_link/menu_link.api.php | 42 -- core/modules/menu_link/menu_link.install | 221 ------ core/modules/menu_link/menu_link.module | 225 ------ core/modules/menu_link/menu_link.services.yml | 7 - .../modules/menu_link/src/Entity/MenuLink.php | 680 ------------------ .../src/MenuLinkAccessController.php | 41 -- core/modules/menu_link/src/MenuLinkForm.php | 311 -------- .../menu_link/src/MenuLinkInterface.php | 45 -- .../modules/menu_link/src/MenuLinkStorage.php | 319 -------- .../src/MenuLinkStorageInterface.php | 110 --- core/modules/menu_link/src/MenuTree.php | 619 ---------------- .../menu_link/src/MenuTreeInterface.php | 182 ----- .../modules/menu_link/src/StaticMenuLinks.php | 76 -- .../menu_link/tests/src/MenuTreeTest.php | 539 -------------- .../src/Plugin/views/sort/MenuHierarchy.php | 66 -- 16 files changed, 3909 deletions(-) delete mode 100644 core/modules/menu_link/menu_link.api.php delete mode 100644 core/modules/menu_link/menu_link.install delete mode 100644 core/modules/menu_link/menu_link.module delete mode 100644 core/modules/menu_link/menu_link.services.yml delete mode 100644 core/modules/menu_link/src/Entity/MenuLink.php delete mode 100644 core/modules/menu_link/src/MenuLinkAccessController.php delete mode 100644 core/modules/menu_link/src/MenuLinkForm.php delete mode 100644 core/modules/menu_link/src/MenuLinkInterface.php delete mode 100644 core/modules/menu_link/src/MenuLinkStorage.php delete mode 100644 core/modules/menu_link/src/MenuLinkStorageInterface.php delete mode 100644 core/modules/menu_link/src/MenuTree.php delete mode 100644 core/modules/menu_link/src/MenuTreeInterface.php delete mode 100644 core/modules/menu_link/src/StaticMenuLinks.php delete mode 100644 core/modules/menu_link/tests/src/MenuTreeTest.php delete mode 100644 core/modules/views/src/Plugin/views/sort/MenuHierarchy.php diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 17c65c171e00..bd387520ea3c 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -255,14 +255,6 @@ * documentation here. */ -/** - * The maximum depth of a menu links tree - matches the number of p columns. - * - * @todo Move this constant to MenuLinkStorage along with all the tree - * functionality. - */ -const MENU_MAX_DEPTH = 9; - /** * @section Rendering menus * Once you have created menus (that contain menu links), you want to render @@ -315,92 +307,6 @@ * @endcode */ -/** - * Localizes a menu link title using t() if possible. - * - * Translate the title and description to allow storage of English title - * strings in the database, yet display of them in the language required - * by the current user. - * - * @param $item - * A menu link entity. - */ -function _menu_item_localize(&$item) { - // Allow default menu links to be translated. - $item['localized_options'] = $item['options']; - // All 'class' attributes are assumed to be an array during rendering, but - // links stored in the database may use an old string value. - // @todo In order to remove this code we need to implement a database update - // including unserializing all existing link options and running this code - // on them, as well as adding validation to menu_link_save(). - if (isset($item['options']['attributes']['class']) && is_string($item['options']['attributes']['class'])) { - $item['localized_options']['attributes']['class'] = explode(' ', $item['options']['attributes']['class']); - } - // If the menu link is defined in code and not customized, we can use t(). - if (!empty($item['machine_name']) && !$item['customized']) { - // @todo Figure out a proper way to support translations of menu links, see - // https://drupal.org/node/2193777. - $item['title'] = t($item['link_title']); - } - else { - $item['title'] = $item['link_title']; - } -} - -/** - * Provides menu link unserializing, access control, and argument handling. - * - * @param array $item - * The passed in item has the following keys: - * - access: (optional) Becomes TRUE if the item is accessible, FALSE - * otherwise. If the key is not set, the access manager is used to - * determine the access. - * - options: (required) Is unserialized and copied to $item['localized_options']. - * - link_title: (required) The title of the menu link. - * - route_name: (required) The route name of the menu link. - * - route_parameters: (required) The unserialized route parameters of the menu link. - * The passed in item is changed by the following keys: - * - href: The actual path to the link. This path is generated from the - * link_path of the menu link entity. - * - title: The title of the link. This title is generated from the - * link_title of the menu link entity. - */ -function _menu_link_translate(&$item) { - if (!is_array($item['options'])) { - $item['options'] = (array) unserialize($item['options']); - } - $item['localized_options'] = $item['options']; - $item['title'] = $item['link_title']; - if ($item['external'] || empty($item['route_name'])) { - $item['access'] = 1; - $item['href'] = $item['link_path']; - $item['route_parameters'] = array(); - // Set to NULL so that drupal_pre_render_link() is certain to skip it. - $item['route_name'] = NULL; - } - else { - $item['href'] = NULL; - if (!is_array($item['route_parameters'])) { - $item['route_parameters'] = (array) unserialize($item['route_parameters']); - } - // menu_tree_check_access() may set this ahead of time for links to nodes. - if (!isset($item['access'])) { - $item['access'] = \Drupal::getContainer()->get('access_manager')->checkNamedRoute($item['route_name'], $item['route_parameters'], \Drupal::currentUser()); - } - // For performance, don't localize a link the user can't access. - if ($item['access']) { - _menu_item_localize($item); - } - } - - // Allow other customizations - e.g. adding a page-specific query string to the - // options array. For performance reasons we only invoke this hook if the link - // has the 'alter' flag set in the options array. - if (!empty($item['options']['alter'])) { - \Drupal::moduleHandler()->alter('translated_menu_link', $item, $map); - } -} - /** * Implements template_preprocess_HOOK() for theme_menu_tree(). */ @@ -764,119 +670,6 @@ function theme_menu_local_tasks(&$variables) { return $output; } -/** - * Sets (or gets) the active menu for the current page. - * - * The active menu for the page determines the active trail. - * - * @return - * An array of menu machine names, in order of preference. The - * 'system.menu:active_menus_default' config item may be used to assert a menu - * order different from the order of creation, or to prevent a particular menu - * from being used at all in the active trail. - */ -function menu_set_active_menu_names($menu_names = NULL) { - $active = &drupal_static(__FUNCTION__); - - if (isset($menu_names) && is_array($menu_names)) { - $active = $menu_names; - } - elseif (!isset($active)) { - $config = \Drupal::config('system.menu'); - $active = $config->get('active_menus_default') ?: array_keys(menu_list_system_menus()); - } - return $active; -} - -/** - * Gets the active menu for the current page. - */ -function menu_get_active_menu_names() { - return menu_set_active_menu_names(); -} - -/** - * Looks up the preferred menu link for a given system path. - * - * @param $path - * The path; for example, 'node/5'. The function will find the corresponding - * menu link ('node/5' if it exists, or fallback to 'node/%'). - * @param $selected_menu - * The name of a menu used to restrict the search for a preferred menu link. - * If not specified, all the menus returned by menu_get_active_menu_names() - * will be used. - * - * @return - * A fully translated menu link, or FALSE if no matching menu link was - * found. The most specific menu link ('node/5' preferred over 'node/%') in - * the most preferred menu (as defined by menu_get_active_menu_names()) is - * returned. - */ -function menu_link_get_preferred($path = NULL, $selected_menu = NULL) { - $preferred_links = &drupal_static(__FUNCTION__); - - if (!isset($path)) { - $path = current_path(); - } - - if (empty($selected_menu)) { - // Use an illegal menu name as the key for the preferred menu link. - $selected_menu = MENU_PREFERRED_LINK; - } - - if (!isset($preferred_links[$path])) { - // Look for the correct menu link by building a list of candidate paths, - // which are ordered by priority (translated hrefs are preferred over - // untranslated paths). Afterwards, the most relevant path is picked from - // the menus, ordered by menu preference. - $path_candidates = array(); - // 1. The current item href. - // @todo simplify this code and convert to using route names. - // @see https://drupal.org/node/2154949 - $path_candidates[$path] = $path; - - // Retrieve a list of menu names, ordered by preference. - $menu_names = menu_get_active_menu_names(); - // Put the selected menu at the front of the list. - array_unshift($menu_names, $selected_menu); - - $menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $path_candidates)); - - // Sort candidates by link path and menu name. - $candidates = array(); - foreach ($menu_links as $candidate) { - $candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate; - // Add any menus not already in the menu name search list. - if (!in_array($candidate['menu_name'], $menu_names)) { - $menu_names[] = $candidate['menu_name']; - } - } - - // Store the most specific link for each menu. Also save the most specific - // link of the most preferred menu in $preferred_link. - foreach ($path_candidates as $link_path) { - if (isset($candidates[$link_path])) { - foreach ($menu_names as $menu_name) { - if (empty($preferred_links[$path][$menu_name]) && isset($candidates[$link_path][$menu_name])) { - $candidate_item = $candidates[$link_path][$menu_name]; - $candidate_item['access'] = \Drupal::service('access_manager')->checkNamedRoute($candidate_item['route_name'], $candidate_item['route_parameters'], \Drupal::currentUser()); - if ($candidate_item['access']) { - _menu_item_localize($candidate_item); - $preferred_links[$path][$menu_name] = $candidate_item; - if (empty($preferred_links[$path][MENU_PREFERRED_LINK])) { - // Store the most specific link. - $preferred_links[$path][MENU_PREFERRED_LINK] = $candidate_item; - } - } - } - } - } - } - } - - return isset($preferred_links[$path][$selected_menu]) ? $preferred_links[$path][$selected_menu] : FALSE; -} - /** * Clears all cached menu data. * @@ -887,225 +680,6 @@ function menu_cache_clear_all() { \Drupal::cache('menu')->invalidateAll(); } -/** - * Saves menu links recursively for menu_links_rebuild_defaults(). - */ -function _menu_link_save_recursive($controller, $machine_name, &$children, &$links) { - $menu_link = $links[$machine_name]; - if ($menu_link->isNew() || !$menu_link->customized) { - if (!isset($menu_link->plid) && !empty($menu_link->parent) && !empty($links[$menu_link->parent])) { - $parent = $links[$menu_link->parent]; - - if (empty($menu_link->menu_name) || $parent->menu_name == $menu_link->menu_name) { - $menu_link->plid = $parent->id(); - $menu_link->menu_name = $parent->menu_name; - } - } - $controller->save($menu_link); - } - if (!empty($children[$machine_name])) { - foreach ($children[$machine_name] as $next_name) { - _menu_link_save_recursive($controller, $next_name, $children, $links); - } - } - // Remove processed link names so we can find stragglers. - unset($children[$machine_name]); -} - -/** - * Builds menu links for the items returned from the menu_link.static service. - */ -function menu_link_rebuild_defaults() { - // Ensure that all configuration used to build the menu items are loaded - // without overrides. - $old_state = \Drupal::configFactory()->getOverrideState(); - \Drupal::configFactory()->setOverrideState(FALSE); - $module_handler = \Drupal::moduleHandler(); - if (!$module_handler->moduleExists('menu_link')) { - // The Menu link module may not be available during install, so rebuild - // when possible. - return; - } - /** @var \Drupal\menu_link\MenuLinkStorageInterface $menu_link_storage */ - $menu_link_storage = \Drupal::entityManager() - ->getStorage('menu_link'); - $links = array(); - $children = array(); - $top_links = array(); - $all_links = \Drupal::service('menu_link.static')->getLinks(); - if ($all_links) { - foreach ($all_links as $machine_name => $link) { - // For performance reasons, do a straight query now and convert to a menu - // link entity later. - // @todo revisit before release. - $existing_item = db_select('menu_links') - ->fields('menu_links') - ->condition('machine_name', $machine_name) - ->execute()->fetchObject(); - if ($existing_item) { - $existing_item->options = unserialize($existing_item->options); - $existing_item->route_parameters = unserialize($existing_item->route_parameters); - $link['mlid'] = $existing_item->mlid; - $link['plid'] = $existing_item->plid; - $link['uuid'] = $existing_item->uuid; - $link['customized'] = $existing_item->customized; - $link['updated'] = $existing_item->updated; - $menu_link = $menu_link_storage->createFromDefaultLink($link); - // @todo Do not create a new entity in order to update it, see - // https://drupal.org/node/2241865 - $menu_link->setOriginalId($existing_item->mlid); - - // Convert the existing item to a typed object. - /** @var \Drupal\menu_link\MenuLinkInterface $existing_item */ - $existing_item = $menu_link_storage->create(get_object_vars($existing_item)); - - if (!$existing_item->customized) { - // A change in the default menu links may move the link to a - // different menu or parent. - if (!empty($link['menu_name']) && ($link['menu_name'] != $existing_item->menu_name)) { - $menu_link->plid = NULL; - $menu_link->menu_name = $link['menu_name']; - } - elseif (!empty($link['parent'])) { - $menu_link->plid = NULL; - } - - $menu_link->original = $existing_item; - } - } - else { - if (empty($link['route_name']) && empty($link['link_path'])) { - \Drupal::logger('menu_link')->error('Menu_link %machine_name does neither provide a route_name nor a link_path, so it got skipped.', array('%machine_name' => $machine_name)); - continue; - } - $menu_link = $menu_link_storage->createFromDefaultLink($link); - } - if (!empty($link['parent'])) { - $children[$link['parent']][$machine_name] = $machine_name; - $menu_link->parent = $link['parent']; - if (empty($link['menu_name'])) { - // Reset the default menu name so it is populated from the parent. - $menu_link->menu_name = NULL; - } - } - else { - // A top level link - we need them to root our tree. - $top_links[$machine_name] = $machine_name; - $menu_link->plid = 0; - } - $links[$machine_name] = $menu_link; - } - } - foreach ($top_links as $machine_name) { - _menu_link_save_recursive($menu_link_storage, $machine_name, $children, $links); - } - // Handle any children we didn't find starting from top-level links. - foreach ($children as $orphan_links) { - foreach ($orphan_links as $machine_name) { - // Force it to the top level. - $links[$machine_name]->plid = 0; - _menu_link_save_recursive($menu_link_storage, $machine_name, $children, $links); - } - } - - // Find any item whose default menu link no longer exists. - if ($all_links) { - $query = \Drupal::entityQuery('menu_link') - ->condition('machine_name', array_keys($all_links), 'NOT IN') - ->exists('machine_name') - ->condition('external', 0) - ->condition('updated', 0) - ->condition('customized', 0) - ->sort('depth', 'DESC'); - $result = $query->execute(); - } - else { - $result = array(); - } - - // Remove all such items. Starting from those with the greatest depth will - // minimize the amount of re-parenting done by the menu link controller. - if ($result) { - menu_link_delete_multiple($result, TRUE); - } - \Drupal::configFactory()->setOverrideState($old_state); -} - -/** - * Returns an array containing all links for a menu. - * - * @param $menu_name - * The name of the menu whose links should be returned. - * - * @return - * An array of menu links. - */ -function menu_load_links($menu_name) { - $links = array(); - - $query = \Drupal::entityQuery('menu_link') - ->condition('menu_name', $menu_name) - // Order by weight so as to be helpful for menus that are only one level - // deep. - ->sort('weight'); - $result = $query->execute(); - - if (!empty($result)) { - $links = menu_link_load_multiple($result); - } - - return $links; -} - -/** - * Deletes all links for a menu. - * - * @param $menu_name - * The name of the menu whose links will be deleted. - */ -function menu_delete_links($menu_name) { - $links = menu_load_links($menu_name); - menu_link_delete_multiple(array_keys($links), FALSE, TRUE); -} - -/** - * Updates the expanded menu item state at most twice per page load. - */ -function _menu_update_expanded_menus() { - $expanded_menus_updated = &drupal_static(__FUNCTION__, 0); - - // Update the expanded menu item state, but at most twice, including at - // the end of the page load when there are multiple links saved or deleted. - if ($expanded_menus_updated == 0) { - // Keep track of which menus have expanded items. - _menu_set_expanded_menus(); - $expanded_menus_updated = 1; - } - elseif ($expanded_menus_updated == 1) { - // Keep track of which menus have expanded items. - drupal_register_shutdown_function('_menu_set_expanded_menus'); - $expanded_menus_updated = 2; - } -} - -/** - * Updates a list of menus with expanded items. - */ -function _menu_set_expanded_menus() { - $names = array(); - $result = Drupal::entityQueryAggregate('menu_link') - ->condition('expanded', 0, '<>') - ->groupBy('menu_name') - ->execute(); - - // Flatten the resulting array. - foreach($result as $k => $v) { - $names[$k] = $v['menu_name']; - } - - \Drupal::state()->set('menu_expanded', $names); -} - /** * @} End of "defgroup menu". */ diff --git a/core/modules/menu_link/menu_link.api.php b/core/modules/menu_link/menu_link.api.php deleted file mode 100644 index 470243671874..000000000000 --- a/core/modules/menu_link/menu_link.api.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @file - * Hooks provided by the Menu link module. - */ - -/** - * @addtogroup hooks - * @{ - */ - -/** - * Alter a menu link after it has been translated and before it is rendered. - * - * This hook is invoked from _menu_link_translate() after a menu link has been - * translated; i.e., after the user access to the link's target page has - * been checked. It is only invoked if $menu_link['options']['alter'] has been - * set to a non-empty value (e.g. TRUE). This flag should be set using - * hook_ENTITY_TYPE_presave() for entity 'menu_link'. - * - * Implementations of this hook are able to alter any property of the menu link. - * For example, this hook may be used to add a page-specific query string to all - * menu links, or hide a certain link by setting: - * @code - * 'hidden' => 1, - * @endcode - * - * @param \Drupal\menu_link\Entity\MenuLink $menu_link - * A menu link entity. - * - * @see hook_menu_link_alter() - */ -function hook_translated_menu_link_alter(\Drupal\menu_link\Entity\MenuLink &$menu_link, $map) { - if ($menu_link->href == 'devel/cache/clear') { - $menu_link->localized_options['query'] = drupal_get_destination(); - } -} - -/** - * @} End of "addtogroup hooks". - */ diff --git a/core/modules/menu_link/menu_link.install b/core/modules/menu_link/menu_link.install deleted file mode 100644 index c789d34ef822..000000000000 --- a/core/modules/menu_link/menu_link.install +++ /dev/null @@ -1,221 +0,0 @@ -<?php - -/** - * @file - * Install, update and uninstall functions for the menu_link module. - */ - -/** - * Implements hook_schema(). - */ -function menu_link_schema() { - $schema['menu_links'] = array( - 'description' => 'Contains the individual links within a menu.', - 'fields' => array( - 'menu_name' => array( - 'description' => "The menu name. All links with the same menu name (such as 'tools') are part of the same menu.", - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'mlid' => array( - 'description' => 'The menu link ID (mlid) is the integer primary key.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'machine_name' => array( - 'description' => 'Unique machine name: Optional human-readable ID for this link.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - ), - 'plid' => array( - 'description' => 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'link_path' => array( - 'description' => 'The Drupal path or external path this link points to.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this link.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'link_title' => array( - 'description' => 'The text displayed for the link.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'options' => array( - 'description' => 'A serialized array of options to be passed to the url() or l() function, such as a query string or HTML attributes.', - 'type' => 'blob', - 'not null' => FALSE, - 'serialize' => TRUE, - ), - 'module' => array( - 'description' => 'The name of the module that generated this link.', - 'type' => 'varchar', - 'length' => DRUPAL_EXTENSION_NAME_MAX_LENGTH, - 'not null' => TRUE, - 'default' => 'system', - ), - 'hidden' => array( - 'description' => 'A flag for whether the link should be rendered in menus. (1 = a disabled menu item that may be shown on admin screens, -1 = a menu callback, 0 = a normal, visible link)', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'small', - ), - 'external' => array( - 'description' => 'A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'small', - ), - 'has_children' => array( - 'description' => 'Flag indicating whether any links have this link as a parent (1 = children exist, 0 = no children).', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'small', - ), - 'expanded' => array( - 'description' => 'Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded)', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'small', - ), - 'weight' => array( - 'description' => 'Link weight among links in the same menu at the same depth.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'depth' => array( - 'description' => 'The depth relative to the top level. A link with plid == 0 will have depth == 1.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'small', - ), - 'customized' => array( - 'description' => 'A flag to indicate that the user has manually created or edited the link (1 = customized, 0 = not customized).', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'small', - ), - 'p1' => array( - 'description' => 'The first mlid in the materialized path. If N = depth, then pN must equal the mlid. If depth > 1 then p(N-1) must equal the plid. All pX where X > depth must equal zero. The columns p1 .. p9 are also called the parents.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'p2' => array( - 'description' => 'The second mlid in the materialized path. See p1.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'p3' => array( - 'description' => 'The third mlid in the materialized path. See p1.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'p4' => array( - 'description' => 'The fourth mlid in the materialized path. See p1.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'p5' => array( - 'description' => 'The fifth mlid in the materialized path. See p1.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'p6' => array( - 'description' => 'The sixth mlid in the materialized path. See p1.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'p7' => array( - 'description' => 'The seventh mlid in the materialized path. See p1.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'p8' => array( - 'description' => 'The eighth mlid in the materialized path. See p1.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'p9' => array( - 'description' => 'The ninth mlid in the materialized path. See p1.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'updated' => array( - 'description' => 'Flag that indicates that this link was generated during the update from Drupal 5.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'small', - ), - 'route_name' => array( - 'description' => 'The machine name of a defined Symfony Route this menu item represents.', - 'type' => 'varchar', - 'length' => 255, - ), - 'route_parameters' => array( - 'description' => 'Serialized array of route parameters of this menu link.', - 'type' => 'blob', - 'size' => 'big', - 'not null' => FALSE, - 'serialize' => TRUE, - ), - ), - 'indexes' => array( - 'path_menu' => array(array('link_path', 128), 'menu_name'), - 'menu_plid_expand_child' => array('menu_name', 'plid', 'expanded', 'has_children'), - 'menu_parents' => array('menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'), - ), - 'primary key' => array('mlid'), - ); - - return $schema; -} diff --git a/core/modules/menu_link/menu_link.module b/core/modules/menu_link/menu_link.module deleted file mode 100644 index 8a2ce38c26e4..000000000000 --- a/core/modules/menu_link/menu_link.module +++ /dev/null @@ -1,225 +0,0 @@ -<?php - -/** - * @file - * Enables users to create menu links. - */ - -use Drupal\Core\Routing\RouteMatchInterface; -use Drupal\Core\Url; -use Drupal\menu_link\Entity\MenuLink; -use Drupal\menu_link\MenuLinkInterface; - -function menu_link_help($route_name, RouteMatchInterface $route_match) { - switch ($route_name) { - case 'help.page.menu_link': - $output = ''; - $output .= '<h3>' . t('About') . '</h3>'; - $output .= '<p>' . t('The Menu Link module allows users to create menu links. It is required by the Menu UI module, which provides an interface for managing menus. See the <a href="!menu-help">Menu UI module help page</a> for more information.', array('!menu-help' => \Drupal::url('help.page', array('name' => 'menu_ui')))) . '</p>'; - return $output; - } -} - -/** - * Entity URI callback. - * - * @param \Drupal\menu_link\Entity\MenuLink $menu_link - * A menu link entity. - */ -function menu_link_uri(MenuLink $menu_link) { - return new Url($menu_link->route_name, $menu_link->route_parameters); -} - -/** - * Loads a menu link entity. - * - * This function should never be called from within node_load() or any other - * function used as a menu object load function since an infinite recursion may - * occur. - * - * @param int $mlid - * The menu link ID. - * @param bool $reset - * (optional) Whether to reset the menu_link_load_multiple() cache. - * - * @return \Drupal\menu_link\Entity\MenuLink|null - * A menu link entity, or NULL if there is no entity with the given ID. - * - * @deprecated in Drupal 8.x, will be removed before Drupal 9.0. - * Use \Drupal\menu_link\Entity\MenuLink::load(). - */ -function menu_link_load($mlid = NULL, $reset = FALSE) { - if ($reset) { - \Drupal::entityManager()->getStorage('menu_link')->resetCache(array($mlid)); - } - return MenuLink::load($mlid); -} - -/** - * Loads menu link entities from the database. - * - * @param array $mlids - * (optional) An array of entity IDs. If omitted, all entities are loaded. - * @param bool $reset - * (optional) Whether to reset the internal cache. - * - * @return array<\Drupal\menu_link\Entity\MenuLink> - * An array of menu link entities indexed by entity IDs. - * - * @see menu_link_load() - * @see entity_load_multiple() - * - * @deprecated in Drupal 8.x, will be removed before Drupal 9.0. - * Use \Drupal\menu_link\Entity\MenuLink::loadMultiple(). - */ -function menu_link_load_multiple(array $mlids = NULL, $reset = FALSE) { - if ($reset) { - \Drupal::entityManager()->getStorage('menu_link')->resetCache($mlids); - } - return MenuLink::loadMultiple($mlids); -} - -/** - * Deletes a menu link. - * - * @param int $mlid - * The menu link ID. - * - * @see menu_link_delete_multiple() - */ -function menu_link_delete($mlid) { - menu_link_delete_multiple(array($mlid)); -} - -/** - * Deletes multiple menu links. - * - * @param array $mlids - * An array of menu link IDs. - * @param bool $force - * (optional) Forces deletion. Internal use only, setting to TRUE is - * discouraged. Defaults to FALSE. - * @param bool $prevent_reparenting - * (optional) Disables the re-parenting logic from the deletion process. - * Defaults to FALSE. - */ -function menu_link_delete_multiple(array $mlids, $force = FALSE, $prevent_reparenting = FALSE) { - if (!$mlids) { - // If no IDs or invalid IDs were passed, do nothing. - return; - } - - $controller = \Drupal::entityManager() - ->getStorage('menu_link'); - if (!$force) { - $entity_query = \Drupal::entityQuery('menu_link'); - $group = $entity_query->orConditionGroup() - ->condition('module', 'system', '<>') - ->condition('updated', 0, '<>'); - - $entity_query->condition('mlid', $mlids, 'IN'); - $entity_query->condition($group); - - $result = $entity_query->execute(); - $entities = $controller->loadMultiple($result); - } - else { - $entities = $controller->loadMultiple($mlids); - } - $controller->setPreventReparenting($prevent_reparenting); - $controller->delete($entities); -} - -/** - * Saves a menu link. - * - * After calling this function, rebuild the menu cache using - * menu_cache_clear_all(). - * - * @param \Drupal\menu_link\Entity\MenuLink $menu_link - * The menu link entity to be saved. - * - * @return int|bool - * Returns SAVED_NEW or SAVED_UPDATED if the save operation succeeded, or - * FALSE if it failed. - */ -function menu_link_save(MenuLink $menu_link) { - return $menu_link->save(); -} - -/** - * Inserts, updates, enables, disables, or deletes an uncustomized menu link. - * - * @param string $module - * The name of the module that owns the link. - * @param string $op - * Operation to perform: insert, update, enable, disable, or delete. - * @param string $link_path - * The path this link points to. - * @param string $link_title - * (optional) Title of the link to insert or new title to update the link to. - * Unused for delete. Defaults to NULL. - * - * @return integer|null - * The insert op returns the mlid of the new item. Others op return NULL. - */ -function menu_link_maintain($module, $op, $link_path, $link_title = NULL) { - $menu_link_controller = \Drupal::entityManager() - ->getStorage('menu_link'); - switch ($op) { - case 'insert': - $menu_link = entity_create('menu_link', array( - 'link_title' => $link_title, - 'link_path' => $link_path, - 'module' => $module,) - ); - return $menu_link->save(); - - case 'update': - $menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $link_path, 'module' => $module, 'customized' => 0)); - foreach ($menu_links as $menu_link) { - $menu_link->original = clone $menu_link; - if (isset($link_title)) { - $menu_link->link_title = $link_title; - } - $menu_link_controller->save($menu_link); - } - break; - - case 'enable': - case 'disable': - $menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $link_path, 'module' => $module, 'customized' => 0)); - foreach ($menu_links as $menu_link) { - $menu_link->original = clone $menu_link; - $menu_link->hidden = ($op == 'disable' ? 1 : 0); - $menu_link->customized = 1; - if (isset($link_title)) { - $menu_link->link_title = $link_title; - } - $menu_link_controller->save($menu_link); - } - break; - - case 'delete': - $result = \Drupal::entityQuery('menu_link')->condition('link_path', $link_path)->execute(); - if (!empty($result)) { - menu_link_delete_multiple($result); - } - break; - } -} - -/** - * Implements hook_system_breadcrumb_alter(). - */ -function menu_link_system_breadcrumb_alter(array &$breadcrumb, RouteMatchInterface $route_match, array $context) { - // Custom breadcrumb behavior for editing menu links, we append a link to - // the menu in which the link is found. - if (($route_match->getRouteName() == 'menu_ui.link_edit') && $menu_link = $route_match->getParameter('menu_link')) { - if (($menu_link instanceof MenuLinkInterface) && !$menu_link->isNew()) { - // Add a link to the menu admin screen. - $menu = entity_load('menu', $menu_link->menu_name); - $breadcrumb[] = Drupal::l($menu->label(), 'menu_ui.menu_edit', array('menu' => $menu->id)); - } - } -} diff --git a/core/modules/menu_link/menu_link.services.yml b/core/modules/menu_link/menu_link.services.yml deleted file mode 100644 index 88f5037de194..000000000000 --- a/core/modules/menu_link/menu_link.services.yml +++ /dev/null @@ -1,7 +0,0 @@ -services: - menu_link.tree: - class: Drupal\menu_link\MenuTree - arguments: ['@database', '@cache.data', '@language_manager', '@request_stack', '@entity.manager', '@entity.query', '@state'] - menu_link.static: - class: Drupal\menu_link\StaticMenuLinks - arguments: ['@module_handler'] diff --git a/core/modules/menu_link/src/Entity/MenuLink.php b/core/modules/menu_link/src/Entity/MenuLink.php deleted file mode 100644 index e2215fdbaba8..000000000000 --- a/core/modules/menu_link/src/Entity/MenuLink.php +++ /dev/null @@ -1,680 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\menu_link\Entity\MenuLink. - */ - -namespace Drupal\menu_link\Entity; - -use Drupal\Component\Utility\NestedArray; -use Drupal\Component\Utility\UrlHelper; -use Drupal\Core\Cache\Cache; -use Drupal\Core\Entity\Entity; -use Drupal\Core\Entity\EntityStorageInterface; -use Drupal\Core\Url; -use Drupal\menu_link\MenuLinkInterface; -use Symfony\Component\Routing\Route; - -/** - * Defines the menu link entity class. - * - * @EntityType( - * id = "menu_link", - * label = @Translation("Menu link"), - * controllers = { - * "storage" = "Drupal\menu_link\MenuLinkStorage", - * "access" = "Drupal\menu_link\MenuLinkAccessController", - * "form" = { - * "default" = "Drupal\menu_link\MenuLinkForm" - * } - * }, - * admin_permission = "administer menu", - * static_cache = FALSE, - * base_table = "menu_links", - * uri_callback = "menu_link_uri", - * translatable = TRUE, - * entity_keys = { - * "id" = "mlid", - * "label" = "link_title", - * "uuid" = "uuid", - * "bundle" = "bundle" - * }, - * ) - */ -class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface { - - /** - * The link's menu name. - * - * @var string - */ - public $menu_name = 'tools'; - - /** - * The link's bundle. - * - * @var string - */ - public $bundle = 'tools'; - - /** - * The menu link ID. - * - * @var int - */ - public $mlid; - - /** - * An optional machine name if defined via the menu_link.static service. - * - * @var string - */ - public $machine_name; - - /** - * The menu link UUID. - * - * @var string - */ - public $uuid; - - /** - * The parent link ID. - * - * @var int - */ - public $plid; - - /** - * The Drupal path or external path this link points to. - * - * @var string - */ - public $link_path; - - /** - * The entity label. - * - * @var string - */ - public $link_title = ''; - - /** - * A serialized array of options to be passed to the url() or l() function, - * such as a query string or HTML attributes. - * - * @var array - */ - public $options = array(); - - /** - * The name of the module that generated this link. - * - * @var string - */ - public $module = 'menu_ui'; - - /** - * A flag for whether the link should be rendered in menus. - * - * @var int - */ - public $hidden = 0; - - /** - * A flag to indicate if the link points to a full URL starting with a - * protocol, like http:// (1 = external, 0 = internal). - * - * @var int - */ - public $external; - - /** - * Flag indicating whether any links have this link as a parent. - * - * @var int - */ - public $has_children = 0; - - /** - * Flag for whether this link should be rendered as expanded in menus. - * Expanded links always have their child links displayed, instead of only - * when the link is in the active trail. - * - * @var int - */ - public $expanded = 0; - - /** - * Link weight among links in the same menu at the same depth. - * - * @var int - */ - public $weight = 0; - - /** - * The depth relative to the top level. A link with plid == 0 will have - * depth == 1. - * - * @var int - */ - public $depth; - - /** - * A flag to indicate that the user has manually created or edited the link. - * - * @var int - */ - public $customized = 0; - - /** - * The first entity ID in the materialized path. - * - * @var int - * - * @todo Investigate whether the p1, p2, .. pX properties can be moved to a - * single array property. - */ - public $p1; - - /** - * The second entity ID in the materialized path. - * - * @var int - */ - public $p2; - - /** - * The third entity ID in the materialized path. - * - * @var int - */ - public $p3; - - /** - * The fourth entity ID in the materialized path. - * - * @var int - */ - public $p4; - - /** - * The fifth entity ID in the materialized path. - * - * @var int - */ - public $p5; - - /** - * The sixth entity ID in the materialized path. - * - * @var int - */ - public $p6; - - /** - * The seventh entity ID in the materialized path. - * - * @var int - */ - public $p7; - - /** - * The eighth entity ID in the materialized path. - * - * @var int - */ - public $p8; - - /** - * The ninth entity ID in the materialized path. - * - * @var int - */ - public $p9; - - /** - * The menu link modification timestamp. - * - * @var int - */ - public $updated = 0; - - /** - * The name of the route associated with this menu link, if any. - * - * @var string - */ - public $route_name; - - /** - * The parameters of the route associated with this menu link, if any. - * - * @var array - */ - public $route_parameters = array(); - - /** - * The route object associated with this menu link, if any. - * - * @var \Symfony\Component\Routing\Route - */ - protected $routeObject; - - /** - * Boolean indicating whether a new revision should be created on save. - * - * @var bool - */ - protected $newRevision = FALSE; - - /** - * Indicates whether this is the default revision. - * - * @var bool - */ - protected $isDefaultRevision = TRUE; - - /** - * {@inheritdoc} - */ - public function setNewRevision($value = TRUE) { - $this->newRevision = $value; - } - /** - * {@inheritdoc} - */ - public function isNewRevision() { - return $this->newRevision || ($this->getEntityType()->hasKey('revision') && !$this->getRevisionId()); - } - - /** - * {@inheritdoc} - */ - public function getRevisionId() { - return NULL; - } - - /** - * {@inheritdoc} - */ - public function isTranslatable() { - // @todo Inject the entity manager and retrieve bundle info from it. - $bundles = entity_get_bundles($this->entityTypeId); - return !empty($bundles[$this->bundle()]['translatable']); - } - - /** - * {@inheritdoc} - */ - public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) { - } - - /** - * Overrides Entity::id(). - */ - public function id() { - return $this->mlid; - } - - /** - * {@inheritdoc} - */ - public function bundle() { - return $this->bundle; - } - - /** - * Overrides Entity::createDuplicate(). - */ - public function createDuplicate() { - $duplicate = parent::createDuplicate(); - $duplicate->plid = NULL; - return $duplicate; - } - - /** - * {@inheritdoc} - */ - public function getRoute() { - if (!$this->route_name) { - return NULL; - } - if (!($this->routeObject instanceof Route)) { - $route_provider = \Drupal::service('router.route_provider'); - $this->routeObject = $route_provider->getRouteByName($this->route_name); - } - return $this->routeObject; - } - - /** - * {@inheritdoc} - */ - public function setRouteObject(Route $route) { - $this->routeObject = $route; - } - - /** - * {@inheritdoc} - */ - public function reset() { - // To reset the link to its original values, we need to retrieve its - // definition from the menu_link.static service. Otherwise, for example, - // the link's menu would not be reset, because properties like the original - // 'menu_name' are not stored anywhere else. Since resetting a link happens - // rarely and this is a one-time operation, retrieving the full set of - // default menu links does little harm. - $all_links = \Drupal::service('menu_link.static')->getLinks(); - $original = $all_links[$this->machine_name]; - $original['machine_name'] = $this->machine_name; - /** @var \Drupal\menu_link\MenuLinkStorageInterface $storage */ - $storage = \Drupal::entityManager()->getStorage($this->entityTypeId); - // @todo Do not create a new entity in order to update it, see - // https://drupal.org/node/2241865 - $new_link = $storage->createFromDefaultLink($original); - $new_link->setOriginalId($this->id()); - // Allow the menu to be determined by the parent - if (!empty($new_link['parent']) && !empty($all_links[$new_link['parent']])) { - // Walk up the tree to find the menu name. - $parent = $all_links[$new_link['parent']]; - $existing_parent = db_select('menu_links') - ->fields('menu_links') - ->condition('machine_name', $parent['machine_name']) - ->execute()->fetchAssoc(); - if ($existing_parent) { - /** @var \Drupal\Core\Entity\EntityInterface $existing_parent */ - $existing_parent = $storage->create($existing_parent); - $new_link->menu_name = $existing_parent->menu_name; - $new_link->plid = $existing_parent->id(); - } - } - // Merge existing menu link's ID and 'has_children' property. - foreach (array('mlid', 'has_children') as $key) { - $new_link->{$key} = $this->{$key}; - } - $new_link->save(); - return $new_link; - } - - /** - * Implements ArrayAccess::offsetExists(). - */ - public function offsetExists($offset) { - return isset($this->{$offset}); - } - - /** - * Implements ArrayAccess::offsetGet(). - */ - public function &offsetGet($offset) { - return $this->{$offset}; - } - - /** - * Implements ArrayAccess::offsetSet(). - */ - public function offsetSet($offset, $value) { - $this->{$offset} = $value; - } - - /** - * Implements ArrayAccess::offsetUnset(). - */ - public function offsetUnset($offset) { - unset($this->{$offset}); - } - - /** - * {@inheritdoc} - */ - public static function preDelete(EntityStorageInterface $storage, array $entities) { - parent::preDelete($storage, $entities); - - // Nothing to do if we don't want to reparent children. - if ($storage->getPreventReparenting()) { - return; - } - - foreach ($entities as $entity) { - // Children get re-attached to the item's parent. - if ($entity->has_children) { - $children = $storage->loadByProperties(array('plid' => $entity->plid)); - foreach ($children as $child) { - $child->plid = $entity->plid; - $storage->save($child); - } - } - } - } - - /** - * {@inheritdoc} - */ - public static function postDelete(EntityStorageInterface $storage, array $entities) { - parent::postDelete($storage, $entities); - - // Update the has_children status of the parent. - foreach ($entities as $entity) { - if (!$storage->getPreventReparenting()) { - $storage->updateParentalStatus($entity); - } - } - - // Also clear the menu system static caches. - menu_reset_static_cache(); - _menu_update_expanded_menus(); - } - - /** - * {@inheritdoc} - */ - public function preSave(EntityStorageInterface $storage) { - parent::preSave($storage); - - // This is the easiest way to handle the unique internal path '<front>', - // since a path marked as external does not need to match a route. - $this->external = (UrlHelper::isExternal($this->link_path) || $this->link_path == '<front>') ? 1 : 0; - - // Try to find a parent link. If found, assign it and derive its menu. - $parent = $this->findParent($storage); - if ($parent) { - $this->plid = $parent->id(); - $this->menu_name = $parent->menu_name; - } - // If no corresponding parent link was found, move the link to the top-level. - else { - $this->plid = 0; - } - - // Directly fill parents for top-level links. - if ($this->plid == 0) { - $this->p1 = $this->id(); - for ($i = 2; $i <= MENU_MAX_DEPTH; $i++) { - $parent_property = "p$i"; - $this->{$parent_property} = 0; - } - $this->depth = 1; - } - // Otherwise, ensure that this link's depth is not beyond the maximum depth - // and fill parents based on the parent link. - else { - if ($this->has_children && $this->original) { - $limit = MENU_MAX_DEPTH - $storage->findChildrenRelativeDepth($this->original) - 1; - } - else { - $limit = MENU_MAX_DEPTH - 1; - } - if ($parent->depth > $limit) { - return FALSE; - } - $this->depth = $parent->depth + 1; - $this->setParents($parent); - } - - // Need to check both plid and menu_name, since plid can be 0 in any menu. - if (isset($this->original) && ($this->plid != $this->original->plid || $this->menu_name != $this->original->menu_name)) { - $storage->moveChildren($this); - } - - // Find the route_name. - if (!$this->external && !isset($this->route_name)) { - $url = Url::createFromPath($this->link_path); - $this->route_name = $url->getRouteName(); - $this->route_parameters = $url->getRouteParameters(); - } - elseif (empty($this->link_path)) { - $this->link_path = \Drupal::urlGenerator()->getPathFromRoute($this->route_name, $this->route_parameters); - } - } - - /** - * {@inheritdoc} - */ - public function postSave(EntityStorageInterface $storage, $update = TRUE) { - parent::postSave($storage, $update); - - // Check the has_children status of the parent. - $storage->updateParentalStatus($this); - - - // Entity::postSave() calls Entity::invalidateTagsOnSave(), which only - // handles the regular cases. The MenuLink entity has two special cases. - $cache_tags = array(); - // Case 1: a newly created menu link is *also* added to a menu, so we must - // invalidate the associated menu's cache tag. - if (!$update) { - $cache_tags = $this->getCacheTag(); - } - // Case 2: a menu link may be moved from one menu to another; the original - // menu's cache tag must also be invalidated. - if (isset($this->original) && $this->menu_name != $this->original->menu_name) { - $cache_tags = NestedArray::mergeDeep($cache_tags, $this->original->getCacheTag()); - } - Cache::invalidateTags($cache_tags); - - // Also clear the menu system static caches. - menu_reset_static_cache(); - - // Track which menu items are expanded. - _menu_update_expanded_menus(); - } - - /** - * {@inheritdoc} - */ - public static function postLoad(EntityStorageInterface $storage, array &$entities) { - parent::postLoad($storage, $entities); - - $routes = array(); - foreach ($entities as $menu_link) { - $menu_link->options = unserialize($menu_link->options); - $menu_link->route_parameters = unserialize($menu_link->route_parameters); - - // By default use the menu_name as type. - $menu_link->bundle = $menu_link->menu_name; - - // For all links that have an associated route, load the route object now - // and save it on the object. That way we avoid a select N+1 problem later. - if ($menu_link->route_name) { - $routes[$menu_link->id()] = $menu_link->route_name; - } - } - - // Now mass-load any routes needed and associate them. - if ($routes) { - $route_objects = \Drupal::service('router.route_provider')->getRoutesByNames($routes); - foreach ($routes as $entity_id => $route) { - // Not all stored routes will be valid on load. - if (isset($route_objects[$route])) { - $entities[$entity_id]->setRouteObject($route_objects[$route]); - } - } - } - } - - /** - * {@inheritdoc} - */ - protected function setParents(MenuLinkInterface $parent) { - $i = 1; - while ($i < $this->depth) { - $p = 'p' . $i++; - $this->{$p} = $parent->{$p}; - } - $p = 'p' . $i++; - // The parent (p1 - p9) corresponding to the depth always equals the mlid. - $this->{$p} = $this->id(); - while ($i <= MENU_MAX_DEPTH) { - $p = 'p' . $i++; - $this->{$p} = 0; - } - } - - /** - * {@inheritdoc} - */ - protected function findParent(EntityStorageInterface $storage) { - $parent = FALSE; - - // This item is explicitly top-level, skip the rest of the parenting. - if (isset($this->plid) && empty($this->plid)) { - return $parent; - } - - // If we have a parent link ID, try to use that. - $candidates = array(); - if (isset($this->plid)) { - $candidates[] = $this->plid; - } - - // Else, if we have a link hierarchy try to find a valid parent in there. - if (!empty($this->depth) && $this->depth > 1) { - for ($depth = $this->depth - 1; $depth >= 1; $depth--) { - $parent_property = "p$depth"; - $candidates[] = $this->$parent_property; - } - } - - foreach ($candidates as $mlid) { - $parent = $storage->load($mlid); - if ($parent) { - break; - } - } - return $parent; - } - - /** - * Builds and returns the renderable array for this menu link. - * - * @return array - * A renderable array representing the content of the link. - */ - public function build() { - $build = array( - '#type' => 'link', - '#title' => $this->title, - '#href' => $this->href, - '#route_name' => $this->route_name ? $this->route_name : NULL, - '#route_parameters' => $this->route_parameters, - '#options' => !empty($this->localized_options) ? $this->localized_options : array(), - ); - return $build; - } - - /** - * {@inheritdoc} - */ - public function getCacheTag() { - return entity_load('menu', $this->menu_name)->getCacheTag(); - } - - /** - * {@inheritdoc} - */ - public function getListCacheTags() { - return entity_load('menu', $this->menu_name)->getListCacheTags(); - } - -} diff --git a/core/modules/menu_link/src/MenuLinkAccessController.php b/core/modules/menu_link/src/MenuLinkAccessController.php deleted file mode 100644 index c6e6b911036d..000000000000 --- a/core/modules/menu_link/src/MenuLinkAccessController.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\menu_link\MenuLinkAccessController. - */ - -namespace Drupal\menu_link; - -use Drupal\Core\Entity\EntityAccessController; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Session\AccountInterface; - -/** - * Defines an access controller for the menu link entity. - * - * @see \Drupal\menu_link\Entity\MenuLink - */ -class MenuLinkAccessController extends EntityAccessController { - - /** - * {@inheritdoc} - */ - protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) { - $access = $account->hasPermission('administer menu'); - if ($access) { - switch ($operation) { - case 'reset': - // Reset allowed for items defined via hook_menu() and customized. - return !empty($entity->machine_name) && $entity->customized; - - case 'delete': - // Only items created by the Menu UI module can be deleted. - return $entity->module == 'menu_ui' || $entity->updated == 1; - - } - } - return $access; - } - -} diff --git a/core/modules/menu_link/src/MenuLinkForm.php b/core/modules/menu_link/src/MenuLinkForm.php deleted file mode 100644 index f5cbca1411c5..000000000000 --- a/core/modules/menu_link/src/MenuLinkForm.php +++ /dev/null @@ -1,311 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\menu_link\MenuLinkForm. - */ - -namespace Drupal\menu_link; - -use Drupal\Component\Utility\UrlHelper; -use Drupal\Core\Entity\EntityForm; -use Drupal\Core\Language\LanguageInterface; -use Drupal\Core\Path\AliasManagerInterface; -use Drupal\Core\Routing\UrlGenerator; -use Symfony\Component\DependencyInjection\ContainerInterface; - -/** - * Form controller for the node edit forms. - */ -class MenuLinkForm extends EntityForm { - - /** - * The menu link storage. - * - * @var \Drupal\menu_link\MenuLinkStorageInterface - */ - protected $menuLinkStorage; - - /** - * The path alias manager. - * - * @var \Drupal\Core\Path\AliasManagerInterface - */ - protected $pathAliasManager; - - /** - * The URL generator. - * - * @var \Drupal\Core\Routing\UrlGenerator - */ - protected $urlGenerator; - - /** - * Constructs a new MenuLinkForm object. - * - * @param \Drupal\menu_link\MenuLinkStorageInterface $menu_link_storage - * The menu link storage. - * @param \Drupal\Core\Path\AliasManagerInterface $path_alias_manager - * The path alias manager. - * @param \Drupal\Core\Routing\UrlGenerator $url_generator - * The URL generator. - */ - public function __construct(MenuLinkStorageInterface $menu_link_storage, AliasManagerInterface $path_alias_manager, UrlGenerator $url_generator) { - $this->menuLinkStorage = $menu_link_storage; - $this->pathAliasManager = $path_alias_manager; - $this->urlGenerator = $url_generator; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity.manager')->getStorage('menu_link'), - $container->get('path.alias_manager'), - $container->get('url_generator') - ); - } - - /** - * Overrides EntityForm::form(). - */ - public function form(array $form, array &$form_state) { - $menu_link = $this->entity; - // Since menu_link_load() no longer returns a translated and access checked - // item, do it here instead. - _menu_link_translate($menu_link); - - $form['link_title'] = array( - '#type' => 'textfield', - '#title' => t('Menu link title'), - '#default_value' => $menu_link->link_title, - '#description' => t('The text to be used for this link in the menu.'), - '#required' => TRUE, - ); - foreach (array('link_path', 'mlid', 'module', 'has_children', 'options') as $key) { - $form[$key] = array('#type' => 'value', '#value' => $menu_link->{$key}); - } - // Any item created or edited via this interface is considered "customized". - $form['customized'] = array('#type' => 'value', '#value' => 1); - - // We are not using url() when constructing this path because it would add - // $base_path. - $path = $menu_link->link_path; - if (isset($menu_link->options['query'])) { - $path .= '?' . $this->urlGenerator->httpBuildQuery($menu_link->options['query']); - } - if (isset($menu_link->options['fragment'])) { - $path .= '#' . $menu_link->options['fragment']; - } - if ($menu_link->module == 'menu_ui') { - $form['link_path'] = array( - '#type' => 'textfield', - '#title' => t('Path'), - '#maxlength' => 255, - '#default_value' => $path, - '#description' => t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')), - '#required' => TRUE, - ); - } - else { - $form['_path'] = array( - '#type' => 'item', - '#title' => t('Path'), - '#description' => l($menu_link->link_title, $menu_link->href, $menu_link->options), - ); - } - - $form['description'] = array( - '#type' => 'textarea', - '#title' => t('Description'), - '#default_value' => isset($menu_link->options['attributes']['title']) ? $menu_link->options['attributes']['title'] : '', - '#rows' => 1, - '#description' => t('Shown when hovering over the menu link.'), - ); - $form['enabled'] = array( - '#type' => 'checkbox', - '#title' => t('Enabled'), - '#default_value' => !$menu_link->hidden, - '#description' => t('Menu links that are not enabled will not be listed in any menu.'), - ); - $form['expanded'] = array( - '#type' => 'checkbox', - '#title' => t('Show as expanded'), - '#default_value' => $menu_link->expanded, - '#description' => t('If selected and this menu link has children, the menu will always appear expanded.'), - ); - - // Generate a list of possible parents (not including this link or descendants). - $options = menu_ui_parent_options(menu_ui_get_menus(), $menu_link); - $default = $menu_link->menu_name . ':' . $menu_link->plid; - if (!isset($options[$default])) { - $default = 'tools:0'; - } - $form['parent'] = array( - '#type' => 'select', - '#title' => t('Parent link'), - '#default_value' => $default, - '#options' => $options, - '#description' => t('The maximum depth for a link and all its children is fixed at !maxdepth. Some menu links may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)), - '#attributes' => array('class' => array('menu-title-select')), - ); - - // Get number of items in menu so the weight selector is sized appropriately. - $delta = $this->menuLinkStorage->countMenuLinks($menu_link->menu_name); - $form['weight'] = array( - '#type' => 'weight', - '#title' => t('Weight'), - // Old hardcoded value. - '#delta' => max($delta, 50), - '#default_value' => $menu_link->weight, - '#description' => t('Optional. In the menu, the heavier links will sink and the lighter links will be positioned nearer the top.'), - ); - - // Language module allows to configure the menu link language independently - // of the menu language. It also allows to optionally show the language - // selector on the menu link form so that the language of each menu link can - // be configured individually. - if ($this->moduleHandler->moduleExists('language')) { - $language_configuration = language_get_default_configuration('menu_link', $menu_link->bundle()); - $default_langcode = ($menu_link->isNew() ? $language_configuration['langcode'] : $menu_link->langcode); - $language_show = $language_configuration['language_show']; - } - // Without Language module menu links inherit the menu language and no - // language selector is shown. - else { - $default_langcode = ($menu_link->isNew() ? entity_load('menu', $menu_link->menu_name)->language()->getId() : $menu_link->langcode); - $language_show = FALSE; - } - - $form['langcode'] = array( - '#type' => 'language_select', - '#title' => t('Language'), - '#languages' => LanguageInterface::STATE_ALL, - '#default_value' => $default_langcode, - '#access' => $language_show, - ); - - return parent::form($form, $form_state, $menu_link); - } - - /** - * Overrides EntityForm::actions(). - */ - protected function actions(array $form, array &$form_state) { - $element = parent::actions($form, $form_state); - $element['submit']['#button_type'] = 'primary'; - return $element; - } - - /** - * Overrides EntityForm::validate(). - */ - public function validate(array $form, array &$form_state) { - $menu_link = $this->buildEntity($form, $form_state); - - $normal_path = $this->pathAliasManager->getPathByAlias($menu_link->link_path); - if ($menu_link->link_path != $normal_path) { - drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $menu_link->link_path, '%normal_path' => $normal_path))); - $menu_link->link_path = $normal_path; - $form_state['values']['link_path'] = $normal_path; - } - if (!UrlHelper::isExternal($menu_link->link_path)) { - $parsed_link = parse_url($menu_link->link_path); - if (isset($parsed_link['query'])) { - $menu_link->options['query'] = array(); - parse_str($parsed_link['query'], $menu_link->options['query']); - } - else { - // Use unset() rather than setting to empty string - // to avoid redundant serialized data being stored. - unset($menu_link->options['query']); - } - if (isset($parsed_link['fragment'])) { - $menu_link->options['fragment'] = $parsed_link['fragment']; - } - else { - unset($menu_link->options['fragment']); - } - if (isset($parsed_link['path']) && $menu_link->link_path != $parsed_link['path']) { - $menu_link->link_path = $parsed_link['path']; - } - } - if (!trim($menu_link->link_path) || !drupal_valid_path($menu_link->link_path, TRUE)) { - $this->setFormError('link_path', $form_state, $this->t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $menu_link->link_path))); - } - - parent::validate($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function buildEntity(array $form, array &$form_state) { - // @todo: Remove this when menu links are converted to content entities in - // http://drupal.org/node/1842858. - $entity = clone $this->entity; - // If you submit a form, the form state comes from caching, which forces - // the controller to be the one before caching. Ensure to have the - // controller of the current request. - $form_state['controller'] = $this; - - // Copy top-level form values to entity properties, without changing - // existing entity properties that are not being edited by - // this form. - foreach ($form_state['values'] as $key => $value) { - $entity->$key = $value; - } - - // Invoke all specified builders for copying form values to entity properties. - if (isset($form['#entity_builders'])) { - foreach ($form['#entity_builders'] as $function) { - call_user_func_array($function, array($entity->getEntityTypeId(), $entity, &$form, &$form_state)); - } - } - - return $entity; - } - - /** - * Overrides EntityForm::submit(). - */ - public function submit(array $form, array &$form_state) { - // Build the menu link object from the submitted values. - $menu_link = parent::submit($form, $form_state); - - // The value of "hidden" is the opposite of the value supplied by the - // "enabled" checkbox. - $menu_link->hidden = (int) !$menu_link->enabled; - unset($menu_link->enabled); - - $menu_link->options['attributes']['title'] = $menu_link->description; - list($menu_link->menu_name, $menu_link->plid) = explode(':', $menu_link->parent); - - return $menu_link; - } - - /** - * Overrides EntityForm::save(). - */ - public function save(array $form, array &$form_state) { - $menu_link = $this->entity; - - $saved = $menu_link->save(); - - if ($saved) { - drupal_set_message(t('The menu link has been saved.')); - $form_state['redirect_route'] = array( - 'route_name' => 'menu_ui.menu_edit', - 'route_parameters' => array( - 'menu' => $menu_link->menu_name, - ), - ); - } - else { - drupal_set_message(t('There was an error saving the menu link.'), 'error'); - $form_state['rebuild'] = TRUE; - } - } - -} diff --git a/core/modules/menu_link/src/MenuLinkInterface.php b/core/modules/menu_link/src/MenuLinkInterface.php deleted file mode 100644 index fdfab6765116..000000000000 --- a/core/modules/menu_link/src/MenuLinkInterface.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\menu_link\MenuLinkInterface. - */ - -namespace Drupal\menu_link; - -use Drupal\Core\Entity\EntityInterface; -use Symfony\Component\Routing\Route; - -/** - * Provides an interface defining a menu link entity. - */ -interface MenuLinkInterface extends EntityInterface { - - /** - * Returns the Route object associated with this link, if any. - * - * @return \Symfony\Component\Routing\Route|null - * The route object for this menu link, or NULL if there isn't one. - */ - public function getRoute(); - - /** - * Sets the route object for this link. - * - * This should only be called by MenuLinkStorage when loading - * the link object. Calling it at other times could result in unpredictable - * behavior. - * - * @param \Symfony\Component\Routing\Route $route - */ - public function setRouteObject(Route $route); - - /** - * Resets a system-defined menu link. - * - * @return \Drupal\menu_link\MenuLinkInterface - * A menu link entity. - */ - public function reset(); - -} diff --git a/core/modules/menu_link/src/MenuLinkStorage.php b/core/modules/menu_link/src/MenuLinkStorage.php deleted file mode 100644 index 1ece00d171eb..000000000000 --- a/core/modules/menu_link/src/MenuLinkStorage.php +++ /dev/null @@ -1,319 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\menu_link\MenuLinkStorage. - */ - -namespace Drupal\menu_link; - -use Drupal\Core\Entity\EntityDatabaseStorage; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityStorageException; - -/** - * Controller class for menu links. - * - * This extends the Drupal\entity\EntityDatabaseStorage class, adding - * required special handling for menu_link entities. - */ -class MenuLinkStorage extends EntityDatabaseStorage implements MenuLinkStorageInterface { - - /** - * Indicates whether the delete operation should re-parent children items. - * - * @var bool - */ - protected $preventReparenting = FALSE; - - /** - * {@inheritdoc} - */ - public function create(array $values = array()) { - // The bundle of menu links being the menu name is not enforced but is the - // default behavior if no bundle is set. - if (!isset($values['bundle']) && isset($values['menu_name'])) { - $values['bundle'] = $values['menu_name']; - } - return parent::create($values); - } - - /** - * {@inheritdoc} - */ - public function save(EntityInterface $entity) { - - // We return SAVED_UPDATED by default because the logic below might not - // update the entity if its values haven't changed, so returning FALSE - // would be confusing in that situation. - $return = SAVED_UPDATED; - - $transaction = $this->database->startTransaction(); - try { - // Load the stored entity, if any. - if (!$entity->isNew() && !isset($entity->original)) { - $id = $entity->id(); - if ($entity->getOriginalId() !== NULL) { - $id = $entity->getOriginalId(); - } - $entity->original = $this->loadUnchanged($id); - } - - if ($entity->isNew()) { - $entity->mlid = $this->database->insert($this->entityType->getBaseTable())->fields(array('menu_name' => $entity->menu_name))->execute(); - $entity->enforceIsNew(); - } - - // Unlike the save() method from EntityDatabaseStorage, we invoke the - // 'presave' hook first because we want to allow modules to alter the - // entity before all the logic from our preSave() method. - $this->invokeHook('presave', $entity); - $entity->preSave($this); - - // If every value in $entity->original is the same in the $entity, there - // is no reason to run the update queries or clear the caches. We use - // array_intersect_key() with the $entity as the first parameter because - // $entity may have additional keys left over from building a router entry. - // The intersect removes the extra keys, allowing a meaningful comparison. - if ($entity->isNew() || (array_intersect_key(get_object_vars($entity), get_object_vars($entity->original)) != get_object_vars($entity->original))) { - $return = drupal_write_record($this->entityType->getBaseTable(), $entity, $this->idKey); - - if ($return) { - if (!$entity->isNew()) { - $this->resetCache(array($entity->{$this->idKey})); - $entity->postSave($this, TRUE); - $this->invokeHook('update', $entity); - } - else { - $return = SAVED_NEW; - $this->resetCache(); - - $entity->enforceIsNew(FALSE); - $entity->postSave($this, FALSE); - $this->invokeHook('insert', $entity); - } - } - } - - // Ignore replica server temporarily. - db_ignore_replica(); - unset($entity->original); - - return $return; - } - catch (\Exception $e) { - $transaction->rollback(); - watchdog_exception($this->entityTypeId, $e); - throw new EntityStorageException($e->getMessage(), $e->getCode(), $e); - } - } - - /** - * {@inheritdoc} - */ - public function setPreventReparenting($value = FALSE) { - $this->preventReparenting = $value; - } - - /** - * {@inheritdoc} - */ - public function getPreventReparenting() { - return $this->preventReparenting; - } - - /** - * {@inheritdoc} - */ - public function loadUpdatedCustomized(array $router_paths) { - $query = parent::buildQuery(NULL); - $query - ->condition(db_or() - ->condition('updated', 1) - ->condition(db_and() - ->condition('router_path', $router_paths, 'NOT IN') - ->condition('external', 0) - ->condition('customized', 1) - ) - ); - $query_result = $query->execute(); - - // We provide the necessary arguments for PDO to create objects of the - // specified entity class. - // @see \Drupal\Core\Entity\EntityInterface::__construct() - $query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityClass, array(array(), $this->entityTypeId)); - - return $query_result->fetchAllAssoc($this->idKey); - } - - /** - * {@inheritdoc} - */ - public function loadModuleAdminTasks() { - // @todo - this code will move out of the menu link entity, so we are doing - // a straight SQL query for expediency. - $result = $this->database->select('menu_links'); - $result->condition('machine_name', 'system.admin'); - $result->addField('menu_links', 'mlid'); - $plid = $result->execute()->fetchField(); - - $query = $this->database->select('menu_links', 'base', array('fetch' => \PDO::FETCH_ASSOC)); - $query->fields('base'); - $query - ->condition('base.hidden', 0, '>=') - ->condition('base.module', '', '>') - ->condition('base.machine_name', '', '>') - ->condition('base.p1', $plid); - $entities = $query->execute()->fetchAll(); - - return $entities; - } - - /** - * {@inheritdoc} - */ - public function updateParentalStatus(EntityInterface $entity, $exclude = FALSE) { - // If plid == 0, there is nothing to update. - if ($entity->plid) { - // Check if at least one visible child exists in the table. - $query = $this->getQuery(); - $query - ->condition('menu_name', $entity->menu_name) - ->condition('hidden', 0) - ->condition('plid', $entity->plid) - ->count(); - - if ($exclude) { - $query->condition('mlid', $entity->id(), '<>'); - } - - $parent_has_children = ((bool) $query->execute()) ? 1 : 0; - $this->database->update('menu_links') - ->fields(array('has_children' => $parent_has_children)) - ->condition('mlid', $entity->plid) - ->execute(); - } - } - - /** - * {@inheritdoc} - */ - public function findChildrenRelativeDepth(EntityInterface $entity) { - // @todo Since all we need is a specific field from the base table, does it - // make sense to convert to EFQ? - $query = $this->database->select('menu_links'); - $query->addField('menu_links', 'depth'); - $query->condition('menu_name', $entity->menu_name); - $query->orderBy('depth', 'DESC'); - $query->range(0, 1); - - $i = 1; - $p = 'p1'; - while ($i <= MENU_MAX_DEPTH && $entity->{$p}) { - $query->condition($p, $entity->{$p}); - $p = 'p' . ++$i; - } - - $max_depth = $query->execute()->fetchField(); - - return ($max_depth > $entity->depth) ? $max_depth - $entity->depth : 0; - } - - /** - * {@inheritdoc} - */ - public function moveChildren(EntityInterface $entity) { - $query = $this->database->update($this->entityType->getBaseTable()); - - $query->fields(array('menu_name' => $entity->menu_name)); - - $p = 'p1'; - $expressions = array(); - for ($i = 1; $i <= $entity->depth; $p = 'p' . ++$i) { - $expressions[] = array($p, ":p_$i", array(":p_$i" => $entity->{$p})); - } - $j = $entity->original->depth + 1; - while ($i <= MENU_MAX_DEPTH && $j <= MENU_MAX_DEPTH) { - $expressions[] = array('p' . $i++, 'p' . $j++, array()); - } - while ($i <= MENU_MAX_DEPTH) { - $expressions[] = array('p' . $i++, 0, array()); - } - - $shift = $entity->depth - $entity->original->depth; - if ($shift > 0) { - // The order of expressions must be reversed so the new values don't - // overwrite the old ones before they can be used because "Single-table - // UPDATE assignments are generally evaluated from left to right" - // @see http://dev.mysql.com/doc/refman/5.0/en/update.html - $expressions = array_reverse($expressions); - } - foreach ($expressions as $expression) { - $query->expression($expression[0], $expression[1], $expression[2]); - } - - $query->expression('depth', 'depth + :depth', array(':depth' => $shift)); - $query->condition('menu_name', $entity->original->menu_name); - $p = 'p1'; - for ($i = 1; $i <= MENU_MAX_DEPTH && $entity->original->{$p}; $p = 'p' . ++$i) { - $query->condition($p, $entity->original->{$p}); - } - - $query->execute(); - - // Check the has_children status of the parent, while excluding this item. - $this->updateParentalStatus($entity->original, TRUE); - } - - /** - * {@inheritdoc} - */ - public function countMenuLinks($menu_name) { - $query = $this->getQuery(); - $query - ->condition('menu_name', $menu_name) - ->count(); - return $query->execute(); - } - - /** - * {@inheritdoc} - */ - public function getParentFromHierarchy(EntityInterface $entity) { - $parent_path = $entity->link_path; - do { - $parent = FALSE; - $parent_path = substr($parent_path, 0, strrpos($parent_path, '/')); - - $query = $this->getQuery(); - $query - ->condition('mlid', $entity->id(), '<>') - ->condition('module', 'system') - // We always respect the link's 'menu_name'; inheritance for router - // items is ensured in _menu_router_build(). - ->condition('menu_name', $entity->menu_name) - ->condition('link_path', $parent_path); - - $result = $query->execute(); - // Only valid if we get a unique result. - if (count($result) == 1) { - $parent = $this->load(reset($result)); - } - } while ($parent === FALSE && $parent_path); - - return $parent; - } - - /** - * {@inheritdoc} - */ - public function createFromDefaultLink(array $item) { - // Suggested items are disabled by default. - $item += array( - 'hidden' => 0, - 'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])), - ); - return $this->create($item); - } - -} diff --git a/core/modules/menu_link/src/MenuLinkStorageInterface.php b/core/modules/menu_link/src/MenuLinkStorageInterface.php deleted file mode 100644 index 3d3fd5d10c51..000000000000 --- a/core/modules/menu_link/src/MenuLinkStorageInterface.php +++ /dev/null @@ -1,110 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\menu_link\MenuLinkStorageInterface. -*/ - -namespace Drupal\menu_link; - -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityStorageInterface; - -/** - * Defines a common interface for menu link entity controller classes. - */ -interface MenuLinkStorageInterface extends EntityStorageInterface { - - /** - * Sets an internal flag that allows us to prevent the reparenting operations - * executed during deletion. - * - * @param bool $value - * TRUE if reparenting should be allowed, FALSE if it should be prevented. - */ - public function setPreventReparenting($value = FALSE); - - /** - * Gets value of internal flag that allows/prevents reparenting operations - * executed during deletion. - * - * @return bool - * TRUE if reparenting is allowed, FALSE if it is prevented. - */ - public function getPreventReparenting(); - - /** - * Loads system menu link as needed by system_get_module_admin_tasks(). - * - * @return array - * An array of menu link entities indexed by their IDs. - */ - public function loadModuleAdminTasks(); - - /** - * Checks and updates the 'has_children' property for the parent of a link. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * A menu link entity. - */ - public function updateParentalStatus(EntityInterface $entity, $exclude = FALSE); - - /** - * Finds the depth of an item's children relative to its depth. - * - * For example, if the item has a depth of 2 and the maximum of any child in - * the menu link tree is 5, the relative depth is 3. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * A menu link entity. - * - * @return int - * The relative depth, or zero. - */ - public function findChildrenRelativeDepth(EntityInterface $entity); - - /** - * Updates the children of a menu link that is being moved. - * - * The menu name, parents (p1 - p6), and depth are updated for all children of - * the link, and the has_children status of the previous parent is updated. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * A menu link entity. - */ - public function moveChildren(EntityInterface $entity); - - /** - * Returns the number of menu links from a menu. - * - * @param string $menu_name - * The unique name of a menu. - */ - public function countMenuLinks($menu_name); - - /** - * Tries to derive menu link's parent from the path hierarchy. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * A menu link entity. - * - * @return \Drupal\Core\Entity\EntityInterface|false - * A menu link entity or FALSE if not valid parent was found. - */ - public function getParentFromHierarchy(EntityInterface $entity); - - /** - * Builds a menu link entity from a default item. - * - * This function should only be called for link data from - * the menu_link.static service. - * - * @param array $item - * An item returned from the menu_link.static service. - * - * @return \Drupal\menu_link\MenuLinkInterface - * A menu link entity. - */ - public function createFromDefaultLink(array $item); - -} diff --git a/core/modules/menu_link/src/MenuTree.php b/core/modules/menu_link/src/MenuTree.php deleted file mode 100644 index adbf85d33e38..000000000000 --- a/core/modules/menu_link/src/MenuTree.php +++ /dev/null @@ -1,619 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\menu_link\MenuTree. - */ - -namespace Drupal\menu_link; - -use Drupal\Core\Cache\Cache; -use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Database\Connection; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Entity\Query\QueryFactory; -use Drupal\Core\State\StateInterface; -use Drupal\Core\Language\LanguageManagerInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\RequestStack; - -/** - * Provides the default implementation of a menu tree. - */ -class MenuTree implements MenuTreeInterface { - - /** - * The database connection. - * - * @var \Drupal\Core\Database\Connection - * The database connection. - */ - protected $database; - - /** - * The cache backend. - * - * @var \Drupal\Core\Cache\CacheBackendInterface - */ - protected $cache; - - /** - * The language manager. - * - * @var \Drupal\Core\Language\LanguageManagerInterface - */ - protected $languageManager; - - /** - * The request stack. - * - * @var \Symfony\Component\HttpFoundation\RequestStack - */ - protected $requestStack; - - /** - * The menu link storage. - * - * @var \Drupal\menu_link\MenuLinkStorageInterface - */ - protected $menuLinkStorage; - - /** - * The entity query factory. - * - * @var \Drupal\Core\Entity\Query\QueryFactory - */ - protected $queryFactory; - - /** - * The state. - * - * @var \Drupal\Core\State\StateInterface - */ - protected $state; - - /** - * A list of active trail paths keyed by $menu_name. - * - * @var array - */ - protected $trailPaths; - - /** - * Stores the rendered menu output keyed by $menu_name. - * - * @var array - */ - protected $menuOutput; - - /** - * Stores the menu tree used by the doBuildTree method, keyed by a cache ID. - * - * This cache ID is built using the $menu_name, the current language and - * some parameters passed into an entity query. - */ - protected $menuTree; - - /** - * Stores the full menu tree data keyed by a cache ID. - * - * This variable distinct from static::$menuTree by having also items without - * access by the current user. - * - * This cache ID is built with the menu name, a passed in root link ID, the - * current language as well as the maximum depth. - * - * @var array - */ - protected $menuFullTrees; - - /** - * Stores the menu tree data on the current page keyed by a cache ID. - * - * This contains less information than a tree built with buildAllData. - * - * @var array - */ - protected $menuPageTrees; - - /** - * Constructs a new MenuTree. - * - * @param \Drupal\Core\Database\Connection $database - * The database connection. - * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend - * The cache backend. - * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager - * The language manager. - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. - * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query_factory - * The entity query factory. - * @param \Drupal\Core\State\StateInterface $state - * The state. - */ - public function __construct(Connection $database, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, RequestStack $request_stack, EntityManagerInterface $entity_manager, QueryFactory $entity_query_factory, StateInterface $state) { - $this->database = $database; - $this->cache = $cache_backend; - $this->languageManager = $language_manager; - $this->requestStack = $request_stack; - $this->menuLinkStorage = $entity_manager->getStorage('menu_link'); - $this->queryFactory = $entity_query_factory; - $this->state = $state; - } - - /** - * {@inheritdoc} - */ - public function buildAllData($menu_name, $link = NULL, $max_depth = NULL) { - $language_interface = $this->languageManager->getCurrentLanguage(); - - // Use $mlid as a flag for whether the data being loaded is for the whole - // tree. - $mlid = isset($link['mlid']) ? $link['mlid'] : 0; - // Generate a cache ID (cid) specific for this $menu_name, $link, $language, - // and depth. - $cid = 'links:' . $menu_name . ':all:' . $mlid . ':' . $language_interface->id . ':' . (int) $max_depth; - - if (!isset($this->menuFullTrees[$cid])) { - // If the static variable doesn't have the data, check {cache_menu}. - $cache = $this->cache->get($cid); - if ($cache && $cache->data) { - // If the cache entry exists, it contains the parameters for - // menu_build_tree(). - $tree_parameters = $cache->data; - } - // If the tree data was not in the cache, build $tree_parameters. - if (!isset($tree_parameters)) { - $tree_parameters = array( - 'min_depth' => 1, - 'max_depth' => $max_depth, - ); - if ($mlid) { - // The tree is for a single item, so we need to match the values in - // its p columns and 0 (the top level) with the plid values of other - // links. - $parents = array(0); - for ($i = 1; $i < MENU_MAX_DEPTH; $i++) { - if (!empty($link["p$i"])) { - $parents[] = $link["p$i"]; - } - } - $tree_parameters['expanded'] = $parents; - $tree_parameters['active_trail'] = $parents; - $tree_parameters['active_trail'][] = $mlid; - } - - // Cache the tree building parameters using the page-specific cid. - $this->cache->set($cid, $tree_parameters, Cache::PERMANENT, array('menu' => $menu_name)); - } - - // Build the tree using the parameters; the resulting tree will be cached - // by $this->doBuildTree()). - $this->menuFullTrees[$cid] = $this->buildTree($menu_name, $tree_parameters); - } - - return $this->menuFullTrees[$cid]; - } - - /** - * {@inheritdoc} - */ - public function buildPageData($menu_name, $max_depth = NULL, $only_active_trail = FALSE) { - $language_interface = $this->languageManager->getCurrentLanguage(); - - // Load the request corresponding to the current page. - $request = $this->requestStack->getCurrentRequest(); - $system_path = NULL; - if ($route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) { - // @todo https://drupal.org/node/2068471 is adding support so we can tell - // if this is called on a 404/403 page. - $system_path = $request->attributes->get('_system_path'); - $page_not_403 = 1; - } - if (isset($system_path)) { - if (isset($max_depth)) { - $max_depth = min($max_depth, MENU_MAX_DEPTH); - } - // Generate a cache ID (cid) specific for this page. - $cid = 'links:' . $menu_name . ':page:' . $system_path . ':' . $language_interface->id . ':' . $page_not_403 . ':' . (int) $max_depth; - // If we are asked for the active trail only, and $menu_name has not been - // built and cached for this page yet, then this likely means that it - // won't be built anymore, as this function is invoked from - // template_preprocess_page(). So in order to not build a giant menu tree - // that needs to be checked for access on all levels, we simply check - // whether we have the menu already in cache, or otherwise, build a - // minimum tree containing the active trail only. - if (!isset($this->menuPageTrees[$cid]) && $only_active_trail) { - $cid .= ':trail'; - } - - if (!isset($this->menuPageTrees[$cid])) { - // If the static variable doesn't have the data, check {cache_menu}. - $cache = $this->cache->get($cid); - if ($cache && $cache->data) { - // If the cache entry exists, it contains the parameters for - // menu_build_tree(). - $tree_parameters = $cache->data; - } - // If the tree data was not in the cache, build $tree_parameters. - if (!isset($tree_parameters)) { - $tree_parameters = array( - 'min_depth' => 1, - 'max_depth' => $max_depth, - ); - $active_trail = $this->getActiveTrailIds($menu_name); - - // If this page is accessible to the current user, build the tree - // parameters accordingly. - if ($page_not_403) { - // The active trail contains more than only array(0 => 0). - if (count($active_trail) > 1) { - // If we are asked to build links for the active trail only,skip - // the entire 'expanded' handling. - if ($only_active_trail) { - $tree_parameters['only_active_trail'] = TRUE; - } - } - $parents = $active_trail; - - $expanded = $this->state->get('menu_expanded'); - // Check whether the current menu has any links set to be expanded. - if (!$only_active_trail && $expanded && in_array($menu_name, $expanded)) { - // Collect all the links set to be expanded, and then add all of - // their children to the list as well. - do { - $query = $this->queryFactory->get('menu_link') - ->condition('menu_name', $menu_name) - ->condition('expanded', 1) - ->condition('has_children', 1) - ->condition('plid', $parents, 'IN') - ->condition('mlid', $parents, 'NOT IN'); - $result = $query->execute(); - $parents += $result; - } while (!empty($result)); - } - $tree_parameters['expanded'] = $parents; - $tree_parameters['active_trail'] = $active_trail; - } - // If access is denied, we only show top-level links in menus. - else { - $tree_parameters['expanded'] = $active_trail; - $tree_parameters['active_trail'] = $active_trail; - } - // Cache the tree building parameters using the page-specific cid. - $this->cache->set($cid, $tree_parameters, Cache::PERMANENT, array('menu' => $menu_name)); - } - - // Build the tree using the parameters; the resulting tree will be - // cached by $tihs->buildTree(). - $this->menuPageTrees[$cid] = $this->buildTree($menu_name, $tree_parameters); - } - return $this->menuPageTrees[$cid]; - } - - return array(); - } - - /** - * {@inheritdoc} - */ - public function getActiveTrailIds($menu_name) { - // Parent mlids; used both as key and value to ensure uniqueness. - // We always want all the top-level links with plid == 0. - $active_trail = array(0 => 0); - - $request = $this->requestStack->getCurrentRequest(); - - if ($route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) { - // @todo https://drupal.org/node/2068471 is adding support so we can tell - // if this is called on a 404/403 page. - // Check if the active trail has been overridden for this menu tree. - $active_path = $this->getPath($menu_name); - // Find a menu link corresponding to the current path. If - // $active_path is NULL, let menu_link_get_preferred() determine - // the path. - if ($active_link = $this->menuLinkGetPreferred($menu_name, $active_path)) { - if ($active_link['menu_name'] == $menu_name) { - // Use all the coordinates, except the last one because - // there can be no child beyond the last column. - for ($i = 1; $i < MENU_MAX_DEPTH; $i++) { - if ($active_link['p' . $i]) { - $active_trail[$active_link['p' . $i]] = $active_link['p' . $i]; - } - } - } - } - } - return $active_trail; - } - - /** - * {@inheritdoc} - */ - public function setPath($menu_name, $path = NULL) { - if (isset($path)) { - $this->trailPaths[$menu_name] = $path; - } - } - - /** - * {@inheritdoc} - */ - public function getPath($menu_name) { - return isset($this->trailPaths[$menu_name]) ? $this->trailPaths[$menu_name] : NULL; - } - - /** - * {@inheritdoc} - */ - public function renderMenu($menu_name) { - - if (!isset($this->menuOutput[$menu_name])) { - $tree = $this->buildPageData($menu_name); - $this->menuOutput[$menu_name] = $this->renderTree($tree); - } - return $this->menuOutput[$menu_name]; - } - - /** - * {@inheritdoc} - */ - public function renderTree($tree) { - $build = array(); - $items = array(); - $menu_name = $tree ? end($tree)['link']['menu_name'] : ''; - - // Pull out just the menu links we are going to render so that we - // get an accurate count for the first/last classes. - foreach ($tree as $data) { - if ($data['link']['access'] && !$data['link']['hidden']) { - $items[] = $data; - } - } - - foreach ($items as $data) { - $class = array(); - // Set a class for the <li>-tag. Since $data['below'] may contain local - // tasks, only set 'expanded' class if the link also has children within - // the current menu. - if ($data['link']['has_children'] && $data['below']) { - $class[] = 'expanded'; - } - elseif ($data['link']['has_children']) { - $class[] = 'collapsed'; - } - else { - $class[] = 'leaf'; - } - // Set a class if the link is in the active trail. - if ($data['link']['in_active_trail']) { - $class[] = 'active-trail'; - $data['link']['localized_options']['attributes']['class'][] = 'active-trail'; - } - - // Allow menu-specific theme overrides. - $element['#theme'] = 'menu_link__' . strtr($data['link']['menu_name'], '-', '_'); - $element['#attributes']['class'] = $class; - $element['#title'] = $data['link']['title']; - // @todo Use route name and parameters to generate the link path, unless - // it is external. - $element['#href'] = $data['link']['link_path']; - $element['#localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : array(); - $element['#below'] = $data['below'] ? $this->renderTree($data['below']) : $data['below']; - $element['#original_link'] = $data['link']; - // Index using the link's unique mlid. - $build[$data['link']['mlid']] = $element; - } - if ($build) { - // Make sure drupal_render() does not re-order the links. - $build['#sorted'] = TRUE; - // Add the theme wrapper for outer markup. - // Allow menu-specific theme overrides. - $build['#theme_wrappers'][] = 'menu_tree__' . strtr($menu_name, '-', '_'); - // Set cache tag. - $menu_name = $data['link']['menu_name']; - $build['#cache']['tags']['menu'][$menu_name] = $menu_name; - } - - return $build; - } - - /** - * {@inheritdoc} - */ - public function buildTree($menu_name, array $parameters = array()) { - // Build the menu tree. - $tree = $this->doBuildTree($menu_name, $parameters); - // Check access for the current user to each item in the tree. - $this->checkAccess($tree); - return $tree; - } - - /** - * Builds a menu tree. - * - * This function may be used build the data for a menu tree only, for example - * to further massage the data manually before further processing happens. - * MenuTree::checkAccess() needs to be invoked afterwards. - * - * @param string $menu_name - * The name of the menu. - * @param array $parameters - * The parameters passed into static::buildTree() - * - * @see static::buildTree() - */ - protected function doBuildTree($menu_name, array $parameters = array()) { - $language_interface = $this->languageManager->getCurrentLanguage(); - - // Build the cache id; sort parents to prevent duplicate storage and remove - // default parameter values. - if (isset($parameters['expanded'])) { - sort($parameters['expanded']); - } - $tree_cid = 'links:' . $menu_name . ':tree-data:' . $language_interface->id . ':' . hash('sha256', serialize($parameters)); - - // If we do not have this tree in the static cache, check {cache_menu}. - if (!isset($this->menuTree[$tree_cid])) { - $cache = $this->cache->get($tree_cid); - if ($cache && $cache->data) { - $this->menuTree[$tree_cid] = $cache->data; - } - } - - if (!isset($this->menuTree[$tree_cid])) { - $query = $this->queryFactory->get('menu_link'); - for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) { - $query->sort('p' . $i, 'ASC'); - } - $query->condition('menu_name', $menu_name); - if (!empty($parameters['expanded'])) { - $query->condition('plid', $parameters['expanded'], 'IN'); - } - elseif (!empty($parameters['only_active_trail'])) { - $query->condition('mlid', $parameters['active_trail'], 'IN'); - } - $min_depth = (isset($parameters['min_depth']) ? $parameters['min_depth'] : 1); - if ($min_depth != 1) { - $query->condition('depth', $min_depth, '>='); - } - if (isset($parameters['max_depth'])) { - $query->condition('depth', $parameters['max_depth'], '<='); - } - // Add custom query conditions, if any were passed. - if (isset($parameters['conditions'])) { - foreach ($parameters['conditions'] as $column => $value) { - $query->condition($column, $value); - } - } - - // Build an ordered array of links using the query result object. - $links = array(); - if ($result = $query->execute()) { - $links = $this->menuLinkStorage->loadMultiple($result); - } - $active_trail = (isset($parameters['active_trail']) ? $parameters['active_trail'] : array()); - $tree = $this->doBuildTreeData($links, $active_trail, $min_depth); - - // Cache the data, if it is not already in the cache. - $this->cache->set($tree_cid, $tree, Cache::PERMANENT, array('menu' => $menu_name)); - $this->menuTree[$tree_cid] = $tree; - } - - return $this->menuTree[$tree_cid]; - } - - /** - * Sorts the menu tree and recursively checks access for each item. - * - * @param array $tree - * The menu tree you wish to operate on. - */ - protected function checkAccess(&$tree) { - $new_tree = array(); - foreach ($tree as $key => $v) { - $item = &$tree[$key]['link']; - $this->menuLinkTranslate($item); - if ($item['access'] || ($item['in_active_trail'] && strpos($item['href'], '%') !== FALSE)) { - if ($tree[$key]['below']) { - $this->checkAccess($tree[$key]['below']); - } - // The weights are made a uniform 5 digits by adding 50000 as an offset. - // After _menu_link_translate(), $item['title'] has the localized link - // title. Adding the mlid to the end of the index insures that it is - // unique. - $new_tree[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $tree[$key]; - } - } - // Sort siblings in the tree based on the weights and localized titles. - ksort($new_tree); - $tree = $new_tree; - } - - /** - * {@inheritdoc} - */ - public function buildTreeData(array $links, array $parents = array(), $depth = 1) { - $tree = $this->doBuildTreeData($links, $parents, $depth); - $this->checkAccess($tree); - return $tree; - } - - /** - * Prepares the data for calling $this->treeDataRecursive(). - */ - protected function doBuildTreeData(array $links, array $parents = array(), $depth = 1) { - // Reverse the array so we can use the more efficient array_pop() function. - $links = array_reverse($links); - return $this->treeDataRecursive($links, $parents, $depth); - } - - /** - * Builds the data representing a menu tree. - * - * The function is a bit complex because the rendering of a link depends on - * the next menu link. - * - * @param array $links - * A flat array of menu links that are part of the menu. Each array element - * is an associative array of information about the menu link, containing - * the fields from the {menu_links} table, and optionally additional - * information from the {menu_router} table, if the menu item appears in - * both tables. This array must be ordered depth-first. - * See _menu_build_tree() for a sample query. - * @param array $parents - * An array of the menu link ID values that are in the path from the current - * page to the root of the menu tree. - * @param int $depth - * The minimum depth to include in the returned menu tree. - * - * @return array - */ - protected function treeDataRecursive(&$links, $parents, $depth) { - $tree = array(); - while ($item = array_pop($links)) { - // We need to determine if we're on the path to root so we can later build - // the correct active trail. - $item['in_active_trail'] = in_array($item['mlid'], $parents); - // Add the current link to the tree. - $tree[$item['mlid']] = array( - 'link' => $item, - 'below' => array(), - ); - // Look ahead to the next link, but leave it on the array so it's - // available to other recursive function calls if we return or build a - // sub-tree. - $next = end($links); - // Check whether the next link is the first in a new sub-tree. - if ($next && $next['depth'] > $depth) { - // Recursively call doBuildTreeData to build the sub-tree. - $tree[$item['mlid']]['below'] = $this->treeDataRecursive($links, $parents, $next['depth']); - // Fetch next link after filling the sub-tree. - $next = end($links); - } - // Determine if we should exit the loop and return. - if (!$next || $next['depth'] < $depth) { - break; - } - } - return $tree; - } - - /** - * Wraps menu_link_get_preferred(). - */ - protected function menuLinkGetPreferred($menu_name, $active_path) { - return menu_link_get_preferred($active_path, $menu_name); - } - - /** - * Wraps _menu_link_translate(). - */ - protected function menuLinkTranslate(&$item) { - _menu_link_translate($item); - } - -} diff --git a/core/modules/menu_link/src/MenuTreeInterface.php b/core/modules/menu_link/src/MenuTreeInterface.php deleted file mode 100644 index 418f602570c0..000000000000 --- a/core/modules/menu_link/src/MenuTreeInterface.php +++ /dev/null @@ -1,182 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\menu_link\MenuTreeInterface. - */ - -namespace Drupal\menu_link; - -/** - * Defines an interface for trees out of menu links. - */ -interface MenuTreeInterface { - - /** - * Returns a rendered menu tree. - * - * The menu item's LI element is given one of the following classes: - * - expanded: The menu item is showing its submenu. - * - collapsed: The menu item has a submenu which is not shown. - * - leaf: The menu item has no submenu. - * - * @param array $tree - * A data structure representing the tree as returned from menu_tree_data. - * - * @return array - * A structured array to be rendered by drupal_render(). - */ - public function renderTree($tree); - - /** - * Sets the path for determining the active trail of the specified menu tree. - * - * This path will also affect the breadcrumbs under some circumstances. - * Breadcrumbs are built using the preferred link returned by - * menu_link_get_preferred(). If the preferred link is inside one of the menus - * specified in calls to static::setPath(), the preferred link will be - * overridden by the corresponding path returned by static::getPath(). - * - * @param string $menu_name - * The name of the affected menu tree. - * @param string $path - * The path to use when finding the active trail. - */ - public function setPath($menu_name, $path = NULL); - - /** - * Gets the path for determining the active trail of the specified menu tree. - * - * @param string $menu_name - * The menu name of the requested tree. - * - * @return string - * A string containing the path. If no path has been specified with - * static::setPath(), NULL is returned. - */ - public function getPath($menu_name); - - /** - * Gets the active trail IDs of the specified menu tree. - * - * @param string $menu_name - * The menu name of the requested tree. - * - * @return array - * An array containing the active trail: a list of mlids. - */ - public function getActiveTrailIds($menu_name); - - /** - * Sorts and returns the built data representing a menu tree. - * - * @param array $links - * A flat array of menu links that are part of the menu. Each array element - * is an associative array of information about the menu link, containing - * the fields from the {menu_links} table, and optionally additional - * information from the {menu_router} table, if the menu item appears in - * both tables. This array must be ordered depth-first. - * See _menu_build_tree() for a sample query. - * @param array $parents - * An array of the menu link ID values that are in the path from the current - * page to the root of the menu tree. - * @param int $depth - * The minimum depth to include in the returned menu tree. - * - * @return array - * An array of menu links in the form of a tree. Each item in the tree is an - * associative array containing: - * - link: The menu link item from $links, with additional element - * 'in_active_trail' (TRUE if the link ID was in $parents). - * - below: An array containing the sub-tree of this item, where each - * element is a tree item array with 'link' and 'below' elements. This - * array will be empty if the menu item has no items in its sub-tree - * having a depth greater than or equal to $depth. - */ - public function buildTreeData(array $links, array $parents = array(), $depth = 1); - - /** - * Gets the data structure for a named menu tree, based on the current page. - * - * The tree order is maintained by storing each parent in an individual - * field, see http://drupal.org/node/141866 for more. - * - * @param string $menu_name - * The named menu links to return. - * @param int $max_depth - * (optional) The maximum depth of links to retrieve. - * @param bool $only_active_trail - * (optional) Whether to only return the links in the active trail (TRUE) - * instead of all links on every level of the menu link tree (FALSE). - * Defaults to FALSE. - * - * @return array - * An array of menu links, in the order they should be rendered. The array - * is a list of associative arrays -- these have two keys, link and below. - * link is a menu item, ready for theming as a link. Below represents the - * submenu below the link if there is one, and it is a subtree that has the - * same structure described for the top-level array. - */ - public function buildPageData($menu_name, $max_depth = NULL, $only_active_trail = FALSE); - - /** - * Gets the data structure representing a named menu tree. - * - * Since this can be the full tree including hidden items, the data returned - * may be used for generating an an admin interface or a select. - * - * @param string $menu_name - * The named menu links to return - * @param array $link - * A fully loaded menu link, or NULL. If a link is supplied, only the - * path to root will be included in the returned tree - as if this link - * represented the current page in a visible menu. - * @param int $max_depth - * Optional maximum depth of links to retrieve. Typically useful if only one - * or two levels of a sub tree are needed in conjunction with a non-NULL - * $link, in which case $max_depth should be greater than $link['depth']. - * - * @return array - * An tree of menu links in an array, in the order they should be rendered. - */ - public function buildAllData($menu_name, $link = NULL, $max_depth = NULL); - - /** - * Renders a menu tree based on the current path. - * - * @param string $menu_name - * The name of the menu. - * - * @return array - * A structured array representing the specified menu on the current page, - * to be rendered by drupal_render(). - */ - public function renderMenu($menu_name); - - /** - * Builds a menu tree, translates links, and checks access. - * - * @param string $menu_name - * The name of the menu. - * @param array $parameters - * (optional) An associative array of build parameters. Possible keys: - * - expanded: An array of parent link ids to return only menu links that - * are children of one of the plids in this list. If empty, the whole menu - * tree is built, unless 'only_active_trail' is TRUE. - * - active_trail: An array of mlids, representing the coordinates of the - * currently active menu link. - * - only_active_trail: Whether to only return links that are in the active - * trail. This option is ignored, if 'expanded' is non-empty. - * - min_depth: The minimum depth of menu links in the resulting tree. - * Defaults to 1, which is the default to build a whole tree for a menu - * (excluding menu container itself). - * - max_depth: The maximum depth of menu links in the resulting tree. - * - conditions: An associative array of custom database select query - * condition key/value pairs; see _menu_build_tree() for the actual query. - * - * @return array - * A fully built menu tree. - */ - public function buildTree($menu_name, array $parameters = array()); - -} diff --git a/core/modules/menu_link/src/StaticMenuLinks.php b/core/modules/menu_link/src/StaticMenuLinks.php deleted file mode 100644 index d42c5ffbc5d4..000000000000 --- a/core/modules/menu_link/src/StaticMenuLinks.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\Core\Menu\StaticMenuLinks. - */ - -namespace Drupal\menu_link; - -use Drupal\Component\Discovery\YamlDiscovery; -use Drupal\Core\Extension\ModuleHandlerInterface; - -/** - * Provides a service which finds and alters default menu links in yml files. - */ -class StaticMenuLinks { - - /** - * The module handler. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - /** - * Constructs a new StaticMenuLinks. - * - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler. - */ - public function __construct(ModuleHandlerInterface $module_handler) { - $this->moduleHandler = $module_handler; - } - - /** - * Gets the menu links defined in YAML files. - * - * @return array - * An array of default menu links. - */ - public function getLinks() { - $discovery = $this->getDiscovery(); - foreach ($discovery->findAll() as $module => $menu_links) { - foreach ($menu_links as $machine_name => $menu_link) { - $all_links[$machine_name] = $menu_link; - $all_links[$machine_name]['machine_name'] = $machine_name; - $all_links[$machine_name]['module'] = $module; - } - } - - $this->moduleHandler->alter('menu_link_defaults', $all_links); - foreach ($all_links as $machine_name => $menu_link) { - // Set the machine_name to the menu links added dynamically. - if (!isset($menu_link['machine_name'])) { - $all_links[$machine_name]['machine_name'] = $machine_name; - } - // Change the key to match the DB column for now. - $all_links[$machine_name]['link_title'] = $all_links[$machine_name]['title']; - unset($all_links[$machine_name]['title']); - } - - return $all_links; - } - - /** - * Creates a YAML discovery for menu links. - * - * @return \Drupal\Component\Discovery\YamlDiscovery - * An YAML discovery instance. - */ - protected function getDiscovery() { - return new YamlDiscovery('links.menu', $this->moduleHandler->getModuleDirectories()); - } - -} - diff --git a/core/modules/menu_link/tests/src/MenuTreeTest.php b/core/modules/menu_link/tests/src/MenuTreeTest.php deleted file mode 100644 index 747335ad4a61..000000000000 --- a/core/modules/menu_link/tests/src/MenuTreeTest.php +++ /dev/null @@ -1,539 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\menu_link\Tests\MenuTreeTest. - */ - -namespace Drupal\menu_link\Tests { - -use Drupal\Core\Cache\Cache; -use Drupal\Core\Entity\EntityStorageInterface; -use Drupal\Core\Language\Language; -use Drupal\menu_link\MenuTree; -use Drupal\Tests\UnitTestCase; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; - -if (!defined('MENU_MAX_DEPTH')) { - define('MENU_MAX_DEPTH', 9); -} - -/** - * @coversDefaultClass \Drupal\menu_link\MenuTree - * @group menu_link - */ -class MenuTreeTest extends UnitTestCase { - - /** - * The tested menu tree. - * - * @var \Drupal\menu_link\MenuTree|\Drupal\menu_link\Tests\TestMenuTree - */ - protected $menuTree; - - /** - * The mocked database connection. - * - * @var \Drupal\Core\DatabaseConnection|\PHPUnit_Framework_MockObject_MockObject - */ - protected $connection; - - /** - * The mocked cache backend. - * - * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $cacheBackend; - - /** - * The mocked language manager. - * - * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $languageManager; - - /** - * The test request stack. - * - * @var \Symfony\Component\HttpFoundation\RequestStack. - */ - protected $requestStack; - - /** - * The mocked entity manager. - * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $entityManager; - - /** - * The mocked entity query factor.y - * - * @var \Drupal\Core\Entity\Query\QueryFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $entityQueryFactory; - - /** - * The mocked state. - * - * @var \Drupal\Core\State\StateInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $state; - - /** - * Stores some default values for a menu link. - * - * @var array - */ - protected $defaultMenuLink = array( - 'menu_name' => 'main-menu', - 'mlid' => 1, - 'title' => 'Example 1', - 'route_name' => 'example1', - 'link_path' => 'example1', - 'access' => 1, - 'hidden' => FALSE, - 'has_children' => FALSE, - 'in_active_trail' => TRUE, - 'localized_options' => array('attributes' => array('title' => '')), - 'weight' => 0, - ); - - /** - * {@inheritdoc} - */ - protected function setUp() { - $this->connection = $this->getMockBuilder('Drupal\Core\Database\Connection') - ->disableOriginalConstructor() - ->getMock(); - $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); - $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); - $this->requestStack = new RequestStack(); - $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); - $this->entityQueryFactory = $this->getMockBuilder('Drupal\Core\Entity\Query\QueryFactory') - ->disableOriginalConstructor() - ->getMock(); - $this->state = $this->getMock('Drupal\Core\State\StateInterface'); - - $this->menuTree = new TestMenuTree($this->connection, $this->cacheBackend, $this->languageManager, $this->requestStack, $this->entityManager, $this->entityQueryFactory, $this->state); - } - - /** - * Tests active paths. - * - * @covers ::setPath - * @covers ::getPath - */ - public function testActivePaths() { - $this->assertNull($this->menuTree->getPath('test_menu1')); - - $this->menuTree->setPath('test_menu1', 'example_path1'); - $this->assertEquals('example_path1', $this->menuTree->getPath('test_menu1')); - $this->assertNull($this->menuTree->getPath('test_menu2')); - - $this->menuTree->setPath('test_menu2', 'example_path2'); - $this->assertEquals('example_path1', $this->menuTree->getPath('test_menu1')); - $this->assertEquals('example_path2', $this->menuTree->getPath('test_menu2')); - } - - /** - * Tests buildTreeData with a single level. - * - * @covers ::buildTreeData - * @covers ::doBuildTreeData - */ - public function testBuildTreeDataWithSingleLevel() { - $items = array(); - $items[] = array( - 'mlid' => 1, - 'depth' => 1, - 'weight' => 0, - 'title' => '', - 'route_name' => 'example1', - 'access' => TRUE, - ); - $items[] = array( - 'mlid' => 2, - 'depth' => 1, - 'weight' => 0, - 'title' => '', - 'route_name' => 'example2', - 'access' => TRUE, - ); - - $result = $this->menuTree->buildTreeData($items, array(), 1); - - $this->assertCount(2, $result); - $result1 = array_shift($result); - $this->assertEquals($items[0] + array('in_active_trail' => FALSE), $result1['link']); - $result2 = array_shift($result); - $this->assertEquals($items[1] + array('in_active_trail' => FALSE), $result2['link']); - } - - /** - * Tests buildTreeData with a single level and one item being active. - * - * @covers ::buildTreeData - * @covers ::doBuildTreeData - */ - public function testBuildTreeDataWithSingleLevelAndActiveItem() { - $items = array(); - $items[] = array( - 'mlid' => 1, - 'depth' => 1, - 'weight' => 0, - 'title' => '', - 'route_name' => 'example1', - 'access' => TRUE, - ); - $items[] = array( - 'mlid' => 2, - 'depth' => 1, - 'weight' => 0, - 'title' => '', - 'route_name' => 'example2', - 'access' => TRUE, - ); - - $result = $this->menuTree->buildTreeData($items, array(1), 1); - - $this->assertCount(2, $result); - $result1 = array_shift($result); - $this->assertEquals($items[0] + array('in_active_trail' => TRUE), $result1['link']); - $result2 = array_shift($result); - $this->assertEquals($items[1] + array('in_active_trail' => FALSE), $result2['link']); - } - - /** - * Tests buildTreeData with a single level and none item being active. - * - * @covers ::buildTreeData - * @covers ::doBuildTreeData - */ - public function testBuildTreeDataWithSingleLevelAndNoActiveItem() { - $items = array(); - $items[] = array( - 'mlid' => 1, - 'depth' => 1, - 'weight' => 0, - 'title' => '', - 'route_name' => 'example1', - 'access' => TRUE, - ); - $items[] = array( - 'mlid' => 2, - 'depth' => 1, - 'weight' => 0, - 'title' => '', - 'route_name' => 'example2', - 'access' => TRUE, - ); - - $result = $this->menuTree->buildTreeData($items, array(3), 1); - - $this->assertCount(2, $result); - $result1 = array_shift($result); - $this->assertEquals($items[0] + array('in_active_trail' => FALSE), $result1['link']); - $result2 = array_shift($result); - $this->assertEquals($items[1] + array('in_active_trail' => FALSE), $result2['link']); - } - - /** - * Tests buildTreeData with a more complex example. - * - * @covers ::buildTreeData - * @covers ::doBuildTreeData - */ - public function testBuildTreeWithComplexData() { - $items = array( - 1 => array('mlid' => 1, 'depth' => 1, 'route_name' => 'example1', 'access' => TRUE, 'weight' => 0, 'title' => ''), - 2 => array('mlid' => 2, 'depth' => 1, 'route_name' => 'example2', 'access' => TRUE, 'weight' => 0, 'title' => ''), - 3 => array('mlid' => 3, 'depth' => 2, 'route_name' => 'example3', 'access' => TRUE, 'weight' => 0, 'title' => ''), - 4 => array('mlid' => 4, 'depth' => 3, 'route_name' => 'example4', 'access' => TRUE, 'weight' => 0, 'title' => ''), - 5 => array('mlid' => 5, 'depth' => 1, 'route_name' => 'example5', 'access' => TRUE, 'weight' => 0, 'title' => ''), - ); - - $tree = $this->menuTree->buildTreeData($items); - - // Validate that parent items #1, #2, and #5 exist on the root level. - $this->assertEquals($items[1]['mlid'], $tree['50000 1']['link']['mlid']); - $this->assertEquals($items[2]['mlid'], $tree['50000 2']['link']['mlid']); - $this->assertEquals($items[5]['mlid'], $tree['50000 5']['link']['mlid']); - - // Validate that child item #4 exists at the correct location in the hierarchy. - $this->assertEquals($items[4]['mlid'], $tree['50000 2']['below']['50000 3']['below']['50000 4']['link']['mlid']); - } - - /** - * Tests getActiveTrailIds(). - * - * @covers ::getActiveTrailIds() - */ - public function testGetActiveTrailIds() { - $menu_link = array( - 'mlid' => 10, - 'route_name' => 'example1', - 'p1' => 3, - 'p2' => 2, - 'p3' => 1, - 'p4' => 4, - 'p5' => 9, - 'p6' => 5, - 'p7' => 6, - 'p8' => 7, - 'p9' => 8, - 'menu_name' => 'test_menu' - ); - $this->menuTree->setPreferredMenuLink('test_menu', 'test/path', $menu_link); - $request = (new Request()); - $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'test_route'); - $this->requestStack->push($request); - $this->menuTree->setPath('test_menu', 'test/path'); - - $trail = $this->menuTree->getActiveTrailIds('test_menu'); - $this->assertEquals(array(0 => 0, 3 => 3, 2 => 2, 1 => 1, 4 => 4, 9 => 9, 5 => 5, 6 => 6, 7 => 7), $trail); - } - - /** - * Tests getActiveTrailIds() without preferred link. - * - * @covers ::getActiveTrailIds() - */ - public function testGetActiveTrailIdsWithoutPreferredLink() { - $request = (new Request()); - $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'test_route'); - $this->requestStack->push($request); - $this->menuTree->setPath('test_menu', 'test/path'); - - $trail = $this->menuTree->getActiveTrailIds('test_menu'); - $this->assertEquals(array(0 => 0), $trail); - } - - - /** - * Tests buildTree with simple menu_name and no parameters. - */ - public function testBuildTreeWithoutParameters() { - $language = new Language(array('id' => 'en')); - $this->languageManager->expects($this->any()) - ->method('getCurrentLanguage') - ->will($this->returnValue($language)); - - // Setup query and the query result. - $query = $this->getMock('Drupal\Core\Entity\Query\QueryInterface'); - $this->entityQueryFactory->expects($this->once()) - ->method('get') - ->with('menu_link') - ->will($this->returnValue($query)); - $query->expects($this->once()) - ->method('condition') - ->with('menu_name', 'test_menu'); - $query->expects($this->once()) - ->method('execute') - ->will($this->returnValue(array(1, 2, 3))); - - $storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface'); - $base = array( - 'access' => TRUE, - 'weight' => 0, - 'title' => 'title', - ); - $menu_link = $base + array( - 'mlid' => 1, - 'p1' => 3, - 'p2' => 2, - 'p3' => 1, - ); - $links[1] = $menu_link; - $menu_link = $base + array( - 'mlid' => 3, - 'p1' => 3, - 'depth' => 1, - ); - $links[3] = $menu_link; - $menu_link = $base + array( - 'mlid' => 2, - 'p1' => 3, - 'p2' => 2, - 'depth' => 2, - ); - $links[2] = $menu_link; - $storage->expects($this->once()) - ->method('loadMultiple') - ->with(array(1, 2, 3)) - ->will($this->returnValue($links)); - $this->menuTree->setStorage($storage); - - // Ensure that static/non static caching works. - // First setup no working caching. - $this->cacheBackend->expects($this->at(0)) - ->method('get') - ->with('links:test_menu:tree-data:en:35786c7117b4e38d0f169239752ce71158266ae2f6e4aa230fbbb87bd699c0e3') - ->will($this->returnValue(FALSE)); - $this->cacheBackend->expects($this->at(1)) - ->method('set') - ->with('links:test_menu:tree-data:en:35786c7117b4e38d0f169239752ce71158266ae2f6e4aa230fbbb87bd699c0e3', $this->anything(), Cache::PERMANENT, array('menu' => 'test_menu')); - - // Ensure that the static caching triggered. - $this->cacheBackend->expects($this->exactly(1)) - ->method('get'); - - $this->menuTree->buildTree('test_menu'); - $this->menuTree->buildTree('test_menu'); - } - - /** - * Tests the output with a single level. - * - * @covers ::renderTree - */ - public function testOutputWithSingleLevel() { - $tree = array( - '1' => array( - 'link' => array('mlid' => 1) + $this->defaultMenuLink, - 'below' => array(), - ), - '2' => array( - 'link' => array('mlid' => 2) + $this->defaultMenuLink, - 'below' => array(), - ), - ); - - $output = $this->menuTree->renderTree($tree); - - // Validate that the - in main-menu is changed into an underscore - $this->assertEquals($output['1']['#theme'], 'menu_link__main_menu', 'Hyphen is changed to an underscore on menu_link'); - $this->assertEquals($output['2']['#theme'], 'menu_link__main_menu', 'Hyphen is changed to an underscore on menu_link'); - $this->assertEquals($output['#theme_wrappers'][0], 'menu_tree__main_menu', 'Hyphen is changed to an underscore on menu_tree wrapper'); - } - - /** - * Tests the output method with a complex example. - * - * @covers ::renderTree - */ - public function testOutputWithComplexData() { - $tree = array( - '1'=> array( - 'link' => array('mlid' => 1, 'has_children' => 1, 'title' => 'Item 1', 'link_path' => 'a') + $this->defaultMenuLink, - 'below' => array( - '2' => array('link' => array('mlid' => 2, 'title' => 'Item 2', 'link_path' => 'a/b') + $this->defaultMenuLink, - 'below' => array( - '3' => array('link' => array('mlid' => 3, 'title' => 'Item 3', 'in_active_trail' => 0, 'link_path' => 'a/b/c') + $this->defaultMenuLink, - 'below' => array()), - '4' => array('link' => array('mlid' => 4, 'title' => 'Item 4', 'in_active_trail' => 0, 'link_path' => 'a/b/d') + $this->defaultMenuLink, - 'below' => array()) - ) - ) - ) - ), - '5' => array('link' => array('mlid' => 5, 'hidden' => 1, 'title' => 'Item 5', 'link_path' => 'e') + $this->defaultMenuLink, 'below' => array()), - '6' => array('link' => array('mlid' => 6, 'title' => 'Item 6', 'in_active_trail' => 0, 'access' => 0, 'link_path' => 'f') + $this->defaultMenuLink, 'below' => array()), - '7' => array('link' => array('mlid' => 7, 'title' => 'Item 7', 'in_active_trail' => 0, 'link_path' => 'g') + $this->defaultMenuLink, 'below' => array()) - ); - - $output = $this->menuTree->renderTree($tree); - - // Looking for child items in the data - $this->assertEquals( $output['1']['#below']['2']['#href'], 'a/b', 'Checking the href on a child item'); - $this->assertTrue(in_array('active-trail', $output['1']['#below']['2']['#attributes']['class']), 'Checking the active trail class'); - // Validate that the hidden and no access items are missing - $this->assertFalse(isset($output['5']), 'Hidden item should be missing'); - $this->assertFalse(isset($output['6']), 'False access should be missing'); - // Item 7 is after a couple hidden items. Just to make sure that 5 and 6 are - // skipped and 7 still included. - $this->assertTrue(isset($output['7']), 'Item after hidden items is present'); - } - - /** - * Tests menu tree access check with a single level. - * - * @covers ::checkAccess - */ - public function testCheckAccessWithSingleLevel() { - $items = array( - array('mlid' => 1, 'route_name' => 'menu_test_1', 'depth' => 1, 'link_path' => 'menu_test/test_1', 'in_active_trail' => FALSE) + $this->defaultMenuLink, - array('mlid' => 2, 'route_name' => 'menu_test_2', 'depth' => 1, 'link_path' => 'menu_test/test_2', 'in_active_trail' => FALSE) + $this->defaultMenuLink, - ); - - // Register a menuLinkTranslate to mock the access. - $this->menuTree->menuLinkTranslateCallable = function(&$item) { - $item['access'] = $item['mlid'] == 1; - }; - - // Build the menu tree and check access for all of the items. - $tree = $this->menuTree->buildTreeData($items); - - $this->assertCount(1, $tree); - $item = reset($tree); - $this->assertEquals($items[0], $item['link']); - } - -} - -class TestMenuTree extends MenuTree { - - /** - * An alternative callable used for menuLinkTranslate. - * @var callable - */ - public $menuLinkTranslateCallable; - - /** - * Stores the preferred menu link per menu and path. - * - * @var array - */ - protected $preferredMenuLink; - - /** - * {@inheritdoc} - */ - protected function menuLinkTranslate(&$item) { - if (isset($this->menuLinkTranslateCallable)) { - call_user_func_array($this->menuLinkTranslateCallable, array(&$item)); - } - } - - /** - * {@inheritdoc} - */ - protected function menuLinkGetPreferred($menu_name, $active_path) { - return isset($this->preferredMenuLink[$menu_name][$active_path]) ? $this->preferredMenuLink[$menu_name][$active_path] : NULL; - } - - /** - * Set the storage. - * - * @param \Drupal\Core\Entity\EntityStorageInterface $storage - * The menu link storage. - */ - public function setStorage(EntityStorageInterface $storage) { - $this->menuLinkStorage = $storage; - } - - /** - * Sets the preferred menu link. - * - * @param string $menu_name - * The menu name. - * @param string $active_path - * The active path. - * @param array $menu_link - * The preferred menu link. - */ - public function setPreferredMenuLink($menu_name, $active_path, $menu_link) { - $this->preferredMenuLink[$menu_name][$active_path] = $menu_link; - } - -} - -} - -namespace { - if (!defined('MENU_MAX_DEPTH')) { - define('MENU_MAX_DEPTH', 9); - } -} diff --git a/core/modules/views/src/Plugin/views/sort/MenuHierarchy.php b/core/modules/views/src/Plugin/views/sort/MenuHierarchy.php deleted file mode 100644 index 2d8a5fc48f9d..000000000000 --- a/core/modules/views/src/Plugin/views/sort/MenuHierarchy.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -/** - * @file - * Definition of Drupal\views\Plugin\views\sort\MenuHierarchy. - */ - -namespace Drupal\views\Plugin\views\sort; - -use Drupal\views\Views; - -/** - * Sort in menu hierarchy order. - * - * Given a field name of 'p' this produces an ORDER BY on p1, p2, ..., p9; - * and optionally injects multiple joins to {menu_links} to sort by weight - * and title as well. - * - * This is only really useful for the {menu_links} table. - * - * @ViewsSort("menu_hierarchy") - */ -class MenuHierarchy extends SortPluginBase { - - protected function defineOptions() { - $options = parent::defineOptions(); - $options['sort_within_level'] = array('default' => FALSE); - return $options; - } - - public function buildOptionsForm(&$form, &$form_state) { - parent::buildOptionsForm($form, $form_state); - $form['sort_within_level'] = array( - '#type' => 'checkbox', - '#title' => t('Sort within each hierarchy level'), - '#description' => t('Enable this to sort the items within each level of the hierarchy by weight and title. Warning: this may produce a slow query.'), - '#default_value' => $this->options['sort_within_level'], - ); - } - - public function query() { - $this->ensureMyTable(); - $max_depth = isset($this->definition['max depth']) ? $this->definition['max depth'] : MENU_MAX_DEPTH; - for ($i = 1; $i <= $max_depth; ++$i) { - if ($this->options['sort_within_level']) { - $definition = array( - 'table' => 'menu_links', - 'field' => 'mlid', - 'left_table' => $this->tableAlias, - 'left_field' => $this->field . $i - ); - $join = Views::pluginManager('join')->createInstance('standard', $definition); - - $menu_links = $this->query->addTable('menu_links', NULL, $join); - $this->query->addOrderBy($menu_links, 'weight', $this->options['order']); - $this->query->addOrderBy($menu_links, 'link_title', $this->options['order']); - } - - // We need this even when also sorting by weight and title, to make sure - // that children of two parents with the same weight and title are - // correctly separated. - $this->query->addOrderBy($this->tableAlias, $this->field . $i, $this->options['order']); - } - } - -} -- GitLab