2002-12-24 15:40:32 +00:00
< ? php
2003-12-27 21:29:20 +00:00
2004-07-22 16:06:54 +00:00
/**
* @ file
* API for the Drupal menu system .
*/
2013-10-07 05:34:09 +00:00
use Drupal\Component\Utility\String ;
2014-03-31 17:37:55 +00:00
use Drupal\Core\Render\Element ;
2012-09-04 13:32:47 +00:00
use Drupal\Core\Template\Attribute ;
2003-12-17 22:15:35 +00:00
/**
2013-10-18 04:08:20 +00:00
* @ defgroup menu Menu and routing system
2003-12-17 22:15:35 +00:00
* @ {
2004-09-09 05:51:08 +00:00
* Define the navigation menus , and route page requests to code based on URLs .
2004-07-10 18:10:36 +00:00
*
2014-07-02 19:31:15 +00:00
* @ section sec_overview Overview and terminology
* The Drupal routing system defines how Drupal responds to URL requests that
* the web server passes on to Drupal . The routing system is based on the
* @ link http :// symfony . com Symfony framework . @ endlink The central idea is
* that Drupal subsystems and modules can register routes ( basically , URL
* paths and context ); they can also register to respond dynamically to
* routes , for more flexibility . When Drupal receives a URL request , it will
* attempt to match the request to a registered route , and query dynamic
* responders . If a match is made , Drupal will then instantiate the required
* classes , gather the data , format it , and send it back to the web browser .
* Otherwise , Drupal will return a 404 or 403 response .
*
* The menu system uses routes ; it is used for navigation menus , local tasks ,
* local actions , and contextual links :
* - Navigation menus are hierarchies of menu links ; links point to routes or
* URLs .
* - Menu links and their hierarchies can be defined by Drupal subsystems
* and modules , or created in the user interface using the Menu UI module .
* - Local tasks are groups of related routes . Local tasks are usually rendered
* as a group of tabs .
* - Local actions are used for operations such as adding a new item on a page
* that lists items of some type . Local actions are usually rendered as
* buttons .
* - Contextual links are actions that are related to sections of rendered
* output , and are usually rendered as a pop - up list of links . The
* Contextual Links module handles the gathering and rendering of contextual
* links .
*
* The following sections of this topic provide an overview of the routing and
* menu APIs . For more detailed information , see
* https :// www . drupal . org / developing / api / 8 / routing and
* https :// www . drupal . org / developing / api / 8 / menu
*
* @ section sec_register Registering simple routes
* To register a route , add lines similar to this to a module_name . routing . yml
* file in your top - level module directory :
* @ code
* dblog . overview :
* path : '/admin/reports/dblog'
* defaults :
* _content : '\Drupal\dblog\Controller\DbLogController::overview'
* _title : 'Recent log messages'
* requirements :
* _permission : 'access site reports'
* @ endcode
* Some notes :
* - The first line is the machine name of the route . Typically , it is prefixed
* by the machine name of the module that defines the route , or the name of
* a subsystem .
* - The 'path' line gives the URL path of the route ( relative to the site ' s
* base URL ) .
* - The 'defaults' section tells how to build the main content of the route ,
* and can also give other information , such as the page title and additional
* arguments for the route controller method . There are several possibilities
* for how to build the main content , including :
* - _content : A callable , usually a method on a page controller class
* ( see @ ref sec_controller below for details ) .
* - _controller : A callable , usually a method on a page controller class
* ( see @ ref sec_controller below for details ) .
* - _form : A form controller class . See the
* @ link form_api Form API topic @ endlink for more information about
* form controllers .
* - _entity_form : A form for editing an entity . See the
* @ link entity_api Entity API topic @ endlink for more information .
* - The 'requirements' section is used in Drupal to give access permission
* instructions ( it has other uses in the Symfony framework ) . Most
* routes have a simple permission - based access scheme , as shown in this
* example . See the @ link user_api Permission system topic @ endlink for
* more information about permissions .
*
* See https :// www . drupal . org / node / 2092643 for more details about *. routing . yml
* files , and https :// www . drupal . org / node / 2122201 for information on how to
* set up dynamic routes .
*
* @ section sec_placeholders Defining routes with placeholders
* Some routes have placeholders in them , and these can also be defined in a
* module_name . routing . yml file , as in this example from the Block module :
2013-10-18 04:08:20 +00:00
* @ code
2014-07-02 19:31:15 +00:00
* block . admin_edit :
* path : '/admin/structure/block/manage/{block}'
2013-10-18 04:08:20 +00:00
* defaults :
2014-07-02 19:31:15 +00:00
* _entity_form : 'block.default'
* _title : 'Configure block'
2013-10-18 04:08:20 +00:00
* requirements :
2014-07-02 19:31:15 +00:00
* _entity_access : 'block.update'
* @ endcode
* In the path , '{block}' is a placeholder - it will be replaced by the
* ID of the block that is being configured by the entity system . See the
* @ link entity_api Entity API topic @ endlink for more information .
*
* @ section sec_controller Route controllers for simple routes
* For simple routes , after you have defined the route in a *. routing . yml file
* ( see @ ref sec_register above ), the next step is to define a page controller
* class and method . Page controller classes do not necessarily need to
* implement any particular interface or extend any particular base class . The
* only requirement is that the method specified in your *. routing . yml file
* return one of the following , depending on whether you specified _content or
* _controller in the routing file defaults section :
* - A render array ( see the
* @ link theme_render Theme and render topic @ endlink for more information ),
* if _content is used in the routing file .
* - A \Drupal\Core\Page\HtmlFragmentInterface object ( fragment or page ), if
* _content is used in the routing file .
* - A \Symfony\Component\HttpFoundation\Response object , if _controller is
* used in the routing file .
* As a note , if your module registers multiple simple routes , it is usual
* ( and usually easiest ) to put all of their methods on one controller class .
*
* Most controllers will need to display some information stored in the Drupal
* database , which will involve using one or more Drupal services ( see the
* @ link container Services and container topic @ endlink ) . In order to properly
* inject services , a controller should implement
* \Drupal\Core\DependencyInjection\ContainerInjectionInterface ; simple
* controllers can do this by extending the
* \Drupal\Core\Controller\ControllerBase class . See
* \Drupal\dblog\Controller\DbLogController for a straightforward example of
* a controller class .
*
* @ section sec_links Defining menu links for the administrative menu
* Routes for administrative tasks can be added to the main Drupal
* administrative menu hierarchy . To do this , add lines like the following to a
* module_name . menu_links . yml file ( in the top - level directory for your module ) :
* @ code
* dblog . overview :
* title : 'Recent log messages'
* parent : system . admin_reports
* description : 'View events that have recently been logged.'
* route_name : dblog . overview
* weight : - 1
* @ endcode
* Some notes :
* - The first line is the machine name for your menu link , which usually
* matches the machine name of the route ( given in the 'route_name' line ) .
* - parent : The machine name of the menu link that is the parent in the
* administrative hierarchy . See system . menu_links . yml to find the main
* skeleton of the hierarchy .
* - weight : Lower ( negative ) numbers come before higher ( positive ) numbers ,
* for menu items with the same parent .
*
* Menu items from other modules can be altered using
* hook_menu_link_defaults_alter () .
*
* @ todo Derivatives will probably be defined for these ; when they are , add
* documentation here .
*
* @ section sec_tasks Defining groups of local tasks ( tabs )
* Local tasks appear as tabs on a page when there are at least two defined for
* a route , including the base route as the main tab , and additional routes as
* other tabs . Static local tasks can be defined by adding lines like the
* following to a module_name . local_tasks . yml file ( in the top - level directory
* for your module ) :
* @ code
* book . admin :
* route_name : book . admin
* title : 'List'
* base_route : book . admin
* book . settings :
* route_name : book . settings
* title : 'Settings'
* base_route : book . admin
* weight : 100
2013-10-18 04:08:20 +00:00
* @ endcode
2014-07-02 19:31:15 +00:00
* Some notes :
* - The first line is the machine name for your local task , which usually
* matches the machine name of the route ( given in the 'route_name' line ) .
* - base_route : The machine name of the main task ( tab ) for the set of local
* tasks .
* - weight : Lower ( negative ) numbers come before higher ( positive ) numbers ,
* for tasks on the same base route . If there is a tab whose route
* matches the base route , that will be the default / first tab shown .
*
* Local tasks from other modules can be altered using
* hook_menu_local_tasks_alter () .
*
* @ todo Derivatives are in flux for these ; when they are more stable , add
* documentation here .
*
* @ section sec_actions Defining local actions for routes
* Local actions can be defined for operations related to a given route . For
* instance , adding content is a common operation for the content management
* page , so it should be a local action . Static local actions can be
* defined by adding lines like the following to a
* module_name . local_actions . yml file ( in the top - level directory for your
* module ) :
* @ code
* node . add_page :
* route_name : node . add_page
* title : 'Add content'
* appears_on :
* - system . admin_content
* @ endcode
* Some notes :
* - The first line is the machine name for your local action , which usually
* matches the machine name of the route ( given in the 'route_name' line ) .
* - appears_on : Machine names of one or more routes that this local task
* should appear on .
*
* Local actions from other modules can be altered using
* hook_menu_local_actions_alter () .
*
* @ todo Derivatives are in flux for these ; when they are more stable , add
* documentation here .
*
* @ section sec_contextual Defining contextual links
* Contextual links are displayed by the Contextual Links module for user
* interface elements whose render arrays have a '#contextual_links' element
* defined . For example , a block render array might look like this , in part :
* @ code
* array (
* '#contextual_links' => array (
* 'block' => array (
* 'route_parameters' => array ( 'block' => $entity -> id ()),
* ),
* ),
* @ endcode
* In this array , the outer key 'block' defines a " group " for contextual
* links , and the inner array provides values for the route ' s placeholder
* parameters ( see @ ref sec_placeholders above ) .
*
* To declare that a defined route should be a contextual link for a
* contextual links group , put lines like the following in a
* module_name . contextual_links . yml file ( in the top - level directory for your
* module ) :
* @ code
* block_configure :
* title : 'Configure block'
* route_name : 'block.admin_edit'
* group : 'block'
* @ endcode
* Some notes :
* - The first line is the machine name for your contextual link , which usually
* matches the machine name of the route ( given in the 'route_name' line ) .
* - group : This needs to match the link group defined in the render array .
*
* Contextual links from other modules can be altered using
* hook_contextual_links_alter () .
*
* @ todo Derivatives are in flux for these ; when they are more stable , add
* documentation here .
2003-12-17 22:15:35 +00:00
*/
2007-04-06 04:39:51 +00:00
/**
2007-05-16 13:45:17 +00:00
* The maximum depth of a menu links tree - matches the number of p columns .
2013-02-08 23:55:25 +00:00
*
2014-03-27 11:54:40 +00:00
* @ todo Move this constant to MenuLinkStorage along with all the tree
2013-02-08 23:55:25 +00:00
* functionality .
2007-04-06 04:39:51 +00:00
*/
2011-11-29 09:56:53 +00:00
const MENU_MAX_DEPTH = 9 ;
2007-05-16 13:45:17 +00:00
2011-12-06 12:18:12 +00:00
/**
* Reserved key to identify the most specific menu link for a given path .
*
* The value of this constant is a hash of the constant name . We use the hash
* so that the reserved key is over 32 characters in length and will not
* collide with allowed menu names :
* @ code
* sha1 ( 'MENU_PREFERRED_LINK' ) = 1 cf698d64d1aa4b83907cf6ed55db3a7f8e92c91
* @ endcode
*
* @ see menu_link_get_preferred ()
*/
const MENU_PREFERRED_LINK = '1cf698d64d1aa4b83907cf6ed55db3a7f8e92c91' ;
2004-04-15 20:49:42 +00:00
/**
2014-03-01 04:58:58 +00:00
* Localizes a menu link title using t () if possible .
2008-01-28 19:09:19 +00:00
*
2008-01-10 20:04:19 +00:00
* 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
2014-03-01 04:58:58 +00:00
* A menu link entity .
*/
function _menu_item_localize ( & $item ) {
2014-02-26 10:53:29 +00:00
// Allow default menu links to be translated.
2008-02-04 12:07:23 +00:00
$item [ 'localized_options' ] = $item [ 'options' ];
2009-09-18 10:54:20 +00:00
// 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' ]);
}
2014-03-01 04:58:58 +00:00
// 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' ]);
2013-05-02 04:09:48 +00:00
}
else {
2014-03-01 04:58:58 +00:00
$item [ 'title' ] = $item [ 'link_title' ];
2007-05-16 13:45:17 +00:00
}
}
/**
2014-01-29 08:25:00 +00:00
* Provides menu link unserializing , access control , and argument handling .
2012-08-31 15:56:36 +00:00
*
2014-01-29 08:25:00 +00:00
* @ 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 ) {
2010-09-24 00:37:45 +00:00
if ( ! is_array ( $item [ 'options' ])) {
2014-01-29 08:25:00 +00:00
$item [ 'options' ] = ( array ) unserialize ( $item [ 'options' ]);
2010-09-24 00:37:45 +00:00
}
2014-01-29 08:25:00 +00:00
$item [ 'localized_options' ] = $item [ 'options' ];
$item [ 'title' ] = $item [ 'link_title' ];
if ( $item [ 'external' ] || empty ( $item [ 'route_name' ])) {
2007-05-27 20:31:13 +00:00
$item [ 'access' ] = 1 ;
$item [ 'href' ] = $item [ 'link_path' ];
2014-01-29 08:25:00 +00:00
$item [ 'route_parameters' ] = array ();
// Set to NULL so that drupal_pre_render_link() is certain to skip it.
$item [ 'route_name' ] = NULL ;
2007-05-16 13:45:17 +00:00
}
else {
2014-01-29 08:25:00 +00:00
$item [ 'href' ] = NULL ;
if ( ! is_array ( $item [ 'route_parameters' ])) {
$item [ 'route_parameters' ] = ( array ) unserialize ( $item [ 'route_parameters' ]);
2007-05-16 13:45:17 +00:00
}
2007-07-04 15:49:44 +00:00
// menu_tree_check_access() may set this ahead of time for links to nodes.
2007-05-27 20:31:13 +00:00
if ( ! isset ( $item [ 'access' ])) {
2014-01-29 08:25:00 +00:00
$item [ 'access' ] = \Drupal :: getContainer () -> get ( 'access_manager' ) -> checkNamedRoute ( $item [ 'route_name' ], $item [ 'route_parameters' ], \Drupal :: currentUser ());
2007-05-16 13:45:17 +00:00
}
2008-06-12 20:49:39 +00:00
// For performance, don't localize a link the user can't access.
if ( $item [ 'access' ]) {
2014-03-01 04:58:58 +00:00
_menu_item_localize ( $item );
2008-06-12 20:49:39 +00:00
}
2007-05-16 13:45:17 +00:00
}
2008-02-06 19:52:54 +00:00
2008-02-06 19:41:24 +00:00
// 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' ])) {
2014-02-24 10:10:52 +00:00
\Drupal :: moduleHandler () -> alter ( 'translated_menu_link' , $item , $map );
2008-02-06 19:41:24 +00:00
}
2003-09-26 10:04:09 +00:00
}
2004-06-18 15:04:37 +00:00
/**
2012-10-05 18:12:56 +00:00
* Implements template_preprocess_HOOK () for theme_menu_tree () .
2004-06-18 15:04:37 +00:00
*/
2009-09-18 10:54:20 +00:00
function template_preprocess_menu_tree ( & $variables ) {
$variables [ 'tree' ] = $variables [ 'tree' ][ '#children' ];
2004-06-18 15:04:37 +00:00
}
2003-09-28 11:08:17 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a wrapper for a menu sub - tree .
2007-12-06 09:58:34 +00:00
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
2010-04-13 15:23:03 +00:00
* - tree : An HTML string containing the tree ' s items .
2009-10-09 01:00:08 +00:00
*
2010-04-13 15:23:03 +00:00
* @ see template_preprocess_menu_tree ()
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2003-12-08 06:32:19 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_menu_tree ( $variables ) {
return '<ul class="menu">' . $variables [ 'tree' ] . '</ul>' ;
2007-03-12 13:01:10 +00:00
}
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a menu link and submenu .
2007-12-06 09:58:34 +00:00
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
* - element : Structured array data for a menu link .
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2009-09-18 10:54:20 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_menu_link ( array $variables ) {
$element = $variables [ 'element' ];
2009-09-18 10:54:20 +00:00
$sub_menu = '' ;
if ( $element [ '#below' ]) {
$sub_menu = drupal_render ( $element [ '#below' ]);
2007-05-27 20:31:13 +00:00
}
2014-01-23 18:04:41 +00:00
$element [ '#localized_options' ][ 'set_active_class' ] = TRUE ;
2009-09-18 10:54:20 +00:00
$output = l ( $element [ '#title' ], $element [ '#href' ], $element [ '#localized_options' ]);
2012-09-04 13:32:47 +00:00
return '<li' . new Attribute ( $element [ '#attributes' ]) . '>' . $output . $sub_menu . " </li> \n " ;
2007-02-11 09:30:51 +00:00
}
2007-07-04 15:49:44 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a single local task link .
2007-12-06 09:58:34 +00:00
*
2009-10-09 01:00:08 +00:00
* @ param $variables
* An associative array containing :
2010-04-13 15:23:03 +00:00
* - element : A render element containing :
* - #link: A menu link array with 'title', 'href', and 'localized_options'
* keys .
* - #active: A boolean indicating whether the local task is active.
2009-09-18 10:54:20 +00:00
*
2007-12-06 09:58:34 +00:00
* @ ingroup themeable
2007-07-04 15:49:44 +00:00
*/
2009-10-09 01:00:08 +00:00
function theme_menu_local_task ( $variables ) {
2009-10-11 06:05:53 +00:00
$link = $variables [ 'element' ][ '#link' ];
2013-02-18 17:03:05 +00:00
$link += array (
'localized_options' => array (),
);
2009-11-08 12:30:35 +00:00
$link_text = $link [ 'title' ];
if ( ! empty ( $variables [ 'element' ][ '#active' ])) {
// Add text to indicate active tab for non-visual users.
2013-06-17 19:58:27 +00:00
$active = '<span class="visually-hidden">' . t ( '(active tab)' ) . '</span>' ;
2009-11-08 12:30:35 +00:00
2013-10-07 05:34:09 +00:00
// If the link does not contain HTML already, String::checkPlain() it now.
2009-11-08 12:30:35 +00:00
// After we set 'html'=TRUE the link will not be sanitized by l().
if ( empty ( $link [ 'localized_options' ][ 'html' ])) {
2013-10-07 05:34:09 +00:00
$link [ 'title' ] = String :: checkPlain ( $link [ 'title' ]);
2009-11-08 12:30:35 +00:00
}
$link [ 'localized_options' ][ 'html' ] = TRUE ;
2010-05-04 20:29:57 +00:00
$link_text = t ( '!local-task-title!active' , array ( '!local-task-title' => $link [ 'title' ], '!active' => $active ));
2009-11-08 12:30:35 +00:00
}
2014-01-23 18:04:41 +00:00
$link [ 'localized_options' ][ 'set_active_class' ] = TRUE ;
2013-09-20 09:53:46 +00:00
if ( ! empty ( $link [ 'href' ])) {
// @todo - remove this once all pages are converted to routes.
$a_tag = l ( $link_text , $link [ 'href' ], $link [ 'localized_options' ]);
}
else {
2013-09-21 15:23:51 +00:00
$a_tag = \Drupal :: l ( $link_text , $link [ 'route_name' ], $link [ 'route_parameters' ], $link [ 'localized_options' ]);
2013-09-20 09:53:46 +00:00
}
2009-11-08 12:30:35 +00:00
2013-09-20 09:53:46 +00:00
return '<li' . ( ! empty ( $variables [ 'element' ][ '#active' ]) ? ' class="active"' : '' ) . '>' . $a_tag . '</li>' ;
2003-09-28 10:51:40 +00:00
}
2002-12-24 15:40:32 +00:00
2009-08-22 19:58:28 +00:00
/**
2010-04-13 15:23:03 +00:00
* Returns HTML for a single local action link .
2009-08-22 19:58:28 +00:00
*
2009-10-11 19:39:30 +00:00
* @ param $variables
2009-10-09 01:00:08 +00:00
* An associative array containing :
2010-04-13 15:23:03 +00:00
* - element : A render element containing :
* - #link: A menu link array with 'title', 'href', and 'localized_options'
* keys .
2009-09-18 10:54:20 +00:00
*
2009-08-22 19:58:28 +00:00
* @ ingroup themeable
*/
2009-10-09 01:00:08 +00:00
function theme_menu_local_action ( $variables ) {
2009-10-11 06:05:53 +00:00
$link = $variables [ 'element' ][ '#link' ];
2012-11-19 11:43:55 +00:00
$link += array (
'href' => '' ,
'localized_options' => array (),
2013-10-01 22:24:51 +00:00
'route_parameters' => array (),
2012-11-19 11:43:55 +00:00
);
$link [ 'localized_options' ][ 'attributes' ][ 'class' ][] = 'button' ;
2013-01-02 12:00:25 +00:00
$link [ 'localized_options' ][ 'attributes' ][ 'class' ][] = 'button-action' ;
2014-01-23 18:04:41 +00:00
$link [ 'localized_options' ][ 'set_active_class' ] = TRUE ;
2009-12-03 20:21:50 +00:00
$output = '<li>' ;
2013-10-01 22:24:51 +00:00
// @todo Remove this check and the call to l() when all pages are converted to
// routes.
// @todo Figure out how to support local actions without a href properly.
if ( $link [ 'href' ] === '' && ! empty ( $link [ 'route_name' ])) {
$output .= Drupal :: l ( $link [ 'title' ], $link [ 'route_name' ], $link [ 'route_parameters' ], $link [ 'localized_options' ]);
}
else {
$output .= l ( $link [ 'title' ], $link [ 'href' ], $link [ 'localized_options' ]);
}
2012-11-19 11:43:55 +00:00
$output .= " </li> " ;
2009-12-03 20:21:50 +00:00
return $output ;
2009-08-22 19:58:28 +00:00
}
2007-07-25 14:44:03 +00:00
/**
2012-08-31 15:56:36 +00:00
* Returns an array containing the names of system - defined ( default ) menus .
2007-07-25 14:44:03 +00:00
*/
function menu_list_system_menus () {
2009-10-09 08:02:25 +00:00
return array (
2012-10-25 15:53:18 +00:00
'tools' => 'Tools' ,
'admin' => 'Administration' ,
'account' => 'User account menu' ,
'main' => 'Main navigation' ,
2012-11-20 11:36:51 +00:00
'footer' => 'Footer menu' ,
2009-10-09 08:02:25 +00:00
);
2007-07-25 14:44:03 +00:00
}
2007-07-04 15:49:44 +00:00
/**
2012-08-31 15:56:36 +00:00
* Returns an array of links to be rendered as the Main menu .
2007-07-04 15:49:44 +00:00
*/
2008-06-25 09:12:25 +00:00
function menu_main_menu () {
2014-02-18 13:37:58 +00:00
$main_links_source = _menu_get_links_source ( 'main_links' , 'main' );
2012-08-03 17:09:51 +00:00
return menu_navigation_links ( $main_links_source );
2004-06-18 15:04:37 +00:00
}
2007-08-23 16:41:19 +00:00
2007-07-04 15:49:44 +00:00
/**
2012-08-31 15:56:36 +00:00
* Returns an array of links to be rendered as the Secondary links .
2007-07-04 15:49:44 +00:00
*/
2008-06-25 09:12:25 +00:00
function menu_secondary_menu () {
2014-02-18 13:37:58 +00:00
$main_links_source = _menu_get_links_source ( 'main_links' , 'main' );
$secondary_links_source = _menu_get_links_source ( 'secondary_links' , 'account' );
2007-08-20 18:26:41 +00:00
2007-08-23 16:41:19 +00:00
// If the secondary menu source is set as the primary menu, we display the
2007-08-20 18:26:41 +00:00
// second level of the primary menu.
2012-08-03 17:09:51 +00:00
if ( $secondary_links_source == $main_links_source ) {
return menu_navigation_links ( $main_links_source , 1 );
2007-08-20 18:26:41 +00:00
}
else {
2012-08-03 17:09:51 +00:00
return menu_navigation_links ( $secondary_links_source , 0 );
2007-08-20 18:26:41 +00:00
}
}
2014-02-18 13:37:58 +00:00
/**
* Returns the source of links of a menu .
*
* @ param string $name
* A string configuration key of menu link source .
* @ param string $default
* Default menu name .
*
* @ return string
* Returns menu name , if exist
*/
function _menu_get_links_source ( $name , $default ) {
2014-04-13 19:04:02 +00:00
$config = \Drupal :: config ( 'menu_ui.settings' );
return \Drupal :: moduleHandler () -> moduleExists ( 'menu_ui' ) ? $config -> get ( $name ) : $default ;
2014-02-18 13:37:58 +00:00
}
2007-08-20 18:26:41 +00:00
/**
2012-08-31 15:56:36 +00:00
* Returns an array of links for a navigation menu .
2007-08-20 18:26:41 +00:00
*
* @ param $menu_name
* The name of the menu .
* @ param $level
* Optional , the depth of the menu to be returned .
2010-03-26 17:14:46 +00:00
*
2007-08-20 18:26:41 +00:00
* @ return
* An array of links of the specified menu and level .
*/
function menu_navigation_links ( $menu_name , $level = 0 ) {
// Don't even bother querying the menu table if no menu is specified.
2007-09-04 21:10:45 +00:00
if ( empty ( $menu_name )) {
2007-08-20 18:26:41 +00:00
return array ();
}
// Get the menu hierarchy for the current page.
2014-03-26 19:56:01 +00:00
/** @var \Drupal\menu_link\MenuTreeInterface $menu_tree */
$menu_tree = \Drupal :: service ( 'menu_link.tree' );
$tree = $menu_tree -> buildPageData ( $menu_name , $level + 1 );
2007-08-20 18:26:41 +00:00
// Go down the active trail until the right level is reached.
while ( $level -- > 0 && $tree ) {
// Loop through the current level's items until we find one that is in trail.
while ( $item = array_shift ( $tree )) {
if ( $item [ 'link' ][ 'in_active_trail' ]) {
// If the item is in the active trail, we continue in the subtree.
$tree = empty ( $item [ 'below' ]) ? array () : $item [ 'below' ];
break ;
}
}
}
// Create a single level of links.
2007-05-27 20:31:13 +00:00
$links = array ();
foreach ( $tree as $item ) {
2007-08-11 14:06:15 +00:00
if ( ! $item [ 'link' ][ 'hidden' ]) {
2008-10-13 04:46:31 +00:00
$class = '' ;
2008-02-04 12:07:23 +00:00
$l = $item [ 'link' ][ 'localized_options' ];
2014-01-29 08:25:00 +00:00
$l [ 'href' ] = $item [ 'link' ][ 'link_path' ];
2007-08-11 14:06:15 +00:00
$l [ 'title' ] = $item [ 'link' ][ 'title' ];
2008-05-05 20:55:13 +00:00
if ( $item [ 'link' ][ 'in_active_trail' ]) {
2008-10-13 04:46:31 +00:00
$class = ' active-trail' ;
2010-09-24 00:37:45 +00:00
$l [ 'attributes' ][ 'class' ][] = 'active-trail' ;
}
2014-02-07 04:28:14 +00:00
// Keyed with the unique mlid to generate classes in links.html.twig.
2008-10-13 04:46:31 +00:00
$links [ 'menu-' . $item [ 'link' ][ 'mlid' ] . $class ] = $l ;
2007-08-11 14:06:15 +00:00
}
2007-05-27 20:31:13 +00:00
}
return $links ;
2002-12-24 15:40:32 +00:00
}
2007-02-11 09:30:51 +00:00
/**
2009-08-22 19:58:28 +00:00
* Collects the local tasks ( tabs ), action links , and the root path .
2007-02-11 09:30:51 +00:00
*
2013-10-04 04:27:56 +00:00
* @ param int $level
2007-05-16 13:45:17 +00:00
* The level of tasks you ask for . Primary tasks are 0 , secondary are 1.
2010-03-26 17:14:46 +00:00
*
2013-10-04 04:27:56 +00:00
* @ return array
2009-08-22 19:58:28 +00:00
* An array containing
2013-02-18 17:03:05 +00:00
* - tabs : Local tasks for the requested level .
* - actions : Action links for the requested level .
2009-08-22 19:58:28 +00:00
* - root_path : The router path for the current page . If the current page is
* a default local task , then this corresponds to the parent tab .
2013-02-18 17:03:05 +00:00
*
* @ see hook_menu_local_tasks ()
* @ see hook_menu_local_tasks_alter ()
2009-08-22 19:58:28 +00:00
*/
function menu_local_tasks ( $level = 0 ) {
$data = & drupal_static ( __FUNCTION__ );
$root_path = & drupal_static ( __FUNCTION__ . ':root_path' , '' );
$empty = array (
2013-02-18 17:03:05 +00:00
'tabs' => array (),
'actions' => array (),
2009-08-22 19:58:28 +00:00
'root_path' => & $root_path ,
);
if ( ! isset ( $data )) {
2013-09-20 09:53:46 +00:00
// Look for route-based tabs.
$data [ 'tabs' ] = array ();
$data [ 'actions' ] = array ();
2014-06-24 12:39:26 +00:00
$route_name = \Drupal :: routeMatch () -> getRouteName ();
2013-09-20 09:53:46 +00:00
if ( ! empty ( $route_name )) {
2013-09-21 15:23:51 +00:00
$manager = \Drupal :: service ( 'plugin.manager.menu.local_task' );
2013-09-20 09:53:46 +00:00
$local_tasks = $manager -> getTasksBuild ( $route_name );
foreach ( $local_tasks as $level => $items ) {
$data [ 'tabs' ][ $level ] = empty ( $data [ 'tabs' ][ $level ]) ? $items : array_merge ( $data [ 'tabs' ][ $level ], $items );
}
}
2013-02-18 17:03:05 +00:00
// Allow modules to dynamically add further tasks.
2013-09-16 03:58:06 +00:00
$module_handler = \Drupal :: moduleHandler ();
2013-02-18 17:03:05 +00:00
foreach ( $module_handler -> getImplementations ( 'menu_local_tasks' ) as $module ) {
$function = $module . '_menu_local_tasks' ;
2013-10-04 04:27:56 +00:00
$function ( $data , $route_name );
2013-02-18 17:03:05 +00:00
}
// Allow modules to alter local tasks.
2013-10-04 04:27:56 +00:00
$module_handler -> alter ( 'menu_local_tasks' , $data , $route_name );
2007-01-31 21:26:56 +00:00
}
2007-06-30 19:46:58 +00:00
2009-08-22 19:58:28 +00:00
if ( isset ( $data [ 'tabs' ][ $level ])) {
return array (
'tabs' => $data [ 'tabs' ][ $level ],
'actions' => $data [ 'actions' ],
'root_path' => $root_path ,
);
2007-06-30 19:46:58 +00:00
}
2013-02-18 17:03:05 +00:00
elseif ( ! empty ( $data [ 'actions' ])) {
2009-12-03 20:21:50 +00:00
return array ( 'actions' => $data [ 'actions' ]) + $empty ;
}
2009-08-22 19:58:28 +00:00
return $empty ;
2007-02-11 09:30:51 +00:00
}
2007-07-04 15:49:44 +00:00
/**
* Returns the rendered local tasks at the top level .
*/
2007-02-11 09:30:51 +00:00
function menu_primary_local_tasks () {
2009-08-22 19:58:28 +00:00
$links = menu_local_tasks ( 0 );
// Do not display single tabs.
2014-03-31 17:37:55 +00:00
return count ( Element :: getVisibleChildren ( $links [ 'tabs' ])) > 1 ? $links [ 'tabs' ] : '' ;
2004-04-15 20:49:42 +00:00
}
2003-09-28 10:51:40 +00:00
2007-07-04 15:49:44 +00:00
/**
* Returns the rendered local tasks at the second level .
*/
2007-01-24 14:48:36 +00:00
function menu_secondary_local_tasks () {
2009-08-22 19:58:28 +00:00
$links = menu_local_tasks ( 1 );
// Do not display single tabs.
2014-03-31 17:37:55 +00:00
return count ( Element :: getVisibleChildren ( $links [ 'tabs' ])) > 1 ? $links [ 'tabs' ] : '' ;
2009-08-22 19:58:28 +00:00
}
/**
* Returns the rendered local actions at the current level .
*/
2013-05-28 01:20:55 +00:00
function menu_get_local_actions () {
2009-08-22 19:58:28 +00:00
$links = menu_local_tasks ();
2014-06-24 12:39:26 +00:00
$route_name = Drupal :: routeMatch () -> getRouteName ();
2013-09-16 03:58:06 +00:00
$manager = \Drupal :: service ( 'plugin.manager.menu.local_action' );
2013-10-13 12:40:37 +00:00
return $manager -> getActionsForRoute ( $route_name ) + $links [ 'actions' ];
2004-09-16 07:17:56 +00:00
}
2007-05-27 20:31:13 +00:00
/**
2012-08-31 15:56:36 +00:00
* Returns the router path , or the path for a default local task ' s parent .
2007-06-30 19:46:58 +00:00
*/
function menu_tab_root_path () {
2009-08-22 19:58:28 +00:00
$links = menu_local_tasks ();
return $links [ 'root_path' ];
2007-06-30 19:46:58 +00:00
}
/**
2010-11-20 04:03:51 +00:00
* Returns a renderable element for the primary and secondary tabs .
*/
function menu_local_tabs () {
2013-02-18 17:03:05 +00:00
$build = array (
2010-11-20 04:03:51 +00:00
'#theme' => 'menu_local_tasks' ,
'#primary' => menu_primary_local_tasks (),
'#secondary' => menu_secondary_local_tasks (),
);
2013-02-18 17:03:05 +00:00
return ! empty ( $build [ '#primary' ]) || ! empty ( $build [ '#secondary' ]) ? $build : array ();
2010-11-20 04:03:51 +00:00
}
/**
* Returns HTML for primary and secondary local tasks .
2007-05-27 20:31:13 +00:00
*
2012-10-05 18:12:56 +00:00
* @ param $variables
* An associative array containing :
* - primary : ( optional ) An array of local tasks ( tabs ) .
* - secondary : ( optional ) An array of local tasks ( tabs ) .
*
2007-05-27 20:31:13 +00:00
* @ ingroup themeable
2012-10-05 18:12:56 +00:00
* @ see menu_local_tasks ()
2007-05-27 20:31:13 +00:00
*/
2010-11-20 04:03:51 +00:00
function theme_menu_local_tasks ( & $variables ) {
$output = '' ;
2007-05-27 20:31:13 +00:00
2011-08-26 09:52:08 +00:00
if ( ! empty ( $variables [ 'primary' ])) {
2013-06-17 19:58:27 +00:00
$variables [ 'primary' ][ '#prefix' ] = '<h2 class="visually-hidden">' . t ( 'Primary tabs' ) . '</h2>' ;
2011-08-26 09:52:08 +00:00
$variables [ 'primary' ][ '#prefix' ] .= '<ul class="tabs primary">' ;
$variables [ 'primary' ][ '#suffix' ] = '</ul>' ;
$output .= drupal_render ( $variables [ 'primary' ]);
}
if ( ! empty ( $variables [ 'secondary' ])) {
2013-06-17 19:58:27 +00:00
$variables [ 'secondary' ][ '#prefix' ] = '<h2 class="visually-hidden">' . t ( 'Secondary tabs' ) . '</h2>' ;
2011-08-26 09:52:08 +00:00
$variables [ 'secondary' ][ '#prefix' ] .= '<ul class="tabs secondary">' ;
$variables [ 'secondary' ][ '#suffix' ] = '</ul>' ;
$output .= drupal_render ( $variables [ 'secondary' ]);
2007-07-05 08:48:58 +00:00
}
2007-05-27 20:31:13 +00:00
2007-07-05 08:48:58 +00:00
return $output ;
2007-05-27 20:31:13 +00:00
}
2007-07-04 15:49:44 +00:00
/**
2012-08-31 15:56:36 +00:00
* Sets ( or gets ) the active menu for the current page .
*
* The active menu for the page determines the active trail .
2011-12-06 12:18:12 +00:00
*
* @ return
* An array of menu machine names , in order of preference . The
Issue #2167109 by Berdir, sun, alexpott, ACF, acrollet, adamdicarlo, Albert Volkman, andreiashu, andyceo, andypost, anenkov, aspilicious, barbun, beejeebus, boombatower, cam8001, chriscalip, chx, cosmicdreams, dagmar, damiankloip, dawehner, deviance, disasm, dixon_, dstol, ebrowet, Gábor Hojtsy, heyrocker, Hydra, ianthomas_uk, japicoder, jcisio, jibran, julien, justafish, jvns, KarenS, kbasarab, kim.pepper, larowlan, Lars Toomre, leschekfm, Letharion, LinL, lirantal, Lukas von Blarer, marcingy, Mike Wacker, mrf, mtift, mtunay, n3or, nadavoid, nick_schuch, Niklas Fiekas, ParisLiakos, pcambra, penyaskito, pfrenssen, plopesc, Pol, Rok Žlender, rvilar, swentel, tim.plunkett, tobiasb, tsvenson, typhonius, vasi1186, vijaycs85, wamilton, webchick, webflo, wizonesolutions, xjm, yched, YesCT, znerol: Remove Variable subsystem.
2014-01-28 13:07:47 +00:00
* 'system.menu:active_menus_default' config item may be used to assert a menu
2012-11-02 17:23:40 +00:00
* order different from the order of creation , or to prevent a particular menu
* from being used at all in the active trail .
2007-07-04 15:49:44 +00:00
*/
2009-03-20 19:18:11 +00:00
function menu_set_active_menu_names ( $menu_names = NULL ) {
2009-04-25 15:19:12 +00:00
$active = & drupal_static ( __FUNCTION__ );
2007-05-16 13:45:17 +00:00
2009-03-20 19:18:11 +00:00
if ( isset ( $menu_names ) && is_array ( $menu_names )) {
$active = $menu_names ;
2007-05-16 13:45:17 +00:00
}
elseif ( ! isset ( $active )) {
2013-09-16 03:58:06 +00:00
$config = \Drupal :: config ( 'system.menu' );
2012-11-02 17:23:40 +00:00
$active = $config -> get ( 'active_menus_default' ) ? : array_keys ( menu_list_system_menus ());
2007-05-16 13:45:17 +00:00
}
return $active ;
}
2007-07-04 15:49:44 +00:00
/**
2012-08-31 15:56:36 +00:00
* Gets the active menu for the current page .
2007-07-04 15:49:44 +00:00
*/
2009-03-20 19:18:11 +00:00
function menu_get_active_menu_names () {
return menu_set_active_menu_names ();
2007-05-16 13:45:17 +00:00
}
2010-09-24 00:37:45 +00:00
/**
2012-08-31 15:56:36 +00:00
* Looks up the preferred menu link for a given system path .
2010-09-24 00:37:45 +00:00
*
* @ param $path
2013-06-28 18:39:33 +00:00
* The path ; for example , 'node/5' . The function will find the corresponding
2010-09-24 00:37:45 +00:00
* menu link ( 'node/5' if it exists , or fallback to 'node/%' ) .
2011-12-06 12:18:12 +00:00
* @ 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 .
2010-09-24 00:37:45 +00:00
*
* @ return
2011-12-06 12:18:12 +00:00
* A fully translated menu link , or FALSE if no matching menu link was
2010-09-24 00:37:45 +00:00
* 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 .
*/
2011-12-06 12:18:12 +00:00
function menu_link_get_preferred ( $path = NULL , $selected_menu = NULL ) {
2010-09-24 00:37:45 +00:00
$preferred_links = & drupal_static ( __FUNCTION__ );
if ( ! isset ( $path )) {
2012-04-29 15:16:27 +00:00
$path = current_path ();
2010-09-24 00:37:45 +00:00
}
2011-12-06 12:18:12 +00:00
if ( empty ( $selected_menu )) {
// Use an illegal menu name as the key for the preferred menu link.
$selected_menu = MENU_PREFERRED_LINK ;
}
2010-09-24 00:37:45 +00:00
2011-12-06 12:18:12 +00:00
if ( ! isset ( $preferred_links [ $path ])) {
2010-09-24 00:37:45 +00:00
// 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.
2014-01-29 08:25:00 +00:00
// @todo simplify this code and convert to using route names.
// @see https://drupal.org/node/2154949
$path_candidates [ $path ] = $path ;
2010-09-24 00:37:45 +00:00
// Retrieve a list of menu names, ordered by preference.
$menu_names = menu_get_active_menu_names ();
2011-12-06 12:18:12 +00:00
// Put the selected menu at the front of the list.
array_unshift ( $menu_names , $selected_menu );
2010-09-24 00:37:45 +00:00
2013-02-08 23:55:25 +00:00
$menu_links = entity_load_multiple_by_properties ( 'menu_link' , array ( 'link_path' => $path_candidates ));
2010-09-24 00:37:45 +00:00
// Sort candidates by link path and menu name.
$candidates = array ();
2013-02-08 23:55:25 +00:00
foreach ( $menu_links as $candidate ) {
2010-09-24 00:37:45 +00:00
$candidates [ $candidate [ 'link_path' ]][ $candidate [ 'menu_name' ]] = $candidate ;
2011-12-06 12:18:12 +00:00
// Add any menus not already in the menu name search list.
if ( ! in_array ( $candidate [ 'menu_name' ], $menu_names )) {
$menu_names [] = $candidate [ 'menu_name' ];
}
2010-09-24 00:37:45 +00:00
}
2011-12-06 12:18:12 +00:00
// Store the most specific link for each menu. Also save the most specific
// link of the most preferred menu in $preferred_link.
2010-09-24 00:37:45 +00:00
foreach ( $path_candidates as $link_path ) {
2011-12-06 12:18:12 +00:00
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 ];
2014-03-01 04:58:58 +00:00
$candidate_item [ 'access' ] = \Drupal :: service ( 'access_manager' ) -> checkNamedRoute ( $candidate_item [ 'route_name' ], $candidate_item [ 'route_parameters' ], \Drupal :: currentUser ());
2011-12-06 12:18:12 +00:00
if ( $candidate_item [ 'access' ]) {
2014-03-01 04:58:58 +00:00
_menu_item_localize ( $candidate_item );
2011-12-06 12:18:12 +00:00
$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 ;
}
}
}
2010-09-24 00:37:45 +00:00
}
}
}
}
2011-12-06 12:18:12 +00:00
return isset ( $preferred_links [ $path ][ $selected_menu ]) ? $preferred_links [ $path ][ $selected_menu ] : FALSE ;
2010-09-24 00:37:45 +00:00
}
2007-05-16 13:45:17 +00:00
/**
2012-08-31 15:56:36 +00:00
* Clears all cached menu data .
*
* This should be called any time broad changes
2007-07-04 15:49:44 +00:00
* might have been made to the router items or menu links .
2007-05-16 13:45:17 +00:00
*/
function menu_cache_clear_all () {
2014-03-26 13:19:28 +00:00
\Drupal :: cache ( 'data' ) -> deleteAll ();
#610234 by Gábor Hojtsy, ksenzee, cwgordon7, David_Rothstein, seutje, marcvangend, sun, JoshuaRogers, markus_petrux, Bojhan, Rob Loach, Everett Zufelt, drifter, markboulton, leisareichelt, et al: Added Overlay module to core, which shows administrative pages in a JS overlay, retaining context on the front-end site.
2009-12-02 07:28:22 +00:00
menu_reset_static_cache ();
}
/**
* Resets the menu system static cache .
*/
function menu_reset_static_cache () {
2013-09-16 03:58:06 +00:00
\Drupal :: entityManager ()
2014-03-27 11:54:40 +00:00
-> getStorage ( 'menu_link' ) -> resetCache ();
2010-09-24 00:37:45 +00:00
drupal_static_reset ( 'menu_link_get_preferred' );
2007-05-16 13:45:17 +00:00
}
2007-06-05 09:15:02 +00:00
/**
2014-01-29 08:25:00 +00:00
* 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 ]);
}
/**
2014-03-28 23:27:34 +00:00
* Builds menu links for the items returned from the menu_link . static service .
2014-01-29 08:25:00 +00:00
*/
function menu_link_rebuild_defaults () {
2014-02-24 19:38:23 +00:00
// Ensure that all configuration used to build the menu items are loaded
// without overrides.
$old_state = \Drupal :: configFactory () -> getOverrideState ();
\Drupal :: configFactory () -> setOverrideState ( FALSE );
2014-01-29 08:25:00 +00:00
$module_handler = \Drupal :: moduleHandler ();
if ( ! $module_handler -> moduleExists ( 'menu_link' )) {
2013-08-12 13:28:56 +00:00
// The Menu link module may not be available during install, so rebuild
// when possible.
return ;
2009-12-14 20:23:01 +00:00
}
2014-03-27 11:54:40 +00:00
/** @var \Drupal\menu_link\MenuLinkStorageInterface $menu_link_storage */
2014-01-29 08:25:00 +00:00
$menu_link_storage = \Drupal :: entityManager ()
2014-03-27 11:54:40 +00:00
-> getStorage ( 'menu_link' );
2014-01-29 08:25:00 +00:00
$links = array ();
$children = array ();
$top_links = array ();
2014-03-28 23:27:34 +00:00
$all_links = \Drupal :: service ( 'menu_link.static' ) -> getLinks ();
2014-01-29 08:25:00 +00:00
if ( $all_links ) {
foreach ( $all_links as $machine_name => $link ) {
2013-02-08 23:55:25 +00:00
// For performance reasons, do a straight query now and convert to a menu
// link entity later.
// @todo revisit before release.
2008-12-03 14:38:59 +00:00
$existing_item = db_select ( 'menu_links' )
2011-07-14 01:41:00 +00:00
-> fields ( 'menu_links' )
2014-01-29 08:25:00 +00:00
-> condition ( 'machine_name' , $machine_name )
-> execute () -> fetchObject ();
2007-07-04 15:49:44 +00:00
if ( $existing_item ) {
2013-02-08 23:55:25 +00:00
$existing_item -> options = unserialize ( $existing_item -> options );
2013-10-01 12:09:08 +00:00
$existing_item -> route_parameters = unserialize ( $existing_item -> route_parameters );
2014-01-29 08:25:00 +00:00
$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 );
2014-04-16 20:57:51 +00:00
// @todo Do not create a new entity in order to update it, see
// https://drupal.org/node/2241865
$menu_link -> setOriginalId ( $existing_item -> mlid );
2014-01-24 10:23:53 +00:00
// Convert the existing item to a typed object.
2014-01-29 08:25:00 +00:00
/** @var \Drupal\menu_link\MenuLinkInterface $existing_item */
$existing_item = $menu_link_storage -> create ( get_object_vars ( $existing_item ));
if ( ! $existing_item -> customized ) {
2014-03-28 23:27:34 +00:00
// A change in the default menu links may move the link to a
2014-01-29 08:25:00 +00:00
// 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 ;
}
2007-07-04 15:49:44 +00:00
}
2013-02-08 23:55:25 +00:00
else {
2014-01-29 08:25:00 +00:00
if ( empty ( $link [ 'route_name' ]) && empty ( $link [ 'link_path' ])) {
2014-06-26 18:55:12 +00:00
\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 ));
2014-01-29 08:25:00 +00:00
continue ;
}
$menu_link = $menu_link_storage -> createFromDefaultLink ( $link );
2013-02-08 23:55:25 +00:00
}
2014-01-29 08:25:00 +00:00
if ( ! empty ( $link [ 'parent' ])) {
$children [ $link [ 'parent' ]][ $machine_name ] = $machine_name ;
$menu_link -> parent = $link [ 'parent' ];
if ( empty ( $link [ 'menu_name' ])) {
2014-03-20 16:18:35 +00:00
// Reset the default menu name so it is populated from the parent.
$menu_link -> menu_name = NULL ;
2014-01-29 08:25:00 +00:00
}
2011-07-14 01:41:00 +00:00
}
else {
2014-01-29 08:25:00 +00:00
// A top level link - we need them to root our tree.
$top_links [ $machine_name ] = $machine_name ;
$menu_link -> plid = 0 ;
2007-07-04 15:49:44 +00:00
}
2014-01-29 08:25:00 +00:00
$links [ $machine_name ] = $menu_link ;
2007-05-16 13:45:17 +00:00
}
2007-05-06 05:47:52 +00:00
}
2014-01-29 08:25:00 +00:00
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 );
2007-08-29 20:46:18 +00:00
}
2007-08-19 09:46:15 +00:00
}
2013-02-08 23:55:25 +00:00
2014-03-28 23:27:34 +00:00
// Find any item whose default menu link no longer exists.
2014-01-29 08:25:00 +00:00
if ( $all_links ) {
$query = \Drupal :: entityQuery ( 'menu_link' )
-> condition ( 'machine_name' , array_keys ( $all_links ), 'NOT IN' )
2014-02-26 10:53:29 +00:00
-> exists ( 'machine_name' )
2014-01-29 08:25:00 +00:00
-> condition ( 'external' , 0 )
-> condition ( 'updated' , 0 )
-> condition ( 'customized' , 0 )
-> sort ( 'depth' , 'DESC' );
$result = $query -> execute ();
}
else {
$result = array ();
}
2007-05-06 05:47:52 +00:00
2013-02-08 23:55:25 +00:00
// Remove all such items. Starting from those with the greatest depth will
// minimize the amount of re-parenting done by the menu link controller.
2014-01-29 08:25:00 +00:00
if ( $result ) {
2013-02-08 23:55:25 +00:00
menu_link_delete_multiple ( $result , TRUE );
2009-10-17 01:15:40 +00:00
}
2014-02-24 19:38:23 +00:00
\Drupal :: configFactory () -> setOverrideState ( $old_state );
2009-10-17 01:15:40 +00:00
}
/**
* Returns an array containing all links for a menu .
*
* @ param $menu_name
* The name of the menu whose links should be returned .
2010-07-16 02:54:09 +00:00
*
2009-10-17 01:15:40 +00:00
* @ return
* An array of menu links .
*/
function menu_load_links ( $menu_name ) {
2013-02-08 23:55:25 +00:00
$links = array ();
2013-09-16 03:58:06 +00:00
$query = \Drupal :: entityQuery ( 'menu_link' )
2013-02-08 23:55:25 +00:00
-> condition ( 'menu_name' , $menu_name )
2009-10-17 01:15:40 +00:00
// Order by weight so as to be helpful for menus that are only one level
// deep.
2013-02-08 23:55:25 +00:00
-> sort ( 'weight' );
$result = $query -> execute ();
2009-10-17 01:15:40 +00:00
2013-02-08 23:55:25 +00:00
if ( ! empty ( $result )) {
$links = menu_link_load_multiple ( $result );
2009-10-17 01:15:40 +00:00
}
2013-02-08 23:55:25 +00:00
2009-10-17 01:15:40 +00:00
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 );
2013-02-08 23:55:25 +00:00
menu_link_delete_multiple ( array_keys ( $links ), FALSE , TRUE );
2010-11-20 07:19:15 +00:00
}
2007-11-26 08:49:03 +00:00
/**
2014-06-20 17:41:22 +00:00
* Updates the expanded menu item state at most twice per page load .
2007-12-08 14:06:23 +00:00
*/
2014-06-20 17:41:22 +00:00
function _menu_update_expanded_menus () {
$expanded_menus_updated = & drupal_static ( __FUNCTION__ , 0 );
2007-11-26 08:49:03 +00:00
2014-06-20 17:41:22 +00:00
// Update the expanded menu item state, but at most twice, including at
2008-12-30 16:43:20 +00:00
// the end of the page load when there are multiple links saved or deleted.
2014-06-20 17:41:22 +00:00
if ( $expanded_menus_updated == 0 ) {
2007-11-26 08:49:03 +00:00
// Keep track of which menus have expanded items.
_menu_set_expanded_menus ();
2014-06-20 17:41:22 +00:00
$expanded_menus_updated = 1 ;
2007-11-26 08:49:03 +00:00
}
2014-06-20 17:41:22 +00:00
elseif ( $expanded_menus_updated == 1 ) {
2007-11-26 08:49:03 +00:00
// Keep track of which menus have expanded items.
2010-02-17 22:44:52 +00:00
drupal_register_shutdown_function ( '_menu_set_expanded_menus' );
2014-06-20 17:41:22 +00:00
$expanded_menus_updated = 2 ;
2007-11-26 08:49:03 +00:00
}
}
/**
2012-08-31 15:56:36 +00:00
* Updates a list of menus with expanded items .
2007-12-08 14:06:23 +00:00
*/
2007-11-26 08:49:03 +00:00
function _menu_set_expanded_menus () {
2014-03-24 10:43:05 +00:00
$names = array ();
$result = Drupal :: entityQueryAggregate ( 'menu_link' )
-> condition ( 'expanded' , 0 , '<>' )
-> groupBy ( 'menu_name' )
-> execute ();
2014-03-24 11:28:22 +00:00
// Flatten the resulting array.
foreach ( $result as $k => $v ) {
2014-03-24 10:43:05 +00:00
$names [ $k ] = $v [ 'menu_name' ];
}
2013-09-16 03:58:06 +00:00
\Drupal :: state () -> set ( 'menu_expanded' , $names );
2007-05-16 13:45:17 +00:00
}
2008-01-28 16:05:17 +00:00
/**
* @ } End of " defgroup menu " .
*/