Issue #916388 by disasm, podarok, dawehner, andypost, amateescu, Dave Reid: Convert menu links into entities.
parent
167e70ba00
commit
e72ff70409
|
@ -243,6 +243,11 @@ Locale module
|
|||
Menu module
|
||||
- ?
|
||||
|
||||
Menu Link module
|
||||
- Andrei Mateescu 'amateescu' http://drupal.org/user/729614
|
||||
- Károly Négyesi 'chx' http://drupal.org/user/9446
|
||||
- @todo Anyone else from the menu system?
|
||||
|
||||
Node module
|
||||
- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
|
||||
- David Strauss 'David Strauss' http://drupal.org/user/93254
|
||||
|
|
|
@ -5,8 +5,11 @@
|
|||
* API for the Drupal menu system.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
|
||||
use Drupal\menu_link\MenuLinkStorageController;
|
||||
|
||||
/**
|
||||
* @defgroup menu Menu system
|
||||
|
@ -264,6 +267,9 @@ const MENU_MAX_PARTS = 9;
|
|||
|
||||
/**
|
||||
* The maximum depth of a menu links tree - matches the number of p columns.
|
||||
*
|
||||
* @todo Move this constant to MenuLinkStorageController along with all the tree
|
||||
* functionality.
|
||||
*/
|
||||
const MENU_MAX_DEPTH = 9;
|
||||
|
||||
|
@ -1260,20 +1266,15 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail =
|
|||
// Collect all the links set to be expanded, and then add all of
|
||||
// their children to the list as well.
|
||||
do {
|
||||
$result = db_select('menu_links', NULL, array('fetch' => PDO::FETCH_ASSOC))
|
||||
->fields('menu_links', array('mlid'))
|
||||
$query = entity_query('menu_link')
|
||||
->condition('menu_name', $menu_name)
|
||||
->condition('expanded', 1)
|
||||
->condition('has_children', 1)
|
||||
->condition('plid', $parents, 'IN')
|
||||
->condition('mlid', $parents, 'NOT IN')
|
||||
->execute();
|
||||
$num_rows = FALSE;
|
||||
foreach ($result as $item) {
|
||||
$parents[$item['mlid']] = $item['mlid'];
|
||||
$num_rows = TRUE;
|
||||
}
|
||||
} while ($num_rows);
|
||||
->condition('mlid', $parents, 'NOT IN');
|
||||
$result = $query->execute();
|
||||
$parents += $result;
|
||||
} while (!empty($result));
|
||||
}
|
||||
$tree_parameters['expanded'] = $parents;
|
||||
$tree_parameters['active_trail'] = $active_trail;
|
||||
|
@ -1360,48 +1361,23 @@ function _menu_build_tree($menu_name, array $parameters = array()) {
|
|||
}
|
||||
|
||||
if (!isset($trees[$tree_cid])) {
|
||||
// Select the links from the table, and recursively build the tree. We
|
||||
// LEFT JOIN since there is no match in {menu_router} for an external
|
||||
// link.
|
||||
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
|
||||
$query->addTag('translatable');
|
||||
$query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
|
||||
$query->fields('ml');
|
||||
$query->fields('m', array(
|
||||
'load_functions',
|
||||
'to_arg_functions',
|
||||
'access_callback',
|
||||
'access_arguments',
|
||||
'page_callback',
|
||||
'page_arguments',
|
||||
'tab_parent',
|
||||
'tab_root',
|
||||
'title',
|
||||
'title_callback',
|
||||
'title_arguments',
|
||||
'theme_callback',
|
||||
'theme_arguments',
|
||||
'type',
|
||||
'description',
|
||||
'description_callback',
|
||||
'description_arguments',
|
||||
));
|
||||
$query = entity_query('menu_link');
|
||||
for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) {
|
||||
$query->orderBy('p' . $i, 'ASC');
|
||||
$query->sort('p' . $i, 'ASC');
|
||||
}
|
||||
$query->condition('ml.menu_name', $menu_name);
|
||||
$query->condition('menu_name', $menu_name);
|
||||
if (!empty($parameters['expanded'])) {
|
||||
$query->condition('ml.plid', $parameters['expanded'], 'IN');
|
||||
$query->condition('plid', $parameters['expanded'], 'IN');
|
||||
}
|
||||
elseif (!empty($parameters['only_active_trail'])) {
|
||||
$query->condition('ml.mlid', $parameters['active_trail'], 'IN');
|
||||
$query->condition('mlid', $parameters['active_trail'], 'IN');
|
||||
}
|
||||
$min_depth = (isset($parameters['min_depth']) ? $parameters['min_depth'] : 1);
|
||||
if ($min_depth != 1) {
|
||||
$query->condition('ml.depth', $min_depth, '>=');
|
||||
$query->condition('depth', $min_depth, '>=');
|
||||
}
|
||||
if (isset($parameters['max_depth'])) {
|
||||
$query->condition('ml.depth', $parameters['max_depth'], '<=');
|
||||
$query->condition('depth', $parameters['max_depth'], '<=');
|
||||
}
|
||||
// Add custom query conditions, if any were passed.
|
||||
if (isset($parameters['conditions'])) {
|
||||
|
@ -1412,8 +1388,8 @@ function _menu_build_tree($menu_name, array $parameters = array()) {
|
|||
|
||||
// Build an ordered array of links using the query result object.
|
||||
$links = array();
|
||||
foreach ($query->execute() as $item) {
|
||||
$links[] = $item;
|
||||
if ($result = $query->execute()) {
|
||||
$links = menu_link_load_multiple($result);
|
||||
}
|
||||
$active_trail = (isset($parameters['active_trail']) ? $parameters['active_trail'] : array());
|
||||
$data['tree'] = menu_tree_data($links, $active_trail, $min_depth);
|
||||
|
@ -2450,18 +2426,11 @@ function menu_link_get_preferred($path = NULL, $selected_menu = NULL) {
|
|||
// Put the selected menu at the front of the list.
|
||||
array_unshift($menu_names, $selected_menu);
|
||||
|
||||
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
|
||||
$query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
|
||||
$query->fields('ml');
|
||||
// Weight must be taken from {menu_links}, not {menu_router}.
|
||||
$query->addField('ml', 'weight', 'link_weight');
|
||||
$query->fields('m');
|
||||
$query->condition('ml.link_path', $path_candidates, 'IN');
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $path_candidates));
|
||||
|
||||
// Sort candidates by link path and menu name.
|
||||
$candidates = array();
|
||||
foreach ($query->execute() as $candidate) {
|
||||
$candidate['weight'] = $candidate['link_weight'];
|
||||
foreach ($menu_links as $candidate) {
|
||||
$candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate;
|
||||
// Add any menus not already in the menu name search list.
|
||||
if (!in_array($candidate['menu_name'], $menu_names)) {
|
||||
|
@ -2580,38 +2549,6 @@ function menu_get_active_title() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a translated, access-checked menu link that is ready for rendering.
|
||||
*
|
||||
* This function should never be called from within node_load() or any other
|
||||
* function used as a menu object load function since an infinite recursion may
|
||||
* occur.
|
||||
*
|
||||
* @param $mlid
|
||||
* The mlid of the menu item.
|
||||
*
|
||||
* @return
|
||||
* A menu link, with $item['access'] filled and link translated for
|
||||
* rendering.
|
||||
*/
|
||||
function menu_link_load($mlid) {
|
||||
if (is_numeric($mlid)) {
|
||||
$query = db_select('menu_links', 'ml');
|
||||
$query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
|
||||
$query->fields('ml');
|
||||
// Weight should be taken from {menu_links}, not {menu_router}.
|
||||
$query->addField('ml', 'weight', 'link_weight');
|
||||
$query->fields('m');
|
||||
$query->condition('ml.mlid', $mlid);
|
||||
if ($item = $query->execute()->fetchAssoc()) {
|
||||
$item['weight'] = $item['link_weight'];
|
||||
_menu_link_translate($item);
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cached cached data for a single named menu.
|
||||
*/
|
||||
|
@ -2636,6 +2573,8 @@ function menu_cache_clear_all() {
|
|||
* Resets the menu system static cache.
|
||||
*/
|
||||
function menu_reset_static_cache() {
|
||||
drupal_container()->get('plugin.manager.entity')
|
||||
->getStorageController('menu_link')->resetCache();
|
||||
drupal_static_reset('_menu_build_tree');
|
||||
drupal_static_reset('menu_tree');
|
||||
drupal_static_reset('menu_tree_all_data');
|
||||
|
@ -2732,157 +2671,118 @@ function menu_get_router() {
|
|||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a link from a router item.
|
||||
*/
|
||||
function _menu_link_build($item) {
|
||||
// Suggested items are disabled by default.
|
||||
if ($item['type'] == MENU_SUGGESTED_ITEM) {
|
||||
$item['hidden'] = 1;
|
||||
}
|
||||
// Hide all items that are not visible in the tree.
|
||||
elseif (!($item['type'] & MENU_VISIBLE_IN_TREE)) {
|
||||
$item['hidden'] = -1;
|
||||
}
|
||||
// Note, we set this as 'system', so that we can be sure to distinguish all
|
||||
// the menu links generated automatically from entries in {menu_router}.
|
||||
$item['module'] = 'system';
|
||||
$item += array(
|
||||
'menu_name' => 'tools',
|
||||
'link_title' => $item['title'],
|
||||
'link_path' => $item['path'],
|
||||
'hidden' => 0,
|
||||
'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])),
|
||||
);
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds menu links for the items in the menu router.
|
||||
*
|
||||
* @todo This function should be removed/refactored.
|
||||
*/
|
||||
function _menu_navigation_links_rebuild($menu) {
|
||||
if (module_exists('menu_link')) {
|
||||
$menu_link_controller = drupal_container()->get('plugin.manager.entity')
|
||||
->getStorageController('menu_link');
|
||||
}
|
||||
else {
|
||||
// The Menu link module is not available at install time, so we need to
|
||||
// hardcode the default storage controller.
|
||||
$menu_link_controller = new MenuLinkStorageController('menu_link');
|
||||
}
|
||||
|
||||
// Add normal and suggested items as links.
|
||||
$menu_links = array();
|
||||
foreach ($menu as $path => $item) {
|
||||
if ($item['_visible']) {
|
||||
$menu_links[$path] = $item;
|
||||
$sort[$path] = $item['_number_parts'];
|
||||
$router_items = array();
|
||||
foreach ($menu as $path => $router_item) {
|
||||
if ($router_item['_visible']) {
|
||||
$router_items[$path] = $router_item;
|
||||
$sort[$path] = $router_item['_number_parts'];
|
||||
}
|
||||
}
|
||||
if ($menu_links) {
|
||||
// Keep an array of processed menu links, to allow menu_link_save() to
|
||||
// check this for parents instead of querying the database.
|
||||
if ($router_items) {
|
||||
// Keep an array of processed menu links, to allow
|
||||
// Drupal\menu_link\MenuLinkStorageController::save() to check this for
|
||||
// parents instead of querying the database.
|
||||
$parent_candidates = array();
|
||||
// Make sure no child comes before its parent.
|
||||
array_multisort($sort, SORT_NUMERIC, $menu_links);
|
||||
array_multisort($sort, SORT_NUMERIC, $router_items);
|
||||
|
||||
foreach ($menu_links as $key => $item) {
|
||||
foreach ($router_items as $key => $router_item) {
|
||||
// For performance reasons, do a straight query now and convert to a menu
|
||||
// link entity later.
|
||||
// @todo revisit before release.
|
||||
$existing_item = db_select('menu_links')
|
||||
->fields('menu_links')
|
||||
->condition('link_path', $item['path'])
|
||||
->condition('link_path', $router_item['path'])
|
||||
->condition('module', 'system')
|
||||
->execute()->fetchAssoc();
|
||||
->execute()->fetchAll();
|
||||
if ($existing_item) {
|
||||
$item['mlid'] = $existing_item['mlid'];
|
||||
$existing_item = reset($existing_item);
|
||||
$existing_item->options = unserialize($existing_item->options);
|
||||
|
||||
$router_item['mlid'] = $existing_item->mlid;
|
||||
$router_item['uuid'] = $existing_item->uuid;
|
||||
// A change in hook_menu may move the link to a different menu
|
||||
if (empty($item['menu_name']) || ($item['menu_name'] == $existing_item['menu_name'])) {
|
||||
$item['menu_name'] = $existing_item['menu_name'];
|
||||
$item['plid'] = $existing_item['plid'];
|
||||
if (empty($router_item['menu_name']) || ($router_item['menu_name'] == $existing_item->menu_name)) {
|
||||
$router_item['menu_name'] = $existing_item->menu_name;
|
||||
$router_item['plid'] = $existing_item->plid;
|
||||
}
|
||||
else {
|
||||
// It moved to a new menu. Let menu_link_save() try to find a new
|
||||
// parent based on the path.
|
||||
unset($item['plid']);
|
||||
// It moved to a new menu.
|
||||
// Let Drupal\menu_link\MenuLinkStorageController::save() try to find
|
||||
// a new parent based on the path.
|
||||
unset($router_item['plid']);
|
||||
}
|
||||
$item['has_children'] = $existing_item['has_children'];
|
||||
$item['updated'] = $existing_item['updated'];
|
||||
}
|
||||
if ($existing_item && $existing_item['customized']) {
|
||||
$parent_candidates[$existing_item['mlid']] = $existing_item;
|
||||
$router_item['has_children'] = $existing_item->has_children;
|
||||
$router_item['updated'] = $existing_item->updated;
|
||||
|
||||
// Convert the existing item to a typed object.
|
||||
$existing_item = $menu_link_controller->create(get_object_vars($existing_item));
|
||||
}
|
||||
else {
|
||||
$item = _menu_link_build($item);
|
||||
menu_link_save($item, $existing_item, $parent_candidates);
|
||||
$parent_candidates[$item['mlid']] = $item;
|
||||
unset($menu_links[$key]);
|
||||
$existing_item = NULL;
|
||||
}
|
||||
|
||||
if ($existing_item && $existing_item->customized) {
|
||||
$parent_candidates[$existing_item->mlid] = $existing_item;
|
||||
}
|
||||
else {
|
||||
$menu_link = MenuLink::buildFromRouterItem($router_item);
|
||||
$menu_link->original = $existing_item;
|
||||
$menu_link->parentCandidates = $parent_candidates;
|
||||
$menu_link_controller->save($menu_link);
|
||||
$parent_candidates[$menu_link->id()] = $menu_link;
|
||||
unset($router_items[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$paths = array_keys($menu);
|
||||
// Updated and customized items whose router paths are gone need new ones.
|
||||
$result = db_select('menu_links', NULL, array('fetch' => PDO::FETCH_ASSOC))
|
||||
->fields('menu_links', array(
|
||||
'link_path',
|
||||
'mlid',
|
||||
'router_path',
|
||||
'updated',
|
||||
))
|
||||
->condition(db_or()
|
||||
->condition('updated', 1)
|
||||
->condition(db_and()
|
||||
->condition('router_path', $paths, 'NOT IN')
|
||||
->condition('external', 0)
|
||||
->condition('customized', 1)
|
||||
)
|
||||
)
|
||||
->execute();
|
||||
foreach ($result as $item) {
|
||||
$router_path = _menu_find_router_path($item['link_path']);
|
||||
if (!empty($router_path) && ($router_path != $item['router_path'] || $item['updated'])) {
|
||||
$menu_links = $menu_link_controller->loadUpdatedCustomized($paths);
|
||||
foreach ($menu_links as $menu_link) {
|
||||
$router_path = _menu_find_router_path($menu_link->link_path);
|
||||
if (!empty($router_path) && ($router_path != $menu_link->router_path || $menu_link->updated)) {
|
||||
// If the router path and the link path matches, it's surely a working
|
||||
// item, so we clear the updated flag.
|
||||
$updated = $item['updated'] && $router_path != $item['link_path'];
|
||||
db_update('menu_links')
|
||||
->fields(array(
|
||||
'router_path' => $router_path,
|
||||
'updated' => (int) $updated,
|
||||
))
|
||||
->condition('mlid', $item['mlid'])
|
||||
->execute();
|
||||
$updated = $menu_link->updated && $router_path != $menu_link->link_path;
|
||||
|
||||
$menu_link->router_path = $router_path;
|
||||
$menu_link->updated = (int) $updated;
|
||||
$menu_link_controller->save($menu_link);
|
||||
}
|
||||
}
|
||||
|
||||
// Find any item whose router path does not exist any more.
|
||||
$result = db_select('menu_links')
|
||||
->fields('menu_links')
|
||||
$query = entity_query('menu_link')
|
||||
->condition('router_path', $paths, 'NOT IN')
|
||||
->condition('external', 0)
|
||||
->condition('updated', 0)
|
||||
->condition('customized', 0)
|
||||
->orderBy('depth', 'DESC')
|
||||
->execute();
|
||||
// Remove all such items. Starting from those with the greatest depth will
|
||||
// minimize the amount of re-parenting done by menu_link_delete().
|
||||
foreach ($result as $item) {
|
||||
_menu_delete_item($item, TRUE);
|
||||
}
|
||||
}
|
||||
->sort('depth', 'DESC');
|
||||
$result = $query->execute();
|
||||
|
||||
/**
|
||||
* Clones an array of menu links.
|
||||
*
|
||||
* @param $links
|
||||
* An array of menu links to clone.
|
||||
* @param $menu_name
|
||||
* (optional) The name of a menu that the links will be cloned for. If not
|
||||
* set, the cloned links will be in the same menu as the original set of
|
||||
* links that were passed in.
|
||||
*
|
||||
* @return
|
||||
* An array of menu links with the same properties as the passed-in array,
|
||||
* but with the link identifiers removed so that a new link will be created
|
||||
* when any of them is passed in to menu_link_save().
|
||||
*
|
||||
* @see menu_link_save()
|
||||
*/
|
||||
function menu_links_clone($links, $menu_name = NULL) {
|
||||
foreach ($links as &$link) {
|
||||
unset($link['mlid']);
|
||||
unset($link['plid']);
|
||||
if (isset($menu_name)) {
|
||||
$link['menu_name'] = $menu_name;
|
||||
}
|
||||
// Remove all such items. Starting from those with the greatest depth will
|
||||
// minimize the amount of re-parenting done by the menu link controller.
|
||||
if (!empty($result)) {
|
||||
menu_link_delete_multiple($result, TRUE);
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2895,18 +2795,19 @@ function menu_links_clone($links, $menu_name = NULL) {
|
|||
* An array of menu links.
|
||||
*/
|
||||
function menu_load_links($menu_name) {
|
||||
$links = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC))
|
||||
->fields('ml')
|
||||
->condition('ml.menu_name', $menu_name)
|
||||
$links = array();
|
||||
|
||||
$query = entity_query('menu_link')
|
||||
->condition('menu_name', $menu_name)
|
||||
// Order by weight so as to be helpful for menus that are only one level
|
||||
// deep.
|
||||
->orderBy('weight')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
->sort('weight');
|
||||
$result = $query->execute();
|
||||
|
||||
foreach ($links as &$link) {
|
||||
$link['options'] = unserialize($link['options']);
|
||||
if (!empty($result)) {
|
||||
$links = menu_link_load_multiple($result);
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
|
@ -2918,333 +2819,7 @@ function menu_load_links($menu_name) {
|
|||
*/
|
||||
function menu_delete_links($menu_name) {
|
||||
$links = menu_load_links($menu_name);
|
||||
foreach ($links as $link) {
|
||||
// To speed up the deletion process, we reset some link properties that
|
||||
// would trigger re-parenting logic in _menu_delete_item() and
|
||||
// _menu_update_parental_status().
|
||||
$link['has_children'] = FALSE;
|
||||
$link['plid'] = 0;
|
||||
_menu_delete_item($link);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete one or several menu links.
|
||||
*
|
||||
* @param $mlid
|
||||
* A valid menu link mlid or NULL. If NULL, $path is used.
|
||||
* @param $path
|
||||
* The path to the menu items to be deleted. $mlid must be NULL.
|
||||
*/
|
||||
function menu_link_delete($mlid, $path = NULL) {
|
||||
if (isset($mlid)) {
|
||||
_menu_delete_item(db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchAssoc());
|
||||
}
|
||||
else {
|
||||
$result = db_query("SELECT * FROM {menu_links} WHERE link_path = :link_path", array(':link_path' => $path));
|
||||
foreach ($result as $link) {
|
||||
_menu_delete_item($link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a single menu link.
|
||||
*
|
||||
* @param $item
|
||||
* Item to be deleted.
|
||||
* @param $force
|
||||
* Forces deletion. Internal use only, setting to TRUE is discouraged.
|
||||
*
|
||||
* @see menu_link_delete()
|
||||
*/
|
||||
function _menu_delete_item($item, $force = FALSE) {
|
||||
$item = is_object($item) ? get_object_vars($item) : $item;
|
||||
if ($item && ($item['module'] != 'system' || $item['updated'] || $force)) {
|
||||
// Children get re-attached to the item's parent.
|
||||
if ($item['has_children']) {
|
||||
$result = db_query("SELECT mlid FROM {menu_links} WHERE plid = :plid", array(':plid' => $item['mlid']));
|
||||
foreach ($result as $m) {
|
||||
$child = menu_link_load($m->mlid);
|
||||
$child['plid'] = $item['plid'];
|
||||
menu_link_save($child);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify modules we are deleting the item.
|
||||
module_invoke_all('menu_link_delete', $item);
|
||||
|
||||
db_delete('menu_links')->condition('mlid', $item['mlid'])->execute();
|
||||
|
||||
// Update the has_children status of the parent.
|
||||
_menu_update_parental_status($item);
|
||||
menu_cache_clear($item['menu_name']);
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a menu link.
|
||||
*
|
||||
* After calling this function, rebuild the menu cache using
|
||||
* menu_cache_clear_all().
|
||||
*
|
||||
* @param $item
|
||||
* An associative array representing a menu link item, with elements:
|
||||
* - link_path: (required) The path of the menu item, which should be
|
||||
* normalized first by calling drupal_container()->get('path.alias_manager')->getSystemPath() on it.
|
||||
* - link_title: (required) Title to appear in menu for the link.
|
||||
* - menu_name: (optional) The machine name of the menu for the link.
|
||||
* Defaults to 'tools'.
|
||||
* - weight: (optional) Integer to determine position in menu. Default is 0.
|
||||
* - expanded: (optional) Boolean that determines if the item is expanded.
|
||||
* - options: (optional) An array of options, see l() for more.
|
||||
* - mlid: (optional) Menu link identifier, the primary integer key for each
|
||||
* menu link. Can be set to an existing value, or to 0 or NULL
|
||||
* to insert a new link.
|
||||
* - plid: (optional) The mlid of the parent.
|
||||
* - router_path: (optional) The path of the relevant router item.
|
||||
* @param $existing_item
|
||||
* Optional, the current record from the {menu_links} table as an array.
|
||||
* @param $parent_candidates
|
||||
* Optional array of menu links keyed by mlid. Used by
|
||||
* _menu_navigation_links_rebuild() only.
|
||||
*
|
||||
* @return
|
||||
* The mlid of the saved menu link, or FALSE if the menu link could not be
|
||||
* saved.
|
||||
*/
|
||||
function menu_link_save(&$item, $existing_item = array(), $parent_candidates = array()) {
|
||||
drupal_alter('menu_link', $item);
|
||||
|
||||
// This is the easiest way to handle the unique internal path '<front>',
|
||||
// since a path marked as external does not need to match a router path.
|
||||
$item['external'] = (url_is_external($item['link_path']) || $item['link_path'] == '<front>') ? 1 : 0;
|
||||
// Load defaults.
|
||||
$item += array(
|
||||
'menu_name' => 'tools',
|
||||
'weight' => 0,
|
||||
'link_title' => '',
|
||||
'hidden' => 0,
|
||||
'has_children' => 0,
|
||||
'expanded' => 0,
|
||||
'options' => array(),
|
||||
'module' => 'menu',
|
||||
'customized' => 0,
|
||||
'updated' => 0,
|
||||
);
|
||||
if (isset($item['mlid'])) {
|
||||
if (!$existing_item) {
|
||||
$existing_item = db_query('SELECT * FROM {menu_links} WHERE mlid = :mlid', array('mlid' => $item['mlid']))->fetchAssoc();
|
||||
}
|
||||
if ($existing_item) {
|
||||
$existing_item['options'] = unserialize($existing_item['options']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$existing_item = FALSE;
|
||||
}
|
||||
|
||||
// Try to find a parent link. If found, assign it and derive its menu.
|
||||
$parent = _menu_link_find_parent($item, $parent_candidates);
|
||||
if (!empty($parent['mlid'])) {
|
||||
$item['plid'] = $parent['mlid'];
|
||||
$item['menu_name'] = $parent['menu_name'];
|
||||
}
|
||||
// If no corresponding parent link was found, move the link to the top-level.
|
||||
else {
|
||||
$item['plid'] = 0;
|
||||
}
|
||||
$menu_name = $item['menu_name'];
|
||||
|
||||
if (!$existing_item) {
|
||||
$item['mlid'] = db_insert('menu_links')
|
||||
->fields(array(
|
||||
'menu_name' => $item['menu_name'],
|
||||
'plid' => $item['plid'],
|
||||
'link_path' => $item['link_path'],
|
||||
'hidden' => $item['hidden'],
|
||||
'external' => $item['external'],
|
||||
'has_children' => $item['has_children'],
|
||||
'expanded' => $item['expanded'],
|
||||
'weight' => $item['weight'],
|
||||
'module' => $item['module'],
|
||||
'link_title' => $item['link_title'],
|
||||
'options' => serialize($item['options']),
|
||||
'customized' => $item['customized'],
|
||||
'updated' => $item['updated'],
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Directly fill parents for top-level links.
|
||||
if ($item['plid'] == 0) {
|
||||
$item['p1'] = $item['mlid'];
|
||||
for ($i = 2; $i <= MENU_MAX_DEPTH; $i++) {
|
||||
$item["p$i"] = 0;
|
||||
}
|
||||
$item['depth'] = 1;
|
||||
}
|
||||
// Otherwise, ensure that this link's depth is not beyond the maximum depth
|
||||
// and fill parents based on the parent link.
|
||||
else {
|
||||
if ($item['has_children'] && $existing_item) {
|
||||
$limit = MENU_MAX_DEPTH - menu_link_children_relative_depth($existing_item) - 1;
|
||||
}
|
||||
else {
|
||||
$limit = MENU_MAX_DEPTH - 1;
|
||||
}
|
||||
if ($parent['depth'] > $limit) {
|
||||
return FALSE;
|
||||
}
|
||||
$item['depth'] = $parent['depth'] + 1;
|
||||
_menu_link_parents_set($item, $parent);
|
||||
}
|
||||
// Need to check both plid and menu_name, since plid can be 0 in any menu.
|
||||
if ($existing_item && ($item['plid'] != $existing_item['plid'] || $menu_name != $existing_item['menu_name'])) {
|
||||
_menu_link_move_children($item, $existing_item);
|
||||
}
|
||||
// Find the router_path.
|
||||
if (empty($item['router_path']) || !$existing_item || ($existing_item['link_path'] != $item['link_path'])) {
|
||||
if ($item['external']) {
|
||||
$item['router_path'] = '';
|
||||
}
|
||||
else {
|
||||
// Find the router path which will serve this path.
|
||||
$item['parts'] = explode('/', $item['link_path'], MENU_MAX_PARTS);
|
||||
$item['router_path'] = _menu_find_router_path($item['link_path']);
|
||||
}
|
||||
}
|
||||
// If every value in $existing_item is the same in the $item, there is no
|
||||
// reason to run the update queries or clear the caches. We use
|
||||
// array_intersect_key() with the $item as the first parameter because
|
||||
// $item may have additional keys left over from building a router entry.
|
||||
// The intersect removes the extra keys, allowing a meaningful comparison.
|
||||
if (!$existing_item || (array_intersect_key($item, $existing_item) != $existing_item)) {
|
||||
db_update('menu_links')
|
||||
->fields(array(
|
||||
'menu_name' => $item['menu_name'],
|
||||
'plid' => $item['plid'],
|
||||
'link_path' => $item['link_path'],
|
||||
'router_path' => $item['router_path'],
|
||||
'hidden' => $item['hidden'],
|
||||
'external' => $item['external'],
|
||||
'has_children' => $item['has_children'],
|
||||
'expanded' => $item['expanded'],
|
||||
'weight' => $item['weight'],
|
||||
'depth' => $item['depth'],
|
||||
'p1' => $item['p1'],
|
||||
'p2' => $item['p2'],
|
||||
'p3' => $item['p3'],
|
||||
'p4' => $item['p4'],
|
||||
'p5' => $item['p5'],
|
||||
'p6' => $item['p6'],
|
||||
'p7' => $item['p7'],
|
||||
'p8' => $item['p8'],
|
||||
'p9' => $item['p9'],
|
||||
'module' => $item['module'],
|
||||
'link_title' => $item['link_title'],
|
||||
'options' => serialize($item['options']),
|
||||
'customized' => $item['customized'],
|
||||
))
|
||||
->condition('mlid', $item['mlid'])
|
||||
->execute();
|
||||
// Check the has_children status of the parent.
|
||||
_menu_update_parental_status($item);
|
||||
menu_cache_clear($menu_name);
|
||||
if ($existing_item && $menu_name != $existing_item['menu_name']) {
|
||||
menu_cache_clear($existing_item['menu_name']);
|
||||
}
|
||||
// Notify modules we have acted on a menu item.
|
||||
$hook = 'menu_link_insert';
|
||||
if ($existing_item) {
|
||||
$hook = 'menu_link_update';
|
||||
}
|
||||
module_invoke_all($hook, $item);
|
||||
// Now clear the cache.
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
return $item['mlid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a possible parent for a given menu link.
|
||||
*
|
||||
* Because the parent of a given link might not exist anymore in the database,
|
||||
* we apply a set of heuristics to determine a proper parent:
|
||||
*
|
||||
* - use the passed parent link if specified and existing.
|
||||
* - else, use the first existing link down the previous link hierarchy
|
||||
* - else, for system menu links (derived from hook_menu()), reparent
|
||||
* based on the path hierarchy.
|
||||
*
|
||||
* @param $menu_link
|
||||
* A menu link.
|
||||
* @param $parent_candidates
|
||||
* An array of menu links keyed by mlid.
|
||||
*
|
||||
* @return
|
||||
* A menu link structure of the possible parent or FALSE if no valid parent
|
||||
* has been found.
|
||||
*/
|
||||
function _menu_link_find_parent($menu_link, $parent_candidates = array()) {
|
||||
$parent = FALSE;
|
||||
|
||||
// This item is explicitely top-level, skip the rest of the parenting.
|
||||
if (isset($menu_link['plid']) && empty($menu_link['plid'])) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
// If we have a parent link ID, try to use that.
|
||||
$candidates = array();
|
||||
if (isset($menu_link['plid'])) {
|
||||
$candidates[] = $menu_link['plid'];
|
||||
}
|
||||
|
||||
// Else, if we have a link hierarchy try to find a valid parent in there.
|
||||
if (!empty($menu_link['depth']) && $menu_link['depth'] > 1) {
|
||||
for ($depth = $menu_link['depth'] - 1; $depth >= 1; $depth--) {
|
||||
$candidates[] = $menu_link['p' . $depth];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($candidates as $mlid) {
|
||||
if (isset($parent_candidates[$mlid])) {
|
||||
$parent = $parent_candidates[$mlid];
|
||||
}
|
||||
else {
|
||||
$parent = db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchAssoc();
|
||||
}
|
||||
if ($parent) {
|
||||
return $parent;
|
||||
}
|
||||
}
|
||||
|
||||
// If everything else failed, try to derive the parent from the path
|
||||
// hierarchy. This only makes sense for links derived from menu router
|
||||
// items (ie. from hook_menu()).
|
||||
if ($menu_link['module'] == 'system') {
|
||||
$query = db_select('menu_links');
|
||||
$query->condition('module', 'system');
|
||||
// We always respect the link's 'menu_name'; inheritance for router items is
|
||||
// ensured in _menu_router_build().
|
||||
$query->condition('menu_name', $menu_link['menu_name']);
|
||||
|
||||
// Find the parent - it must be unique.
|
||||
$parent_path = $menu_link['link_path'];
|
||||
do {
|
||||
$parent = FALSE;
|
||||
$parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
|
||||
$new_query = clone $query;
|
||||
$new_query->condition('link_path', $parent_path);
|
||||
// Only valid if we get a unique result.
|
||||
if ($new_query->countQuery()->execute()->fetchField() == 1) {
|
||||
$parent = $new_query->fields('menu_links')->execute()->fetchAssoc();
|
||||
}
|
||||
} while ($parent === FALSE && $parent_path);
|
||||
}
|
||||
|
||||
return $parent;
|
||||
menu_link_delete_multiple(array_keys($links), FALSE, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3319,188 +2894,6 @@ function _menu_find_router_path($link_path) {
|
|||
return $router_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts, updates, enables, disables, or deletes an uncustomized menu link.
|
||||
*
|
||||
* @param string $module
|
||||
* The name of the module that owns the link.
|
||||
* @param string $op
|
||||
* Operation to perform: insert, update, enable, disable, or delete.
|
||||
* @param string $link_path
|
||||
* The path this link points to.
|
||||
* @param string $link_title
|
||||
* (optional) Title of the link to insert or new title to update the link to.
|
||||
* Unused for delete.
|
||||
*
|
||||
* @return integer|null
|
||||
* The insert op returns the mlid of the new item. Others op return NULL.
|
||||
*/
|
||||
function menu_link_maintain($module, $op, $link_path, $link_title = NULL) {
|
||||
switch ($op) {
|
||||
case 'insert':
|
||||
$menu_link = array(
|
||||
'link_title' => $link_title,
|
||||
'link_path' => $link_path,
|
||||
'module' => $module,
|
||||
);
|
||||
return menu_link_save($menu_link);
|
||||
|
||||
case 'update':
|
||||
$result = db_query("SELECT * FROM {menu_links} WHERE link_path = :link_path AND module = :module AND customized = 0", array(':link_path' => $link_path, ':module' => $module))->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($result as $link) {
|
||||
$existing = $link;
|
||||
if (isset($link_title)) {
|
||||
$link['link_title'] = $link_title;
|
||||
}
|
||||
$link['options'] = unserialize($link['options']);
|
||||
menu_link_save($link, $existing);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
case 'disable':
|
||||
$result = db_query("SELECT * FROM {menu_links} WHERE link_path = :link_path AND module = :module AND customized = 0", array(':link_path' => $link_path, ':module' => $module))->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($result as $link) {
|
||||
$existing = $link;
|
||||
$link['hidden'] = ($op == 'disable' ? 1 : 0);
|
||||
$link['customized'] = 1;
|
||||
if (isset($link_title)) {
|
||||
$link['link_title'] = $link_title;
|
||||
}
|
||||
$link['options'] = unserialize($link['options']);
|
||||
menu_link_save($link, $existing);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
menu_link_delete(NULL, $link_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the depth of an item's children relative to its depth.
|
||||
*
|
||||
* For example, if the item has a depth of 2, and the maximum of any child in
|
||||
* the menu link tree is 5, the relative depth is 3.
|
||||
*
|
||||
* @param $item
|
||||
* An array representing a menu link item.
|
||||
*
|
||||
* @return
|
||||
* The relative depth, or zero.
|
||||
*
|
||||
*/
|
||||
function menu_link_children_relative_depth($item) {
|
||||
$query = db_select('menu_links');
|
||||
$query->addField('menu_links', 'depth');
|
||||
$query->condition('menu_name', $item['menu_name']);
|
||||
$query->orderBy('depth', 'DESC');
|
||||
$query->range(0, 1);
|
||||
|
||||
$i = 1;
|
||||
$p = 'p1';
|
||||
while ($i <= MENU_MAX_DEPTH && $item[$p]) {
|
||||
$query->condition($p, $item[$p]);
|
||||
$p = 'p' . ++$i;
|
||||
}
|
||||
|
||||
$max_depth = $query->execute()->fetchField();
|
||||
|
||||
return ($max_depth > $item['depth']) ? $max_depth - $item['depth'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the children of a menu link that is being moved.
|
||||
*
|
||||
* The menu name, parents (p1 - p6), and depth are updated for all children of
|
||||
* the link, and the has_children status of the previous parent is updated.
|
||||
*/
|
||||
function _menu_link_move_children($item, $existing_item) {
|
||||
$query = db_update('menu_links');
|
||||
|
||||
$query->fields(array('menu_name' => $item['menu_name']));
|
||||
|
||||
$p = 'p1';
|
||||
$expressions = array();
|
||||
for ($i = 1; $i <= $item['depth']; $p = 'p' . ++$i) {
|
||||
$expressions[] = array($p, ":p_$i", array(":p_$i" => $item[$p]));
|
||||
}
|
||||
$j = $existing_item['depth'] + 1;
|
||||
while ($i <= MENU_MAX_DEPTH && $j <= MENU_MAX_DEPTH) {
|
||||
$expressions[] = array('p' . $i++, 'p' . $j++, array());
|
||||
}
|
||||
while ($i <= MENU_MAX_DEPTH) {
|
||||
$expressions[] = array('p' . $i++, 0, array());
|
||||
}
|
||||
|
||||
$shift = $item['depth'] - $existing_item['depth'];
|
||||
if ($shift > 0) {
|
||||
// The order of expressions must be reversed so the new values don't
|
||||
// overwrite the old ones before they can be used because "Single-table
|
||||
// UPDATE assignments are generally evaluated from left to right"
|
||||
// see: http://dev.mysql.com/doc/refman/5.0/en/update.html
|
||||
$expressions = array_reverse($expressions);
|
||||
}
|
||||
foreach ($expressions as $expression) {
|
||||
$query->expression($expression[0], $expression[1], $expression[2]);
|
||||
}
|
||||
|
||||
$query->expression('depth', 'depth + :depth', array(':depth' => $shift));
|
||||
$query->condition('menu_name', $existing_item['menu_name']);
|
||||
$p = 'p1';
|
||||
for ($i = 1; $i <= MENU_MAX_DEPTH && $existing_item[$p]; $p = 'p' . ++$i) {
|
||||
$query->condition($p, $existing_item[$p]);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
|
||||
// Check the has_children status of the parent, while excluding this item.
|
||||
_menu_update_parental_status($existing_item, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and updates the 'has_children' status for the parent of a link.
|
||||
*/
|
||||
function _menu_update_parental_status($item, $exclude = FALSE) {
|
||||
// If plid == 0, there is nothing to update.
|
||||
if ($item['plid']) {
|
||||
// Check if at least one visible child exists in the table.
|
||||
$query = db_select('menu_links');
|
||||
$query->addField('menu_links', 'mlid');
|
||||
$query->condition('menu_name', $item['menu_name']);
|
||||
$query->condition('hidden', 0);
|
||||
$query->condition('plid', $item['plid']);
|
||||
$query->range(0, 1);
|
||||
if ($exclude) {
|
||||
$query->condition('mlid', $item['mlid'], '<>');
|
||||
}
|
||||
$parent_has_children = ((bool) $query->execute()->fetchField()) ? 1 : 0;
|
||||
db_update('menu_links')
|
||||
->fields(array('has_children' => $parent_has_children))
|
||||
->condition('mlid', $item['plid'])
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the p1 through p9 values for a menu link being saved.
|
||||
*/
|
||||
function _menu_link_parents_set(&$item, $parent) {
|
||||
$i = 1;
|
||||
while ($i < $item['depth']) {
|
||||
$p = 'p' . $i++;
|
||||
$item[$p] = $parent[$p];
|
||||
}
|
||||
$p = 'p' . $i++;
|
||||
// The parent (p1 - p9) corresponding to the depth always equals the mlid.
|
||||
$item[$p] = $item['mlid'];
|
||||
while ($i <= MENU_MAX_DEPTH) {
|
||||
$p = 'p' . $i++;
|
||||
$item[$p] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the router table based on the data from hook_menu().
|
||||
*/
|
||||
|
|
|
@ -389,7 +389,7 @@ function aggregator_save_category($edit) {
|
|||
->execute();
|
||||
$op = 'insert';
|
||||
}
|
||||
if (isset($op)) {
|
||||
if (isset($op) && module_exists('menu_link')) {
|
||||
menu_link_maintain('aggregator', $op, $link_path, $edit['title']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ class CategorizeFeedItemTest extends AggregatorTestBase {
|
|||
$this->assertTrue(!empty($category), 'The category found in database.');
|
||||
|
||||
$link_path = 'aggregator/categories/' . $category->cid;
|
||||
$menu_link = db_query("SELECT * FROM {menu_links} WHERE link_path = :link_path", array(':link_path' => $link_path))->fetch();
|
||||
$this->assertTrue(!empty($menu_link), 'The menu link associated with the category found in database.');
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $link_path));
|
||||
$this->assertTrue(!empty($menu_links), 'The menu link associated with the category found in database.');
|
||||
|
||||
$feed = $this->createFeed();
|
||||
db_insert('aggregator_category_feed')
|
||||
|
|
|
@ -3,5 +3,6 @@ description = Allows users to create and organize related content in an outline.
|
|||
package = Core
|
||||
version = VERSION
|
||||
core = 8.x
|
||||
dependencies[] = menu_link
|
||||
dependencies[] = node
|
||||
configure = admin/content/book/settings
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
use Drupal\node\Plugin\Core\Entity\Node;
|
||||
use Drupal\entity\Plugin\Core\Entity\EntityDisplay;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
|
||||
use Drupal\menu_link\MenuLinkStorageController;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
|
@ -558,7 +560,8 @@ function _book_update_outline(Node $node) {
|
|||
}
|
||||
}
|
||||
|
||||
if (menu_link_save($node->book)) {
|
||||
$node->book = entity_create('menu_link', $node->book);
|
||||
if ($node->book->save()) {
|
||||
if ($new) {
|
||||
// Insert new.
|
||||
db_insert('book')
|
||||
|
@ -926,7 +929,7 @@ function book_node_prepare(Node $node) {
|
|||
* The depth limit for items in the parent select.
|
||||
*/
|
||||
function _book_parent_depth_limit($book_link) {
|
||||
return MENU_MAX_DEPTH - 1 - (($book_link['mlid'] && $book_link['has_children']) ? menu_link_children_relative_depth($book_link) : 0);
|
||||
return MENU_MAX_DEPTH - 1 - (($book_link['mlid'] && $book_link['has_children']) ? entity_get_controller('menu_link')->findChildrenRelativeDepth($book_link) : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -105,12 +105,12 @@ class MenuNodeTest extends WebTestBase {
|
|||
$this->assertNoLink($node_title);
|
||||
|
||||
// Add a menu link to the Administration menu.
|
||||
$item = array(
|
||||
$item = entity_create('menu_link', array(
|
||||
'link_path' => 'node/' . $node->nid,
|
||||
'link_title' => $this->randomName(16),
|
||||
'menu_name' => 'admin',
|
||||
);
|
||||
menu_link_save($item);
|
||||
));
|
||||
$item->save();
|
||||
|
||||
// Assert that disabled Administration menu is not shown on the
|
||||
// node/$nid/edit page.
|
||||
|
@ -127,12 +127,12 @@ class MenuNodeTest extends WebTestBase {
|
|||
// Create a second node.
|
||||
$child_node = $this->drupalCreateNode(array('type' => 'article'));
|
||||
// Assign a menu link to the second node, being a child of the first one.
|
||||
$child_item = array(
|
||||
$child_item = entity_create('menu_link', array(
|
||||
'link_path' => 'node/'. $child_node->nid,
|
||||
'link_title' => $this->randomName(16),
|
||||
'plid' => $item['mlid'],
|
||||
);
|
||||
menu_link_save($child_item);
|
||||
));
|
||||
$child_item->save();
|
||||
// Edit the first node.
|
||||
$this->drupalGet('node/'. $node->nid .'/edit');
|
||||
// Assert that it is not possible to set the parent of the first node to itself or the second node.
|
||||
|
|
|
@ -78,12 +78,12 @@ class MenuTest extends WebTestBase {
|
|||
$item = $this->getStandardMenuLink();
|
||||
$old_title = $item['link_title'];
|
||||
$this->modifyMenuLink($item);
|
||||
$item = menu_link_load($item['mlid']);
|
||||
$item = entity_load('menu_link', $item['mlid']);
|
||||
// Verify that a change to the description is saved.
|
||||
$description = $this->randomName(16);
|
||||
$item['options']['attributes']['title'] = $description;
|
||||
menu_link_save($item);
|
||||
$saved_item = menu_link_load($item['mlid']);
|
||||
$saved_item = entity_load('menu_link', $item['mlid']);
|
||||
$this->assertEqual($description, $saved_item['options']['attributes']['title'], 'Saving an existing link updates the description (title attribute)');
|
||||
$this->resetMenuLink($item, $old_title);
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ class MenuTest extends WebTestBase {
|
|||
$this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $label)), 'Custom menu was deleted');
|
||||
$this->assertFalse(menu_load($menu_name), 'Custom menu was deleted');
|
||||
// Test if all menu links associated to the menu were removed from database.
|
||||
$result = db_query("SELECT menu_name FROM {menu_links} WHERE menu_name = :menu_name", array(':menu_name' => $menu_name))->fetchField();
|
||||
$result = entity_load_multiple_by_properties('menu_link', array('menu_name' => $menu_name));
|
||||
$this->assertFalse($result, 'All menu links associated to the custom menu were deleted.');
|
||||
}
|
||||
|
||||
|
@ -282,7 +282,9 @@ class MenuTest extends WebTestBase {
|
|||
* @param string $link Link path.
|
||||
* @param string $menu_name Menu name.
|
||||
* @param string $weight Menu weight
|
||||
* @return array Menu link created.
|
||||
*
|
||||
* @return \Drupal\menu_link\Plugin\Core\Entity\MenuLink $menu_link
|
||||
* A menu link entity.
|
||||
*/
|
||||
function addMenuLink($plid = 0, $link = '<front>', $menu_name = 'tools', $expanded = TRUE, $weight = '0') {
|
||||
// View add menu link page.
|
||||
|
@ -303,14 +305,14 @@ class MenuTest extends WebTestBase {
|
|||
// Add menu link.
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
// Unlike most other modules, there is no confirmation message displayed.
|
||||
$this->assertText($title, 'Menu link was added');
|
||||
$this->assertText('The menu link has been saved.');
|
||||
|
||||
$item = db_query('SELECT * FROM {menu_links} WHERE link_title = :title', array(':title' => $title))->fetchAssoc();
|
||||
$this->assertTrue(t('Menu link was found in database.'));
|
||||
$this->assertMenuLink($item['mlid'], array('menu_name' => $menu_name, 'link_path' => $link, 'has_children' => 0, 'plid' => $plid));
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('link_title' => $title));
|
||||
$menu_link = reset($menu_links);
|
||||
$this->assertTrue('Menu link was found in database.');
|
||||
$this->assertMenuLink($menu_link->id(), array('menu_name' => $menu_name, 'link_path' => $link, 'has_children' => 0, 'plid' => $plid));
|
||||
|
||||
return $item;
|
||||
return $menu_link;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -393,8 +395,7 @@ class MenuTest extends WebTestBase {
|
|||
$edit['link_title'] = $title;
|
||||
$this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
// Unlike most other modules, there is no confirmation message displayed.
|
||||
|
||||
$this->assertText('The menu link has been saved.');
|
||||
// Verify menu link.
|
||||
$this->drupalGet('admin/structure/menu/manage/' . $item['menu_name']);
|
||||
$this->assertText($title, 'Menu link was edited');
|
||||
|
@ -501,8 +502,8 @@ class MenuTest extends WebTestBase {
|
|||
*/
|
||||
function assertMenuLink($mlid, array $expected_item) {
|
||||
// Retrieve menu link.
|
||||
$item = db_query('SELECT * FROM {menu_links} WHERE mlid = :mlid', array(':mlid' => $mlid))->fetchAssoc();
|
||||
$options = unserialize($item['options']);
|
||||
$item = entity_load('menu_link', $mlid);
|
||||
$options = $item->options;
|
||||
if (!empty($options['query'])) {
|
||||
$item['link_path'] .= '?' . drupal_http_build_query($options['query']);
|
||||
}
|
||||
|
@ -518,12 +519,21 @@ class MenuTest extends WebTestBase {
|
|||
* Get standard menu link.
|
||||
*/
|
||||
private function getStandardMenuLink() {
|
||||
// Retrieve menu link id of the Log out menu link, which will always be on the front page.
|
||||
$mlid = db_query("SELECT mlid FROM {menu_links} WHERE module = 'system' AND router_path = 'user/logout'")->fetchField();
|
||||
$mlid = 0;
|
||||
// Retrieve menu link id of the Log out menu link, which will always be on
|
||||
// the front page.
|
||||
$query = entity_query('menu_link')
|
||||
->condition('module', 'system')
|
||||
->condition('router_path', 'user/logout');
|
||||
$result = $query->execute();
|
||||
if (!empty($result)) {
|
||||
$mlid = reset($result);
|
||||
}
|
||||
|
||||
$this->assertTrue($mlid > 0, 'Standard menu link id was found');
|
||||
// Load menu link.
|
||||
// Use api function so that link is translated for rendering.
|
||||
$item = menu_link_load($mlid);
|
||||
$item = entity_load('menu_link', $mlid);
|
||||
$this->assertTrue((bool) $item, 'Standard menu link was loaded');
|
||||
return $item;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Administrative page callbacks for menu module.
|
||||
*/
|
||||
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
|
||||
use Drupal\system\Plugin\Core\Entity\Menu;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
|
||||
|
@ -71,16 +71,19 @@ function menu_overview_form($form, &$form_state) {
|
|||
$form_state += array('menu_overview_form_parents' => array());
|
||||
|
||||
$form['#attached']['css'] = array(drupal_get_path('module', 'menu') . '/menu.admin.css');
|
||||
$sql = "
|
||||
SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, m.description_callback, m.description_arguments, ml.*
|
||||
FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
|
||||
WHERE ml.menu_name = :menu
|
||||
ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC";
|
||||
$result = db_query($sql, array(':menu' => $form_state['menu']->id()), array('fetch' => PDO::FETCH_ASSOC));
|
||||
|
||||
$links = array();
|
||||
foreach ($result as $item) {
|
||||
$links[] = $item;
|
||||
$query = entity_query('menu_link')
|
||||
->condition('menu_name', $form_state['menu']->id());
|
||||
for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) {
|
||||
$query->sort('p' . $i, 'ASC');
|
||||
}
|
||||
$result = $query->execute();
|
||||
|
||||
if (!empty($result)) {
|
||||
$links = menu_link_load_multiple($result);
|
||||
}
|
||||
|
||||
$delta = max(count($links), 50);
|
||||
$tree = menu_tree_data($links);
|
||||
$node_links = array();
|
||||
|
@ -305,197 +308,10 @@ function theme_menu_overview_form($variables) {
|
|||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; Build the menu link editing form.
|
||||
*/
|
||||
function menu_edit_item($form, &$form_state, $type, $item, $menu) {
|
||||
if ($type == 'add' || empty($item)) {
|
||||
// This is an add form, initialize the menu link.
|
||||
$item = array('link_title' => '', 'mlid' => 0, 'plid' => 0, 'menu_name' => $menu->id(), 'weight' => 0, 'link_path' => '', 'options' => array(), 'module' => 'menu', 'expanded' => 0, 'hidden' => 0, 'has_children' => 0);
|
||||
}
|
||||
else {
|
||||
// Get the human-readable menu title from the given menu name.
|
||||
$titles = menu_get_menus();
|
||||
$current_title = $titles[$item['menu_name']];
|
||||
|
||||
// Get the current breadcrumb and add a link to that menu's overview page.
|
||||
$breadcrumb = menu_get_active_breadcrumb();
|
||||
$breadcrumb[] = l($current_title, 'admin/structure/menu/manage/' . $item['menu_name']);
|
||||
drupal_set_breadcrumb($breadcrumb);
|
||||
}
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['link_title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Menu link title'),
|
||||
'#default_value' => $item['link_title'],
|
||||
'#description' => t('The text to be used for this link in the menu.'),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
foreach (array('link_path', 'mlid', 'module', 'has_children', 'options') as $key) {
|
||||
$form[$key] = array('#type' => 'value', '#value' => $item[$key]);
|
||||
}
|
||||
// Any item created or edited via this interface is considered "customized".
|
||||
$form['customized'] = array('#type' => 'value', '#value' => 1);
|
||||
$form['original_item'] = array('#type' => 'value', '#value' => $item);
|
||||
|
||||
$path = $item['link_path'];
|
||||
if (isset($item['options']['query'])) {
|
||||
$path .= '?' . drupal_http_build_query($item['options']['query']);
|
||||
}
|
||||
if (isset($item['options']['fragment'])) {
|
||||
$path .= '#' . $item['options']['fragment'];
|
||||
}
|
||||
if ($item['module'] == 'menu') {
|
||||
$form['link_path'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Path'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $path,
|
||||
'#description' => t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['actions']['delete'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Delete'),
|
||||
'#access' => $item['mlid'],
|
||||
'#submit' => array('menu_item_delete_submit'),
|
||||
'#weight' => 10,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['_path'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Path'),
|
||||
'#description' => l($item['link_title'], $item['href'], $item['options']),
|
||||
);
|
||||
}
|
||||
$form['description'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Description'),
|
||||
'#default_value' => isset($item['options']['attributes']['title']) ? $item['options']['attributes']['title'] : '',
|
||||
'#rows' => 1,
|
||||
'#description' => t('Shown when hovering over the menu link.'),
|
||||
);
|
||||
$form['enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enabled'),
|
||||
'#default_value' => !$item['hidden'],
|
||||
'#description' => t('Menu links that are not enabled will not be listed in any menu.'),
|
||||
);
|
||||
$form['expanded'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show as expanded'),
|
||||
'#default_value' => $item['expanded'],
|
||||
'#description' => t('If selected and this menu link has children, the menu will always appear expanded.'),
|
||||
);
|
||||
|
||||
// Generate a list of possible parents (not including this link or descendants).
|
||||
$options = menu_parent_options(menu_get_menus(), $item);
|
||||
$default = $item['menu_name'] . ':' . $item['plid'];
|
||||
if (!isset($options[$default])) {
|
||||
$default = 'tools:0';
|
||||
}
|
||||
$form['parent'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Parent link'),
|
||||
'#default_value' => $default,
|
||||
'#options' => $options,
|
||||
'#description' => t('The maximum depth for a link and all its children is fixed at !maxdepth. Some menu links may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)),
|
||||
'#attributes' => array('class' => array('menu-title-select')),
|
||||
);
|
||||
|
||||
// Get number of items in menu so the weight selector is sized appropriately.
|
||||
$sql = "SELECT COUNT(*) FROM {menu_links} WHERE menu_name = :menu";
|
||||
$result = db_query($sql, array(':menu' => $item['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $row) {
|
||||
foreach ($row as $menu_item_count) {
|
||||
$delta = $menu_item_count;
|
||||
}
|
||||
}
|
||||
if ($delta < 50) {
|
||||
// Old hardcoded value.
|
||||
$delta = 50;
|
||||
}
|
||||
$form['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#title' => t('Weight'),
|
||||
'#delta' => $delta,
|
||||
'#default_value' => $item['weight'],
|
||||
'#description' => t('Optional. In the menu, the heavier links will sink and the lighter links will be positioned nearer the top.'),
|
||||
);
|
||||
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#button_type' => 'primary');
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate form values for a menu link being added or edited.
|
||||
*/
|
||||
function menu_edit_item_validate($form, &$form_state) {
|
||||
$item = &$form_state['values'];
|
||||
$normal_path = drupal_container()->get('path.alias_manager')->getSystemPath($item['link_path']);
|
||||
if ($item['link_path'] != $normal_path) {
|
||||
drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $item['link_path'], '%normal_path' => $normal_path)));
|
||||
$item['link_path'] = $normal_path;
|
||||
}
|
||||
if (!url_is_external($item['link_path'])) {
|
||||
$parsed_link = parse_url($item['link_path']);
|
||||
if (isset($parsed_link['query'])) {
|
||||
$item['options']['query'] = drupal_get_query_array($parsed_link['query']);
|
||||
}
|
||||
else {
|
||||
// Use unset() rather than setting to empty string
|
||||
// to avoid redundant serialized data being stored.
|
||||
unset($item['options']['query']);
|
||||
}
|
||||
if (isset($parsed_link['fragment'])) {
|
||||
$item['options']['fragment'] = $parsed_link['fragment'];
|
||||
}
|
||||
else {
|
||||
unset($item['options']['fragment']);
|
||||
}
|
||||
if (isset($parsed_link['path']) && $item['link_path'] != $parsed_link['path']) {
|
||||
$item['link_path'] = $parsed_link['path'];
|
||||
}
|
||||
}
|
||||
if (!trim($item['link_path']) || !drupal_valid_path($item['link_path'], TRUE)) {
|
||||
form_set_error('link_path', t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $item['link_path'])));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit function for the delete button on the menu item editing form.
|
||||
*/
|
||||
function menu_item_delete_submit($form, &$form_state) {
|
||||
$form_state['redirect'] = 'admin/structure/menu/item/' . $form_state['values']['mlid'] . '/delete';
|
||||
}
|
||||
|
||||
/**
|
||||
* Process menu and menu item add/edit form submissions.
|
||||
*/
|
||||
function menu_edit_item_submit($form, &$form_state) {
|
||||
$item = &$form_state['values'];
|
||||
|
||||
// The value of "hidden" is the opposite of the value
|
||||
// supplied by the "enabled" checkbox.
|
||||
$item['hidden'] = (int) !$item['enabled'];
|
||||
unset($item['enabled']);
|
||||
|
||||
$item['options']['attributes']['title'] = $item['description'];
|
||||
list($item['menu_name'], $item['plid']) = explode(':', $item['parent']);
|
||||
if (!menu_link_save($item)) {
|
||||
drupal_set_message(t('There was an error saving the menu link.'), 'error');
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Your configuration has been saved.'));
|
||||
}
|
||||
$form_state['redirect'] = 'admin/structure/menu/manage/' . $item['menu_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; check access and get a confirm form for deletion of a custom menu.
|
||||
*/
|
||||
function menu_delete_menu_page($menu) {
|
||||
function menu_delete_menu_page(Menu $menu) {
|
||||
// System-defined menus may not be deleted.
|
||||
$system_menus = menu_list_system_menus();
|
||||
if (isset($system_menus[$menu->id()])) {
|
||||
|
@ -510,7 +326,8 @@ function menu_delete_menu_page($menu) {
|
|||
function menu_delete_menu_confirm($form, &$form_state, Menu $menu) {
|
||||
$form['#menu'] = $menu;
|
||||
$caption = '';
|
||||
$num_links = db_query("SELECT COUNT(*) FROM {menu_links} WHERE menu_name = :menu", array(':menu' => $menu->id()))->fetchField();
|
||||
$num_links = drupal_container()->get('plugin.manager.entity')
|
||||
->getStorageController('menu_link')->countMenuLinks($menu->id());
|
||||
if ($num_links) {
|
||||
$caption .= '<p>' . format_plural($num_links, '<strong>Warning:</strong> There is currently 1 menu link in %title. It will be deleted (system-defined items will be reset).', '<strong>Warning:</strong> There are currently @count menu links in %title. They will be deleted (system-defined links will be reset).', array('%title' => $menu->label())) . '</p>';
|
||||
}
|
||||
|
@ -532,16 +349,16 @@ function menu_delete_menu_confirm_submit($form, &$form_state) {
|
|||
}
|
||||
|
||||
// Reset all the menu links defined by the system via hook_menu().
|
||||
$result = db_query("SELECT * FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.menu_name = :menu AND ml.module = 'system' ORDER BY m.number_parts ASC", array(':menu' => $menu->id()), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $link) {
|
||||
menu_reset_item($link);
|
||||
// @todo Convert this to an EFQ once we figure out 'ORDER BY m.number_parts'.
|
||||
$result = db_query("SELECT mlid FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.menu_name = :menu AND ml.module = 'system' ORDER BY m.number_parts ASC", array(':menu' => $menu->id()), array('fetch' => PDO::FETCH_ASSOC))->fetchCol();
|
||||
$menu_links = menu_link_load_multiple($result);
|
||||
foreach ($menu_links as $link) {
|
||||
$link->reset();
|
||||
}
|
||||
|
||||
// Delete all links to the overview page for this menu.
|
||||
$result = db_query("SELECT mlid FROM {menu_links} ml WHERE ml.link_path = :link", array(':link' => 'admin/structure/menu/manage/' . $menu->id()), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $link) {
|
||||
menu_link_delete($link['mlid']);
|
||||
}
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => 'admin/structure/menu/manage/' . $menu->id()));
|
||||
menu_link_delete_multiple(array_keys($menu_links));
|
||||
|
||||
// Delete the custom menu and all its menu links.
|
||||
$menu->delete();
|
||||
|
@ -561,7 +378,7 @@ function menu_edit_menu_name_exists($value) {
|
|||
$custom_exists = entity_load('menu', $value);
|
||||
// 'menu-' is added to the menu name to avoid name-space conflicts.
|
||||
$value = 'menu-' . $value;
|
||||
$link_exists = db_query_range("SELECT 1 FROM {menu_links} WHERE menu_name = :menu", 0, 1, array(':menu' => $value))->fetchField();
|
||||
$link_exists = entity_query('menu_link')->condition('menu_name', $value)->range(0,1)->count()->execute();
|
||||
|
||||
return $custom_exists || $link_exists;
|
||||
}
|
||||
|
@ -575,26 +392,23 @@ function menu_edit_menu_submit($form, &$form_state) {
|
|||
if ($form['#insert']) {
|
||||
// Add 'menu-' to the menu name to help avoid name-space conflicts.
|
||||
$menu['id'] = 'menu-' . $menu['id'];
|
||||
$link['link_title'] = $menu['label'];
|
||||
$link['link_path'] = $path . $menu['id'];
|
||||
$link['router_path'] = $path . '%';
|
||||
$link['module'] = 'menu';
|
||||
$link['plid'] = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :link AND module = :module", array(
|
||||
':link' => 'admin/structure/menu',
|
||||
':module' => 'system'
|
||||
))
|
||||
->fetchField();
|
||||
|
||||
menu_link_save($link);
|
||||
$system_link = entity_load_multiple_by_properties('menu_link', array('link_path' => 'admin/structure/menu', 'module' => 'system'));
|
||||
$system_link = reset($system_link);
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'link_title' => $menu['label'],
|
||||
'link_path' => $path . $menu['id'],
|
||||
'router_path' => $path . '%',
|
||||
'plid' => $system_link->id(),
|
||||
));
|
||||
$menu_link->save();
|
||||
menu_save($menu);
|
||||
}
|
||||
else {
|
||||
menu_save($menu);
|
||||
$result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :path", array(':path' => $path . $menu['id']), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $m) {
|
||||
$link = menu_link_load($m['mlid']);
|
||||
$link['link_title'] = $menu['label'];
|
||||
menu_link_save($link);
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $path . $menu['id']));
|
||||
foreach ($menu_links as $menu_link) {
|
||||
$menu_link->link_title = $menu['label'];
|
||||
$menu_link->save();
|
||||
}
|
||||
}
|
||||
drupal_set_message(t('Your configuration has been saved.'));
|
||||
|
@ -602,53 +416,80 @@ function menu_edit_menu_submit($form, &$form_state) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Menu callback; Check access and present a confirm form for deleting a menu link.
|
||||
* Menu callback: Provides the menu link submission form.
|
||||
*
|
||||
* @param \Drupal\system\Plugin\Core\Entity\Menu $menu
|
||||
* An entity representing a custom menu.
|
||||
*
|
||||
* @return
|
||||
* Returns the menu link submission form.
|
||||
*/
|
||||
function menu_item_delete_page($item) {
|
||||
function menu_link_add(Menu $menu) {
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'mlid' => 0,
|
||||
'plid' => 0,
|
||||
'menu_name' => $menu->id(),
|
||||
));
|
||||
drupal_set_title(t('Add menu link'));
|
||||
return entity_get_form($menu_link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; Check access and present a confirm form for deleting a menu
|
||||
* link.
|
||||
*/
|
||||
function menu_link_delete_page(MenuLink $menu_link) {
|
||||
// Links defined via hook_menu may not be deleted. Updated items are an
|
||||
// exception, as they can be broken.
|
||||
if ($item['module'] == 'system' && !$item['updated']) {
|
||||
if ($menu_link->module == 'system' && !$menu_link->updated) {
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
return drupal_get_form('menu_item_delete_form', $item);
|
||||
return drupal_get_form('menu_link_delete_form', $menu_link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a confirm form for deletion of a single menu link.
|
||||
*/
|
||||
function menu_item_delete_form($form, &$form_state, $item) {
|
||||
$form['#item'] = $item;
|
||||
return confirm_form($form, t('Are you sure you want to delete the custom menu link %item?', array('%item' => $item['link_title'])), 'admin/structure/menu/manage/' . $item['menu_name']);
|
||||
function menu_link_delete_form($form, &$form_state, MenuLink $menu_link) {
|
||||
$form['#menu_link'] = $menu_link;
|
||||
return confirm_form($form,
|
||||
t('Are you sure you want to delete the custom menu link %item?', array('%item' => $menu_link->link_title)),
|
||||
'admin/structure/menu/manage/' . $menu_link->menu_name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process menu delete form submissions.
|
||||
* Processes menu link delete form submissions.
|
||||
*/
|
||||
function menu_item_delete_form_submit($form, &$form_state) {
|
||||
$item = $form['#item'];
|
||||
menu_link_delete($item['mlid']);
|
||||
$t_args = array('%title' => $item['link_title']);
|
||||
function menu_link_delete_form_submit($form, &$form_state) {
|
||||
$menu_link = $form['#menu_link'];
|
||||
menu_link_delete($menu_link->id());
|
||||
$t_args = array('%title' => $menu_link->link_title);
|
||||
drupal_set_message(t('The menu link %title has been deleted.', $t_args));
|
||||
watchdog('menu', 'Deleted menu link %title.', $t_args, WATCHDOG_NOTICE);
|
||||
$form_state['redirect'] = 'admin/structure/menu/manage/' . $item['menu_name'];
|
||||
$form_state['redirect'] = 'admin/structure/menu/manage/' . $menu_link->menu_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; reset a single modified menu link.
|
||||
* Menu callback; Reset a single modified menu link.
|
||||
*/
|
||||
function menu_reset_item_confirm($form, &$form_state, $item) {
|
||||
$form['item'] = array('#type' => 'value', '#value' => $item);
|
||||
return confirm_form($form, t('Are you sure you want to reset the link %item to its default values?', array('%item' => $item['link_title'])), 'admin/structure/menu/manage/' . $item['menu_name'], t('Any customizations will be lost. This action cannot be undone.'), t('Reset'));
|
||||
function menu_link_reset_form($form, &$form_state, MenuLink $menu_link) {
|
||||
$form['#menu_link'] = $menu_link;
|
||||
return confirm_form($form,
|
||||
t('Are you sure you want to reset the link %item to its default values?', array('%item' => $menu_link->link_title)),
|
||||
'admin/structure/menu/manage/' . $menu_link->menu_name,
|
||||
t('Any customizations will be lost. This action cannot be undone.'),
|
||||
t('Reset'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process menu reset item form submissions.
|
||||
* Processes menu link reset form submissions.
|
||||
*/
|
||||
function menu_reset_item_confirm_submit($form, &$form_state) {
|
||||
$item = $form_state['values']['item'];
|
||||
$new_item = menu_reset_item($item);
|
||||
function menu_link_reset_form_submit($form, &$form_state) {
|
||||
$menu_link = $form['#menu_link'];
|
||||
$new_menu_link = $menu_link->reset();
|
||||
drupal_set_message(t('The menu link was reset to its default settings.'));
|
||||
$form_state['redirect'] = 'admin/structure/menu/manage/' . $new_item['menu_name'];
|
||||
$form_state['redirect'] = 'admin/structure/menu/manage/' . $new_menu_link->menu_name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,4 +3,5 @@ description = Allows administrators to customize the site navigation menu.
|
|||
package = Core
|
||||
version = VERSION
|
||||
core = 8.x
|
||||
dependencies[] = menu_link
|
||||
configure = admin/structure/menu
|
||||
|
|
|
@ -16,6 +16,8 @@ use Drupal\block\Plugin\Core\Entity\Block;
|
|||
use Drupal\system\Plugin\Core\Entity\Menu;
|
||||
use Drupal\system\Plugin\block\block\SystemMenuBlock;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
|
||||
use Drupal\menu_link\MenuLinkStorageController;
|
||||
|
||||
/**
|
||||
* Maximum length of menu name as entered by the user. Database length is 32
|
||||
|
@ -110,9 +112,9 @@ function menu_menu() {
|
|||
// Not officially a local action, but displayed as such in
|
||||
// menu_overview_form().
|
||||
$items['admin/structure/menu/manage/%menu/add'] = array(
|
||||
'title' => 'Add link',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('menu_edit_item', 'add', NULL, 4),
|
||||
'title' => 'Add menu link',
|
||||
'page callback' => 'menu_link_add',
|
||||
'page arguments' => array(4),
|
||||
'access arguments' => array('administer menu'),
|
||||
'file' => 'menu.admin.inc',
|
||||
);
|
||||
|
@ -131,21 +133,20 @@ function menu_menu() {
|
|||
);
|
||||
$items['admin/structure/menu/item/%menu_link/edit'] = array(
|
||||
'title' => 'Edit menu link',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('menu_edit_item', 'edit', 4, NULL),
|
||||
'page callback' => 'entity_get_form',
|
||||
'page arguments' => array(4),
|
||||
'access arguments' => array('administer menu'),
|
||||
'file' => 'menu.admin.inc',
|
||||
);
|
||||
$items['admin/structure/menu/item/%menu_link/reset'] = array(
|
||||
'title' => 'Reset menu link',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('menu_reset_item_confirm', 4),
|
||||
'page arguments' => array('menu_link_reset_form', 4),
|
||||
'access arguments' => array('administer menu'),
|
||||
'file' => 'menu.admin.inc',
|
||||
);
|
||||
$items['admin/structure/menu/item/%menu_link/delete'] = array(
|
||||
'title' => 'Delete menu link',
|
||||
'page callback' => 'menu_item_delete_page',
|
||||
'page callback' => 'menu_link_delete_page',
|
||||
'page arguments' => array(4),
|
||||
'access arguments' => array('administer menu'),
|
||||
'file' => 'menu.admin.inc',
|
||||
|
@ -195,23 +196,29 @@ function menu_theme() {
|
|||
*/
|
||||
function menu_enable() {
|
||||
menu_router_rebuild();
|
||||
$base_link = db_query("SELECT mlid AS plid, menu_name FROM {menu_links} WHERE link_path = 'admin/structure/menu' AND module = 'system'")->fetchAssoc();
|
||||
$base_link['router_path'] = 'admin/structure/menu/manage/%';
|
||||
$base_link['module'] = 'menu';
|
||||
$system_link = entity_load_multiple_by_properties('menu_link', array('link_path' => 'admin/structure/menu', 'module' => 'system'));
|
||||
$system_link = reset($system_link);
|
||||
|
||||
$base_link = entity_create('menu_link', array(
|
||||
'menu_name' => $system_link->menu_name,
|
||||
'router_path' => 'admin/structure/menu/manage/%',
|
||||
'module' => 'menu',
|
||||
));
|
||||
|
||||
$menus = entity_load_multiple('menu');
|
||||
foreach ($menus as $menu) {
|
||||
// $link is passed by reference to menu_link_save(), so we make a copy of $base_link.
|
||||
$link = $base_link;
|
||||
$link['mlid'] = 0;
|
||||
$link['link_title'] = $menu->label();
|
||||
$link['link_path'] = 'admin/structure/menu/manage/' . $menu->id();
|
||||
$menu_link = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :path AND plid = :plid", array(
|
||||
':path' => $link['link_path'],
|
||||
':plid' => $link['plid']
|
||||
))
|
||||
->fetchField();
|
||||
if (!$menu_link) {
|
||||
menu_link_save($link);
|
||||
$link = $base_link->createDuplicate();
|
||||
$link->plid = $system_link->id();
|
||||
$link->link_title = $menu->label();
|
||||
$link->link_path = 'admin/structure/menu/manage/' . $menu->id();
|
||||
|
||||
$query = entity_query('menu_link')
|
||||
->condition('link_path', $link->link_path)
|
||||
->condition('plid', $link->plid);
|
||||
$result = $query->execute();
|
||||
|
||||
if (empty($result)) {
|
||||
$link->save();
|
||||
}
|
||||
}
|
||||
menu_cache_clear_all();
|
||||
|
@ -293,24 +300,26 @@ function menu_menu_delete(Menu $menu) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a list of menu items that are valid possible parents for the given menu item.
|
||||
* Returns a list of menu links that are valid possible parents for the given
|
||||
* menu link.
|
||||
*
|
||||
* @param $menus
|
||||
* @param array $menus
|
||||
* An array of menu names and titles, such as from menu_get_menus().
|
||||
* @param $item
|
||||
* The menu item or the node type for which to generate a list of parents.
|
||||
* If $item['mlid'] == 0 then the complete tree is returned.
|
||||
* @param $type
|
||||
* @param \Drupal\menu_link\Plugin\Core\Entity\MenuLink $menu_link
|
||||
* The menu link for which to generate a list of parents.
|
||||
* If $menu_link->id() == 0 then the complete tree is returned.
|
||||
* @param string $type
|
||||
* The node type for which to generate a list of parents.
|
||||
* If $item itself is a node type then $type is ignored.
|
||||
* @return
|
||||
* An array of menu link titles keyed on the a string containing the menu name
|
||||
* and mlid. The list excludes the given item and its children.
|
||||
*
|
||||
* @return array
|
||||
* An array of menu link titles keyed by a string containing the menu name and
|
||||
* mlid. The list excludes the given item and its children.
|
||||
*
|
||||
* @todo This has to be turned into a #process form element callback. The
|
||||
* 'override_parent_selector' variable is entirely superfluous.
|
||||
*/
|
||||
function menu_parent_options($menus, $item, $type = '') {
|
||||
function menu_parent_options(array $menus, MenuLink $menu_link = NULL, $type = NULL) {
|
||||
// The menu_links table can be practically any size and we need a way to
|
||||
// allow contrib modules to provide more scalable pattern choosers.
|
||||
// hook_form_alter is too late in itself because all the possible parents are
|
||||
|
@ -319,14 +328,12 @@ function menu_parent_options($menus, $item, $type = '') {
|
|||
return array();
|
||||
}
|
||||
|
||||
$available_menus = array();
|
||||
if (!is_array($item)) {
|
||||
// If $item is not an array then it is a node type.
|
||||
// Use it as $type and prepare a dummy menu item for _menu_get_options().
|
||||
$type = $item;
|
||||
$item = array('mlid' => 0);
|
||||
if (!$menu_link) {
|
||||
$menu_link = entity_create('menu_link', array('mlid' => 0));
|
||||
}
|
||||
if (empty($type)) {
|
||||
|
||||
$available_menus = array();
|
||||
if (!$type) {
|
||||
// If no node type is set, use all menus given to this function.
|
||||
$available_menus = $menus;
|
||||
}
|
||||
|
@ -338,7 +345,7 @@ function menu_parent_options($menus, $item, $type = '') {
|
|||
}
|
||||
}
|
||||
|
||||
return _menu_get_options($menus, $available_menus, $item);
|
||||
return _menu_get_options($menus, $available_menus, $menu_link);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -402,26 +409,6 @@ function _menu_parents_recurse($tree, $menu_name, $indent, &$options, $exclude,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset a system-defined menu link.
|
||||
*/
|
||||
function menu_reset_item($link) {
|
||||
// To reset the link to its original values, we need to retrieve its
|
||||
// definition from hook_menu(). Otherwise, for example, the link's menu would
|
||||
// not be reset, because properties like the original 'menu_name' are not
|
||||
// stored anywhere else. Since resetting a link happens rarely and this is a
|
||||
// one-time operation, retrieving the full menu router does no harm.
|
||||
$menu = menu_get_router();
|
||||
$router_item = $menu[$link['router_path']];
|
||||
$new_link = _menu_link_build($router_item);
|
||||
// Merge existing menu link's ID and 'has_children' property.
|
||||
foreach (array('mlid', 'has_children') as $key) {
|
||||
$new_link[$key] = $link[$key];
|
||||
}
|
||||
menu_link_save($new_link);
|
||||
return $new_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_view_alter().
|
||||
*/
|
||||
|
@ -455,7 +442,7 @@ function menu_node_save(Node $node) {
|
|||
if (isset($node->menu)) {
|
||||
$link = &$node->menu;
|
||||
if (empty($link['enabled'])) {
|
||||
if (!empty($link['mlid'])) {
|
||||
if (!$link->isNew()) {
|
||||
menu_link_delete($link['mlid']);
|
||||
}
|
||||
}
|
||||
|
@ -482,9 +469,13 @@ function menu_node_save(Node $node) {
|
|||
*/
|
||||
function menu_node_predelete(Node $node) {
|
||||
// Delete all menu module links that point to this node.
|
||||
$result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :path AND module = 'menu'", array(':path' => 'node/' . $node->nid), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $m) {
|
||||
menu_link_delete($m['mlid']);
|
||||
$query = entity_query('menu_link')
|
||||
->condition('link_path', 'node/' . $node->nid)
|
||||
->condition('module', 'menu');
|
||||
$result = $query->execute();
|
||||
|
||||
if (!empty($result)) {
|
||||
menu_link_delete_multiple($result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -495,42 +486,48 @@ function menu_node_prepare(Node $node) {
|
|||
if (empty($node->menu)) {
|
||||
// Prepare the node for the edit form so that $node->menu always exists.
|
||||
$menu_name = strtok(variable_get('menu_parent_' . $node->type, 'main:0'), ':');
|
||||
$item = array();
|
||||
$menu_link = FALSE;
|
||||
if (isset($node->nid)) {
|
||||
$mlid = FALSE;
|
||||
// Give priority to the default menu
|
||||
$type_menus = variable_get('menu_options_' . $node->type, array('main' => 'main'));
|
||||
if (in_array($menu_name, $type_menus)) {
|
||||
$mlid = db_query_range("SELECT mlid FROM {menu_links} WHERE link_path = :path AND menu_name = :menu_name AND module = 'menu' ORDER BY mlid ASC", 0, 1, array(
|
||||
':path' => 'node/' . $node->nid,
|
||||
':menu_name' => $menu_name,
|
||||
))->fetchField();
|
||||
$query = entity_query('menu_link')
|
||||
->condition('link_path', 'node/' . $node->nid)
|
||||
->condition('menu_name', $menu_name)
|
||||
->condition('module', 'menu')
|
||||
->sort('mlid', 'ASC')
|
||||
->range(0, 1);
|
||||
$result = $query->execute();
|
||||
|
||||
$mlid = (!empty($result)) ? reset($result) : FALSE;
|
||||
}
|
||||
// Check all allowed menus if a link does not exist in the default menu.
|
||||
if (!$mlid && !empty($type_menus)) {
|
||||
$mlid = db_query_range("SELECT mlid FROM {menu_links} WHERE link_path = :path AND module = 'menu' AND menu_name IN (:type_menus) ORDER BY mlid ASC", 0, 1, array(
|
||||
':path' => 'node/' . $node->nid,
|
||||
':type_menus' => array_values($type_menus),
|
||||
))->fetchField();
|
||||
$query = entity_query('menu_link')
|
||||
->condition('link_path', 'node/' . $node->nid)
|
||||
->condition('menu_name', array_values($type_menus), 'IN')
|
||||
->condition('module', 'menu')
|
||||
->sort('mlid', 'ASC')
|
||||
->range(0, 1);
|
||||
$result = $query->execute();
|
||||
|
||||
$mlid = (!empty($result)) ? reset($result) : FALSE;
|
||||
}
|
||||
if ($mlid) {
|
||||
$item = menu_link_load($mlid);
|
||||
$menu_link = menu_link_load($mlid);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$menu_link) {
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'mlid' => 0,
|
||||
'plid' => 0,
|
||||
'menu_name' => $menu_name,
|
||||
));
|
||||
}
|
||||
// Set default values.
|
||||
$node->menu = $item + array(
|
||||
'link_title' => '',
|
||||
'mlid' => 0,
|
||||
'plid' => 0,
|
||||
'menu_name' => $menu_name,
|
||||
'weight' => 0,
|
||||
'options' => array(),
|
||||
'module' => 'menu',
|
||||
'expanded' => 0,
|
||||
'hidden' => 0,
|
||||
'has_children' => 0,
|
||||
'customized' => 0,
|
||||
);
|
||||
$node->menu = $menu_link;
|
||||
}
|
||||
// Find the depth limit for the parent select.
|
||||
if (!isset($node->menu['parent_depth_limit'])) {
|
||||
|
@ -542,7 +539,7 @@ function menu_node_prepare(Node $node) {
|
|||
* Find the depth limit for items in the parent select.
|
||||
*/
|
||||
function _menu_parent_depth_limit($item) {
|
||||
return MENU_MAX_DEPTH - 1 - (($item['mlid'] && $item['has_children']) ? menu_link_children_relative_depth($item) : 0);
|
||||
return MENU_MAX_DEPTH - 1 - (($item['mlid'] && $item['has_children']) ? entity_get_controller('menu_link')->findChildrenRelativeDepth($item) : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -558,10 +555,7 @@ function menu_form_node_form_alter(&$form, $form_state) {
|
|||
$node = $form_state['controller']->getEntity($form_state);
|
||||
$link = $node->menu;
|
||||
$type = $node->type;
|
||||
// menu_parent_options() is goofy and can actually handle either a menu link
|
||||
// or a node type both as second argument. Pick based on whether there is
|
||||
// a link already (menu_node_prepare() sets mlid default to 0).
|
||||
$options = menu_parent_options(menu_get_menus(), $link['mlid'] ? $link : $type, $type);
|
||||
$options = menu_parent_options(menu_get_menus(), $link, $type);
|
||||
// If no possible parent menu items were found, there is nothing to display.
|
||||
if (empty($options)) {
|
||||
return;
|
||||
|
@ -632,13 +626,7 @@ function menu_form_node_form_alter(&$form, $form_state) {
|
|||
);
|
||||
|
||||
// Get number of items in menu so the weight selector is sized appropriately.
|
||||
$sql = "SELECT COUNT(*) FROM {menu_links} WHERE menu_name = :menu";
|
||||
$result = db_query($sql, array(':menu' => $link['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $row) {
|
||||
foreach ($row as $menu_items) {
|
||||
$delta = $menu_items;
|
||||
}
|
||||
}
|
||||
$delta = entity_get_controller('menu_link')->countMenuLinks($link->menu_name);
|
||||
if ($delta < 50) {
|
||||
// Old hardcoded value
|
||||
$delta = 50;
|
||||
|
@ -658,6 +646,7 @@ function menu_form_node_form_alter(&$form, $form_state) {
|
|||
* @see menu_form_node_form_alter()
|
||||
*/
|
||||
function menu_node_submit(Node $node, $form, $form_state) {
|
||||
$node->menu = entity_create('menu_link', $form_state['values']['menu']);
|
||||
// Decompose the selected menu parent option into 'menu_name' and 'plid', if
|
||||
// the form used the default parent selection widget.
|
||||
if (!empty($form_state['values']['menu']['parent'])) {
|
||||
|
@ -693,7 +682,8 @@ function menu_form_node_type_form_alter(&$form, $form_state) {
|
|||
// all available menu items.
|
||||
// Otherwise it is not possible to dynamically add options to the list.
|
||||
// @todo Convert menu_parent_options() into a #process callback.
|
||||
$options = menu_parent_options(menu_get_menus(), array('mlid' => 0));
|
||||
$menu_link = entity_create('menu_link', array('mlid' => 0));
|
||||
$options = menu_parent_options(menu_get_menus(), $menu_link);
|
||||
$form['menu']['menu_parent'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Default parent item'),
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link\MenuLinkFormController.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityFormController;
|
||||
|
||||
/**
|
||||
* Form controller for the node edit forms.
|
||||
*/
|
||||
class MenuLinkFormController extends EntityFormController {
|
||||
|
||||
/**
|
||||
* Overrides EntityFormController::form().
|
||||
*/
|
||||
public function form(array $form, array &$form_state, EntityInterface $menu_link) {
|
||||
// Since menu_link_load() no longer returns a translated and access checked
|
||||
// item, do it here instead.
|
||||
_menu_link_translate($menu_link);
|
||||
|
||||
if (!$menu_link->isNew()) {
|
||||
// Get the human-readable menu title from the given menu name.
|
||||
$titles = menu_get_menus();
|
||||
$current_title = $titles[$menu_link->menu_name];
|
||||
|
||||
// Get the current breadcrumb and add a link to that menu's overview page.
|
||||
$breadcrumb = menu_get_active_breadcrumb();
|
||||
$breadcrumb[] = l($current_title, 'admin/structure/menu/manage/' . $menu_link->menu_name);
|
||||
drupal_set_breadcrumb($breadcrumb);
|
||||
}
|
||||
|
||||
$form['link_title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Menu link title'),
|
||||
'#default_value' => $menu_link->link_title,
|
||||
'#description' => t('The text to be used for this link in the menu.'),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
foreach (array('link_path', 'mlid', 'module', 'has_children', 'options') as $key) {
|
||||
$form[$key] = array('#type' => 'value', '#value' => $menu_link->{$key});
|
||||
}
|
||||
// Any item created or edited via this interface is considered "customized".
|
||||
$form['customized'] = array('#type' => 'value', '#value' => 1);
|
||||
|
||||
// We are not using url() when constructing this path because it would add
|
||||
// $base_path.
|
||||
$path = $menu_link->link_path;
|
||||
if (isset($menu_link->options['query'])) {
|
||||
$path .= '?' . drupal_http_build_query($menu_link->options['query']);
|
||||
}
|
||||
if (isset($menu_link->options['fragment'])) {
|
||||
$path .= '#' . $menu_link->options['fragment'];
|
||||
}
|
||||
if ($menu_link->module == 'menu') {
|
||||
$form['link_path'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Path'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $path,
|
||||
'#description' => t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['_path'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Path'),
|
||||
'#description' => l($menu_link->link_title, $menu_link->href, $menu_link->options),
|
||||
);
|
||||
}
|
||||
|
||||
$form['description'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Description'),
|
||||
'#default_value' => isset($menu_link->options['attributes']['title']) ? $menu_link->options['attributes']['title'] : '',
|
||||
'#rows' => 1,
|
||||
'#description' => t('Shown when hovering over the menu link.'),
|
||||
);
|
||||
$form['enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enabled'),
|
||||
'#default_value' => !$menu_link->hidden,
|
||||
'#description' => t('Menu links that are not enabled will not be listed in any menu.'),
|
||||
);
|
||||
$form['expanded'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show as expanded'),
|
||||
'#default_value' => $menu_link->expanded,
|
||||
'#description' => t('If selected and this menu link has children, the menu will always appear expanded.'),
|
||||
);
|
||||
|
||||
// Generate a list of possible parents (not including this link or descendants).
|
||||
$options = menu_parent_options(menu_get_menus(), $menu_link);
|
||||
$default = $menu_link->menu_name . ':' . $menu_link->plid;
|
||||
if (!isset($options[$default])) {
|
||||
$default = 'tools:0';
|
||||
}
|
||||
$form['parent'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Parent link'),
|
||||
'#default_value' => $default,
|
||||
'#options' => $options,
|
||||
'#description' => t('The maximum depth for a link and all its children is fixed at !maxdepth. Some menu links may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)),
|
||||
'#attributes' => array('class' => array('menu-title-select')),
|
||||
);
|
||||
|
||||
// Get number of items in menu so the weight selector is sized appropriately.
|
||||
$delta = drupal_container()->get('plugin.manager.entity')
|
||||
->getStorageController('menu_link')->countMenuLinks($menu_link->menu_name);
|
||||
$form['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#title' => t('Weight'),
|
||||
// Old hardcoded value.
|
||||
'#delta' => max($delta, 50),
|
||||
'#default_value' => $menu_link->weight,
|
||||
'#description' => t('Optional. In the menu, the heavier links will sink and the lighter links will be positioned nearer the top.'),
|
||||
);
|
||||
|
||||
$form['langcode'] = array(
|
||||
'#type' => 'language_select',
|
||||
'#title' => t('Language'),
|
||||
'#languages' => LANGUAGE_ALL,
|
||||
'#default_value' => $menu_link->langcode,
|
||||
);
|
||||
|
||||
return parent::form($form, $form_state, $menu_link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides EntityFormController::actions().
|
||||
*/
|
||||
protected function actions(array $form, array &$form_state) {
|
||||
$element = parent::actions($form, $form_state);
|
||||
$element['submit']['#button_type'] = 'primary';
|
||||
$element['delete']['#access'] = $this->getEntity($form_state)->module == 'menu';
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides EntityFormController::validate().
|
||||
*/
|
||||
public function validate(array $form, array &$form_state) {
|
||||
$menu_link = $this->buildEntity($form, $form_state);
|
||||
|
||||
$normal_path = drupal_container()->get('path.alias_manager.cached')->getSystemPath($menu_link->link_path);
|
||||
if ($menu_link->link_path != $normal_path) {
|
||||
drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $menu_link->link_path, '%normal_path' => $normal_path)));
|
||||
$menu_link->link_path = $normal_path;
|
||||
}
|
||||
if (!url_is_external($menu_link->link_path)) {
|
||||
$parsed_link = parse_url($menu_link->link_path);
|
||||
if (isset($parsed_link['query'])) {
|
||||
$menu_link->options['query'] = drupal_get_query_array($parsed_link['query']);
|
||||
}
|
||||
else {
|
||||
// Use unset() rather than setting to empty string
|
||||
// to avoid redundant serialized data being stored.
|
||||
unset($menu_link->options['query']);
|
||||
}
|
||||
if (isset($parsed_link['fragment'])) {
|
||||
$menu_link->options['fragment'] = $parsed_link['fragment'];
|
||||
}
|
||||
else {
|
||||
unset($menu_link->options['fragment']);
|
||||
}
|
||||
if (isset($parsed_link['path']) && $menu_link->link_path != $parsed_link['path']) {
|
||||
$menu_link->link_path = $parsed_link['path'];
|
||||
}
|
||||
}
|
||||
if (!trim($menu_link->link_path) || !drupal_valid_path($menu_link->link_path, TRUE)) {
|
||||
form_set_error('link_path', t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $menu_link->link_path)));
|
||||
}
|
||||
|
||||
parent::validate($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides EntityFormController::submit().
|
||||
*/
|
||||
public function submit(array $form, array &$form_state) {
|
||||
// Build the menu link object from the submitted values.
|
||||
$menu_link = parent::submit($form, $form_state);
|
||||
|
||||
// The value of "hidden" is the opposite of the value supplied by the
|
||||
// "enabled" checkbox.
|
||||
$menu_link->hidden = (int) !$menu_link->enabled;
|
||||
unset($menu_link->enabled);
|
||||
|
||||
$menu_link->options['attributes']['title'] = $menu_link->description;
|
||||
list($menu_link->menu_name, $menu_link->plid) = explode(':', $menu_link->parent);
|
||||
|
||||
return $menu_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides EntityFormController::save().
|
||||
*/
|
||||
public function save(array $form, array &$form_state) {
|
||||
$menu_link = $this->getEntity($form_state);
|
||||
|
||||
$saved = $menu_link->save();
|
||||
|
||||
if ($saved) {
|
||||
drupal_set_message(t('The menu link has been saved.'));
|
||||
$form_state['redirect'] = 'admin/structure/menu/manage/' . $menu_link->menu_name;
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('There was an error saving the menu link.'), 'error');
|
||||
$form_state['rebuild'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides EntityFormController::delete().
|
||||
*/
|
||||
public function delete(array $form, array &$form_state) {
|
||||
$menu_link = $this->getEntity($form_state);
|
||||
$form_state['redirect'] = 'admin/structure/menu/item/' . $menu_link->id() . '/delete';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,564 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link\MenuLinkStorageController.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link;
|
||||
|
||||
use Drupal\Core\Entity\DatabaseStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
|
||||
/**
|
||||
* Controller class for menu links.
|
||||
*
|
||||
* This extends the Drupal\entity\DatabaseStorageController class, adding
|
||||
* required special handling for menu_link entities.
|
||||
*/
|
||||
class MenuLinkStorageController extends DatabaseStorageController {
|
||||
|
||||
/**
|
||||
* Indicates whether the delete operation should re-parent children items.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $preventReparenting = FALSE;
|
||||
|
||||
/**
|
||||
* Holds an array of router item schema fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $routerItemFields = array();
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::__construct().
|
||||
*/
|
||||
public function __construct($entityType) {
|
||||
parent::__construct($entityType);
|
||||
|
||||
if (empty(static::$routerItemFields)) {
|
||||
static::$routerItemFields = array_diff(drupal_schema_fields_sql('menu_router'), array('weight'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::buildQuery().
|
||||
*/
|
||||
protected function buildQuery($ids, $revision_id = FALSE) {
|
||||
$query = parent::buildQuery($ids, $revision_id);
|
||||
// Specify additional fields from the {menu_router} table.
|
||||
$query->leftJoin('menu_router', 'm', 'base.router_path = m.path');
|
||||
$query->fields('m', static::$routerItemFields);
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::attachLoad().
|
||||
*
|
||||
* @todo Don't call parent::attachLoad() at all because we want to be able to
|
||||
* control the entity load hooks.
|
||||
*/
|
||||
protected function attachLoad(&$menu_links, $load_revision = FALSE) {
|
||||
foreach ($menu_links as &$menu_link) {
|
||||
$menu_link->options = unserialize($menu_link->options);
|
||||
|
||||
// Use the weight property from the menu link.
|
||||
$menu_link->router_item['weight'] = $menu_link->weight;
|
||||
}
|
||||
|
||||
parent::attachLoad($menu_links, $load_revision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::save().
|
||||
*/
|
||||
public function save(EntityInterface $entity) {
|
||||
// We return SAVED_UPDATED by default because the logic below might not
|
||||
// update the entity if its values haven't changed, so returning FALSE
|
||||
// would be confusing in that situation.
|
||||
$return = SAVED_UPDATED;
|
||||
|
||||
$transaction = db_transaction();
|
||||
try {
|
||||
// Load the stored entity, if any.
|
||||
if (!$entity->isNew() && !isset($entity->original)) {
|
||||
$entity->original = entity_load_unchanged($this->entityType, $entity->id());
|
||||
}
|
||||
|
||||
if ($entity->isNew()) {
|
||||
$entity->mlid = db_insert($this->entityInfo['base_table'])->fields(array('menu_name' => 'tools'))->execute();
|
||||
$entity->enforceIsNew();
|
||||
}
|
||||
|
||||
// Unlike the save() method from DatabaseStorageController, we invoke the
|
||||
// 'presave' hook first because we want to allow modules to alter the
|
||||
// entity before all the logic from our preSave() method.
|
||||
$this->invokeHook('presave', $entity);
|
||||
$this->preSave($entity);
|
||||
|
||||
// If every value in $entity->original is the same in the $entity, there
|
||||
// is no reason to run the update queries or clear the caches. We use
|
||||
// array_intersect_key() with the $entity as the first parameter because
|
||||
// $entity may have additional keys left over from building a router entry.
|
||||
// The intersect removes the extra keys, allowing a meaningful comparison.
|
||||
if ($entity->isNew() || (array_intersect_key(get_object_vars($entity), get_object_vars($entity->original)) != get_object_vars($entity->original))) {
|
||||
$return = drupal_write_record($this->entityInfo['base_table'], $entity, $this->idKey);
|
||||
|
||||
if ($return) {
|
||||
if (!$entity->isNew()) {
|
||||
$this->resetCache(array($entity->{$this->idKey}));
|
||||
$this->postSave($entity, TRUE);
|
||||
$this->invokeHook('update', $entity);
|
||||
}
|
||||
else {
|
||||
$return = SAVED_NEW;
|
||||
$this->resetCache();
|
||||
|
||||
$entity->enforceIsNew(FALSE);
|
||||
$this->postSave($entity, FALSE);
|
||||
$this->invokeHook('insert', $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore slave server temporarily.
|
||||
db_ignore_slave();
|
||||
unset($entity->original);
|
||||
|
||||
return $return;
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$transaction->rollback();
|
||||
watchdog_exception($this->entityType, $e);
|
||||
throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::preSave().
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
// This is the easiest way to handle the unique internal path '<front>',
|
||||
// since a path marked as external does not need to match a router path.
|
||||
$entity->external = (url_is_external($entity->link_path) || $entity->link_path == '<front>') ? 1 : 0;
|
||||
|
||||
// Try to find a parent link. If found, assign it and derive its menu.
|
||||
$parent_candidates = !empty($entity->parentCandidates) ? $entity->parentCandidates : array();
|
||||
$parent = $this->findParent($entity, $parent_candidates);
|
||||
if ($parent) {
|
||||
$entity->plid = $parent->id();
|
||||
$entity->menu_name = $parent->menu_name;
|
||||
}
|
||||
// If no corresponding parent link was found, move the link to the top-level.
|
||||
else {
|
||||
$entity->plid = 0;
|
||||
}
|
||||
|
||||
// Directly fill parents for top-level links.
|
||||
if ($entity->plid == 0) {
|
||||
$entity->p1 = $entity->id();
|
||||
for ($i = 2; $i <= MENU_MAX_DEPTH; $i++) {
|
||||
$parent_property = "p$i";
|
||||
$entity->$parent_property = 0;
|
||||
}
|
||||
$entity->depth = 1;
|
||||
}
|
||||
// Otherwise, ensure that this link's depth is not beyond the maximum depth
|
||||
// and fill parents based on the parent link.
|
||||
else {
|
||||
if ($entity->has_children && $entity->original) {
|
||||
$limit = MENU_MAX_DEPTH - $this->findChildrenRelativeDepth($entity->original) - 1;
|
||||
}
|
||||
else {
|
||||
$limit = MENU_MAX_DEPTH - 1;
|
||||
}
|
||||
if ($parent->depth > $limit) {
|
||||
return FALSE;
|
||||
}
|
||||
$entity->depth = $parent->depth + 1;
|
||||
$this->setParents($entity, $parent);
|
||||
}
|
||||
|
||||
// Need to check both plid and menu_name, since plid can be 0 in any menu.
|
||||
if (isset($entity->original) && ($entity->plid != $entity->original->plid || $entity->menu_name != $entity->original->menu_name)) {
|
||||
$this->moveChildren($entity, $entity->original);
|
||||
}
|
||||
// Find the router_path.
|
||||
if (empty($entity->router_path) || empty($entity->original) || (isset($entity->original) && $entity->original->link_path != $entity->link_path)) {
|
||||
if ($entity->external) {
|
||||
$entity->router_path = '';
|
||||
}
|
||||
else {
|
||||
// Find the router path which will serve this path.
|
||||
$entity->parts = explode('/', $entity->link_path, MENU_MAX_PARTS);
|
||||
$entity->router_path = _menu_find_router_path($entity->link_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DatabaseStorageController::postSave().
|
||||
*/
|
||||
function postSave(EntityInterface $entity, $update) {
|
||||
// Check the has_children status of the parent.
|
||||
$this->updateParentalStatus($entity);
|
||||
|
||||
menu_cache_clear($entity->menu_name);
|
||||
if (isset($entity->original) && $entity->menu_name != $entity->original->menu_name) {
|
||||
menu_cache_clear($entity->original->menu_name);
|
||||
}
|
||||
|
||||
// Now clear the cache.
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an internal flag that allows us to prevent the reparenting operations
|
||||
* executed during deletion.
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function preventReparenting($value = FALSE) {
|
||||
$this->preventReparenting = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::preDelete().
|
||||
*/
|
||||
protected function preDelete($entities) {
|
||||
// Nothing to do if we don't want to reparent children.
|
||||
if ($this->preventReparenting) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
// Children get re-attached to the item's parent.
|
||||
if ($entity->has_children) {
|
||||
$children = $this->loadByProperties(array('plid' => $entity->plid));
|
||||
foreach ($children as $child) {
|
||||
$child->plid = $entity->plid;
|
||||
$this->save($child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
$affected_menus = array();
|
||||
// Update the has_children status of the parent.
|
||||
foreach ($entities as $entity) {
|
||||
if (!$this->preventReparenting) {
|
||||
$this->updateParentalStatus($entity);
|
||||
}
|
||||
|
||||
// Store all menu names for which we need to clear the cache.
|
||||
if (!isset($affected_menus[$entity->menu_name])) {
|
||||
$affected_menus[$entity->menu_name] = $entity->menu_name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($affected_menus as $menu_name) {
|
||||
menu_cache_clear($menu_name);
|
||||
}
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads updated and customized menu links for specific router paths.
|
||||
*
|
||||
* Note that this is a low-level method and it doesn't return fully populated
|
||||
* menu link entities. (e.g. no fields are attached)
|
||||
*
|
||||
* @param array $router_paths
|
||||
* An array of router paths.
|
||||
*
|
||||
* @return array
|
||||
* An array of menu link objects indexed by their ids.
|
||||
*/
|
||||
public function loadUpdatedCustomized(array $router_paths) {
|
||||
$query = parent::buildQuery(NULL);
|
||||
$query
|
||||
->condition(db_or()
|
||||
->condition('updated', 1)
|
||||
->condition(db_and()
|
||||
->condition('router_path', $router_paths, 'NOT IN')
|
||||
->condition('external', 0)
|
||||
->condition('customized', 1)
|
||||
)
|
||||
);
|
||||
$query_result = $query->execute();
|
||||
|
||||
if (!empty($this->entityInfo['class'])) {
|
||||
// We provide the necessary arguments for PDO to create objects of the
|
||||
// specified entity class.
|
||||
// @see Drupal\Core\Entity\EntityInterface::__construct()
|
||||
$query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
|
||||
}
|
||||
|
||||
return $query_result->fetchAllAssoc($this->idKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads system menu link as needed by system_get_module_admin_tasks().
|
||||
*
|
||||
* @return array
|
||||
* An array of menu link entities indexed by their IDs.
|
||||
*/
|
||||
public function loadModuleAdminTasks() {
|
||||
$query = $this->buildQuery(NULL);
|
||||
$query
|
||||
->condition('base.link_path', 'admin/%', 'LIKE')
|
||||
->condition('base.hidden', 0, '>=')
|
||||
->condition('base.module', 'system')
|
||||
->condition('m.number_parts', 1, '>')
|
||||
->condition('m.page_callback', 'system_admin_menu_block_page', '<>');
|
||||
$ids = $query->execute()->fetchCol(1);
|
||||
|
||||
return $this->load($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and updates the 'has_children' property for the parent of a link.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
*/
|
||||
protected function updateParentalStatus(EntityInterface $entity, $exclude = FALSE) {
|
||||
// If plid == 0, there is nothing to update.
|
||||
if ($entity->plid && ($parent_entity = $this->load(array($entity->plid)))) {
|
||||
// Check if at least one visible child exists in the table.
|
||||
$query = entity_query($this->entityType);
|
||||
$query
|
||||
->condition('menu_name', $entity->menu_name)
|
||||
->condition('hidden', 0)
|
||||
->condition('plid', $entity->plid)
|
||||
->count();
|
||||
|
||||
if ($exclude) {
|
||||
$query->condition('mlid', $entity->id(), '<>');
|
||||
}
|
||||
|
||||
$parent_has_children = ((bool) $query->execute()) ? 1 : 0;
|
||||
$parent_entity = reset($parent_entity);
|
||||
$parent_entity->has_children = $parent_has_children;
|
||||
$parent_entity->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a possible parent for a given menu link entity.
|
||||
*
|
||||
* Because the parent of a given link might not exist anymore in the database,
|
||||
* we apply a set of heuristics to determine a proper parent:
|
||||
*
|
||||
* - use the passed parent link if specified and existing.
|
||||
* - else, use the first existing link down the previous link hierarchy
|
||||
* - else, for system menu links (derived from hook_menu()), reparent
|
||||
* based on the path hierarchy.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
* @param array $parent_candidates
|
||||
* An array of menu link entities keyed by mlid.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface|false
|
||||
* A menu link entity structure of the possible parent or FALSE if no valid
|
||||
* parent has been found.
|
||||
*/
|
||||
protected function findParent(EntityInterface $entity, array $parent_candidates = array()) {
|
||||
$parent = FALSE;
|
||||
|
||||
// This item is explicitely top-level, skip the rest of the parenting.
|
||||
if (isset($entity->plid) && empty($entity->plid)) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
// If we have a parent link ID, try to use that.
|
||||
$candidates = array();
|
||||
if (isset($entity->plid)) {
|
||||
$candidates[] = $entity->plid;
|
||||
}
|
||||
|
||||
// Else, if we have a link hierarchy try to find a valid parent in there.
|
||||
if (!empty($entity->depth) && $entity->depth > 1) {
|
||||
for ($depth = $entity->depth - 1; $depth >= 1; $depth--) {
|
||||
$parent_property = "p$depth";
|
||||
$candidates[] = $entity->$parent_property;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($candidates as $mlid) {
|
||||
if (isset($parent_candidates[$mlid])) {
|
||||
$parent = $parent_candidates[$mlid];
|
||||
}
|
||||
else {
|
||||
$parent = $this->load(array($mlid));
|
||||
$parent = reset($parent);
|
||||
}
|
||||
if ($parent) {
|
||||
return $parent;
|
||||
}
|
||||
}
|
||||
|
||||
// If everything else failed, try to derive the parent from the path
|
||||
// hierarchy. This only makes sense for links derived from menu router
|
||||
// items (ie. from hook_menu()).
|
||||
if ($entity->module == 'system') {
|
||||
// Find the parent - it must be unique.
|
||||
$parent_path = $entity->link_path;
|
||||
do {
|
||||
$parent = FALSE;
|
||||
$parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
|
||||
|
||||
// @todo Return to the previous method of cloning the entity query when
|
||||
// http://drupal.org/node/1829942 is fixed.
|
||||
$query = entity_query($this->entityType);
|
||||
$query
|
||||
->condition('mlid', $entity->id(), '<>')
|
||||
->condition('module', 'system')
|
||||
// We always respect the link's 'menu_name'; inheritance for router
|
||||
// items is ensured in _menu_router_build().
|
||||
->condition('menu_name', $entity->menu_name)
|
||||
->condition('link_path', $parent_path);
|
||||
|
||||
$count_query = clone $query;
|
||||
// Only valid if we get a unique result.
|
||||
if ($count_query->count()->execute() == 1) {
|
||||
$result = $query->execute();
|
||||
$parent = $this->load($result);
|
||||
$parent = reset($parent);
|
||||
}
|
||||
} while ($parent === FALSE && $parent_path);
|
||||
}
|
||||
|
||||
return $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the p1 through p9 properties for a menu link entity being saved.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
* @param \Drupal\Core\Entity\EntityInterface $parent
|
||||
* A menu link entity.
|
||||
*/
|
||||
protected function setParents(EntityInterface $entity, EntityInterface $parent) {
|
||||
$i = 1;
|
||||
while ($i < $entity->depth) {
|
||||
$p = 'p' . $i++;
|
||||
$entity->{$p} = $parent->{$p};
|
||||
}
|
||||
$p = 'p' . $i++;
|
||||
// The parent (p1 - p9) corresponding to the depth always equals the mlid.
|
||||
$entity->{$p} = $entity->id();
|
||||
while ($i <= MENU_MAX_DEPTH) {
|
||||
$p = 'p' . $i++;
|
||||
$entity->{$p} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the depth of an item's children relative to its depth.
|
||||
*
|
||||
* For example, if the item has a depth of 2 and the maximum of any child in
|
||||
* the menu link tree is 5, the relative depth is 3.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
*
|
||||
* @return int
|
||||
* The relative depth, or zero.
|
||||
*/
|
||||
public function findChildrenRelativeDepth(EntityInterface $entity) {
|
||||
// @todo Since all we need is a specific field from the base table, does it
|
||||
// make sense to convert to EFQ?
|
||||
$query = db_select('menu_links');
|
||||
$query->addField('menu_links', 'depth');
|
||||
$query->condition('menu_name', $entity->menu_name);
|
||||
$query->orderBy('depth', 'DESC');
|
||||
$query->range(0, 1);
|
||||
|
||||
$i = 1;
|
||||
$p = 'p1';
|
||||
while ($i <= MENU_MAX_DEPTH && $entity->{$p}) {
|
||||
$query->condition($p, $entity->{$p});
|
||||
$p = 'p' . ++$i;
|
||||
}
|
||||
|
||||
$max_depth = $query->execute()->fetchField();
|
||||
|
||||
return ($max_depth > $entity->depth) ? $max_depth - $entity->depth : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the children of a menu link that is being moved.
|
||||
*
|
||||
* The menu name, parents (p1 - p6), and depth are updated for all children of
|
||||
* the link, and the has_children status of the previous parent is updated.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
*/
|
||||
protected function moveChildren(EntityInterface $entity) {
|
||||
$query = db_update($this->entityInfo['base_table']);
|
||||
|
||||
$query->fields(array('menu_name' => $entity->menu_name));
|
||||
|
||||
$p = 'p1';
|
||||
$expressions = array();
|
||||
for ($i = 1; $i <= $entity->depth; $p = 'p' . ++$i) {
|
||||
$expressions[] = array($p, ":p_$i", array(":p_$i" => $entity->{$p}));
|
||||
}
|
||||
$j = $entity->original->depth + 1;
|
||||
while ($i <= MENU_MAX_DEPTH && $j <= MENU_MAX_DEPTH) {
|
||||
$expressions[] = array('p' . $i++, 'p' . $j++, array());
|
||||
}
|
||||
while ($i <= MENU_MAX_DEPTH) {
|
||||
$expressions[] = array('p' . $i++, 0, array());
|
||||
}
|
||||
|
||||
$shift = $entity->depth - $entity->original->depth;
|
||||
if ($shift > 0) {
|
||||
// The order of expressions must be reversed so the new values don't
|
||||
// overwrite the old ones before they can be used because "Single-table
|
||||
// UPDATE assignments are generally evaluated from left to right"
|
||||
// @see http://dev.mysql.com/doc/refman/5.0/en/update.html
|
||||
$expressions = array_reverse($expressions);
|
||||
}
|
||||
foreach ($expressions as $expression) {
|
||||
$query->expression($expression[0], $expression[1], $expression[2]);
|
||||
}
|
||||
|
||||
$query->expression('depth', 'depth + :depth', array(':depth' => $shift));
|
||||
$query->condition('menu_name', $entity->original->menu_name);
|
||||
$p = 'p1';
|
||||
for ($i = 1; $i <= MENU_MAX_DEPTH && $entity->original->{$p}; $p = 'p' . ++$i) {
|
||||
$query->condition($p, $entity->original->{$p});
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
|
||||
// Check the has_children status of the parent, while excluding this item.
|
||||
$this->updateParentalStatus($entity->original, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of menu links from a menu.
|
||||
*
|
||||
* @param string $menu_name
|
||||
* The unique name of a menu.
|
||||
*/
|
||||
public function countMenuLinks($menu_name) {
|
||||
$query = entity_query($this->entityType);
|
||||
$query
|
||||
->condition('menu_name', $menu_name)
|
||||
->count();
|
||||
return $query->execute();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link\Plugin\Core\Entity\MenuLink.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Annotation\Plugin;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\Entity;
|
||||
|
||||
/**
|
||||
* Defines the menu link entity class.
|
||||
*
|
||||
* @Plugin(
|
||||
* id = "menu_link",
|
||||
* label = @Translation("Menu link"),
|
||||
* module = "menu_link",
|
||||
* controller_class = "Drupal\menu_link\MenuLinkStorageController",
|
||||
* form_controller_class = {
|
||||
* "default" = "Drupal\menu_link\MenuLinkFormController"
|
||||
* },
|
||||
* static_cache = FALSE,
|
||||
* base_table = "menu_links",
|
||||
* uri_callback = "menu_link_uri",
|
||||
* entity_keys = {
|
||||
* "id" = "mlid",
|
||||
* "label" = "link_title",
|
||||
* "uuid" = "uuid"
|
||||
* },
|
||||
* bundles = {
|
||||
* "menu_link" = {
|
||||
* "label" = "Menu link",
|
||||
* }
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class MenuLink extends Entity implements \ArrayAccess, ContentEntityInterface {
|
||||
|
||||
/**
|
||||
* The link's menu name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $menu_name = 'tools';
|
||||
|
||||
/**
|
||||
* The menu link ID.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $mlid;
|
||||
|
||||
/**
|
||||
* The menu link UUID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $uuid;
|
||||
|
||||
/**
|
||||
* The parent link ID.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $plid;
|
||||
|
||||
/**
|
||||
* The Drupal path or external path this link points to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $link_path;
|
||||
|
||||
/**
|
||||
* For links corresponding to a Drupal path (external = 0), this connects the
|
||||
* link to a {menu_router}.path for joins.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $router_path;
|
||||
|
||||
/**
|
||||
* The entity label.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $link_title = '';
|
||||
|
||||
/**
|
||||
* A serialized array of options to be passed to the url() or l() function,
|
||||
* such as a query string or HTML attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $options = array();
|
||||
|
||||
/**
|
||||
* The name of the module that generated this link.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $module = 'menu';
|
||||
|
||||
/**
|
||||
* A flag for whether the link should be rendered in menus.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $hidden = 0;
|
||||
|
||||
/**
|
||||
* A flag to indicate if the link points to a full URL starting with a
|
||||
* protocol, like http:// (1 = external, 0 = internal).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $external;
|
||||
|
||||
/**
|
||||
* Flag indicating whether any links have this link as a parent.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $has_children = 0;
|
||||
|
||||
/**
|
||||
* Flag for whether this link should be rendered as expanded in menus.
|
||||
* Expanded links always have their child links displayed, instead of only
|
||||
* when the link is in the active trail.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $expanded = 0;
|
||||
|
||||
/**
|
||||
* Link weight among links in the same menu at the same depth.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $weight = 0;
|
||||
|
||||
/**
|
||||
* The depth relative to the top level. A link with plid == 0 will have
|
||||
* depth == 1.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $depth;
|
||||
|
||||
/**
|
||||
* A flag to indicate that the user has manually created or edited the link.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $customized = 0;
|
||||
|
||||
/**
|
||||
* The first entity ID in the materialized path.
|
||||
*
|
||||
* @var int
|
||||
*
|
||||
* @todo Investigate whether the p1, p2, .. pX properties can be moved to a
|
||||
* single array property.
|
||||
*/
|
||||
public $p1;
|
||||
|
||||
/**
|
||||
* The second entity ID in the materialized path.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $p2;
|
||||
|
||||
/**
|
||||
* The third entity ID in the materialized path.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $p3;
|
||||
|
||||
/**
|
||||
* The fourth entity ID in the materialized path.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $p4;
|
||||
|
||||
/**
|
||||
* The fifth entity ID in the materialized path.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $p5;
|
||||
|
||||
/**
|
||||
* The sixth entity ID in the materialized path.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $p6;
|
||||
|
||||
/**
|
||||
* The seventh entity ID in the materialized path.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $p7;
|
||||
|
||||
/**
|
||||
* The eighth entity ID in the materialized path.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $p8;
|
||||
|
||||
/**
|
||||
* The ninth entity ID in the materialized path.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $p9;
|
||||
|
||||
/**
|
||||
* The menu link modification timestamp.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $updated = 0;
|
||||
|
||||
/**
|
||||
* Overrides Entity::id().
|
||||
*/
|
||||
public function id() {
|
||||
return $this->mlid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Entity::createDuplicate().
|
||||
*/
|
||||
public function createDuplicate() {
|
||||
$duplicate = parent::createDuplicate();
|
||||
$duplicate->plid = NULL;
|
||||
return $duplicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a system-defined menu link.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* A menu link entity.
|
||||
*/
|
||||
public function reset() {
|
||||
// To reset the link to its original values, we need to retrieve its
|
||||
// definition from hook_menu(). Otherwise, for example, the link's menu
|
||||
// would not be reset, because properties like the original 'menu_name' are
|
||||
// not stored anywhere else. Since resetting a link happens rarely and this
|
||||
// is a one-time operation, retrieving the full menu router does no harm.
|
||||
$menu = menu_get_router();
|
||||
$router_item = $menu[$this->router_path];
|
||||
$new_link = self::buildFromRouterItem($router_item);
|
||||
// Merge existing menu link's ID and 'has_children' property.
|
||||
foreach (array('mlid', 'has_children') as $key) {
|
||||
$new_link->{$key} = $this->{$key};
|
||||
}
|
||||
$new_link->save();
|
||||
return $new_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a menu link entity from a router item.
|
||||
*
|
||||
* @param array $item
|
||||
* A menu router item.
|
||||
*
|
||||
* @return MenuLink
|
||||
* A menu link entity.
|
||||
*/
|
||||
public static function buildFromRouterItem(array $item) {
|
||||
// Suggested items are disabled by default.
|
||||
if ($item['type'] == MENU_SUGGESTED_ITEM) {
|
||||
$item['hidden'] = 1;
|
||||
}
|
||||
// Hide all items that are not visible in the tree.
|
||||
elseif (!($item['type'] & MENU_VISIBLE_IN_TREE)) {
|
||||
$item['hidden'] = -1;
|
||||
}
|
||||
// Note, we set this as 'system', so that we can be sure to distinguish all
|
||||
// the menu links generated automatically from entries in {menu_router}.
|
||||
$item['module'] = 'system';
|
||||
$item += array(
|
||||
'link_title' => $item['title'],
|
||||
'link_path' => $item['path'],
|
||||
'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])),
|
||||
);
|
||||
return drupal_container()->get('plugin.manager.entity')
|
||||
->getStorageController('menu_link')->create($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements ArrayAccess::offsetExists().
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->{$offset});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements ArrayAccess::offsetGet().
|
||||
*/
|
||||
public function &offsetGet($offset) {
|
||||
return $this->{$offset};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements ArrayAccess::offsetSet().
|
||||
*/
|
||||
public function offsetSet($offset, $value) {
|
||||
$this->{$offset} = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements ArrayAccess::offsetUnset().
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->{$offset});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Menu link module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter menu links when loaded and before they are rendered.
|
||||
*
|
||||
* This hook is only invoked if $menu_link->options['alter'] has been set to a
|
||||
* non-empty value (e.g., TRUE). This flag should be set using
|
||||
* hook_menu_link_presave().
|
||||
* @ todo The paragraph above is lying! This hasn't been (re)implemented yet.
|
||||
*
|
||||
* Implementations of this hook are able to alter any property of the menu link.
|
||||
* For example, this hook may be used to add a page-specific query string to all
|
||||
* menu links, or hide a certain link by setting:
|
||||
* @code
|
||||
* 'hidden' => 1,
|
||||
* @endcode
|
||||
*
|
||||
* @param array $menu_links
|
||||
* An array of menu link entities.
|
||||
*
|
||||
* @see hook_menu_link_presave()
|
||||
*/
|
||||
function hook_menu_link_load($menu_links) {
|
||||
foreach ($menu_links as $menu_link) {
|
||||
if ($menu_link->href == 'devel/cache/clear') {
|
||||
$menu_link->options['query'] = drupal_get_destination();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Alter the data of a menu link entity before it is created or updated.
|
||||
*
|
||||
* @param \Drupal\menu_link\Plugin\Core\Entity\MenuLink $menu_link
|
||||
* A menu link entity.
|
||||
*
|
||||
* @see hook_menu_link_load()
|
||||
*/
|
||||
function hook_menu_link_presave($menu_link) {
|
||||
// Make all new admin links hidden (a.k.a disabled).
|
||||
if (strpos($menu_link->link_path, 'admin') === 0 && $menu_link->isNew()) {
|
||||
$menu_link->hidden = 1;
|
||||
}
|
||||
// Flag a link to be altered by hook_menu_link_load().
|
||||
if ($menu_link->link_path == 'devel/cache/clear') {
|
||||
$menu_link->options['alter'] = TRUE;
|
||||
}
|
||||
// Flag a menu link to be altered by hook_menu_link_load(), but only if it is
|
||||
// derived from a menu router item; i.e., do not alter a custom menu link
|
||||
// pointing to the same path that has been created by a user.
|
||||
if ($menu_link->link_path == 'user' && $menu_link->module == 'system') {
|
||||
$menu_link->options['alter'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform modules that a menu link has been created.
|
||||
*
|
||||
* This hook is used to notify modules that menu links have been
|
||||
* created. Contributed modules may use the information to perform
|
||||
* actions based on the information entered into the menu system.
|
||||
*
|
||||
* @param \Drupal\menu_link\Plugin\Core\Entity\MenuLink $menu_link
|
||||
* A menu link entity.
|
||||
*
|
||||
* @see hook_menu_link_presave()
|
||||
* @see hook_menu_link_update()
|
||||
* @see hook_menu_link_delete()
|
||||
*/
|
||||
function hook_menu_link_insert($menu_link) {
|
||||
// In our sample case, we track menu items as editing sections
|
||||
// of the site. These are stored in our table as 'disabled' items.
|
||||
$record['mlid'] = $menu_link->id();
|
||||
$record['menu_name'] = $menu_link->menu_name;
|
||||
$record['status'] = 0;
|
||||
drupal_write_record('menu_example', $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform modules that a menu link has been updated.
|
||||
*
|
||||
* This hook is used to notify modules that menu items have been
|
||||
* updated. Contributed modules may use the information to perform
|
||||
* actions based on the information entered into the menu system.
|
||||
*
|
||||
* @param \Drupal\menu_link\Plugin\Core\Entity\MenuLink $menu_link
|
||||
* A menu link entity.
|
||||
*
|
||||
* @see hook_menu_link_presave()
|
||||
* @see hook_menu_link_insert()
|
||||
* @see hook_menu_link_delete()
|
||||
*/
|
||||
function hook_menu_link_update($menu_link) {
|
||||
// If the parent menu has changed, update our record.
|
||||
$menu_name = db_query("SELECT menu_name FROM {menu_example} WHERE mlid = :mlid", array(':mlid' => $menu_link->id()))->fetchField();
|
||||
if ($menu_name != $menu_link->menu_name) {
|
||||
db_update('menu_example')
|
||||
->fields(array('menu_name' => $menu_link->menu_name))
|
||||
->condition('mlid', $menu_link->id())
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform modules that a menu link has been deleted.
|
||||
*
|
||||
* This hook is used to notify modules that menu links have been
|
||||
* deleted. Contributed modules may use the information to perform
|
||||
* actions based on the information entered into the menu system.
|
||||
*
|
||||
* @param \Drupal\menu_link\Plugin\Core\Entity\MenuLink $menu_link
|
||||
* A menu link entity.
|
||||
*
|
||||
* @see hook_menu_link_presave()
|
||||
* @see hook_menu_link_insert()
|
||||
* @see hook_menu_link_update()
|
||||
*/
|
||||
function hook_menu_link_delete($menu_link) {
|
||||
// Delete the record from our table.
|
||||
db_delete('menu_example')
|
||||
->condition('mlid', $menu_link->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
|
@ -0,0 +1,8 @@
|
|||
name = Menu Link
|
||||
description = Provides menu links, trees and bunnies!
|
||||
package = Core
|
||||
version = VERSION
|
||||
core = 8.x
|
||||
; @todo Menu links functionality has been moved from system.module and menu.inc
|
||||
; to this module, so make it required until everything is moved over.
|
||||
required = TRUE
|
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the menu_link module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function menu_link_schema() {
|
||||
$schema['menu_links'] = array(
|
||||
'description' => 'Contains the individual links within a menu.',
|
||||
'fields' => array(
|
||||
'menu_name' => array(
|
||||
'description' => "The menu name. All links with the same menu name (such as 'tools') are part of the same menu.",
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'mlid' => array(
|
||||
'description' => 'The menu link ID (mlid) is the integer primary key.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'uuid' => array(
|
||||
'description' => 'Unique Key: Universally unique identifier for this entity.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'plid' => array(
|
||||
'description' => 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'link_path' => array(
|
||||
'description' => 'The Drupal path or external path this link points to.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'router_path' => array(
|
||||
'description' => 'For links corresponding to a Drupal path (external = 0), this connects the link to a {menu_router}.path for joins.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'langcode' => array(
|
||||
'description' => 'The {language}.langcode of this link.',
|
||||
'type' => 'varchar',
|
||||
'length' => 12,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'link_title' => array(
|
||||
'description' => 'The text displayed for the link, which may be modified by a title callback stored in {menu_router}.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'translatable' => TRUE,
|
||||
),
|
||||
'options' => array(
|
||||
'description' => 'A serialized array of options to be passed to the url() or l() function, such as a query string or HTML attributes.',
|
||||
'type' => 'blob',
|
||||
'not null' => FALSE,
|
||||
'translatable' => TRUE,
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'module' => array(
|
||||
'description' => 'The name of the module that generated this link.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => 'system',
|
||||
),
|
||||
'hidden' => array(
|
||||
'description' => 'A flag for whether the link should be rendered in menus. (1 = a disabled menu item that may be shown on admin screens, -1 = a menu callback, 0 = a normal, visible link)',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'external' => array(
|
||||
'description' => 'A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'has_children' => array(
|
||||
'description' => 'Flag indicating whether any links have this link as a parent (1 = children exist, 0 = no children).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'expanded' => array(
|
||||
'description' => 'Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded)',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'weight' => array(
|
||||
'description' => 'Link weight among links in the same menu at the same depth.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'depth' => array(
|
||||
'description' => 'The depth relative to the top level. A link with plid == 0 will have depth == 1.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'customized' => array(
|
||||
'description' => 'A flag to indicate that the user has manually created or edited the link (1 = customized, 0 = not customized).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'p1' => array(
|
||||
'description' => 'The first mlid in the materialized path. If N = depth, then pN must equal the mlid. If depth > 1 then p(N-1) must equal the plid. All pX where X > depth must equal zero. The columns p1 .. p9 are also called the parents.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p2' => array(
|
||||
'description' => 'The second mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p3' => array(
|
||||
'description' => 'The third mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p4' => array(
|
||||
'description' => 'The fourth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p5' => array(
|
||||
'description' => 'The fifth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p6' => array(
|
||||
'description' => 'The sixth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p7' => array(
|
||||
'description' => 'The seventh mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p8' => array(
|
||||
'description' => 'The eighth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p9' => array(
|
||||
'description' => 'The ninth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'updated' => array(
|
||||
'description' => 'Flag that indicates that this link was generated during the update from Drupal 5.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'path_menu' => array(array('link_path', 128), 'menu_name'),
|
||||
'menu_plid_expand_child' => array('menu_name', 'plid', 'expanded', 'has_children'),
|
||||
'menu_parents' => array('menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'),
|
||||
'router_path' => array(array('router_path', 128)),
|
||||
),
|
||||
'primary key' => array('mlid'),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Enables users to create menu links.
|
||||
*/
|
||||
|
||||
use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
|
||||
|
||||
/**
|
||||
* Entity URI callback.
|
||||
*
|
||||
* @param \Drupal\menu_link\Plugin\Core\Entity\MenuLink $menu_link
|
||||
* A menu link entity.
|
||||
*/
|
||||
function menu_link_uri(MenuLink $menu_link) {
|
||||
return array(
|
||||
'path' => $menu_link->link_path,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a menu link entity.
|
||||
*
|
||||
* This function should never be called from within node_load() or any other
|
||||
* function used as a menu object load function since an infinite recursion may
|
||||
* occur.
|
||||
*
|
||||
* @param int $mlid
|
||||
* The menu link ID.
|
||||
* @param bool $reset
|
||||
* (optional) Whether to reset the menu_link_load_multiple() cache.
|
||||
*
|
||||
* @return \Drupal\menu_link\Plugin\Core\Entity\MenuLink|false
|
||||
* A menu link entity, or FALSE if there is no entity with the given ID.
|
||||
*/
|
||||
function menu_link_load($mlid = NULL, $reset = FALSE) {
|
||||
return entity_load('menu_link', $mlid, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads menu link entities from the database.
|
||||
*
|
||||
* @param array $mlids
|
||||
* (optional) An array of entity IDs. If omitted, all entities are loaded.
|
||||
* @param bool $reset
|
||||
* (optional) Whether to reset the internal cache.
|
||||
*
|
||||
* @return array<\Drupal\menu_link\Plugin\Core\Entity\MenuLink>
|
||||
* An array of menu link entities indexed by entity IDs.
|
||||
*
|
||||
* @see menu_link_load()
|
||||
* @see entity_load_multiple()
|
||||
*/
|
||||
function menu_link_load_multiple(array $mlids = NULL, $reset = FALSE) {
|
||||
return entity_load_multiple('menu_link', $mlids, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a menu link.
|
||||
*
|
||||
* @param int $mlid
|
||||
* The menu link ID.
|
||||
*
|
||||
* @see menu_link_delete_multiple()
|
||||
*/
|
||||
function menu_link_delete($mlid) {
|
||||
menu_link_delete_multiple(array($mlid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple menu links.
|
||||
*
|
||||
* @param array $mlids
|
||||
* An array of menu link IDs.
|
||||
* @param bool $force
|
||||
* (optional) Forces deletion. Internal use only, setting to TRUE is
|
||||
* discouraged. Defaults to FALSE.
|
||||
* @param bool $prevent_reparenting
|
||||
* (optional) Disables the re-parenting logic from the deletion process.
|
||||
* Defaults to FALSE.
|
||||
*/
|
||||
function menu_link_delete_multiple(array $mlids, $force = FALSE, $prevent_reparenting = FALSE) {
|
||||
if (!$mlids) {
|
||||
// If no IDs or invalid IDs were passed, do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
$controller = drupal_container()->get('plugin.manager.entity')
|
||||
->getStorageController('menu_link');
|
||||
if (!$force) {
|
||||
$entity_query = entity_query('menu_link');
|
||||
$group = $entity_query->orConditionGroup()
|
||||
->condition('module', 'system', '<>')
|
||||
->condition('updated', 0, '<>');
|
||||
|
||||
$entity_query->condition('mlid', $mlids, 'IN');
|
||||
$entity_query->condition($group);
|
||||
|
||||
$result = $entity_query->execute();
|
||||
$entities = $controller->load($result);
|
||||
}
|
||||
else {
|
||||
$entities = $controller->load($mlids);
|
||||
}
|
||||
$controller->preventReparenting($prevent_reparenting);
|
||||
$controller->delete($entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a menu link.
|
||||
*
|
||||
* After calling this function, rebuild the menu cache using
|
||||
* menu_cache_clear_all().
|
||||
*
|
||||
* @param \Drupal\menu_link\Plugin\Core\Entity\MenuLink $menu_link
|
||||
* The menu link entity to be saved.
|
||||
*/
|
||||
function menu_link_save(MenuLink $menu_link) {
|
||||
$menu_link->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts, updates, enables, disables, or deletes an uncustomized menu link.
|
||||
*
|
||||
* @param string $module
|
||||
* The name of the module that owns the link.
|
||||
* @param string $op
|
||||
* Operation to perform: insert, update, enable, disable, or delete.
|
||||
* @param string $link_path
|
||||
* The path this link points to.
|
||||
* @param string $link_title
|
||||
* (optional) Title of the link to insert or new title to update the link to.
|
||||
* Unused for delete. Defaults to NULL.
|
||||
*
|
||||
* @return integer|null
|
||||
* The insert op returns the mlid of the new item. Others op return NULL.
|
||||
*/
|
||||
function menu_link_maintain($module, $op, $link_path, $link_title = NULL) {
|
||||
$menu_link_controller = drupal_container()->get('plugin.manager.entity')
|
||||
->getStorageController('menu_link');
|
||||
switch ($op) {
|
||||
case 'insert':
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'link_title' => $link_title,
|
||||
'link_path' => $link_path,
|
||||
'module' => $module,)
|
||||
);
|
||||
return $menu_link->save();
|
||||
|
||||
case 'update':
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $link_path, 'module' => $module, 'customized' => 0));
|
||||
foreach ($menu_links as $menu_link) {
|
||||
$menu_link->original = clone $menu_link;
|
||||
if (isset($link_title)) {
|
||||
$menu_link->link_title = $link_title;
|
||||
}
|
||||
$menu_link_controller->save($menu_link);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
case 'disable':
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $link_path, 'module' => $module, 'customized' => 0));
|
||||
foreach ($menu_links as $menu_link) {
|
||||
$menu_link->original = clone $menu_link;
|
||||
$menu_link->hidden = ($op == 'disable' ? 1 : 0);
|
||||
$menu_link->customized = 1;
|
||||
if (isset($link_title)) {
|
||||
$menu_link->link_title = $link_title;
|
||||
}
|
||||
$menu_link_controller->save($menu_link);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$result = entity_query('menu_link')->condition('link_path', $link_path)->execute();
|
||||
if (!empty($result)) {
|
||||
menu_link_delete_multiple($result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,21 @@ use Drupal\Core\Entity\EntityInterface;
|
|||
*/
|
||||
class ShortcutStorageController extends ConfigStorageController {
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\config\ConfigStorageController::attachLoad().
|
||||
*/
|
||||
protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
|
||||
parent::attachLoad($queried_entities, $revision_id);
|
||||
|
||||
foreach ($queried_entities as $id => $entity) {
|
||||
$links = menu_load_links('shortcut-' . $id);
|
||||
foreach ($links as $menu_link) {
|
||||
$entity->links[$menu_link->uuid()] = $menu_link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\config\ConfigStorageController::save().
|
||||
*/
|
||||
|
@ -27,8 +42,22 @@ class ShortcutStorageController extends ConfigStorageController {
|
|||
// Size of menu_name is 32 so id could be 23 = 32 - strlen('shortcut-').
|
||||
$id = substr($entity->id(), 0, 23);
|
||||
$entity->set('id', $id);
|
||||
$entity->set('links', menu_links_clone($default_set->links, $id));
|
||||
$entity->set('links', $default_set->links);
|
||||
foreach ($entity->links as $link) {
|
||||
$link = $link->createDuplicate();
|
||||
$link->menu_name = $id;
|
||||
unset($link->mlid);
|
||||
$link->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Just store the UUIDs.
|
||||
if (isset($entity->links)) {
|
||||
foreach ($entity->links as $uuid => $link) {
|
||||
$entity->links[$uuid] = $uuid;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::save($entity);
|
||||
}
|
||||
|
||||
|
@ -39,13 +68,14 @@ class ShortcutStorageController extends ConfigStorageController {
|
|||
// Process links in shortcut set.
|
||||
// If links were provided for the set, save them.
|
||||
if (isset($entity->links)) {
|
||||
foreach ($entity->links as &$link) {
|
||||
foreach ($entity->links as $uuid) {
|
||||
$menu_link = entity_load_by_uuid('menu_link', $uuid);
|
||||
// Do not specifically associate these links with the shortcut module,
|
||||
// since other modules may make them editable via the menu system.
|
||||
// However, we do need to specify the correct menu name.
|
||||
$link['menu_name'] = 'shortcut-' . $entity->id();
|
||||
$link['plid'] = 0;
|
||||
menu_link_save($link);
|
||||
$menu_link->menu_name = 'shortcut-' . $entity->id();
|
||||
$menu_link->plid = 0;
|
||||
$menu_link->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,10 @@ class ShortcutLinksTest extends ShortcutTestBase {
|
|||
theme_enable(array('seven'));
|
||||
config('system.theme')->set('admin', 'seven')->save();
|
||||
variable_set('node_admin_theme', TRUE);
|
||||
$this->drupalGet($this->set->links[0]['link_path']);
|
||||
|
||||
$link = reset($this->set->links);
|
||||
|
||||
$this->drupalGet($link->link_path);
|
||||
$this->assertRaw(t('Remove from %title shortcuts', array('%title' => $this->set->label())), '"Add to shortcuts" link properly switched to "Remove from shortcuts".');
|
||||
}
|
||||
|
||||
|
@ -79,7 +82,8 @@ class ShortcutLinksTest extends ShortcutTestBase {
|
|||
// Attempt to rename shortcut link.
|
||||
$new_link_name = $this->randomName();
|
||||
|
||||
$this->drupalPost('admin/config/user-interface/shortcut/link/' . $set->links[0]['mlid'], array('shortcut_link[link_title]' => $new_link_name, 'shortcut_link[link_path]' => $set->links[0]['link_path']), t('Save'));
|
||||
$link = reset($set->links);
|
||||
$this->drupalPost('admin/config/user-interface/shortcut/link/' . $link->mlid, array('shortcut_link[link_title]' => $new_link_name, 'shortcut_link[link_path]' => $link->link_path), t('Save'));
|
||||
$saved_set = shortcut_set_load($set->id());
|
||||
$titles = $this->getShortcutInformation($saved_set, 'link_title');
|
||||
$this->assertTrue(in_array($new_link_name, $titles), 'Shortcut renamed: ' . $new_link_name);
|
||||
|
@ -95,7 +99,8 @@ class ShortcutLinksTest extends ShortcutTestBase {
|
|||
// Tests changing a shortcut path.
|
||||
$new_link_path = 'admin/config';
|
||||
|
||||
$this->drupalPost('admin/config/user-interface/shortcut/link/' . $set->links[0]['mlid'], array('shortcut_link[link_title]' => $set->links[0]['link_title'], 'shortcut_link[link_path]' => $new_link_path), t('Save'));
|
||||
$link = reset($set->links);
|
||||
$this->drupalPost('admin/config/user-interface/shortcut/link/' . $link->mlid, array('shortcut_link[link_title]' => $link->link_title, 'shortcut_link[link_path]' => $new_link_path), t('Save'));
|
||||
$saved_set = shortcut_set_load($set->id());
|
||||
$paths = $this->getShortcutInformation($saved_set, 'link_path');
|
||||
$this->assertTrue(in_array($new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path);
|
||||
|
@ -108,10 +113,11 @@ class ShortcutLinksTest extends ShortcutTestBase {
|
|||
function testShortcutLinkDelete() {
|
||||
$set = $this->set;
|
||||
|
||||
$this->drupalPost('admin/config/user-interface/shortcut/link/' . $set->links[0]['mlid'] . '/delete', array(), 'Delete');
|
||||
$link = reset($set->links);
|
||||
$this->drupalPost('admin/config/user-interface/shortcut/link/' . $link->mlid . '/delete', array(), 'Delete');
|
||||
$saved_set = shortcut_set_load($set->id());
|
||||
$mlids = $this->getShortcutInformation($saved_set, 'mlid');
|
||||
$this->assertFalse(in_array($set->links[0]['mlid'], $mlids), 'Successfully deleted a shortcut.');
|
||||
$this->assertFalse(in_array($link->mlid, $mlids), 'Successfully deleted a shortcut.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -91,7 +91,9 @@ class ShortcutSetsTest extends ShortcutTestBase {
|
|||
$set = $this->set;
|
||||
$old_mlids = $this->getShortcutInformation($set, 'mlid');
|
||||
|
||||
$set->links[] = $this->generateShortcutLink('admin', $this->randomName());
|
||||
$menu_link = $this->generateShortcutLink('admin', $this->randomName());
|
||||
$menu_link->save();
|
||||
$set->links[$menu_link->uuid()] = $menu_link;
|
||||
$set->save();
|
||||
$saved_set = shortcut_set_load($set->id());
|
||||
|
||||
|
|
|
@ -51,16 +51,20 @@ abstract class ShortcutTestBase extends WebTestBase {
|
|||
|
||||
// Populate the default shortcut set.
|
||||
$shortcut_set = shortcut_set_load('default');
|
||||
$shortcut_set->links[] = array(
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'link_path' => 'node/add',
|
||||
'link_title' => st('Add content'),
|
||||
'weight' => -20,
|
||||
);
|
||||
$shortcut_set->links[] = array(
|
||||
));
|
||||
$menu_link->save();
|
||||
$shortcut_set->links[$menu_link->uuid()] = $menu_link;
|
||||
$menu_item = entity_create('menu_link', array(
|
||||
'link_path' => 'admin/content',
|
||||
'link_title' => st('Find content'),
|
||||
'weight' => -19,
|
||||
);
|
||||
));
|
||||
$menu_item->save();
|
||||
$shortcut_set->links[$menu_item->uuid()] = $menu_item;
|
||||
$shortcut_set->save();
|
||||
}
|
||||
|
||||
|
@ -84,11 +88,13 @@ abstract class ShortcutTestBase extends WebTestBase {
|
|||
$set = entity_create('shortcut', array(
|
||||
'id' => isset($id) ? $id : strtolower($this->randomName()),
|
||||
'label' => empty($label) ? $this->randomString() : $label,
|
||||
'links' => (!$default_links) ? array() : array(
|
||||
$this->generateShortcutLink('node/add'),
|
||||
$this->generateShortcutLink('admin/content'),
|
||||
),
|
||||
));
|
||||
if ($default_links) {
|
||||
$menu_link = $this->generateShortcutLink('node/add');
|
||||
$set->links[$menu_link->uuid()] = $menu_link;
|
||||
$menu_link = $this->generateShortcutLink('admin/content');
|
||||
$set->links[$menu_link->uuid()] = $menu_link;
|
||||
}
|
||||
$set->save();
|
||||
return $set;
|
||||
}
|
||||
|
@ -97,10 +103,11 @@ abstract class ShortcutTestBase extends WebTestBase {
|
|||
* Creates a generic shortcut link.
|
||||
*/
|
||||
function generateShortcutLink($path, $title = '') {
|
||||
$link = array(
|
||||
$link = entity_create('menu_link', array(
|
||||
'link_path' => $path,
|
||||
'link_title' => !empty($title) ? $title : $this->randomName(),
|
||||
);
|
||||
));
|
||||
$link->save();
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
@ -121,8 +128,8 @@ abstract class ShortcutTestBase extends WebTestBase {
|
|||
*/
|
||||
function getShortcutInformation($set, $key) {
|
||||
$info = array();
|
||||
foreach ($set->links as $link) {
|
||||
$info[] = $link[$key];
|
||||
foreach ($set->links as $uuid => $link) {
|
||||
$info[] = $link->{$key};
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ function shortcut_set_switch_submit($form, &$form_state) {
|
|||
$set = entity_create('shortcut', array(
|
||||
'id' => $form_state['values']['id'],
|
||||
'label' => $form_state['values']['label'],
|
||||
'links' => menu_links_clone($default_set->links),
|
||||
'links' => $default_set->links,
|
||||
));
|
||||
$set->save();
|
||||
$replacements = array(
|
||||
|
@ -209,7 +209,7 @@ function shortcut_set_customize($form, &$form_state, $shortcut_set) {
|
|||
'links' => array(),
|
||||
);
|
||||
|
||||
foreach ($shortcut_set->links as $link) {
|
||||
foreach ($shortcut_set->links as $uuid => $link) {
|
||||
$mlid = $link['mlid'];
|
||||
$form['shortcuts']['links'][$mlid]['name']['#markup'] = l($link['link_title'], $link['link_path']);
|
||||
$form['shortcuts']['links'][$mlid]['weight'] = array(
|
||||
|
@ -362,10 +362,10 @@ function shortcut_link_edit($form, &$form_state, $shortcut_link) {
|
|||
*/
|
||||
function _shortcut_link_form_elements($shortcut_link = NULL) {
|
||||
if (!isset($shortcut_link)) {
|
||||
$shortcut_link = array(
|
||||
$shortcut_link = entity_create('menu_link', array(
|
||||
'link_title' => '',
|
||||
'link_path' => ''
|
||||
);
|
||||
));
|
||||
}
|
||||
else {
|
||||
$shortcut_link['link_path'] = ($shortcut_link['link_path'] == '<front>') ? '' : drupal_container()->get('path.alias_manager')->getPathAlias($shortcut_link['link_path']);
|
||||
|
@ -421,7 +421,10 @@ function shortcut_link_edit_submit($form, &$form_state) {
|
|||
}
|
||||
$form_state['values']['shortcut_link']['link_path'] = $shortcut_path;
|
||||
|
||||
$shortcut_link = array_merge($form_state['values']['original_shortcut_link'], $form_state['values']['shortcut_link']);
|
||||
$shortcut_link = $form_state['values']['original_shortcut_link'];
|
||||
foreach ($form_state['values']['shortcut_link'] as $key => $value) {
|
||||
$shortcut_link[$key] = $value;
|
||||
}
|
||||
|
||||
menu_link_save($shortcut_link);
|
||||
$set_name = str_replace('shortcut-', '' , $shortcut_link['menu_name']);
|
||||
|
@ -460,9 +463,11 @@ function shortcut_admin_add_link($shortcut_link, &$shortcut_set) {
|
|||
if (empty($shortcut_link['link_path'])) {
|
||||
$shortcut_link['link_path'] = '<front>';
|
||||
}
|
||||
$menu_link = entity_create('menu_link', $shortcut_link);
|
||||
$menu_link->save();
|
||||
|
||||
// Add the link to the end of the list.
|
||||
$shortcut_set->links[] = $shortcut_link;
|
||||
$shortcut_set->links[$menu_link->uuid()] = $menu_link;
|
||||
shortcut_set_reset_link_weights($shortcut_set);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,4 +3,5 @@ description = Allows users to manage customizable lists of shortcut links.
|
|||
package = Core
|
||||
version = VERSION
|
||||
core = 8.x
|
||||
dependencies[] = menu_link
|
||||
configure = admin/config/user-interface/shortcut
|
||||
|
|
|
@ -337,17 +337,6 @@ function shortcut_set_load($id) {
|
|||
return entity_load('shortcut', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_shortcut_load().
|
||||
*
|
||||
* Loads menu links attached to each of shortcuts.
|
||||
*/
|
||||
function shortcut_shortcut_load($entities) {
|
||||
foreach ($entities as $id => $entity) {
|
||||
$entity->set('links', menu_load_links('shortcut-' . $id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the link weights in a shortcut set to match their current order.
|
||||
*
|
||||
|
@ -361,9 +350,8 @@ function shortcut_shortcut_load($entities) {
|
|||
*/
|
||||
function shortcut_set_reset_link_weights(&$shortcut_set) {
|
||||
$weight = -50;
|
||||
foreach ($shortcut_set->links as &$link) {
|
||||
$link['weight'] = $weight;
|
||||
$weight++;
|
||||
foreach ($shortcut_set->links as $menu_link) {
|
||||
$menu_link->weight = ++$weight;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,7 +554,7 @@ function shortcut_preprocess_page(&$variables) {
|
|||
$shortcut_set = shortcut_current_displayed_set();
|
||||
|
||||
// Check if $link is already a shortcut and set $link_mode accordingly.
|
||||
foreach ($shortcut_set->links as $shortcut) {
|
||||
foreach ($shortcut_set->links as $uuid => $shortcut) {
|
||||
if ($link == $shortcut['link_path']) {
|
||||
$mlid = $shortcut['mlid'];
|
||||
break;
|
||||
|
|
|
@ -209,13 +209,13 @@ class BreadcrumbTest extends MenuTestBase {
|
|||
$node2 = $this->drupalCreateNode(array(
|
||||
'type' => $type,
|
||||
'title' => $title,
|
||||
'menu' => array(
|
||||
'menu' => entity_create('menu_link', array(
|
||||
'enabled' => 1,
|
||||
'link_title' => 'Parent ' . $title,
|
||||
'description' => '',
|
||||
'menu_name' => $menu,
|
||||
'plid' => 0,
|
||||
),
|
||||
)),
|
||||
));
|
||||
$nid2 = $node2->nid;
|
||||
|
||||
|
@ -237,13 +237,13 @@ class BreadcrumbTest extends MenuTestBase {
|
|||
$node3 = $this->drupalCreateNode(array(
|
||||
'type' => $type,
|
||||
'title' => $title,
|
||||
'menu' => array(
|
||||
'menu' => entity_create('menu_link', array(
|
||||
'enabled' => 1,
|
||||
'link_title' => 'Child ' . $title,
|
||||
'description' => '',
|
||||
'menu_name' => $menu,
|
||||
'plid' => $node2->menu['mlid'],
|
||||
),
|
||||
)),
|
||||
));
|
||||
$nid3 = $node3->nid;
|
||||
|
||||
|
@ -277,7 +277,8 @@ class BreadcrumbTest extends MenuTestBase {
|
|||
'link_path' => 'node',
|
||||
);
|
||||
$this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
|
||||
$link = db_query('SELECT * FROM {menu_links} WHERE link_title = :title', array(':title' => 'Root'))->fetchAssoc();
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('link_title' => 'Root'));
|
||||
$link = reset($menu_links);
|
||||
|
||||
$edit = array(
|
||||
'menu[parent]' => $link['menu_name'] . ':' . $link['mlid'],
|
||||
|
@ -335,10 +336,8 @@ class BreadcrumbTest extends MenuTestBase {
|
|||
'parent' => "$menu:{$parent_mlid}",
|
||||
);
|
||||
$this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
|
||||
$tags[$name]['link'] = db_query('SELECT * FROM {menu_links} WHERE link_title = :title AND link_path = :href', array(
|
||||
':title' => $edit['link_title'],
|
||||
':href' => $edit['link_path'],
|
||||
))->fetchAssoc();
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('link_title' => $edit['link_title'], 'link_path' => $edit['link_path']));
|
||||
$tags[$name]['link'] = reset($menu_links);
|
||||
$tags[$name]['link']['link_path'] = $edit['link_path'];
|
||||
$parent_mlid = $tags[$name]['link']['mlid'];
|
||||
}
|
||||
|
@ -436,20 +435,16 @@ class BreadcrumbTest extends MenuTestBase {
|
|||
'link_path' => 'user',
|
||||
);
|
||||
$this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
|
||||
$link_user = db_query('SELECT * FROM {menu_links} WHERE link_title = :title AND link_path = :href', array(
|
||||
':title' => $edit['link_title'],
|
||||
':href' => $edit['link_path'],
|
||||
))->fetchAssoc();
|
||||
$menu_links_user = entity_load_multiple_by_properties('menu_link', array('link_title' => $edit['link_title'], 'link_path' => $edit['link_path']));
|
||||
$link_user = reset($menu_links_user);
|
||||
|
||||
$edit = array(
|
||||
'link_title' => $this->admin_user->name . ' link',
|
||||
'link_path' => 'user/' . $this->admin_user->uid,
|
||||
);
|
||||
$this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
|
||||
$link_admin_user = db_query('SELECT * FROM {menu_links} WHERE link_title = :title AND link_path = :href', array(
|
||||
':title' => $edit['link_title'],
|
||||
':href' => $edit['link_path'],
|
||||
))->fetchAssoc();
|
||||
$menu_links_admin_user = entity_load_multiple_by_properties('menu_link', array('link_title' => $edit['link_title'], 'link_path' => $edit['link_path']));
|
||||
$link_admin_user = reset($menu_links_admin_user);
|
||||
|
||||
// Verify expected breadcrumbs for the two separate links.
|
||||
$this->drupalLogout();
|
||||
|
|
|
@ -26,7 +26,8 @@ class LinksTest extends WebTestBase {
|
|||
*/
|
||||
function createLinkHierarchy($module = 'menu_test') {
|
||||
// First remove all the menu links.
|
||||
db_truncate('menu_links')->execute();
|
||||
$menu_links = menu_link_load_multiple();
|
||||
menu_link_delete_multiple(array_keys($menu_links), TRUE, TRUE);
|
||||
|
||||
// Then create a simple link hierarchy:
|
||||
// - $parent
|
||||
|
@ -43,31 +44,36 @@ class LinksTest extends WebTestBase {
|
|||
$links['parent'] = $base_options + array(
|
||||
'link_path' => 'menu-test/parent',
|
||||
);
|
||||
menu_link_save($links['parent']);
|
||||
$links['parent'] = entity_create('menu_link', $links['parent']);
|
||||
$links['parent']->save();
|
||||
|
||||
$links['child-1'] = $base_options + array(
|
||||
'link_path' => 'menu-test/parent/child-1',
|
||||
'plid' => $links['parent']['mlid'],
|
||||
);
|
||||
menu_link_save($links['child-1']);
|
||||
$links['child-1'] = entity_create('menu_link', $links['child-1']);
|
||||
$links['child-1']->save();
|
||||
|
||||
$links['child-1-1'] = $base_options + array(
|
||||
'link_path' => 'menu-test/parent/child-1/child-1-1',
|
||||
'plid' => $links['child-1']['mlid'],
|
||||
);
|
||||
menu_link_save($links['child-1-1']);
|
||||
$links['child-1-1'] = entity_create('menu_link', $links['child-1-1']);
|
||||
$links['child-1-1']->save();
|
||||
|
||||
$links['child-1-2'] = $base_options + array(
|
||||
'link_path' => 'menu-test/parent/child-1/child-1-2',
|
||||
'plid' => $links['child-1']['mlid'],
|
||||
);
|
||||
menu_link_save($links['child-1-2']);
|
||||
$links['child-1-2'] = entity_create('menu_link', $links['child-1-2']);
|
||||
$links['child-1-2']->save();
|
||||
|
||||
$links['child-2'] = $base_options + array(
|
||||
'link_path' => 'menu-test/parent/child-2',
|
||||
'plid' => $links['parent']['mlid'],
|
||||
);
|
||||
menu_link_save($links['child-2']);
|
||||
$links['child-2'] = entity_create('menu_link', $links['child-2']);
|
||||
$links['child-2']->save();
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
|
|
@ -312,27 +312,30 @@ class MenuRouterTest extends WebTestBase {
|
|||
$admin_user = $this->drupalCreateUser(array('administer site configuration'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'";
|
||||
$name = db_query($sql)->fetchField();
|
||||
$this->assertEqual($name, 'original', 'Menu name is "original".');
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('router_path' => 'menu_name_test'));
|
||||
$menu_link = reset($menu_links);
|
||||
$this->assertEqual($menu_link->menu_name, 'original', 'Menu name is "original".');
|
||||
|
||||
// Change the menu_name parameter in menu_test.module, then force a menu
|
||||
// rebuild.
|
||||
menu_test_menu_name('changed');
|
||||
menu_router_rebuild();
|
||||
|
||||
$sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'";
|
||||
$name = db_query($sql)->fetchField();
|
||||
$this->assertEqual($name, 'changed', 'Menu name was successfully changed after rebuild.');
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('router_path' => 'menu_name_test'));
|
||||
$menu_link = reset($menu_links);
|
||||
$this->assertEqual($menu_link->menu_name, 'changed', 'Menu name was successfully changed after rebuild.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for menu hierarchy.
|
||||
*/
|
||||
function testMenuHierarchy() {
|
||||
$parent_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent'))->fetchAssoc();
|
||||
$child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child'))->fetchAssoc();
|
||||
$unattached_child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child2/child'))->fetchAssoc();
|
||||
$parent_links = entity_load_multiple_by_properties('menu_link', array('link_path' => 'menu-test/hierarchy/parent'));
|
||||
$parent_link = reset($parent_links);
|
||||
$child_links = entity_load_multiple_by_properties('menu_link', array('link_path' => 'menu-test/hierarchy/parent/child'));
|
||||
$child_link = reset($child_links);
|
||||
$unattached_child_links = entity_load_multiple_by_properties('menu_link', array('link_path' => 'menu-test/hierarchy/parent/child2/child'));
|
||||
$unattached_child_link = reset($unattached_child_links);
|
||||
|
||||
$this->assertEqual($child_link['plid'], $parent_link['mlid'], 'The parent of a directly attached child is correct.');
|
||||
$this->assertEqual($unattached_child_link['plid'], $parent_link['mlid'], 'The parent of a non-directly attached child is correct.');
|
||||
|
@ -343,12 +346,16 @@ class MenuRouterTest extends WebTestBase {
|
|||
*/
|
||||
function testMenuHidden() {
|
||||
// Verify links for one dynamic argument.
|
||||
$links = db_select('menu_links', 'ml')
|
||||
->fields('ml')
|
||||
->condition('ml.router_path', 'menu-test/hidden/menu%', 'LIKE')
|
||||
->orderBy('ml.router_path')
|
||||
->execute()
|
||||
->fetchAllAssoc('router_path', PDO::FETCH_ASSOC);
|
||||
$query = entity_query('menu_link')
|
||||
->condition('router_path', 'menu-test/hidden/menu', 'STARTS_WITH')
|
||||
->sort('router_path');
|
||||
$result = $query->execute();
|
||||
$menu_links = menu_link_load_multiple($result);
|
||||
|
||||
$links = array();
|
||||
foreach ($menu_links as $menu_link) {
|
||||
$links[$menu_link->router_path] = $menu_link;
|
||||
}
|
||||
|
||||
$parent = $links['menu-test/hidden/menu'];
|
||||
$depth = $parent['depth'] + 1;
|
||||
|
@ -391,12 +398,16 @@ class MenuRouterTest extends WebTestBase {
|
|||
$this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
|
||||
|
||||
// Verify links for two dynamic arguments.
|
||||
$links = db_select('menu_links', 'ml')
|
||||
->fields('ml')
|
||||
->condition('ml.router_path', 'menu-test/hidden/block%', 'LIKE')
|
||||
->orderBy('ml.router_path')
|
||||
->execute()
|
||||
->fetchAllAssoc('router_path', PDO::FETCH_ASSOC);
|
||||
$query = entity_query('menu_link')
|
||||
->condition('router_path', 'menu-test/hidden/block', 'STARTS_WITH')
|
||||
->sort('router_path');
|
||||
$result = $query->execute();
|
||||
$menu_links = menu_link_load_multiple($result);
|
||||
|
||||
$links = array();
|
||||
foreach ($menu_links as $menu_link) {
|
||||
$links[$menu_link->router_path] = $menu_link;
|
||||
}
|
||||
|
||||
$parent = $links['menu-test/hidden/block'];
|
||||
$depth = $parent['depth'] + 1;
|
||||
|
@ -472,7 +483,7 @@ class MenuRouterTest extends WebTestBase {
|
|||
*/
|
||||
function testMenuLinkOptions() {
|
||||
// Create a menu link with options.
|
||||
$menu_link = array(
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'link_title' => 'Menu link options test',
|
||||
'link_path' => 'test-page',
|
||||
'module' => 'menu_test',
|
||||
|
@ -484,7 +495,7 @@ class MenuRouterTest extends WebTestBase {
|
|||
'testparam' => 'testvalue',
|
||||
),
|
||||
),
|
||||
);
|
||||
));
|
||||
menu_link_save($menu_link);
|
||||
|
||||
// Load front page.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Menu;
|
||||
|
||||
use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
|
||||
use Drupal\simpletest\UnitTestBase;
|
||||
|
||||
/**
|
||||
|
@ -16,13 +17,7 @@ class TreeDataUnitTest extends UnitTestBase {
|
|||
/**
|
||||
* Dummy link structure acceptable for menu_tree_data().
|
||||
*/
|
||||
var $links = array(
|
||||
1 => array('mlid' => 1, 'depth' => 1),
|
||||
2 => array('mlid' => 2, 'depth' => 1),
|
||||
3 => array('mlid' => 3, 'depth' => 2),
|
||||
4 => array('mlid' => 4, 'depth' => 3),
|
||||
5 => array('mlid' => 5, 'depth' => 1),
|
||||
);
|
||||
protected $links = array();
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
|
@ -35,7 +30,15 @@ class TreeDataUnitTest extends UnitTestBase {
|
|||
/**
|
||||
* Validate the generation of a proper menu tree hierarchy.
|
||||
*/
|
||||
function testMenuTreeData() {
|
||||
public function testMenuTreeData() {
|
||||
$this->links = array(
|
||||
1 => new MenuLink(array('mlid' => 1, 'depth' => 1), 'menu_link'),
|
||||
2 => new MenuLink(array('mlid' => 2, 'depth' => 1), 'menu_link'),
|
||||
3 => new MenuLink(array('mlid' => 3, 'depth' => 2), 'menu_link'),
|
||||
4 => new MenuLink(array('mlid' => 4, 'depth' => 3), 'menu_link'),
|
||||
5 => new MenuLink(array('mlid' => 5, 'depth' => 1), 'menu_link'),
|
||||
);
|
||||
|
||||
$tree = menu_tree_data($this->links);
|
||||
|
||||
// Validate that parent items #1, #2, and #5 exist on the root level.
|
||||
|
|
|
@ -7,33 +7,19 @@
|
|||
|
||||
namespace Drupal\system\Tests\Menu;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
|
||||
/**
|
||||
* Menu tree output related tests.
|
||||
*/
|
||||
class TreeOutputTest extends WebTestBase {
|
||||
class TreeOutputTest extends DrupalUnitTestBase {
|
||||
|
||||
public static $modules = array('system', 'menu_link');
|
||||
|
||||
/**
|
||||
* Dummy link structure acceptable for menu_tree_output().
|
||||
*/
|
||||
var $tree_data = array(
|
||||
'1'=> array(
|
||||
'link' => array( 'menu_name' => 'main-menu', 'mlid' => 1, 'hidden'=>0, 'has_children' => 1, 'title' => 'Item 1', 'in_active_trail' => 1, 'access'=>1, 'href' => 'a', 'localized_options' => array('attributes' => array('title' =>'')) ),
|
||||
'below' => array(
|
||||
'2' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 2, 'hidden'=>0, 'has_children' => 1, 'title' => 'Item 2', 'in_active_trail' => 1, 'access'=>1, 'href' => 'a/b', 'localized_options' => array('attributes' => array('title' =>'')) ),
|
||||
'below' => array(
|
||||
'3' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 3, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 3', 'in_active_trail' => 0, 'access'=>1, 'href' => 'a/b/c', 'localized_options' => array('attributes' => array('title' =>'')) ),
|
||||
'below' => array() ),
|
||||
'4' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 4, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 4', 'in_active_trail' => 0, 'access'=>1, 'href' => 'a/b/d', 'localized_options' => array('attributes' => array('title' =>'')) ),
|
||||
'below' => array() )
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
'5' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 5, 'hidden'=>1, 'has_children' => 0, 'title' => 'Item 5', 'in_active_trail' => 0, 'access'=>1, 'href' => 'e', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) ),
|
||||
'6' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 6, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 6', 'in_active_trail' => 0, 'access'=>0, 'href' => 'f', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) ),
|
||||
'7' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 7, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 7', 'in_active_trail' => 0, 'access'=>1, 'href' => 'g', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) )
|
||||
);
|
||||
protected $tree_data = array();
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
|
@ -45,12 +31,35 @@ class TreeOutputTest extends WebTestBase {
|
|||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'menu_router');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the generation of a proper menu tree output.
|
||||
*/
|
||||
function testMenuTreeData() {
|
||||
$storage_controller = $this->container->get('plugin.manager.entity')->getStorageController('menu_link');
|
||||
// @todo Prettify this tree buildup code, it's very hard to read.
|
||||
$this->tree_data = array(
|
||||
'1'=> array(
|
||||
'link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 1, 'hidden' => 0, 'has_children' => 1, 'title' => 'Item 1', 'in_active_trail' => 1, 'access' => 1, 'href' => 'a', 'localized_options' => array('attributes' => array('title' =>'')))),
|
||||
'below' => array(
|
||||
'2' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 2, 'hidden' => 0, 'has_children' => 1, 'title' => 'Item 2', 'in_active_trail' => 1, 'access' => 1, 'href' => 'a/b', 'localized_options' => array('attributes' => array('title' =>'')))),
|
||||
'below' => array(
|
||||
'3' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 3, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 3', 'in_active_trail' => 0, 'access' => 1, 'href' => 'a/b/c', 'localized_options' => array('attributes' => array('title' =>'')))),
|
||||
'below' => array() ),
|
||||
'4' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 4, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 4', 'in_active_trail' => 0, 'access' => 1, 'href' => 'a/b/d', 'localized_options' => array('attributes' => array('title' =>'')))),
|
||||
'below' => array() )
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
'5' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 5, 'hidden' => 1, 'has_children' => 0, 'title' => 'Item 5', 'in_active_trail' => 0, 'access' => 1, 'href' => 'e', 'localized_options' => array('attributes' => array('title' =>'')))), 'below' => array()),
|
||||
'6' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 6, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 6', 'in_active_trail' => 0, 'access' => 0, 'href' => 'f', 'localized_options' => array('attributes' => array('title' =>'')))), 'below' => array()),
|
||||
'7' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 7, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 7', 'in_active_trail' => 0, 'access' => 1, 'href' => 'g', 'localized_options' => array('attributes' => array('title' =>'')))), 'below' => array())
|
||||
);
|
||||
|
||||
$output = menu_tree_output($this->tree_data);
|
||||
|
||||
// Validate that the - in main-menu is changed into an underscore
|
||||
|
|
|
@ -19,33 +19,39 @@ function system_admin_config_page() {
|
|||
drupal_set_message(t('One or more problems were detected with your Drupal installation. Check the <a href="@status">status report</a> for more information.', array('@status' => url('admin/reports/status'))), 'error');
|
||||
}
|
||||
$blocks = array();
|
||||
if ($admin = db_query("SELECT menu_name, mlid FROM {menu_links} WHERE link_path = 'admin/config' AND module = 'system'")->fetchAssoc()) {
|
||||
$result = db_query("
|
||||
SELECT m.*, ml.*
|
||||
FROM {menu_links} ml
|
||||
INNER JOIN {menu_router} m ON ml.router_path = m.path
|
||||
WHERE ml.link_path <> 'admin/help' AND menu_name = :menu_name AND ml.plid = :mlid AND hidden = 0", $admin, array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $item) {
|
||||
_menu_link_translate($item);
|
||||
if (!$item['access']) {
|
||||
continue;
|
||||
}
|
||||
// The link description, either derived from 'description' in hook_menu()
|
||||
// or customized via menu module is used as title attribute.
|
||||
if (!empty($item['localized_options']['attributes']['title'])) {
|
||||
$item['description'] = $item['localized_options']['attributes']['title'];
|
||||
unset($item['localized_options']['attributes']['title']);
|
||||
}
|
||||
$block = $item;
|
||||
$block['content'] = '';
|
||||
$block['content'] .= theme('admin_block_content', array('content' => system_admin_menu_block($item)));
|
||||
if (!empty($block['content'])) {
|
||||
$block['show'] = TRUE;
|
||||
}
|
||||
if ($system_link = entity_load_multiple_by_properties('menu_link', array('link_path' => 'admin/config', 'module' => 'system'))) {
|
||||
$system_link = reset($system_link);
|
||||
$query = entity_query('menu_link')
|
||||
->condition('link_path', 'admin/help', '<>')
|
||||
->condition('menu_name', $system_link->menu_name)
|
||||
->condition('plid', $system_link->id())
|
||||
->condition('hidden', 0);
|
||||
$result = $query->execute();
|
||||
if (!empty($result)) {
|
||||
$menu_links = menu_link_load_multiple($result);
|
||||
|
||||
// Prepare for sorting as in function _menu_tree_check_access().
|
||||
// The weight is offset so it is always positive, with a uniform 5-digits.
|
||||
$blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block;
|
||||
foreach ($menu_links as $item) {
|
||||
_menu_link_translate($item);
|
||||
if (!$item['access']) {
|
||||
continue;
|
||||
}
|
||||
// The link description, either derived from 'description' in hook_menu()
|
||||
// or customized via menu module is used as title attribute.
|
||||
if (!empty($item['localized_options']['attributes']['title'])) {
|
||||
$item['description'] = $item['localized_options']['attributes']['title'];
|
||||
unset($item['localized_options']['attributes']['title']);
|
||||
}
|
||||
$block = $item;
|
||||
$block['content'] = '';
|
||||
$block['content'] .= theme('admin_block_content', array('content' => system_admin_menu_block($item)));
|
||||
if (!empty($block['content'])) {
|
||||
$block['show'] = TRUE;
|
||||
}
|
||||
|
||||
// Prepare for sorting as in function _menu_tree_check_access().
|
||||
// The weight is offset so it is always positive, with a uniform 5-digits.
|
||||
$blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($blocks) {
|
||||
|
|
|
@ -897,127 +897,6 @@ function hook_menu_alter(&$items) {
|
|||
$items['node/add']['access callback'] = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the data being saved to the {menu_links} table by menu_link_save().
|
||||
*
|
||||
* @param $item
|
||||
* Associative array defining a menu link as passed into menu_link_save().
|
||||
*
|
||||
* @see hook_translated_menu_link_alter()
|
||||
*/
|
||||
function hook_menu_link_alter(&$item) {
|
||||
// Make all new admin links hidden (a.k.a disabled).
|
||||
if (strpos($item['link_path'], 'admin') === 0 && empty($item['mlid'])) {
|
||||
$item['hidden'] = 1;
|
||||
}
|
||||
// Flag a link to be altered by hook_translated_menu_link_alter().
|
||||
if ($item['link_path'] == 'devel/cache/clear') {
|
||||
$item['options']['alter'] = TRUE;
|
||||
}
|
||||
// Flag a link to be altered by hook_translated_menu_link_alter(), but only
|
||||
// if it is derived from a menu router item; i.e., do not alter a custom
|
||||
// menu link pointing to the same path that has been created by a user.
|
||||
if ($item['link_path'] == 'user' && $item['module'] == 'system') {
|
||||
$item['options']['alter'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter a menu link after it has been translated and before it is rendered.
|
||||
*
|
||||
* This hook is invoked from _menu_link_translate() after a menu link has been
|
||||
* translated; i.e., after dynamic path argument placeholders (%) have been
|
||||
* replaced with actual values, the user access to the link's target page has
|
||||
* been checked, and the link has been localized. It is only invoked if
|
||||
* $item['options']['alter'] has been set to a non-empty value (e.g., TRUE).
|
||||
* This flag should be set using hook_menu_link_alter().
|
||||
*
|
||||
* Implementations of this hook are able to alter any property of the menu link.
|
||||
* For example, this hook may be used to add a page-specific query string to all
|
||||
* menu links, or hide a certain link by setting:
|
||||
* @code
|
||||
* 'hidden' => 1,
|
||||
* @endcode
|
||||
*
|
||||
* @param $item
|
||||
* Associative array defining a menu link after _menu_link_translate()
|
||||
* @param $map
|
||||
* Associative array containing the menu $map (path parts and/or objects).
|
||||
*
|
||||
* @see hook_menu_link_alter()
|
||||
*/
|
||||
function hook_translated_menu_link_alter(&$item, $map) {
|
||||
if ($item['href'] == 'devel/cache/clear') {
|
||||
$item['localized_options']['query'] = drupal_get_destination();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform modules that a menu link has been created.
|
||||
*
|
||||
* This hook is used to notify modules that menu items have been
|
||||
* created. Contributed modules may use the information to perform
|
||||
* actions based on the information entered into the menu system.
|
||||
*
|
||||
* @param $link
|
||||
* Associative array defining a menu link as passed into menu_link_save().
|
||||
*
|
||||
* @see hook_menu_link_update()
|
||||
* @see hook_menu_link_delete()
|
||||
*/
|
||||
function hook_menu_link_insert($link) {
|
||||
// In our sample case, we track menu items as editing sections
|
||||
// of the site. These are stored in our table as 'disabled' items.
|
||||
$record['mlid'] = $link['mlid'];
|
||||
$record['menu_name'] = $link['menu_name'];
|
||||
$record['status'] = 0;
|
||||
drupal_write_record('menu_example', $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform modules that a menu link has been updated.
|
||||
*
|
||||
* This hook is used to notify modules that menu items have been
|
||||
* updated. Contributed modules may use the information to perform
|
||||
* actions based on the information entered into the menu system.
|
||||
*
|
||||
* @param $link
|
||||
* Associative array defining a menu link as passed into menu_link_save().
|
||||
*
|
||||
* @see hook_menu_link_insert()
|
||||
* @see hook_menu_link_delete()
|
||||
*/
|
||||
function hook_menu_link_update($link) {
|
||||
// If the parent menu has changed, update our record.
|
||||
$menu_name = db_query("SELECT menu_name FROM {menu_example} WHERE mlid = :mlid", array(':mlid' => $link['mlid']))->fetchField();
|
||||
if ($menu_name != $link['menu_name']) {
|
||||
db_update('menu_example')
|
||||
->fields(array('menu_name' => $link['menu_name']))
|
||||
->condition('mlid', $link['mlid'])
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform modules that a menu link has been deleted.
|
||||
*
|
||||
* This hook is used to notify modules that menu items have been
|
||||
* deleted. Contributed modules may use the information to perform
|
||||
* actions based on the information entered into the menu system.
|
||||
*
|
||||
* @param $link
|
||||
* Associative array defining a menu link as passed into menu_link_save().
|
||||
*
|
||||
* @see hook_menu_link_insert()
|
||||
* @see hook_menu_link_update()
|
||||
*/
|
||||
function hook_menu_link_delete($link) {
|
||||
// Delete the record from our table.
|
||||
db_delete('menu_example')
|
||||
->condition('mlid', $link['mlid'])
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter tabs and actions displayed on the page before they are rendered.
|
||||
*
|
||||
|
|
|
@ -964,192 +964,6 @@ function system_schema() {
|
|||
'primary key' => array('path'),
|
||||
);
|
||||
|
||||
$schema['menu_links'] = array(
|
||||
'description' => 'Contains the individual links within a menu.',
|
||||
'fields' => array(
|
||||
'menu_name' => array(
|
||||
'description' => "The menu name. All links with the same menu name (such as 'tools') are part of the same menu.",
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'mlid' => array(
|
||||
'description' => 'The menu link ID (mlid) is the integer primary key.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'plid' => array(
|
||||
'description' => 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'link_path' => array(
|
||||
'description' => 'The Drupal path or external path this link points to.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'router_path' => array(
|
||||
'description' => 'For links corresponding to a Drupal path (external = 0), this connects the link to a {menu_router}.path for joins.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'link_title' => array(
|
||||
'description' => 'The text displayed for the link, which may be modified by a title callback stored in {menu_router}.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'translatable' => TRUE,
|
||||
),
|
||||
'options' => array(
|
||||
'description' => 'A serialized array of options to be passed to the url() or l() function, such as a query string or HTML attributes.',
|
||||
'type' => 'blob',
|
||||
'not null' => FALSE,
|
||||
'translatable' => TRUE,
|
||||
),
|
||||
'module' => array(
|
||||
'description' => 'The name of the module that generated this link.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => 'system',
|
||||
),
|
||||
'hidden' => array(
|
||||
'description' => 'A flag for whether the link should be rendered in menus. (1 = a disabled menu item that may be shown on admin screens, -1 = a menu callback, 0 = a normal, visible link)',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'external' => array(
|
||||
'description' => 'A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'has_children' => array(
|
||||
'description' => 'Flag indicating whether any links have this link as a parent (1 = children exist, 0 = no children).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'expanded' => array(
|
||||
'description' => 'Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded)',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'weight' => array(
|
||||
'description' => 'Link weight among links in the same menu at the same depth.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'depth' => array(
|
||||
'description' => 'The depth relative to the top level. A link with plid == 0 will have depth == 1.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'customized' => array(
|
||||
'description' => 'A flag to indicate that the user has manually created or edited the link (1 = customized, 0 = not customized).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
'p1' => array(
|
||||
'description' => 'The first mlid in the materialized path. If N = depth, then pN must equal the mlid. If depth > 1 then p(N-1) must equal the plid. All pX where X > depth must equal zero. The columns p1 .. p9 are also called the parents.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p2' => array(
|
||||
'description' => 'The second mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p3' => array(
|
||||
'description' => 'The third mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p4' => array(
|
||||
'description' => 'The fourth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p5' => array(
|
||||
'description' => 'The fifth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p6' => array(
|
||||
'description' => 'The sixth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p7' => array(
|
||||
'description' => 'The seventh mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p8' => array(
|
||||
'description' => 'The eighth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'p9' => array(
|
||||
'description' => 'The ninth mlid in the materialized path. See p1.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'updated' => array(
|
||||
'description' => 'Flag that indicates that this link was generated during the update from Drupal 5.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'small',
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'path_menu' => array(array('link_path', 128), 'menu_name'),
|
||||
'menu_plid_expand_child' => array('menu_name', 'plid', 'expanded', 'has_children'),
|
||||
'menu_parents' => array('menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'),
|
||||
'router_path' => array(array('router_path', 128)),
|
||||
),
|
||||
'primary key' => array('mlid'),
|
||||
);
|
||||
|
||||
$schema['queue'] = array(
|
||||
'description' => 'Stores items in queues.',
|
||||
'fields' => array(
|
||||
|
@ -2244,6 +2058,42 @@ function system_update_8047() {
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the new Menu link module.
|
||||
*
|
||||
* Creates the langcode and UUID columns for menu links.
|
||||
*/
|
||||
function system_update_8048() {
|
||||
// Enable the module without re-installing the schema.
|
||||
update_module_enable(array('menu_link'));
|
||||
|
||||
// Add the langcode column if it doesn't exist.
|
||||
if (!db_field_exists('menu_links', 'langcode')) {
|
||||
$column = array(
|
||||
'description' => 'The {language}.langcode of this entity.',
|
||||
'type' => 'varchar',
|
||||
'length' => 12,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
);
|
||||
db_add_field('menu_links', 'langcode', $column);
|
||||
}
|
||||
|
||||
// Add the UUID column.
|
||||
$column = array(
|
||||
'description' => 'Unique Key: Universally unique identifier for this entity.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => FALSE,
|
||||
);
|
||||
$keys = array(
|
||||
'unique keys' => array(
|
||||
'uuid' => array('uuid'),
|
||||
),
|
||||
);
|
||||
db_add_field('menu_links', 'uuid', $column, $keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup updates-7.x-to-8.x".
|
||||
* The next series of updates should start at 9000.
|
||||
|
|
|
@ -2677,7 +2677,10 @@ function system_admin_menu_block($item) {
|
|||
}
|
||||
|
||||
if (!isset($item['mlid'])) {
|
||||
$item += db_query("SELECT mlid, menu_name FROM {menu_links} ml WHERE ml.router_path = :path AND module = 'system'", array(':path' => $item['path']))->fetchAssoc();
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('router_path' => $item['path'], 'module' => 'system'));
|
||||
$menu_link = reset($menu_links);
|
||||
$item['mlid'] = $menu_link->id();
|
||||
$item['menu_name'] = $menu_link->menu_name;
|
||||
}
|
||||
|
||||
if (isset($cache[$item['mlid']])) {
|
||||
|
@ -2685,17 +2688,8 @@ function system_admin_menu_block($item) {
|
|||
}
|
||||
|
||||
$content = array();
|
||||
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
|
||||
$query->join('menu_router', 'm', 'm.path = ml.router_path');
|
||||
$query
|
||||
->fields('ml')
|
||||
// Weight should be taken from {menu_links}, not {menu_router}.
|
||||
->fields('m', array_diff(drupal_schema_fields_sql('menu_router'), array('weight')))
|
||||
->condition('ml.plid', $item['mlid'])
|
||||
->condition('ml.menu_name', $item['menu_name'])
|
||||
->condition('ml.hidden', 0);
|
||||
|
||||
foreach ($query->execute() as $link) {
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link', array('plid' => $item['mlid'], 'menu_name' => $item['menu_name'], 'hidden' => 0));
|
||||
foreach ($menu_links as $link) {
|
||||
_menu_link_translate($link);
|
||||
if ($link['access']) {
|
||||
// The link description, either derived from 'description' in
|
||||
|
@ -3460,18 +3454,8 @@ function system_get_module_admin_tasks($module, $info) {
|
|||
|
||||
if (!isset($links)) {
|
||||
$links = array();
|
||||
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
|
||||
$query->join('menu_router', 'm', 'm.path = ml.router_path');
|
||||
$query
|
||||
->fields('ml')
|
||||
// Weight should be taken from {menu_links}, not {menu_router}.
|
||||
->fields('m', array_diff(drupal_schema_fields_sql('menu_router'), array('weight')))
|
||||
->condition('ml.link_path', 'admin/%', 'LIKE')
|
||||
->condition('ml.hidden', 0, '>=')
|
||||
->condition('ml.module', 'system')
|
||||
->condition('m.number_parts', 1, '>')
|
||||
->condition('m.page_callback', 'system_admin_menu_block_page', '<>');
|
||||
foreach ($query->execute() as $link) {
|
||||
$menu_links = entity_get_controller('menu_link')->loadModuleAdminTasks();
|
||||
foreach ($menu_links as $link) {
|
||||
_menu_link_translate($link);
|
||||
if ($link['access']) {
|
||||
$links[$link['router_path']] = $link;
|
||||
|
@ -3523,6 +3507,7 @@ function system_get_module_admin_tasks($module, $info) {
|
|||
$item['title'] = t('Configure @module permissions', array('@module' => $info['name']));
|
||||
unset($item['description']);
|
||||
$item['localized_options']['fragment'] = 'module-' . $module;
|
||||
$item = entity_create('menu_link', $item);
|
||||
$admin_tasks["admin/people/permissions#module-$module"] = $item;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,5 +5,6 @@ package = Core
|
|||
version = VERSION
|
||||
|
||||
dependencies[] = breakpoint
|
||||
dependencies[] = menu_link
|
||||
|
||||
configure = admin/structure/toolbar
|
||||
|
|
|
@ -511,8 +511,13 @@ function toolbar_toolbar() {
|
|||
*/
|
||||
function toolbar_get_menu_tree() {
|
||||
$tree = array();
|
||||
$admin_link = db_query('SELECT * FROM {menu_links} WHERE menu_name = :menu_name AND module = :module AND link_path = :path', array(':menu_name' => 'admin', ':module' => 'system', ':path' => 'admin'))->fetchAssoc();
|
||||
if ($admin_link) {
|
||||
$query = entity_query('menu_link')
|
||||
->condition('menu_name', 'admin')
|
||||
->condition('module', 'system')
|
||||
->condition('link_path', 'admin');
|
||||
$result = $query->execute();
|
||||
if (!empty($result)) {
|
||||
$admin_link = menu_link_load(reset($result));
|
||||
$tree = menu_build_tree('admin', array(
|
||||
'expanded' => array($admin_link['mlid']),
|
||||
'min_depth' => $admin_link['depth'] + 1,
|
||||
|
|
|
@ -1119,21 +1119,21 @@ function user_menu_site_status_alter(&$menu_site_status, $path) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_link_alter().
|
||||
* Implements hook_menu_link_presave().
|
||||
*/
|
||||
function user_menu_link_alter(&$link) {
|
||||
function user_menu_link_presave($link) {
|
||||
// The path 'user' must be accessible for anonymous users, but only visible
|
||||
// for authenticated users. Authenticated users should see "My account", but
|
||||
// anonymous users should not see it at all. Therefore, invoke
|
||||
// user_translated_menu_link_alter() to conditionally hide the link.
|
||||
if ($link['link_path'] == 'user' && $link['module'] == 'system') {
|
||||
$link['options']['alter'] = TRUE;
|
||||
// user_menu_link_load() to conditionally hide the link.
|
||||
if ($link->link_path == 'user' && $link->module == 'system') {
|
||||
$link->options['alter'] = TRUE;
|
||||
}
|
||||
|
||||
// Force the Logout link to appear on the top-level of 'account' menu by
|
||||
// default (i.e., unless it has been customized).
|
||||
if ($link['link_path'] == 'user/logout' && $link['module'] == 'system' && empty($link['customized'])) {
|
||||
$link['plid'] = 0;
|
||||
if ($link->link_path == 'user/logout' && $link->module == 'system' && empty($link->customized)) {
|
||||
$link->plid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1149,12 +1149,14 @@ function user_menu_breadcrumb_alter(&$active_trail, $item) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements hook_translated_menu_link_alter().
|
||||
* Implements hook_menu_link_load().
|
||||
*/
|
||||
function user_translated_menu_link_alter(&$link) {
|
||||
function user_menu_link_load($menu_links) {
|
||||
// Hide the "User account" link for anonymous users.
|
||||
if ($link['link_path'] == 'user' && $link['module'] == 'system' && !$GLOBALS['user']->uid) {
|
||||
$link['hidden'] = 1;
|
||||
foreach ($menu_links as $link) {
|
||||
if ($link['link_path'] == 'user' && $link['module'] == 'system' && !$GLOBALS['user']->uid) {
|
||||
$link['hidden'] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ class FilterEqualityTest extends ViewUnitTestBase {
|
|||
parent::setUp();
|
||||
|
||||
$this->enableModules(array('system'));
|
||||
$this->enableModules(array('menu_link'));
|
||||
}
|
||||
|
||||
function viewsData() {
|
||||
|
|
|
@ -38,6 +38,7 @@ class FilterInOperatorTest extends ViewUnitTestBase {
|
|||
parent::setUp();
|
||||
|
||||
$this->enableModules(array('system'));
|
||||
$this->enableModules(array('menu_link'));
|
||||
}
|
||||
|
||||
function viewsData() {
|
||||
|
|
|
@ -38,6 +38,7 @@ class FilterNumericTest extends ViewUnitTestBase {
|
|||
parent::setUp();
|
||||
|
||||
$this->enableModules(array('system'));
|
||||
$this->enableModules(array('menu_link'));
|
||||
}
|
||||
|
||||
function viewsData() {
|
||||
|
|
|
@ -37,6 +37,7 @@ class FilterStringTest extends ViewUnitTestBase {
|
|||
parent::setUp();
|
||||
|
||||
$this->enableModules(array('system'));
|
||||
$this->enableModules(array('menu_link'));
|
||||
}
|
||||
|
||||
function viewsData() {
|
||||
|
|
|
@ -236,12 +236,12 @@ function standard_install() {
|
|||
->execute();
|
||||
|
||||
// Create a Home link in the main menu.
|
||||
$item = array(
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'link_title' => st('Home'),
|
||||
'link_path' => '<front>',
|
||||
'menu_name' => 'main',
|
||||
);
|
||||
menu_link_save($item);
|
||||
));
|
||||
$menu_link->save();
|
||||
|
||||
// Enable the Contact link in the footer menu.
|
||||
menu_link_maintain('system', 'enable', 'contact');
|
||||
|
@ -250,16 +250,22 @@ function standard_install() {
|
|||
|
||||
// Populate the default shortcut set.
|
||||
$shortcut_set = shortcut_set_load('default');
|
||||
$shortcut_set->links[] = array(
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'link_path' => 'node/add',
|
||||
'link_title' => st('Add content'),
|
||||
'weight' => -20,
|
||||
);
|
||||
$shortcut_set->links[] = array(
|
||||
));
|
||||
$menu_link->save();
|
||||
$shortcut_set->links[$menu_link->uuid()] = $menu_link;
|
||||
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'link_path' => 'admin/content',
|
||||
'link_title' => st('Find content'),
|
||||
'weight' => -19,
|
||||
);
|
||||
));
|
||||
$menu_link->save();
|
||||
$shortcut_set->links[$menu_link->uuid()] = $menu_link;
|
||||
|
||||
$shortcut_set->save();
|
||||
|
||||
// Enable the admin theme.
|
||||
|
|
Loading…
Reference in New Issue