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-01-22 20:35:48 +00:00
use Drupal\Core\Cache\Cache ;
2013-05-25 20:12:45 +00:00
use Drupal\Core\Language\Language ;
2012-09-04 13:32:47 +00:00
use Drupal\Core\Template\Attribute ;
2014-02-26 10:53:29 +00:00
use Drupal\menu_link\MenuLinkInterface ;
2013-09-06 20:53:28 +00:00
use Symfony\Cmf\Component\Routing\RouteObjectInterface ;
2012-09-04 13:32:47 +00:00
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
*
2013-10-18 04:08:20 +00:00
* The Drupal routing system defines how Drupal responds to URLs passed to the
* browser . The menu system , which depends on the routing system , is used for
* navigation . The Menu module allows menus to be created in the user interface
* as hierarchical lists of links .
*
* @ section registering_paths Registering router paths
* To register a path , you need to add lines similar to this in a
* module . routing . yml file :
* @ code
* block . admin_display :
* path : '/admin/structure/block'
* defaults :
* _content : '\Drupal\block\Controller\BlockListController::listing'
* requirements :
* _permission : 'administer blocks'
* @ endcode
* @ todo Add more information here , especially about controllers and what all
* the stuff in the routing . yml file means .
*
* @ section Defining menu links
2014-03-01 04:58:58 +00:00
* Once you have a route defined , you can use hook_menu_link_defaults () to
* define links for your module ' s paths in the main Navigation menu or other
* menus . See the hook_menu_link_defaults () documentation for more details .
2013-10-18 04:08:20 +00:00
*
* @ todo The rest of this topic has not been reviewed or updated for Drupal 8. x
* and is not correct !
* @ todo It is quite likely that hook_menu () will be replaced with a different
* hook , configuration system , or plugin system before the 8.0 release .
2004-07-10 18:10:36 +00:00
*
* Drupal ' s menu system follows a simple hierarchy defined by paths .
* Implementations of hook_menu () define menu items and assign them to
* paths ( which should be unique ) . The menu system aggregates these items
* and determines the menu hierarchy from the paths . For example , if the
* paths defined were a , a / b , e , a / b / c / d , f / g , and a / b / h , the menu system
* would form the structure :
* - a
* - a / b
* - a / b / c / d
* - a / b / h
* - e
* - f / g
* Note that the number of elements in the path does not necessarily
* determine the depth of the menu item in the tree .
*
* When responding to a page request , the menu system looks to see if the
* path requested by the browser is registered as a menu item with a
* callback . If not , the system searches up the menu tree for the most
* complete match with a callback it can find . If the path a / b / i is
* requested in the tree above , the callback for a / b would be used .
*
2005-11-24 22:04:10 +00:00
* The found callback function is called with any arguments specified
2007-05-16 13:45:17 +00:00
* in the " page arguments " attribute of its menu item . The
2005-11-24 22:04:10 +00:00
* attribute must be an array . After these arguments , any remaining
* components of the path are appended as further arguments . In this
* way , the callback for a / b above could respond to a request for
* a / b / i differently than a request for a / b / j .
2004-07-10 18:10:36 +00:00
*
* For an illustration of this process , see page_example . module .
*
* Access to the callback functions is also protected by the menu system .
2007-05-16 13:45:17 +00:00
* The " access callback " with an optional " access arguments " of each menu
* item is called before the page callback proceeds . If this returns TRUE ,
2010-12-08 06:48:39 +00:00
* then access is granted ; if FALSE , then access is denied . Default local task
* menu items ( see next paragraph ) may omit this attribute to use the value
* provided by the parent item .
2004-07-10 18:10:36 +00:00
*
* In the default Drupal interface , you will notice many links rendered as
* tabs . These are known in the menu system as " local tasks " , and they are
* rendered as tabs by default , though other presentations are possible .
* Local tasks function just as other menu items in most respects . It is
* convention that the names of these tasks should be short verbs if
* possible . In addition , a " default " local task should be provided for
* each set . When visiting a local task ' s parent menu item , the default
* local task will be rendered as if it is selected ; this provides for a
* normal tab user experience . This default task is special in that it
* links not to its provided path , but to its parent item ' s path instead .
* The default task ' s path is only used to place it appropriately in the
* menu hierarchy .
2007-05-16 13:45:17 +00:00
*
* Everything described so far is stored in the menu_router table . The
* menu_links table holds the visible menu links . By default these are
2007-07-04 21:33:55 +00:00
* derived from the same hook_menu definitions , however you are free to
2007-05-16 13:45:17 +00:00
* add more with menu_link_save () .
2003-12-17 22:15:35 +00:00
*/
2004-06-18 15:04:37 +00:00
/**
2010-10-08 05:07:53 +00:00
* @ defgroup menu_flags Menu flags
2004-07-10 15:51:48 +00:00
* @ {
2004-06-18 15:04:37 +00:00
* Flags for use in the " type " attribute of menu items .
*/
2004-09-09 05:51:08 +00:00
2008-05-26 17:12:55 +00:00
/**
* Internal menu flag -- menu item is the root of the menu tree .
*/
2011-11-29 09:56:53 +00:00
const MENU_IS_ROOT = 0x0001 ;
2008-05-26 17:12:55 +00:00
/**
* Internal menu flag -- menu item is visible in the menu tree .
*/
2011-11-29 09:56:53 +00:00
const MENU_VISIBLE_IN_TREE = 0x0002 ;
2008-05-26 17:12:55 +00:00
/**
2010-01-25 10:38:35 +00:00
* Internal menu flag -- menu item links back to its parent .
2008-05-26 17:12:55 +00:00
*/
2011-11-29 09:56:53 +00:00
const MENU_LINKS_TO_PARENT = 0x0008 ;
2008-05-26 17:12:55 +00:00
/**
* Internal menu flag -- menu item can be modified by administrator .
*/
2011-11-29 09:56:53 +00:00
const MENU_MODIFIED_BY_ADMIN = 0x0020 ;
2008-05-26 17:12:55 +00:00
/**
* Internal menu flag -- menu item was created by administrator .
*/
2011-11-29 09:56:53 +00:00
const MENU_CREATED_BY_ADMIN = 0x0040 ;
2008-05-26 17:12:55 +00:00
/**
* Internal menu flag -- menu item is a local task .
*/
2011-11-29 09:56:53 +00:00
const MENU_IS_LOCAL_TASK = 0x0080 ;
2004-09-09 05:51:08 +00:00
2004-07-10 15:51:48 +00:00
/**
2012-05-17 12:58:49 +00:00
* @ } End of " defgroup menu_flags " .
2004-07-10 15:51:48 +00:00
*/
/**
2010-10-08 05:07:53 +00:00
* @ defgroup menu_item_types Menu item types
2004-07-10 15:51:48 +00:00
* @ {
2011-04-12 20:47:04 +00:00
* Definitions for various menu item types .
2011-01-03 18:03:54 +00:00
*
2004-07-10 15:51:48 +00:00
* Menu item definitions provide one of these constants , which are shortcuts for
2011-01-03 18:03:54 +00:00
* combinations of @ link menu_flags Menu flags @ endlink .
2004-07-10 15:51:48 +00:00
*/
2003-12-17 22:15:35 +00:00
2004-06-18 15:04:37 +00:00
/**
2013-09-26 08:32:39 +00:00
* Menu type -- A " normal " menu item that ' s shown in menus .
2008-05-26 17:12:55 +00:00
*
2004-06-18 15:04:37 +00:00
* Normal menu items show up in the menu tree and can be moved / hidden by
2004-07-10 18:10:36 +00:00
* the administrator . Use this for most menu items . It is the default value if
* no menu item type is specified .
2004-06-18 15:04:37 +00:00
*/
2014-03-01 04:58:58 +00:00
define ( 'MENU_NORMAL_ITEM' , MENU_VISIBLE_IN_TREE );
2004-04-15 20:49:42 +00:00
2004-06-18 15:04:37 +00:00
/**
2008-05-26 17:12:55 +00:00
* Menu type -- A hidden , internal callback , typically used for API calls .
*
2004-06-18 15:04:37 +00:00
* Callbacks simply register a path so that the correct function is fired
2013-09-26 08:32:39 +00:00
* when the URL is accessed . They do not appear in menus .
2004-06-18 15:04:37 +00:00
*/
2011-11-29 09:56:53 +00:00
const MENU_CALLBACK = 0x0000 ;
2002-12-24 15:40:32 +00:00
2004-06-18 15:04:37 +00:00
/**
2008-05-26 17:12:55 +00:00
* Menu type -- A normal menu item , hidden until enabled by an administrator .
*
2004-07-10 18:10:36 +00:00
* Modules may " suggest " menu items that the administrator may enable . They act
* just as callbacks do until enabled , at which time they act like normal items .
2007-11-24 20:59:32 +00:00
* Note for the value : 0x0010 was a flag which is no longer used , but this way
* the values of MENU_CALLBACK and MENU_SUGGESTED_ITEM are separate .
2004-06-18 15:04:37 +00:00
*/
2014-03-01 04:58:58 +00:00
define ( 'MENU_SUGGESTED_ITEM' , 0x0010 );
2013-05-28 01:20:55 +00:00
2004-06-18 15:04:37 +00:00
/**
2012-05-17 12:58:49 +00:00
* @ } End of " defgroup menu_item_types " .
2004-07-10 15:51:48 +00:00
*/
/**
2010-10-08 05:07:53 +00:00
* @ defgroup menu_status_codes Menu status codes
2004-07-10 15:51:48 +00:00
* @ {
2004-06-18 15:04:37 +00:00
* Status codes for menu callbacks .
*/
2004-09-09 05:51:08 +00:00
2008-05-26 17:12:55 +00:00
/**
* Internal menu status code -- Menu item inaccessible because site is offline .
*/
2011-11-29 09:56:53 +00:00
const MENU_SITE_OFFLINE = 4 ;
2004-09-09 05:51:08 +00:00
2010-07-07 08:05:01 +00:00
/**
* Internal menu status code -- Everything is working fine .
*/
2011-11-29 09:56:53 +00:00
const MENU_SITE_ONLINE = 5 ;
2010-07-07 08:05:01 +00:00
2004-07-10 15:51:48 +00:00
/**
2012-05-17 12:58:49 +00:00
* @ } End of " defgroup menu_status_codes " .
2004-07-10 15:51:48 +00:00
*/
2004-04-15 20:49:42 +00:00
2007-04-06 04:39:51 +00:00
/**
2010-10-08 05:07:53 +00:00
* @ defgroup menu_tree_parameters Menu tree parameters
2007-04-06 04:39:51 +00:00
* @ {
2011-01-03 18:03:54 +00:00
* Parameters for a menu tree .
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
*
* @ todo Move this constant to MenuLinkStorageController along with all the tree
* 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
2007-04-06 04:39:51 +00:00
/**
2012-05-17 12:58:49 +00:00
* @ } End of " defgroup menu_tree_parameters " .
2007-02-11 09:30:51 +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-06-30 19:46:58 +00:00
/**
* Generates elements for the $arg array in the help hook .
*/
function drupal_help_arg ( $arg = array ()) {
// Note - the number of empty elements should be > MENU_MAX_PARTS.
return $arg + array ( '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' );
}
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 ) {
$config = \Drupal :: config ( 'menu.settings' );
return \Drupal :: moduleHandler () -> moduleExists ( 'menu' ) ? $config -> get ( $name ) : $default ;
}
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' ;
}
2012-04-29 15:16:27 +00:00
// Normally, l() compares the href of every link with the current path and
// sets the active class accordingly. But local tasks do not appear in
// menu trees, so if the current path is a local task, and this link is
// its tab root, then we have to set the class manually.
2014-02-24 19:38:23 +00:00
if ( $item [ 'link' ][ 'href' ] != current_path ()) {
2010-09-24 00:37:45 +00:00
$l [ 'attributes' ][ 'class' ][] = 'active' ;
2008-05-05 20:55:13 +00:00
}
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 ();
2013-09-21 15:23:51 +00:00
$route_name = \Drupal :: request () -> attributes -> get ( RouteObjectInterface :: ROUTE_NAME );
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.
2013-02-18 17:03:05 +00:00
return count ( element_get_visible_children ( $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.
2013-02-18 17:03:05 +00:00
return count ( element_get_visible_children ( $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 ();
2013-10-01 22:24:51 +00:00
$route_name = Drupal :: request () -> attributes -> get ( RouteObjectInterface :: ROUTE_NAME );
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
}
2007-09-06 12:47:20 +00:00
/**
2012-08-31 15:56:36 +00:00
* Sets the active path , which determines which page is loaded .
2007-09-06 12:47:20 +00:00
*
* Note that this may not have the desired effect unless invoked very early
2013-02-06 11:50:46 +00:00
* in the page load or unless you do a subrequest to generate your page output .
2011-09-27 16:33:35 +00:00
*
* @ param $path
* A Drupal path - not a path alias .
2007-09-06 12:47:20 +00:00
*/
function menu_set_active_item ( $path ) {
2012-03-27 06:53:33 +00:00
// Since the active item has changed, the active menu trail may also be out
// of date.
drupal_static_reset ( 'menu_set_active_trail' );
2012-04-29 15:16:27 +00:00
// @todo Refactor to use the Symfony Request object.
_current_path ( $path );
2004-09-16 07:17:56 +00:00
}
2007-07-04 15:49:44 +00:00
/**
2012-08-31 15:56:36 +00:00
* Sets the active trail ( path to the menu tree root ) of the current page .
2011-09-27 16:33:35 +00:00
*
* Any trail set by this function will only be used for functionality that calls
* menu_get_active_trail () . Drupal core only uses trails set here for
2013-09-26 08:32:39 +00:00
* the page title and not for menu trees or page content .
2011-09-27 16:33:35 +00:00
*
* To affect the trail used by menu trees , use menu_tree_set_path () . To affect
* the page content , use menu_set_active_item () instead .
2009-10-01 19:07:12 +00:00
*
* @ param $new_trail
2011-09-27 16:33:35 +00:00
* Menu trail to set ; the value is saved in a static variable and can be
* retrieved by menu_get_active_trail () . The format of this array should be
* the same as the return value of menu_get_active_trail () .
2010-03-26 17:14:46 +00:00
*
2009-10-01 19:07:12 +00:00
* @ return
2011-09-27 16:33:35 +00:00
* The active trail . See menu_get_active_trail () for details .
2007-07-04 15:49:44 +00:00
*/
2007-05-16 13:45:17 +00:00
function menu_set_active_trail ( $new_trail = NULL ) {
2009-04-25 15:19:12 +00:00
$trail = & drupal_static ( __FUNCTION__ );
2007-05-16 13:45:17 +00:00
2014-03-26 19:56:01 +00:00
/** @var \Drupal\menu_link\MenuTreeInterface $menu_tree */
$menu_tree = \Drupal :: service ( 'menu_link.tree' );
2007-05-16 13:45:17 +00:00
if ( isset ( $new_trail )) {
$trail = $new_trail ;
}
elseif ( ! isset ( $trail )) {
$trail = array ();
2010-09-24 00:37:45 +00:00
$trail [] = array (
'title' => t ( 'Home' ),
'href' => '<front>' ,
'link_path' => '' ,
'localized_options' => array (),
'type' => 0 ,
);
// Try to retrieve a menu link corresponding to the current path. If more
// than one exists, the link from the most preferred menu is returned.
$preferred_link = menu_link_get_preferred ();
// There is a link for the current path.
if ( $preferred_link ) {
2014-01-29 08:25:00 +00:00
_menu_link_translate ( $preferred_link );
2010-09-24 00:37:45 +00:00
// Pass TRUE for $only_active_trail to make menu_tree_page_data() build
// a stripped down menu tree containing the active trail only, in case
// the given menu has not been built in this request yet.
2014-03-26 19:56:01 +00:00
$tree = $menu_tree -> buildPageData ( $preferred_link [ 'menu_name' ], NULL , TRUE );
2010-09-24 00:37:45 +00:00
list ( $key , $curr ) = each ( $tree );
2007-05-16 13:45:17 +00:00
}
2010-09-24 00:37:45 +00:00
// There is no link for the current path.
else {
$curr = FALSE ;
2009-03-20 19:18:11 +00:00
}
2007-05-16 13:45:17 +00:00
while ( $curr ) {
2010-09-24 00:37:45 +00:00
$link = $curr [ 'link' ];
if ( $link [ 'in_active_trail' ]) {
// Add the link to the trail, unless it links to its parent.
if ( ! ( $link [ 'type' ] & MENU_LINKS_TO_PARENT )) {
// The menu tree for the active trail may contain additional links
// that have not been translated yet, since they contain dynamic
// argument placeholders (%). Such links are not contained in regular
// menu trees, and have only been loaded for the additional
// translation that happens here, so as to be able to display them in
// the breadcumb for the current page.
// @see _menu_tree_check_access()
// @see _menu_link_translate()
if ( strpos ( $link [ 'href' ], '%' ) !== FALSE ) {
2014-01-29 08:25:00 +00:00
_menu_link_translate ( $link );
2010-09-24 00:37:45 +00:00
}
if ( $link [ 'access' ]) {
$trail [] = $link ;
}
2007-05-16 13:45:17 +00:00
}
2010-09-24 00:37:45 +00:00
$tree = $curr [ 'below' ] ? $curr [ 'below' ] : array ();
2007-05-16 13:45:17 +00:00
}
2010-09-24 00:37:45 +00:00
list ( $key , $curr ) = each ( $tree );
2007-05-16 13:45:17 +00:00
}
2011-04-21 02:29:38 +00:00
// Make sure the current page is in the trail to build the page title, by
// appending either the preferred link or the menu router item for the
// current page. Exclude it if we are on the front page.
2010-09-24 00:37:45 +00:00
$last = end ( $trail );
2012-03-27 06:53:33 +00:00
if ( $preferred_link && $last [ 'href' ] != $preferred_link [ 'href' ] && ! drupal_is_front_page ()) {
2010-09-24 00:37:45 +00:00
$trail [] = $preferred_link ;
2007-09-07 20:31:02 +00:00
}
2007-05-16 13:45:17 +00:00
}
return $trail ;
}
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-07-04 15:49:44 +00:00
/**
2009-10-01 19:07:12 +00:00
* Gets the active trail ( path to root menu root ) of the current page .
*
2011-09-27 16:33:35 +00:00
* If a trail is supplied to menu_set_active_trail (), that value is returned . If
* a trail is not supplied to menu_set_active_trail (), the path to the current
* page is calculated and returned . The calculated trail is also saved as a
* static value for use by subsequent calls to menu_get_active_trail () .
*
* @ return
* Path to menu root of the current page , as an array of menu link items ,
* starting with the site ' s home page . Each link item is an associative array
* with the following components :
* - title : Title of the item .
* - href : Drupal path of the item .
* - localized_options : Options for passing into the l () function .
* - type : A menu type constant , such as MENU_DEFAULT_LOCAL_TASK , or 0 to
* indicate it ' s not really in the menu ( used for the home page item ) .
2007-07-04 15:49:44 +00:00
*/
2007-05-16 13:45:17 +00:00
function menu_get_active_trail () {
return menu_set_active_trail ();
}
/**
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 ()
2013-02-08 23:55:25 +00:00
-> getStorageController ( 'menu_link' ) -> resetCache ();
2010-07-06 07:27:07 +00:00
drupal_static_reset ( '_menu_build_tree' );
#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
drupal_static_reset ( 'menu_tree' );
drupal_static_reset ( 'menu_tree_all_data' );
drupal_static_reset ( 'menu_tree_page_data' );
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 ]);
}
/**
* Gets all default menu link definitions .
2013-02-08 23:55:25 +00:00
*
2014-01-29 08:25:00 +00:00
* @ return array
* An array of default menu links .
2007-06-05 09:15:02 +00:00
*/
2014-01-29 08:25:00 +00:00
function menu_link_get_defaults () {
$module_handler = \Drupal :: moduleHandler ();
$all_links = $module_handler -> invokeAll ( 'menu_link_defaults' );
// Fill in the machine name from the array key.
foreach ( $all_links as $machine_name => & $link ) {
$link [ 'machine_name' ] = $machine_name ;
}
$module_handler -> alter ( 'menu_link_defaults' , $all_links );
return $all_links ;
}
/**
* Builds menu links for the items returned from hook_menu_link_defaults () .
*/
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-01-29 08:25:00 +00:00
/** @var \Drupal\menu_link\MenuLinkStorageControllerInterface $menu_link_storage */
$menu_link_storage = \Drupal :: entityManager ()
2013-08-12 13:28:56 +00:00
-> getStorageController ( 'menu_link' );
2014-01-29 08:25:00 +00:00
$links = array ();
$children = array ();
$top_links = array ();
$all_links = menu_link_get_defaults ();
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 )
2008-12-03 14:38:59 +00:00
-> condition ( 'module' , 'system' )
2014-01-29 08:25:00 +00:00
-> 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-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 ) {
// A change in hook_menu_link_defaults() 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 ;
}
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' ])) {
watchdog ( '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 );
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-01-29 08:25:00 +00:00
// Find any item whose entry in hook_menu_link_defaults() no longer exists.
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
/**
2012-08-31 15:56:36 +00:00
* Clears the page and block caches at most twice per page load .
2007-12-08 14:06:23 +00:00
*/
2007-11-26 08:49:03 +00:00
function _menu_clear_page_cache () {
2009-04-25 15:19:12 +00:00
$cache_cleared = & drupal_static ( __FUNCTION__ , 0 );
2007-11-26 08:49:03 +00:00
2007-11-26 16:19:37 +00:00
// Clear the page and block caches, 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.
2009-04-25 15:19:12 +00:00
if ( $cache_cleared == 0 ) {
2014-02-21 15:21:08 +00:00
Cache :: invalidateTags ( array ( 'content' => TRUE ));
2007-11-26 08:49:03 +00:00
// Keep track of which menus have expanded items.
_menu_set_expanded_menus ();
$cache_cleared = 1 ;
}
elseif ( $cache_cleared == 1 ) {
2014-02-21 15:21:08 +00:00
drupal_register_shutdown_function ( 'Drupal\Core\Cache\Cache::invalidateTags' , array ( 'content' => TRUE ));
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' );
2007-11-26 08:49:03 +00:00
$cache_cleared = 2 ;
}
}
/**
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
}
2007-05-26 10:54:12 +00:00
/**
2009-08-22 18:24:14 +00:00
* Checks whether the site is in maintenance mode .
2008-01-02 14:29:32 +00:00
*
* This function will log the current user out and redirect to front page
2009-08-22 18:24:14 +00:00
* if the current user has no 'access site in maintenance mode' permission .
2008-01-02 14:29:32 +00:00
*
2009-09-30 13:09:30 +00:00
* @ param $check_only
* If this is set to TRUE , the function will perform the access checks and
* return the site offline status , but not log the user out or display any
* messages .
2010-03-26 17:14:46 +00:00
*
2008-01-02 14:29:32 +00:00
* @ return
2009-08-22 18:24:14 +00:00
* FALSE if the site is not in maintenance mode , the user login page is
* displayed , or the user has the 'access site in maintenance mode'
* permission . TRUE for anonymous users not being on the login page when the
* site is in maintenance mode .
2007-05-26 10:54:12 +00:00
*/
2009-09-30 13:09:30 +00:00
function _menu_site_is_offline ( $check_only = FALSE ) {
2009-08-22 18:24:14 +00:00
// Check if site is in maintenance mode.
2013-09-16 03:58:06 +00:00
if ( \Drupal :: state () -> get ( 'system.maintenance_mode' )) {
2009-08-22 18:24:14 +00:00
if ( user_access ( 'access site in maintenance mode' )) {
// Ensure that the maintenance mode message is displayed only once
// (allowing for page redirects) and specifically suppress its display on
// the maintenance mode settings page.
2012-04-29 15:16:27 +00:00
if ( ! $check_only && current_path () != 'admin/config/development/maintenance' ) {
2009-08-22 18:24:14 +00:00
if ( user_access ( 'administer site configuration' )) {
drupal_set_message ( t ( 'Operating in maintenance mode. <a href="@url">Go online.</a>' , array ( '@url' => url ( 'admin/config/development/maintenance' ))), 'status' , FALSE );
}
else {
drupal_set_message ( t ( 'Operating in maintenance mode.' ), 'status' , FALSE );
}
2008-02-10 07:35:40 +00:00
}
2008-01-02 14:29:32 +00:00
}
else {
2010-07-07 08:05:01 +00:00
return TRUE ;
2007-05-26 10:54:12 +00:00
}
}
return FALSE ;
}
2008-01-03 09:59:00 +00:00
2008-01-28 16:05:17 +00:00
/**
* @ } End of " defgroup menu " .
*/