#473082 by sun, Amitaibu, dropcube, and Pasqualle: Added a custom menu API.
parent
b9caae9508
commit
8649db189d
|
@ -1464,7 +1464,13 @@ function menu_get_names() {
|
|||
* Return an array containing the names of system-defined (default) menus.
|
||||
*/
|
||||
function menu_list_system_menus() {
|
||||
return array('navigation' => 'Navigation', 'management' => 'Management', 'user-menu' => 'User menu', 'main-menu' => 'Main menu', 'secondary-menu' => 'Secondary menu');
|
||||
return array(
|
||||
'navigation' => 'Navigation',
|
||||
'management' => 'Management',
|
||||
'user-menu' => 'User menu',
|
||||
'main-menu' => 'Main menu',
|
||||
'secondary-menu' => 'Secondary menu',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -851,6 +851,20 @@ function block_filter_format_delete($format, $fallback) {
|
|||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement hook_menu_delete().
|
||||
*/
|
||||
function block_menu_delete($menu) {
|
||||
db_delete('block')
|
||||
->condition('module', 'menu')
|
||||
->condition('delta', $menu['menu_name'])
|
||||
->execute();
|
||||
db_delete('block_role')
|
||||
->condition('module', 'menu')
|
||||
->condition('delta', $menu['menu_name'])
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement hook_form_FORM_ID_alter().
|
||||
*/
|
||||
|
|
|
@ -408,7 +408,11 @@ function menu_edit_item_submit($form, &$form_state) {
|
|||
*/
|
||||
function menu_edit_menu($form, &$form_state, $type, $menu = array()) {
|
||||
$system_menus = menu_list_system_menus();
|
||||
$menu += array('menu_name' => '', 'title' => '', 'description' => '');
|
||||
$menu += array('menu_name' => '', 'old_name' => '', 'title' => '', 'description' => '');
|
||||
if (!empty($menu['menu_name'])) {
|
||||
$menu['old_name'] = $menu['menu_name'];
|
||||
}
|
||||
$form['old_name'] = array('#type' => 'value', '#value' => $menu['old_name']);
|
||||
|
||||
// The title of a system menu cannot be altered.
|
||||
if (isset($system_menus[$menu['menu_name']])) {
|
||||
|
@ -425,7 +429,7 @@ function menu_edit_menu($form, &$form_state, $type, $menu = array()) {
|
|||
}
|
||||
|
||||
// The internal menu name can only be defined during initial menu creation.
|
||||
if ($type == 'edit') {
|
||||
if (!empty($menu['old_name'])) {
|
||||
$form['#insert'] = FALSE;
|
||||
$form['menu_name'] = array('#type' => 'value', '#value' => $menu['menu_name']);
|
||||
}
|
||||
|
@ -516,41 +520,28 @@ function menu_delete_menu_confirm($form, &$form_state, $menu) {
|
|||
function menu_delete_menu_confirm_submit($form, &$form_state) {
|
||||
$menu = $form['#menu'];
|
||||
$form_state['redirect'] = 'admin/structure/menu';
|
||||
|
||||
// System-defined menus may not be deleted - only menus defined by this module.
|
||||
$system_menus = menu_list_system_menus();
|
||||
if (isset($system_menus[$menu['menu_name']]) || !(db_query("SELECT 1 FROM {menu_custom} WHERE menu_name = :menu", array(':menu' => $menu['menu_name']))->fetchField())) {
|
||||
return;
|
||||
}
|
||||
// Reset all the menu links defined by the system via hook_menu.
|
||||
|
||||
// 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['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $item) {
|
||||
menu_reset_item($item);
|
||||
foreach ($result as $link) {
|
||||
menu_reset_item($link);
|
||||
}
|
||||
|
||||
// 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['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $m) {
|
||||
menu_link_delete($m['mlid']);
|
||||
foreach ($result as $link) {
|
||||
menu_link_delete($link['mlid']);
|
||||
}
|
||||
// Delete all the links in the menu and the menu from the list of custom menus.
|
||||
db_delete('menu_links')
|
||||
->condition('menu_name', $menu['menu_name'])
|
||||
->execute();
|
||||
db_delete('menu_custom')
|
||||
->condition('menu_name', $menu['menu_name'])
|
||||
->execute();
|
||||
// Delete all the blocks for this menu.
|
||||
if (module_exists('block')) {
|
||||
db_delete('block')
|
||||
->condition('module', 'menu')
|
||||
->condition('delta', $menu['menu_name'])
|
||||
->execute();
|
||||
db_delete('block_role')
|
||||
->condition('module', 'menu')
|
||||
->condition('delta', $menu['menu_name'])
|
||||
->execute();
|
||||
}
|
||||
menu_cache_clear_all();
|
||||
cache_clear_all();
|
||||
|
||||
// Delete the custom menu and all its menu links.
|
||||
menu_delete($menu);
|
||||
|
||||
$t_args = array('%title' => $menu['title']);
|
||||
drupal_set_message(t('The custom menu %title has been deleted.', $t_args));
|
||||
watchdog('menu', 'Deleted custom menu %title and all its menu links.', $t_args, WATCHDOG_NOTICE);
|
||||
|
@ -571,8 +562,8 @@ function menu_edit_menu_validate($form, &$form_state) {
|
|||
|
||||
// We will add 'menu-' to the menu name to help avoid name-space conflicts.
|
||||
$item['menu_name'] = 'menu-' . $item['menu_name'];
|
||||
$custom_exists = db_query('SELECT menu_name FROM {menu_custom} WHERE menu_name = :menu', array(':menu' => $item['menu_name']))->fetchField();
|
||||
$link_exists = db_query_range("SELECT menu_name FROM {menu_links} WHERE menu_name = :menu", 0, 1, array(':menu' => $item['menu_name']))->fetchField();
|
||||
$custom_exists = db_query_range('SELECT 1 FROM {menu_custom} WHERE menu_name = :menu', 0, 1, array(':menu' => $item['menu_name']))->fetchField();
|
||||
$link_exists = db_query_range("SELECT 1 FROM {menu_links} WHERE menu_name = :menu", 0, 1, array(':menu' => $item['menu_name']))->fetchField();
|
||||
if ($custom_exists || $link_exists) {
|
||||
form_set_error('menu_name', t('The menu already exists.'));
|
||||
}
|
||||
|
@ -599,22 +590,10 @@ function menu_edit_menu_submit($form, &$form_state) {
|
|||
->fetchField();
|
||||
|
||||
menu_link_save($link);
|
||||
db_insert('menu_custom')
|
||||
->fields(array(
|
||||
'menu_name' => $menu['menu_name'],
|
||||
'title' => $menu['title'],
|
||||
'description' => $menu['description'],
|
||||
))
|
||||
->execute();
|
||||
menu_save($menu);
|
||||
}
|
||||
else {
|
||||
db_update('menu_custom')
|
||||
->fields(array(
|
||||
'title' => $menu['title'],
|
||||
'description' => $menu['description'],
|
||||
))
|
||||
->condition('menu_name', $menu['menu_name'])
|
||||
->execute();
|
||||
menu_save($menu);
|
||||
$result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :path", array(':path' => $path . $menu['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $m) {
|
||||
$link = menu_link_load($m['mlid']);
|
||||
|
|
|
@ -291,7 +291,7 @@ function hook_translated_menu_link_alter(&$item, $map) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Inform modules that a menu link has been created.
|
||||
*
|
||||
* This hook is used to notify modules that menu items have been
|
||||
|
@ -357,6 +357,78 @@ function hook_menu_link_delete($link) {
|
|||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs modules that a custom menu was created.
|
||||
*
|
||||
* This hook is used to notify modules that a custom menu has been created.
|
||||
* Contributed modules may use the information to perform actions based on the
|
||||
* information entered into the menu system.
|
||||
*
|
||||
* @param $menu
|
||||
* An array representing a custom menu:
|
||||
* - menu_name: The unique name of the custom menu.
|
||||
* - title: The human readable menu title.
|
||||
* - description: The custom menu description.
|
||||
*
|
||||
* @see hook_menu_update()
|
||||
* @see hook_menu_delete()
|
||||
*/
|
||||
function hook_menu_insert($menu) {
|
||||
// For example, we track available menus in a variable.
|
||||
$my_menus = variable_get('my_module_menus', array());
|
||||
$my_menus[$menu['menu_name']] = $menu['menu_name'];
|
||||
variable_set('my_module_menus', $my_menus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs modules that a custom menu was updated.
|
||||
*
|
||||
* This hook is used to notify modules that a custom menu has been updated.
|
||||
* Contributed modules may use the information to perform actions based on the
|
||||
* information entered into the menu system.
|
||||
*
|
||||
* @param $menu
|
||||
* An array representing a custom menu:
|
||||
* - menu_name: The unique name of the custom menu.
|
||||
* - title: The human readable menu title.
|
||||
* - description: The custom menu description.
|
||||
* - old_name: The current 'menu_name'. Note that internal menu names cannot
|
||||
* be changed after initial creation.
|
||||
*
|
||||
* @see hook_menu_insert()
|
||||
* @see hook_menu_delete()
|
||||
*/
|
||||
function hook_menu_update($menu) {
|
||||
// For example, we track available menus in a variable.
|
||||
$my_menus = variable_get('my_module_menus', array());
|
||||
$my_menus[$menu['menu_name']] = $menu['menu_name'];
|
||||
variable_set('my_module_menus', $my_menus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs modules that a custom menu was deleted.
|
||||
*
|
||||
* This hook is used to notify modules that a custom menu along with all links
|
||||
* contained in it (if any) has been deleted. Contributed modules may use the
|
||||
* information to perform actions based on the information entered into the menu
|
||||
* system.
|
||||
*
|
||||
* @param $link
|
||||
* An array representing a custom menu:
|
||||
* - menu_name: The unique name of the custom menu.
|
||||
* - title: The human readable menu title.
|
||||
* - description: The custom menu description.
|
||||
*
|
||||
* @see hook_menu_insert()
|
||||
* @see hook_menu_update()
|
||||
*/
|
||||
function hook_menu_delete($menu) {
|
||||
// Delete the record from our variable.
|
||||
$my_menus = variable_get('my_module_menus', array());
|
||||
unset($my_menus[$menu['menu_name']]);
|
||||
variable_set('my_module_menus', $my_menus);
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
||||
|
|
|
@ -11,17 +11,21 @@
|
|||
*/
|
||||
function menu_install() {
|
||||
$system_menus = menu_list_system_menus();
|
||||
$descriptions = array(
|
||||
'navigation' => 'The <em>Navigation</em> menu contains links such as Recent posts (if the Tracker module is enabled). Non-administrative links are added to this menu by default by modules.',
|
||||
'user-menu' => "The <em>User menu</em> contains links related to the user's account, as well as the 'Log out' link.",
|
||||
'management' => 'The <em>Management</em> menu contains links for content creation, structure, user management, and similar site activities.',
|
||||
'main-menu' => 'The <em>Main menu</em> is the default source for the Main links which are often used by themes to show the major sections of a site.',
|
||||
'secondary-menu' => 'The <em>Secondary menu</em> is the default source for the Secondary links which are often used for legal notices, contact details, and other navigation items that play a lesser role than the Main links.',
|
||||
);
|
||||
$t = get_t();
|
||||
$query = db_insert('menu_custom')->fields(array('menu_name', 'title', 'description'));
|
||||
$descriptions = array(
|
||||
'navigation' => $t('The <em>Navigation</em> menu contains links such as Recent posts (if the Tracker module is enabled). Non-administrative links are added to this menu by default by modules.'),
|
||||
'user-menu' => $t("The <em>User menu</em> contains links related to the user's account, as well as the 'Log out' link."),
|
||||
'management' => $t('The <em>Management</em> menu contains links for content creation, structure, user management, and similar site activities.'),
|
||||
'main-menu' => $t('The <em>Main menu</em> is the default source for the Main links which are often used by themes to show the major sections of a site.'),
|
||||
'secondary-menu' => $t('The <em>Secondary menu</em> is the default source for the Secondary links which are often used for legal notices, contact details, and other navigation items that play a lesser role than the Main links.'),
|
||||
);
|
||||
foreach ($system_menus as $menu_name => $title) {
|
||||
$query->values(array('menu_name' => $menu_name, 'title' => $t($title), 'description' => $t($descriptions[$menu_name])))->execute();
|
||||
$menu = array(
|
||||
'menu_name' => $menu_name,
|
||||
'title' => $t($title),
|
||||
'description' => $descriptions[$menu_name],
|
||||
);
|
||||
menu_save($menu);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -198,11 +198,97 @@ function menu_overview_title($menu) {
|
|||
|
||||
/**
|
||||
* Load the data for a single custom menu.
|
||||
*
|
||||
* @param $menu_name
|
||||
* The unique name of a custom menu to load.
|
||||
*/
|
||||
function menu_load($menu_name) {
|
||||
return db_query("SELECT * FROM {menu_custom} WHERE menu_name = :menu", array(':menu' => $menu_name))->fetchAssoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a custom menu.
|
||||
*
|
||||
* @param $menu
|
||||
* An array representing a custom menu:
|
||||
* - menu_name: The unique name of the custom menu.
|
||||
* - title: The human readable menu title.
|
||||
* - description: The custom menu description.
|
||||
* - old_name: For existing menus, the current 'menu_name', otherwise empty.
|
||||
* Decides whether hook_menu_insert() or hook_menu_update() will be invoked.
|
||||
*
|
||||
* Modules should always pass a fully populated $menu when saving a custom
|
||||
* menu, so other modules are able to output proper status or watchdog messages.
|
||||
*
|
||||
* @see menu_load()
|
||||
*/
|
||||
function menu_save($menu) {
|
||||
db_merge('menu_custom')
|
||||
->key(array('menu_name' => $menu['menu_name']))
|
||||
->fields(array(
|
||||
'title' => $menu['title'],
|
||||
'description' => $menu['description'],
|
||||
))
|
||||
->execute();
|
||||
|
||||
// Since custom menus are keyed by name and their machine-name cannot be
|
||||
// changed, there is no real differentiation between inserting and updating a
|
||||
// menu. To overcome this, we define the existing menu_name as 'old_name' in
|
||||
// menu_edit_menu().
|
||||
// @todo Replace this condition when db_merge() returns the proper query
|
||||
// result (insert/update).
|
||||
if (!empty($menu['old_name'])) {
|
||||
module_invoke_all('menu_update', $menu);
|
||||
}
|
||||
else {
|
||||
module_invoke_all('menu_insert', $menu);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a custom menu and all contained links.
|
||||
*
|
||||
* Note that this function deletes all menu links in a custom menu. While menu
|
||||
* links derived from router paths may be restored by rebuilding the menu, all
|
||||
* customized and custom links will be irreversibly gone. Therefore, this
|
||||
* function should usually be called from a user interface (form submit) handler
|
||||
* only, which allows the user to confirm the action.
|
||||
*
|
||||
* @param $menu
|
||||
* An array representing a custom menu:
|
||||
* - menu_name: The unique name of the custom menu.
|
||||
* - title: The human readable menu title.
|
||||
* - description: The custom menu description.
|
||||
*
|
||||
* Modules should always pass a fully populated $menu when deleting a custom
|
||||
* menu, so other modules are able to output proper status or watchdog messages.
|
||||
*
|
||||
* @see menu_load()
|
||||
*
|
||||
* _menu_delete_item() will take care of clearing the page cache. Other modules
|
||||
* should take care of their menu-related data by implementing
|
||||
* hook_menu_delete().
|
||||
*/
|
||||
function menu_delete($menu) {
|
||||
// Delete all links from the menu.
|
||||
$links = db_query("SELECT * FROM {menu_links} WHERE menu_name = :menu_name", array(':menu_name' => $menu['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 the custom menu.
|
||||
db_delete('menu_custom')
|
||||
->condition('menu_name', $menu['menu_name'])
|
||||
->execute();
|
||||
|
||||
module_invoke_all('menu_delete', $menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of menu items that are valid possible parents for the given menu item.
|
||||
*
|
||||
|
@ -496,3 +582,4 @@ function menu_get_menus($all = TRUE) {
|
|||
|
||||
return $query->execute()->fetchAllKeyed();
|
||||
}
|
||||
|
||||
|
|
|
@ -92,11 +92,38 @@ class MenuTestCase extends DrupalWebTestCase {
|
|||
$this->menu = $this->addCustomMenu();
|
||||
$this->doMenuTests($this->menu['menu_name']);
|
||||
$this->addInvalidMenuLink($this->menu['menu_name']);
|
||||
$this->addCustomMenuCRUD();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom menu using CRUD functions.
|
||||
*/
|
||||
function addCustomMenuCRUD() {
|
||||
// Add a new custom menu.
|
||||
$menu_name = substr(md5($this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
|
||||
$title = $this->randomName(16);
|
||||
|
||||
$menu = array(
|
||||
'menu_name' => $menu_name,
|
||||
'title' => $title,
|
||||
'description' => 'Description text',
|
||||
);
|
||||
menu_save($menu);
|
||||
|
||||
// Assert the new menu.
|
||||
$this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit');
|
||||
$this->assertText($title, t('Custom menu was added.'));
|
||||
|
||||
// Edit the menu.
|
||||
$new_title = $this->randomName(16);
|
||||
$menu['title'] = $new_title;
|
||||
menu_save($menu);
|
||||
$this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit');
|
||||
$this->assertText($new_title, t('Custom menu was edited.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom menu.
|
||||
*
|
||||
*/
|
||||
function addCustomMenu() {
|
||||
// Add custom menu.
|
||||
|
@ -152,6 +179,9 @@ class MenuTestCase extends DrupalWebTestCase {
|
|||
$this->assertResponse(200);
|
||||
$this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $title)), t('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();
|
||||
$this->assertFalse($result, t('All menu links associated to the custom menu were deleted.'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,13 +14,12 @@
|
|||
*/
|
||||
function toolbar_install() {
|
||||
$t = get_t();
|
||||
$query = db_insert('menu_custom')
|
||||
->fields(array(
|
||||
'menu_name' => 'admin_shortcuts',
|
||||
'title' => $t('Administration shortcuts'),
|
||||
'description' => $t('The <em>Admininstration shortcuts</em> menu contains commonly used links for administrative tasks.')
|
||||
))
|
||||
->execute();
|
||||
$menu = array(
|
||||
'menu_name' => 'admin_shortcuts',
|
||||
'title' => $t('Administration shortcuts'),
|
||||
'description' => $t('The <em>Administration shortcuts</em> menu contains commonly used links for administrative tasks.'),
|
||||
);
|
||||
menu_save($menu);
|
||||
|
||||
// Add starter convenience shortcuts.
|
||||
menu_rebuild();
|
||||
|
|
Loading…
Reference in New Issue