summaryrefslogtreecommitdiff
path: root/kolab.org/www/drupal-7.26/modules/menu
diff options
context:
space:
mode:
Diffstat (limited to 'kolab.org/www/drupal-7.26/modules/menu')
-rw-r--r--kolab.org/www/drupal-7.26/modules/menu/menu.admin.inc700
-rw-r--r--kolab.org/www/drupal-7.26/modules/menu/menu.admin.js46
-rw-r--r--kolab.org/www/drupal-7.26/modules/menu/menu.api.php87
-rw-r--r--kolab.org/www/drupal-7.26/modules/menu/menu.css12
-rw-r--r--kolab.org/www/drupal-7.26/modules/menu/menu.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/menu/menu.install210
-rw-r--r--kolab.org/www/drupal-7.26/modules/menu/menu.js65
-rw-r--r--kolab.org/www/drupal-7.26/modules/menu/menu.module794
-rw-r--r--kolab.org/www/drupal-7.26/modules/menu/menu.test724
9 files changed, 2651 insertions, 0 deletions
diff --git a/kolab.org/www/drupal-7.26/modules/menu/menu.admin.inc b/kolab.org/www/drupal-7.26/modules/menu/menu.admin.inc
new file mode 100644
index 0000000..66bd6f3
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/menu/menu.admin.inc
@@ -0,0 +1,700 @@
+<?php
+
+/**
+ * @file
+ * Administrative page callbacks for menu module.
+ */
+
+/**
+ * Menu callback which shows an overview page of all the custom menus and their descriptions.
+ */
+function menu_overview_page() {
+ $result = db_query("SELECT * FROM {menu_custom} ORDER BY title", array(), array('fetch' => PDO::FETCH_ASSOC));
+ $header = array(t('Title'), array('data' => t('Operations'), 'colspan' => '3'));
+ $rows = array();
+ foreach ($result as $menu) {
+ $row = array(theme('menu_admin_overview', array('title' => $menu['title'], 'name' => $menu['menu_name'], 'description' => $menu['description'])));
+ $row[] = array('data' => l(t('list links'), 'admin/structure/menu/manage/' . $menu['menu_name']));
+ $row[] = array('data' => l(t('edit menu'), 'admin/structure/menu/manage/' . $menu['menu_name'] . '/edit'));
+ $row[] = array('data' => l(t('add link'), 'admin/structure/menu/manage/' . $menu['menu_name'] . '/add'));
+ $rows[] = $row;
+ }
+
+ return theme('table', array('header' => $header, 'rows' => $rows));
+}
+
+/**
+ * Returns HTML for a menu title and description for the menu overview page.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - title: The menu's title.
+ * - description: The menu's description.
+ *
+ * @ingroup themeable
+ */
+function theme_menu_admin_overview($variables) {
+ $output = check_plain($variables['title']);
+ $output .= '<div class="description">' . filter_xss_admin($variables['description']) . '</div>';
+
+ return $output;
+}
+
+/**
+ * Form for editing an entire menu tree at once.
+ *
+ * Shows for one menu the menu links accessible to the current user and
+ * relevant operations.
+ */
+function menu_overview_form($form, &$form_state, $menu) {
+ global $menu_admin;
+ $form['#attached']['css'] = array(drupal_get_path('module', 'menu') . '/menu.css');
+ $sql = "
+ SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.delivery_callback, m.title, m.title_callback, m.title_arguments, m.type, m.description, 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' => $menu['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
+ $links = array();
+ foreach ($result as $item) {
+ $links[] = $item;
+ }
+ $tree = menu_tree_data($links);
+ $node_links = array();
+ menu_tree_collect_node_links($tree, $node_links);
+ // We indicate that a menu administrator is running the menu access check.
+ $menu_admin = TRUE;
+ menu_tree_check_access($tree, $node_links);
+ $menu_admin = FALSE;
+
+ $form = array_merge($form, _menu_overview_tree_form($tree));
+ $form['#menu'] = $menu;
+
+ if (element_children($form)) {
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save configuration'),
+ );
+ }
+ else {
+ $form['#empty_text'] = t('There are no menu links yet. <a href="@link">Add link</a>.', array('@link' => url('admin/structure/menu/manage/'. $form['#menu']['menu_name'] .'/add')));
+ }
+ return $form;
+}
+
+/**
+ * Recursive helper function for menu_overview_form().
+ *
+ * @param $tree
+ * The menu_tree retrieved by menu_tree_data.
+ */
+function _menu_overview_tree_form($tree) {
+ $form = &drupal_static(__FUNCTION__, array('#tree' => TRUE));
+ foreach ($tree as $data) {
+ $title = '';
+ $item = $data['link'];
+ // Don't show callbacks; these have $item['hidden'] < 0.
+ if ($item && $item['hidden'] >= 0) {
+ $mlid = 'mlid:' . $item['mlid'];
+ $form[$mlid]['#item'] = $item;
+ $form[$mlid]['#attributes'] = $item['hidden'] ? array('class' => array('menu-disabled')) : array('class' => array('menu-enabled'));
+ $form[$mlid]['title']['#markup'] = l($item['title'], $item['href'], $item['localized_options']);
+ if ($item['hidden']) {
+ $form[$mlid]['title']['#markup'] .= ' (' . t('disabled') . ')';
+ }
+ elseif ($item['link_path'] == 'user' && $item['module'] == 'system') {
+ $form[$mlid]['title']['#markup'] .= ' (' . t('logged in users only') . ')';
+ }
+
+ $form[$mlid]['hidden'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Enable @title menu link', array('@title' => $item['title'])),
+ '#title_display' => 'invisible',
+ '#default_value' => !$item['hidden'],
+ );
+ $form[$mlid]['weight'] = array(
+ '#type' => 'weight',
+ '#delta' => 50,
+ '#default_value' => $item['weight'],
+ '#title_display' => 'invisible',
+ '#title' => t('Weight for @title', array('@title' => $item['title'])),
+ );
+ $form[$mlid]['mlid'] = array(
+ '#type' => 'hidden',
+ '#value' => $item['mlid'],
+ );
+ $form[$mlid]['plid'] = array(
+ '#type' => 'hidden',
+ '#default_value' => $item['plid'],
+ );
+ // Build a list of operations.
+ $operations = array();
+ $operations['edit'] = array('#type' => 'link', '#title' => t('edit'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/edit');
+ // Only items created by the menu module can be deleted.
+ if ($item['module'] == 'menu' || $item['updated'] == 1) {
+ $operations['delete'] = array('#type' => 'link', '#title' => t('delete'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/delete');
+ }
+ // Set the reset column.
+ elseif ($item['module'] == 'system' && $item['customized']) {
+ $operations['reset'] = array('#type' => 'link', '#title' => t('reset'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/reset');
+ }
+ $form[$mlid]['operations'] = $operations;
+ }
+
+ if ($data['below']) {
+ _menu_overview_tree_form($data['below']);
+ }
+ }
+ return $form;
+}
+
+/**
+ * Submit handler for the menu overview form.
+ *
+ * This function takes great care in saving parent items first, then items
+ * underneath them. Saving items in the incorrect order can break the menu tree.
+ *
+ * @see menu_overview_form()
+ */
+function menu_overview_form_submit($form, &$form_state) {
+ // When dealing with saving menu items, the order in which these items are
+ // saved is critical. If a changed child item is saved before its parent,
+ // the child item could be saved with an invalid path past its immediate
+ // parent. To prevent this, save items in the form in the same order they
+ // are sent by $_POST, ensuring parents are saved first, then their children.
+ // See http://drupal.org/node/181126#comment-632270
+ $order = array_flip(array_keys($form_state['input'])); // Get the $_POST order.
+ $form = array_merge($order, $form); // Update our original form with the new order.
+
+ $updated_items = array();
+ $fields = array('weight', 'plid');
+ foreach (element_children($form) as $mlid) {
+ if (isset($form[$mlid]['#item'])) {
+ $element = $form[$mlid];
+ // Update any fields that have changed in this menu item.
+ foreach ($fields as $field) {
+ if ($element[$field]['#value'] != $element[$field]['#default_value']) {
+ $element['#item'][$field] = $element[$field]['#value'];
+ $updated_items[$mlid] = $element['#item'];
+ }
+ }
+ // Hidden is a special case, the value needs to be reversed.
+ if ($element['hidden']['#value'] != $element['hidden']['#default_value']) {
+ // Convert to integer rather than boolean due to PDO cast to string.
+ $element['#item']['hidden'] = $element['hidden']['#value'] ? 0 : 1;
+ $updated_items[$mlid] = $element['#item'];
+ }
+ }
+ }
+
+ // Save all our changed items to the database.
+ foreach ($updated_items as $item) {
+ $item['customized'] = 1;
+ menu_link_save($item);
+ }
+ drupal_set_message(t('Your configuration has been saved.'));
+}
+
+/**
+ * Returns HTML for the menu overview form into a table.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - form: A render element representing the form.
+ *
+ * @ingroup themeable
+ */
+function theme_menu_overview_form($variables) {
+ $form = $variables['form'];
+
+ drupal_add_tabledrag('menu-overview', 'match', 'parent', 'menu-plid', 'menu-plid', 'menu-mlid', TRUE, MENU_MAX_DEPTH - 1);
+ drupal_add_tabledrag('menu-overview', 'order', 'sibling', 'menu-weight');
+
+ $header = array(
+ t('Menu link'),
+ array('data' => t('Enabled'), 'class' => array('checkbox')),
+ t('Weight'),
+ array('data' => t('Operations'), 'colspan' => '3'),
+ );
+
+ $rows = array();
+ foreach (element_children($form) as $mlid) {
+ if (isset($form[$mlid]['hidden'])) {
+ $element = &$form[$mlid];
+ // Build a list of operations.
+ $operations = array();
+ foreach (element_children($element['operations']) as $op) {
+ $operations[] = array('data' => drupal_render($element['operations'][$op]), 'class' => array('menu-operations'));
+ }
+ while (count($operations) < 2) {
+ $operations[] = '';
+ }
+
+ // Add special classes to be used for tabledrag.js.
+ $element['plid']['#attributes']['class'] = array('menu-plid');
+ $element['mlid']['#attributes']['class'] = array('menu-mlid');
+ $element['weight']['#attributes']['class'] = array('menu-weight');
+
+ // Change the parent field to a hidden. This allows any value but hides the field.
+ $element['plid']['#type'] = 'hidden';
+
+ $row = array();
+ $row[] = theme('indentation', array('size' => $element['#item']['depth'] - 1)) . drupal_render($element['title']);
+ $row[] = array('data' => drupal_render($element['hidden']), 'class' => array('checkbox', 'menu-enabled'));
+ $row[] = drupal_render($element['weight']) . drupal_render($element['plid']) . drupal_render($element['mlid']);
+ $row = array_merge($row, $operations);
+
+ $row = array_merge(array('data' => $row), $element['#attributes']);
+ $row['class'][] = 'draggable';
+ $rows[] = $row;
+ }
+ }
+ $output = '';
+ if (empty($rows)) {
+ $rows[] = array(array('data' => $form['#empty_text'], 'colspan' => '7'));
+ }
+ $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'menu-overview')));
+ $output .= drupal_render_children($form);
+ 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['menu_name'], '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 = 'navigation: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')),
+ );
+ $form['weight'] = array(
+ '#type' => 'weight',
+ '#title' => t('Weight'),
+ '#delta' => 50,
+ '#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'));
+
+ 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_get_normal_path($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; Build the form that handles the adding/editing of a custom menu.
+ */
+function menu_edit_menu($form, &$form_state, $type, $menu = array()) {
+ $system_menus = menu_list_system_menus();
+ $menu += array(
+ 'menu_name' => '',
+ 'old_name' => !empty($menu['menu_name']) ? $menu['menu_name'] : '',
+ 'title' => '',
+ 'description' => '',
+ );
+ // Allow menu_edit_menu_submit() and other form submit handlers to determine
+ // whether the menu already exists.
+ $form['#insert'] = empty($menu['old_name']);
+ $form['old_name'] = array(
+ '#type' => 'value',
+ '#value' => $menu['old_name'],
+ );
+
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Title'),
+ '#default_value' => $menu['title'],
+ '#required' => TRUE,
+ // The title of a system menu cannot be altered.
+ '#access' => !isset($system_menus[$menu['menu_name']]),
+ );
+
+ $form['menu_name'] = array(
+ '#type' => 'machine_name',
+ '#title' => t('Menu name'),
+ '#default_value' => $menu['menu_name'],
+ '#maxlength' => MENU_MAX_MENU_NAME_LENGTH_UI,
+ '#description' => t('A unique name to construct the URL for the menu. It must only contain lowercase letters, numbers and hyphens.'),
+ '#machine_name' => array(
+ 'exists' => 'menu_edit_menu_name_exists',
+ 'source' => array('title'),
+ 'replace_pattern' => '[^a-z0-9-]+',
+ 'replace' => '-',
+ ),
+ // A menu's machine name cannot be changed.
+ '#disabled' => !empty($menu['old_name']) || isset($system_menus[$menu['menu_name']]),
+ );
+
+ $form['description'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Description'),
+ '#default_value' => $menu['description'],
+ );
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ );
+ // Only custom menus may be deleted.
+ $form['actions']['delete'] = array(
+ '#type' => 'submit',
+ '#value' => t('Delete'),
+ '#access' => $type == 'edit' && !isset($system_menus[$menu['menu_name']]),
+ '#submit' => array('menu_custom_delete_submit'),
+ );
+
+ return $form;
+}
+
+/**
+ * Submit function for the 'Delete' button on the menu editing form.
+ */
+function menu_custom_delete_submit($form, &$form_state) {
+ $form_state['redirect'] = 'admin/structure/menu/manage/' . $form_state['values']['menu_name'] . '/delete';
+}
+
+/**
+ * Menu callback; check access and get a confirm form for deletion of a custom menu.
+ */
+function menu_delete_menu_page($menu) {
+ // System-defined menus may not be deleted.
+ $system_menus = menu_list_system_menus();
+ if (isset($system_menus[$menu['menu_name']])) {
+ return MENU_ACCESS_DENIED;
+ }
+ return drupal_get_form('menu_delete_menu_confirm', $menu);
+}
+
+/**
+ * Build a confirm form for deletion of a custom menu.
+ */
+function menu_delete_menu_confirm($form, &$form_state, $menu) {
+ $form['#menu'] = $menu;
+ $caption = '';
+ $num_links = db_query("SELECT COUNT(*) FROM {menu_links} WHERE menu_name = :menu", array(':menu' => $menu['menu_name']))->fetchField();
+ 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['title'])) . '</p>';
+ }
+ $caption .= '<p>' . t('This action cannot be undone.') . '</p>';
+ return confirm_form($form, t('Are you sure you want to delete the custom menu %title?', array('%title' => $menu['title'])), 'admin/structure/menu/manage/' . $menu['menu_name'], $caption, t('Delete'));
+}
+
+/**
+ * Delete a custom menu and all links in it.
+ */
+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().
+ $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 $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 $link) {
+ menu_link_delete($link['mlid']);
+ }
+
+ // 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);
+}
+
+/**
+ * Returns whether a menu name already exists.
+ *
+ * @see menu_edit_menu()
+ * @see form_validate_machine_name()
+ */
+function menu_edit_menu_name_exists($value) {
+ // 'menu-' is added to the menu name to avoid name-space conflicts.
+ $value = 'menu-' . $value;
+ $custom_exists = db_query_range('SELECT 1 FROM {menu_custom} WHERE menu_name = :menu', 0, 1, array(':menu' => $value))->fetchField();
+ $link_exists = db_query_range("SELECT 1 FROM {menu_links} WHERE menu_name = :menu", 0, 1, array(':menu' => $value))->fetchField();
+
+ return $custom_exists || $link_exists;
+}
+
+/**
+ * Submit function for adding or editing a custom menu.
+ */
+function menu_edit_menu_submit($form, &$form_state) {
+ $menu = $form_state['values'];
+ $path = 'admin/structure/menu/manage/';
+ if ($form['#insert']) {
+ // Add 'menu-' to the menu name to help avoid name-space conflicts.
+ $menu['menu_name'] = 'menu-' . $menu['menu_name'];
+ $link['link_title'] = $menu['title'];
+ $link['link_path'] = $path . $menu['menu_name'];
+ $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);
+ menu_save($menu);
+ }
+ else {
+ 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']);
+ $link['link_title'] = $menu['title'];
+ menu_link_save($link);
+ }
+ }
+ drupal_set_message(t('Your configuration has been saved.'));
+ $form_state['redirect'] = $path . $menu['menu_name'];
+}
+
+/**
+ * Menu callback; Check access and present a confirm form for deleting a menu link.
+ */
+function menu_item_delete_page($item) {
+ // 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']) {
+ return MENU_ACCESS_DENIED;
+ }
+ return drupal_get_form('menu_item_delete_form', $item);
+}
+
+/**
+ * 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']);
+}
+
+/**
+ * Process menu 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']);
+ 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'];
+}
+
+/**
+ * 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'));
+}
+
+/**
+ * Process menu reset item form submissions.
+ */
+function menu_reset_item_confirm_submit($form, &$form_state) {
+ $item = $form_state['values']['item'];
+ $new_item = menu_reset_item($item);
+ drupal_set_message(t('The menu link was reset to its default settings.'));
+ $form_state['redirect'] = 'admin/structure/menu/manage/' . $new_item['menu_name'];
+}
+
+/**
+ * Menu callback; Build the form presenting menu configuration options.
+ */
+function menu_configure() {
+ $form['intro'] = array(
+ '#type' => 'item',
+ '#markup' => t('The menu module allows on-the-fly creation of menu links in the content authoring forms. To configure these settings for a particular content type, visit the <a href="@content-types">Content types</a> page, click the <em>edit</em> link for the content type, and go to the <em>Menu settings</em> section.', array('@content-types' => url('admin/structure/types'))),
+ );
+
+ $menu_options = menu_get_menus();
+
+ $main = variable_get('menu_main_links_source', 'main-menu');
+ $form['menu_main_links_source'] = array(
+ '#type' => 'select',
+ '#title' => t('Source for the Main links'),
+ '#default_value' => variable_get('menu_main_links_source', 'main-menu'),
+ '#empty_option' => t('No Main links'),
+ '#options' => $menu_options,
+ '#tree' => FALSE,
+ '#description' => t('Select what should be displayed as the Main links (typically at the top of the page).'),
+ );
+
+ $form['menu_secondary_links_source'] = array(
+ '#type' => 'select',
+ '#title' => t('Source for the Secondary links'),
+ '#default_value' => variable_get('menu_secondary_links_source', 'user-menu'),
+ '#empty_option' => t('No Secondary links'),
+ '#options' => $menu_options,
+ '#tree' => FALSE,
+ '#description' => t('Select the source for the Secondary links. An advanced option allows you to use the same source for both Main links (currently %main) and Secondary links: if your source menu has two levels of hierarchy, the top level menu links will appear in the Main links, and the children of the active link will appear in the Secondary links.', array('%main' => $main ? $menu_options[$main] : t('none'))),
+ );
+
+ return system_settings_form($form);
+}
diff --git a/kolab.org/www/drupal-7.26/modules/menu/menu.admin.js b/kolab.org/www/drupal-7.26/modules/menu/menu.admin.js
new file mode 100644
index 0000000..4fa094e
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/menu/menu.admin.js
@@ -0,0 +1,46 @@
+(function ($) {
+
+Drupal.behaviors.menuChangeParentItems = {
+ attach: function (context, settings) {
+ $('fieldset#edit-menu input').each(function () {
+ $(this).change(function () {
+ // Update list of available parent menu items.
+ Drupal.menu_update_parent_list();
+ });
+ });
+ }
+};
+
+/**
+ * Function to set the options of the menu parent item dropdown.
+ */
+Drupal.menu_update_parent_list = function () {
+ var values = [];
+
+ $('input:checked', $('fieldset#edit-menu')).each(function () {
+ // Get the names of all checked menus.
+ values.push(Drupal.checkPlain($.trim($(this).val())));
+ });
+
+ var url = Drupal.settings.basePath + 'admin/structure/menu/parents';
+ $.ajax({
+ url: location.protocol + '//' + location.host + url,
+ type: 'POST',
+ data: {'menus[]' : values},
+ dataType: 'json',
+ success: function (options) {
+ // Save key of last selected element.
+ var selected = $('fieldset#edit-menu #edit-menu-parent :selected').val();
+ // Remove all exisiting options from dropdown.
+ $('fieldset#edit-menu #edit-menu-parent').children().remove();
+ // Add new options to dropdown.
+ jQuery.each(options, function(index, value) {
+ $('fieldset#edit-menu #edit-menu-parent').append(
+ $('<option ' + (index == selected ? ' selected="selected"' : '') + '></option>').val(index).text(value)
+ );
+ });
+ }
+ });
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.26/modules/menu/menu.api.php b/kolab.org/www/drupal-7.26/modules/menu/menu.api.php
new file mode 100644
index 0000000..22d93ef
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/menu/menu.api.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Menu module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Respond to a custom menu creation.
+ *
+ * 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);
+}
+
+/**
+ * Respond to a custom menu update.
+ *
+ * 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);
+}
+
+/**
+ * Respond to a custom menu deletion.
+ *
+ * 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 $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_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".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/menu/menu.css b/kolab.org/www/drupal-7.26/modules/menu/menu.css
new file mode 100644
index 0000000..96f861a
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/menu/menu.css
@@ -0,0 +1,12 @@
+
+.menu-operations {
+ width: 100px;
+}
+
+.menu-enabled {
+ width: 70px;
+}
+
+.menu-enabled input {
+ margin-left:25px;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/menu/menu.info b/kolab.org/www/drupal-7.26/modules/menu/menu.info
new file mode 100644
index 0000000..f08d9bd
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/menu/menu.info
@@ -0,0 +1,13 @@
+name = Menu
+description = Allows administrators to customize the site navigation menu.
+package = Core
+version = VERSION
+core = 7.x
+files[] = menu.test
+configure = admin/structure/menu
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/menu/menu.install b/kolab.org/www/drupal-7.26/modules/menu/menu.install
new file mode 100644
index 0000000..346edf9
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/menu/menu.install
@@ -0,0 +1,210 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the menu module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function menu_schema() {
+ $schema['menu_custom'] = array(
+ 'description' => 'Holds definitions for top-level custom menus (for example, Main menu).',
+ 'fields' => array(
+ 'menu_name' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'Primary Key: Unique key for menu. This is used as a block delta so length is 32.',
+ ),
+ 'title' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'Menu title; displayed at top of block.',
+ 'translatable' => TRUE,
+ ),
+ 'description' => array(
+ 'type' => 'text',
+ 'not null' => FALSE,
+ 'description' => 'Menu description.',
+ 'translatable' => TRUE,
+ ),
+ ),
+ 'primary key' => array('menu_name'),
+ );
+
+ return $schema;
+}
+
+/**
+ * Implements hook_install().
+ */
+function menu_install() {
+ $system_menus = menu_list_system_menus();
+ $t = get_t();
+ $descriptions = array(
+ 'navigation' => $t('The <em>Navigation</em> menu contains links intended for site visitors. Links are added to the <em>Navigation</em> menu automatically by some modules.'),
+ 'user-menu' => $t("The <em>User</em> menu 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 administrative tasks.'),
+ 'main-menu' => $t('The <em>Main</em> menu is used on many sites to show the major sections of the site, often in a top navigation bar.'),
+ );
+ foreach ($system_menus as $menu_name => $title) {
+ $menu = array(
+ 'menu_name' => $menu_name,
+ 'title' => $t($title),
+ 'description' => $descriptions[$menu_name],
+ );
+ menu_save($menu);
+ }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function menu_uninstall() {
+ menu_rebuild();
+}
+
+/**
+ * @addtogroup updates-7.x-extra
+ * @{
+ */
+
+/**
+ * Migrate the "Default menu for content" setting to individual node types.
+ */
+function menu_update_7000() {
+ // Act only on sites originally on Drupal 6 that have a custom "Default menu
+ // for content" setting.
+ $default_node_menu = variable_get('menu_default_node_menu');
+ if (isset($default_node_menu)) {
+ // Remove variable no longer used in Drupal 7.
+ variable_del('menu_default_node_menu');
+
+ // Make sure the menu chosen as the default still exists.
+ $defined_menus = db_query('SELECT * FROM {menu_custom}')->fetchAllAssoc('menu_name', PDO::FETCH_ASSOC);
+ // If the menu does not exist, do nothing; nodes will use the default D7
+ // node menu settings.
+ if (!isset($defined_menus[$default_node_menu])) {
+ return;
+ }
+
+ // Update the menu settings for each node type.
+ foreach (_update_7000_node_get_types() as $type => $type_object) {
+ $type_menus = variable_get('menu_options_' . $type);
+ // If the site already has a custom menu setting for this node type (set
+ // on the initial upgrade to Drupal 7.0), don't override it.
+ if (!isset($type_menus)) {
+ // Set up this node type so that the Drupal 6 "Default menu for content"
+ // is still available in the "Menu settings" section.
+ variable_set('menu_options_' . $type, array($default_node_menu));
+ variable_set('menu_parent_' . $type, $default_node_menu . ':0');
+ }
+ }
+ }
+}
+
+/**
+ * Rename "Primary Links" and "Secondary Links" to their Drupal 7 equivalents.
+ */
+function menu_update_7001() {
+ // Migrate D6 menu_primary_links_source to D7 menu_main_links_source (without
+ // renaming).
+ if (variable_get('menu_primary_links_source') !== NULL) {
+ variable_set('menu_main_links_source', variable_get('menu_primary_links_source'));
+ variable_del('menu_primary_links_source');
+ }
+
+ // Rename each menu, and any settings that refer to the old menu name.
+ // - "Primary Links" has become system menu "Main menu".
+ // - "Secondary Links" has become a new custom menu "Secondary menu".
+ $rename = array(
+ 'primary-links' => array('main-menu', 'Main menu'),
+ 'secondary-links' => array('secondary-menu', 'Secondary menu'),
+ );
+ foreach ($rename as $from_menu => $to) {
+ list($to_menu, $to_title) = $to;
+ // Rename the menu, and links in the menu.
+ db_update('menu_custom')
+ ->fields(array('menu_name' => $to_menu, 'title' => $to_title))
+ ->condition('menu_name', $from_menu)
+ ->execute();
+ db_update('menu_links')
+ ->fields(array('menu_name' => $to_menu))
+ ->condition('menu_name', $from_menu)
+ ->execute();
+
+ // Update any content type that used this menu as a default menu.
+ // Note: these variables may be unset/default, in which case we leave them
+ // alone. See menu_update_7000()
+ foreach (_update_7000_node_get_types() as $type => $type_object) {
+ $menu_options = variable_get('menu_options_' . $type);
+ if ($menu_options !== NULL) {
+ variable_set('menu_options_' . $type, str_replace($from_menu, $to_menu, $menu_options));
+ if (variable_get('menu_parent_' . $type) == $from_menu . ':0') {
+ variable_set('menu_parent_' . $type, $to_menu . ':0');
+ }
+ }
+ }
+
+ // Update the "source for primary links" and "source for secondary links" to
+ // follow.
+ if (variable_get('menu_main_links_source') == $from_menu) {
+ variable_set('menu_main_links_source', $to_menu);
+ }
+ if (variable_get('menu_secondary_links_source') == $from_menu) {
+ variable_set('menu_secondary_links_source', $to_menu);
+ }
+ }
+}
+
+/**
+ * Rename the primary/secondary menu blocks to match previously renamed menus.
+ */
+function menu_update_7002(&$sandbox) {
+ // Check for the presence of old or new table names.
+ if (db_table_exists('blocks') || db_table_exists('block')) {
+ $renamed_deltas = array(
+ 'menu' => array(
+ 'primary-links' => 'main-menu',
+ 'secondary-links' => 'secondary-menu',
+ ),
+ );
+
+ $moved_deltas = array(
+ 'menu' => array('main-menu' => 'system'),
+ );
+
+ update_fix_d7_block_deltas($sandbox, $renamed_deltas, $moved_deltas);
+ }
+}
+/**
+ * Add missing custom menus to active menus list.
+ */
+function menu_update_7003(&$sandbox) {
+ // Make sure all custom menus are present in the active menus variable so that
+ // their items may appear in the active trail.
+ // @see menu_set_active_menu_names()
+ $active_menus = variable_get('menu_default_active_menus', array_keys(menu_list_system_menus()));
+ $update_variable = FALSE;
+ foreach (menu_get_names() as $menu_name) {
+ if (!in_array($menu_name, $active_menus) && (strpos($menu_name, 'menu-') === 0)) {
+ $active_menus[] = $menu_name;
+ $update_variable = TRUE;
+ }
+ }
+ if ($update_variable) {
+ variable_set('menu_default_active_menus', $active_menus);
+ }
+ // Clear the menu cache.
+ cache_clear_all(NULL, 'cache_menu');
+}
+
+/**
+ * @} End of "addtogroup updates-7.x-extra".
+ * The next series of updates should start at 8000.
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/menu/menu.js b/kolab.org/www/drupal-7.26/modules/menu/menu.js
new file mode 100644
index 0000000..ff4ef1e
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/menu/menu.js
@@ -0,0 +1,65 @@
+(function ($) {
+
+Drupal.behaviors.menuFieldsetSummaries = {
+ attach: function (context) {
+ $('fieldset.menu-link-form', context).drupalSetSummary(function (context) {
+ if ($('.form-item-menu-enabled input', context).is(':checked')) {
+ return Drupal.checkPlain($('.form-item-menu-link-title input', context).val());
+ }
+ else {
+ return Drupal.t('Not in menu');
+ }
+ });
+ }
+};
+
+/**
+ * Automatically fill in a menu link title, if possible.
+ */
+Drupal.behaviors.menuLinkAutomaticTitle = {
+ attach: function (context) {
+ $('fieldset.menu-link-form', context).each(function () {
+ // Try to find menu settings widget elements as well as a 'title' field in
+ // the form, but play nicely with user permissions and form alterations.
+ var $checkbox = $('.form-item-menu-enabled input', this);
+ var $link_title = $('.form-item-menu-link-title input', context);
+ var $title = $(this).closest('form').find('.form-item-title input');
+ // Bail out if we do not have all required fields.
+ if (!($checkbox.length && $link_title.length && $title.length)) {
+ return;
+ }
+ // If there is a link title already, mark it as overridden. The user expects
+ // that toggling the checkbox twice will take over the node's title.
+ if ($checkbox.is(':checked') && $link_title.val().length) {
+ $link_title.data('menuLinkAutomaticTitleOveridden', true);
+ }
+ // Whenever the value is changed manually, disable this behavior.
+ $link_title.keyup(function () {
+ $link_title.data('menuLinkAutomaticTitleOveridden', true);
+ });
+ // Global trigger on checkbox (do not fill-in a value when disabled).
+ $checkbox.change(function () {
+ if ($checkbox.is(':checked')) {
+ if (!$link_title.data('menuLinkAutomaticTitleOveridden')) {
+ $link_title.val($title.val());
+ }
+ }
+ else {
+ $link_title.val('');
+ $link_title.removeData('menuLinkAutomaticTitleOveridden');
+ }
+ $checkbox.closest('fieldset.vertical-tabs-pane').trigger('summaryUpdated');
+ $checkbox.trigger('formUpdated');
+ });
+ // Take over any title change.
+ $title.keyup(function () {
+ if (!$link_title.data('menuLinkAutomaticTitleOveridden') && $checkbox.is(':checked')) {
+ $link_title.val($title.val());
+ $link_title.val($title.val()).trigger('formUpdated');
+ }
+ });
+ });
+ }
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.26/modules/menu/menu.module b/kolab.org/www/drupal-7.26/modules/menu/menu.module
new file mode 100644
index 0000000..6444791
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/menu/menu.module
@@ -0,0 +1,794 @@
+<?php
+
+/**
+ * @file
+ * Allows administrators to customize the site's navigation menus.
+ *
+ * A menu (in this context) is a hierarchical collection of links, generally
+ * used for navigation. This is not to be confused with the
+ * @link menu Menu system @endlink of menu.inc and hook_menu(), which defines
+ * page routing requests for Drupal, and also allows the defined page routing
+ * URLs to be added to the main site navigation menu.
+ */
+
+/**
+ * Maximum length of menu name as entered by the user. Database length is 32
+ * and we add a menu- prefix.
+ */
+define('MENU_MAX_MENU_NAME_LENGTH_UI', 27);
+
+/**
+ * Implements hook_help().
+ */
+function menu_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#menu':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The Menu module provides an interface for managing menus. A menu is a hierarchical collection of links, which can be within or external to the site, generally used for navigation. Each menu is rendered in a block that can be enabled and positioned through the <a href="@blocks">Blocks administration page</a>. You can view and manage menus on the <a href="@menus">Menus administration page</a>. For more information, see the online handbook entry for the <a href="@menu">Menu module</a>.', array('@blocks' => url('admin/structure/block'), '@menus' => url('admin/structure/menu'), '@menu' => 'http://drupal.org/documentation/modules/menu/')) . '</p>';
+ $output .= '<h3>' . t('Uses') . '</h3>';
+ $output .= '<dl>';
+ $output .= '<dt>' . t('Managing menus') . '</dt>';
+ $output .= '<dd>' . t('Users with the <em>Administer menus and menu items</em> permission can add, edit and delete custom menus on the <a href="@menu">Menus administration page</a>. Custom menus can be special site menus, menus of external links, or any combination of internal and external links. You may create an unlimited number of additional menus, each of which will automatically have an associated block. By selecting <em>list links</em>, you can add, edit, or delete links for a given menu. The links listing page provides a drag-and-drop interface for controlling the order of links, and creating a hierarchy within the menu.', array('@menu' => url('admin/structure/menu'), '@add-menu' => url('admin/structure/menu/add'))) . '</dd>';
+ $output .= '<dt>' . t('Displaying menus') . '</dt>';
+ $output .= '<dd>' . t('After you have created a menu, you must enable and position the associated block on the <a href="@blocks">Blocks administration page</a>.', array('@blocks' => url('admin/structure/block'))) . '</dd>';
+ $output .= '</dl>';
+ return $output;
+ case 'admin/structure/menu/add':
+ return '<p>' . t('You can enable the newly-created block for this menu on the <a href="@blocks">Blocks administration page</a>.', array('@blocks' => url('admin/structure/block'))) . '</p>';
+ }
+ if ($path == 'admin/structure/menu' && module_exists('block')) {
+ return '<p>' . t('Each menu has a corresponding block that is managed on the <a href="@blocks">Blocks administration page</a>.', array('@blocks' => url('admin/structure/block'))) . '</p>';
+ }
+}
+
+/**
+ * Implements hook_permission().
+ */
+function menu_permission() {
+ return array(
+ 'administer menu' => array(
+ 'title' => t('Administer menus and menu items'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_menu().
+ */
+function menu_menu() {
+ $items['admin/structure/menu'] = array(
+ 'title' => 'Menus',
+ 'description' => 'Add new menus to your site, edit existing menus, and rename and reorganize menu links.',
+ 'page callback' => 'menu_overview_page',
+ 'access callback' => 'user_access',
+ 'access arguments' => array('administer menu'),
+ 'file' => 'menu.admin.inc',
+ );
+ $items['admin/structure/menu/parents'] = array(
+ 'title' => 'Parent menu items',
+ 'page callback' => 'menu_parent_options_js',
+ 'type' => MENU_CALLBACK,
+ 'access arguments' => array(TRUE),
+ );
+ $items['admin/structure/menu/list'] = array(
+ 'title' => 'List menus',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ );
+ $items['admin/structure/menu/add'] = array(
+ 'title' => 'Add menu',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('menu_edit_menu', 'add'),
+ 'access arguments' => array('administer menu'),
+ 'type' => MENU_LOCAL_ACTION,
+ 'file' => 'menu.admin.inc',
+ );
+ $items['admin/structure/menu/settings'] = array(
+ 'title' => 'Settings',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('menu_configure'),
+ 'access arguments' => array('administer menu'),
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 5,
+ 'file' => 'menu.admin.inc',
+ );
+ $items['admin/structure/menu/manage/%menu'] = array(
+ 'title' => 'Customize menu',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('menu_overview_form', 4),
+ 'title callback' => 'menu_overview_title',
+ 'title arguments' => array(4),
+ 'access arguments' => array('administer menu'),
+ 'file' => 'menu.admin.inc',
+ );
+ $items['admin/structure/menu/manage/%menu/list'] = array(
+ 'title' => 'List links',
+ 'weight' => -10,
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+ );
+ $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),
+ 'access arguments' => array('administer menu'),
+ 'type' => MENU_LOCAL_ACTION,
+ 'file' => 'menu.admin.inc',
+ );
+ $items['admin/structure/menu/manage/%menu/edit'] = array(
+ 'title' => 'Edit menu',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('menu_edit_menu', 'edit', 4),
+ 'access arguments' => array('administer menu'),
+ 'type' => MENU_LOCAL_TASK,
+ 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+ 'file' => 'menu.admin.inc',
+ );
+ $items['admin/structure/menu/manage/%menu/delete'] = array(
+ 'title' => 'Delete menu',
+ 'page callback' => 'menu_delete_menu_page',
+ 'page arguments' => array(4),
+ 'access arguments' => array('administer menu'),
+ 'file' => 'menu.admin.inc',
+ );
+ $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),
+ '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),
+ '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 arguments' => array(4),
+ 'access arguments' => array('administer menu'),
+ 'file' => 'menu.admin.inc',
+ );
+ return $items;
+}
+
+/**
+ * Implements hook_theme().
+ */
+function menu_theme() {
+ return array(
+ 'menu_overview_form' => array(
+ 'file' => 'menu.admin.inc',
+ 'render element' => 'form',
+ ),
+ 'menu_admin_overview' => array(
+ 'file' => 'menu.admin.inc',
+ 'variables' => array('title' => NULL, 'name' => NULL, 'description' => NULL),
+ ),
+ );
+}
+
+/**
+ * Implements hook_enable().
+ *
+ * Add a link for each custom menu.
+ */
+function menu_enable() {
+ menu_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';
+ $result = db_query("SELECT * FROM {menu_custom}", array(), array('fetch' => PDO::FETCH_ASSOC));
+ foreach ($result 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['title'];
+ $link['link_path'] = 'admin/structure/menu/manage/' . $menu['menu_name'];
+ $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);
+ }
+ }
+ menu_cache_clear_all();
+}
+
+/**
+ * Title callback for the menu overview page and links.
+ */
+function menu_overview_title($menu) {
+ return $menu['title'];
+}
+
+/**
+ * Load the data for a single custom menu.
+ *
+ * @param $menu_name
+ * The unique name of a custom menu to load.
+ * @return
+ * Array defining the custom menu, or FALSE if the menu doesn't exist.
+ */
+function menu_load($menu_name) {
+ $all_menus = menu_load_all();
+ return isset($all_menus[$menu_name]) ? $all_menus[$menu_name] : FALSE;
+}
+
+/**
+ * Load all custom menu data.
+ *
+ * @return
+ * Array of custom menu data.
+ */
+function menu_load_all() {
+ $custom_menus = &drupal_static(__FUNCTION__);
+ if (!isset($custom_menus)) {
+ if ($cached = cache_get('menu_custom', 'cache_menu')) {
+ $custom_menus = $cached->data;
+ }
+ else {
+ $custom_menus = db_query('SELECT * FROM {menu_custom}')->fetchAllAssoc('menu_name', PDO::FETCH_ASSOC);
+ cache_set('menu_custom', $custom_menus, 'cache_menu');
+ }
+ }
+ return $custom_menus;
+}
+
+/**
+ * Save a custom menu.
+ *
+ * @param $menu
+ * An array representing a custom menu:
+ * - menu_name: The unique name of the custom menu (composed of lowercase
+ * letters, numbers, and hyphens).
+ * - title: The human readable menu title.
+ * - description: The custom menu description.
+ *
+ * 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) {
+ $status = db_merge('menu_custom')
+ ->key(array('menu_name' => $menu['menu_name']))
+ ->fields(array(
+ 'title' => $menu['title'],
+ 'description' => $menu['description'],
+ ))
+ ->execute();
+ menu_cache_clear_all();
+
+ switch ($status) {
+ case SAVED_NEW:
+ // Make sure the menu is present in the active menus variable so that its
+ // items may appear in the menu active trail.
+ // @see menu_set_active_menu_names()
+ $active_menus = variable_get('menu_default_active_menus', array_keys(menu_get_menus()));
+ if (!in_array($menu['menu_name'], $active_menus)) {
+ $active_menus[] = $menu['menu_name'];
+ variable_set('menu_default_active_menus', $active_menus);
+ }
+
+ module_invoke_all('menu_insert', $menu);
+ break;
+
+ case SAVED_UPDATED:
+ module_invoke_all('menu_update', $menu);
+ break;
+ }
+}
+
+/**
+ * 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_links() 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.
+ menu_delete_links($menu['menu_name']);
+
+ // Remove menu from active menus variable.
+ $active_menus = variable_get('menu_default_active_menus', array_keys(menu_get_menus()));
+ foreach ($active_menus as $i => $menu_name) {
+ if ($menu['menu_name'] == $menu_name) {
+ unset($active_menus[$i]);
+ variable_set('menu_default_active_menus', $active_menus);
+ }
+ }
+
+ // Delete the custom menu.
+ db_delete('menu_custom')
+ ->condition('menu_name', $menu['menu_name'])
+ ->execute();
+
+ menu_cache_clear_all();
+ module_invoke_all('menu_delete', $menu);
+}
+
+/**
+ * Return a list of menu items that are valid possible parents for the given menu item.
+ *
+ * @param $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
+ * 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.
+ *
+ * @todo This has to be turned into a #process form element callback. The
+ * 'menu_override_parent_selector' variable is entirely superfluous.
+ */
+function menu_parent_options($menus, $item, $type = '') {
+ // 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
+ // retrieved here, unless menu_override_parent_selector is set to TRUE.
+ if (variable_get('menu_override_parent_selector', FALSE)) {
+ 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 (empty($type)) {
+ // If no node type is set, use all menus given to this function.
+ $available_menus = $menus;
+ }
+ else {
+ // If a node type is set, use all available menus for this type.
+ $type_menus = variable_get('menu_options_' . $type, array('main-menu' => 'main-menu'));
+ foreach ($type_menus as $menu) {
+ $available_menus[$menu] = $menu;
+ }
+ }
+
+ return _menu_get_options($menus, $available_menus, $item);
+}
+
+/**
+ * Page callback.
+ * Get all the available menus and menu items as a JavaScript array.
+ */
+function menu_parent_options_js() {
+ $available_menus = array();
+ if (isset($_POST['menus']) && count($_POST['menus'])) {
+ foreach ($_POST['menus'] as $menu) {
+ $available_menus[$menu] = $menu;
+ }
+ }
+ $options = _menu_get_options(menu_get_menus(), $available_menus, array('mlid' => 0));
+
+ drupal_json_output($options);
+}
+
+/**
+ * Helper function to get the items of the given menu.
+ */
+function _menu_get_options($menus, $available_menus, $item) {
+ // If the item has children, there is an added limit to the depth of valid parents.
+ if (isset($item['parent_depth_limit'])) {
+ $limit = $item['parent_depth_limit'];
+ }
+ else {
+ $limit = _menu_parent_depth_limit($item);
+ }
+
+ $options = array();
+ foreach ($menus as $menu_name => $title) {
+ if (isset($available_menus[$menu_name])) {
+ $tree = menu_tree_all_data($menu_name, NULL);
+ $options[$menu_name . ':0'] = '<' . $title . '>';
+ _menu_parents_recurse($tree, $menu_name, '--', $options, $item['mlid'], $limit);
+ }
+ }
+ return $options;
+}
+
+/**
+ * Recursive helper function for menu_parent_options().
+ */
+function _menu_parents_recurse($tree, $menu_name, $indent, &$options, $exclude, $depth_limit) {
+ foreach ($tree as $data) {
+ if ($data['link']['depth'] > $depth_limit) {
+ // Don't iterate through any links on this level.
+ break;
+ }
+ if ($data['link']['mlid'] != $exclude && $data['link']['hidden'] >= 0) {
+ $title = $indent . ' ' . truncate_utf8($data['link']['title'], 30, TRUE, FALSE);
+ if ($data['link']['hidden']) {
+ $title .= ' (' . t('disabled') . ')';
+ }
+ $options[$menu_name . ':' . $data['link']['mlid']] = $title;
+ if ($data['below']) {
+ _menu_parents_recurse($data['below'], $menu_name, $indent . '--', $options, $exclude, $depth_limit);
+ }
+ }
+ }
+}
+
+/**
+ * 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_info().
+ */
+function menu_block_info() {
+ $menus = menu_get_menus(FALSE);
+
+ $blocks = array();
+ foreach ($menus as $name => $title) {
+ $blocks[$name]['info'] = check_plain($title);
+ // Menu blocks can't be cached because each menu item can have
+ // a custom access callback. menu.inc manages its own caching.
+ $blocks[$name]['cache'] = DRUPAL_NO_CACHE;
+ }
+ return $blocks;
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function menu_block_view($delta = '') {
+ $menus = menu_get_menus(FALSE);
+ $data['subject'] = check_plain($menus[$delta]);
+ $data['content'] = menu_tree($delta);
+ // Add contextual links for this block.
+ if (!empty($data['content'])) {
+ $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($delta));
+ }
+ return $data;
+}
+
+/**
+ * Implements hook_block_view_alter().
+ */
+function menu_block_view_alter(&$data, $block) {
+ // Add contextual links for system menu blocks.
+ if ($block->module == 'system' && !empty($data['content'])) {
+ $system_menus = menu_list_system_menus();
+ if (isset($system_menus[$block->delta])) {
+ $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($block->delta));
+ }
+ }
+}
+
+/**
+ * Implements hook_node_insert().
+ */
+function menu_node_insert($node) {
+ menu_node_save($node);
+}
+
+/**
+ * Implements hook_node_update().
+ */
+function menu_node_update($node) {
+ menu_node_save($node);
+}
+
+/**
+ * Helper for hook_node_insert() and hook_node_update().
+ */
+function menu_node_save($node) {
+ if (isset($node->menu)) {
+ $link = &$node->menu;
+ if (empty($link['enabled'])) {
+ if (!empty($link['mlid'])) {
+ menu_link_delete($link['mlid']);
+ }
+ }
+ elseif (trim($link['link_title'])) {
+ $link['link_title'] = trim($link['link_title']);
+ $link['link_path'] = "node/$node->nid";
+ if (trim($link['description'])) {
+ $link['options']['attributes']['title'] = trim($link['description']);
+ }
+ else {
+ // If the description field was left empty, remove the title attribute
+ // from the menu link.
+ unset($link['options']['attributes']['title']);
+ }
+ if (!menu_link_save($link)) {
+ drupal_set_message(t('There was an error saving the menu link.'), 'error');
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_node_delete().
+ */
+function menu_node_delete($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']);
+ }
+}
+
+/**
+ * Implements hook_node_prepare().
+ */
+function menu_node_prepare($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-menu:0'), ':');
+ $item = array();
+ if (isset($node->nid)) {
+ $mlid = FALSE;
+ // Give priority to the default menu
+ $type_menus = variable_get('menu_options_' . $node->type, array('main-menu' => 'main-menu'));
+ 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();
+ }
+ // 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();
+ }
+ if ($mlid) {
+ $item = menu_link_load($mlid);
+ }
+ }
+ // 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,
+ );
+ }
+ // Find the depth limit for the parent select.
+ if (!isset($node->menu['parent_depth_limit'])) {
+ $node->menu['parent_depth_limit'] = _menu_parent_depth_limit($node->menu);
+ }
+}
+
+/**
+ * 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);
+}
+
+/**
+ * Implements hook_form_BASE_FORM_ID_alter().
+ *
+ * Adds menu item fields to the node form.
+ *
+ * @see menu_node_submit()
+ */
+function menu_form_node_form_alter(&$form, $form_state) {
+ // Generate a list of possible parents (not including this link or descendants).
+ // @todo This must be handled in a #process handler.
+ $link = $form['#node']->menu;
+ $type = $form['#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);
+ // If no possible parent menu items were found, there is nothing to display.
+ if (empty($options)) {
+ return;
+ }
+
+ $form['menu'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Menu settings'),
+ '#access' => user_access('administer menu'),
+ '#collapsible' => TRUE,
+ '#collapsed' => !$link['link_title'],
+ '#group' => 'additional_settings',
+ '#attached' => array(
+ 'js' => array(drupal_get_path('module', 'menu') . '/menu.js'),
+ ),
+ '#tree' => TRUE,
+ '#weight' => -2,
+ '#attributes' => array('class' => array('menu-link-form')),
+ );
+ $form['menu']['enabled'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Provide a menu link'),
+ '#default_value' => (int) (bool) $link['mlid'],
+ );
+ $form['menu']['link'] = array(
+ '#type' => 'container',
+ '#parents' => array('menu'),
+ '#states' => array(
+ 'invisible' => array(
+ 'input[name="menu[enabled]"]' => array('checked' => FALSE),
+ ),
+ ),
+ );
+
+ // Populate the element with the link data.
+ foreach (array('mlid', 'module', 'hidden', 'has_children', 'customized', 'options', 'expanded', 'hidden', 'parent_depth_limit') as $key) {
+ $form['menu']['link'][$key] = array('#type' => 'value', '#value' => $link[$key]);
+ }
+
+ $form['menu']['link']['link_title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Menu link title'),
+ '#default_value' => $link['link_title'],
+ );
+
+ $form['menu']['link']['description'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Description'),
+ '#default_value' => isset($link['options']['attributes']['title']) ? $link['options']['attributes']['title'] : '',
+ '#rows' => 1,
+ '#description' => t('Shown when hovering over the menu link.'),
+ );
+
+ $default = ($link['mlid'] ? $link['menu_name'] . ':' . $link['plid'] : variable_get('menu_parent_' . $type, 'main-menu:0'));
+ // If the current parent menu item is not present in options, use the first
+ // available option as default value.
+ // @todo User should not be allowed to access menu link settings in such a
+ // case.
+ if (!isset($options[$default])) {
+ $array = array_keys($options);
+ $default = reset($array);
+ }
+ $form['menu']['link']['parent'] = array(
+ '#type' => 'select',
+ '#title' => t('Parent item'),
+ '#default_value' => $default,
+ '#options' => $options,
+ '#attributes' => array('class' => array('menu-parent-select')),
+ );
+ $form['menu']['link']['weight'] = array(
+ '#type' => 'weight',
+ '#title' => t('Weight'),
+ '#delta' => 50,
+ '#default_value' => $link['weight'],
+ '#description' => t('Menu links with smaller weights are displayed before links with larger weights.'),
+ );
+}
+
+/**
+ * Implements hook_node_submit().
+ *
+ * @see menu_form_node_form_alter()
+ */
+function menu_node_submit($node, $form, $form_state) {
+ // 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'])) {
+ list($node->menu['menu_name'], $node->menu['plid']) = explode(':', $form_state['values']['menu']['parent']);
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Adds menu options to the node type form.
+ */
+function menu_form_node_type_form_alter(&$form, $form_state) {
+ $menu_options = menu_get_menus();
+ $type = $form['#node_type'];
+ $form['menu'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Menu settings'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#attached' => array(
+ 'js' => array(drupal_get_path('module', 'menu') . '/menu.admin.js'),
+ ),
+ '#group' => 'additional_settings',
+ );
+ $form['menu']['menu_options'] = array(
+ '#type' => 'checkboxes',
+ '#title' => t('Available menus'),
+ '#default_value' => variable_get('menu_options_' . $type->type, array('main-menu')),
+ '#options' => $menu_options,
+ '#description' => t('The menus available to place links in for this content type.'),
+ );
+ // To avoid an 'illegal option' error after saving the form we have to load
+ // 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));
+ $form['menu']['menu_parent'] = array(
+ '#type' => 'select',
+ '#title' => t('Default parent item'),
+ '#default_value' => variable_get('menu_parent_' . $type->type, 'main-menu:0'),
+ '#options' => $options,
+ '#description' => t('Choose the menu item to be the default parent for a new link in the content authoring form.'),
+ '#attributes' => array('class' => array('menu-title-select')),
+ );
+
+ // Call Drupal.menu_update_parent_list() to filter the list of
+ // available default parent menu items based on the selected menus.
+ drupal_add_js(
+ '(function ($) { Drupal.menu_update_parent_list(); })(jQuery);',
+ array('scope' => 'footer', 'type' => 'inline')
+ );
+}
+
+/**
+ * Return an associative array of the custom menus names.
+ *
+ * @param $all
+ * If FALSE return only user-added menus, or if TRUE also include
+ * the menus defined by the system.
+ * @return
+ * An array with the machine-readable names as the keys, and human-readable
+ * titles as the values.
+ */
+function menu_get_menus($all = TRUE) {
+ if ($custom_menus = menu_load_all()) {
+ if (!$all) {
+ $custom_menus = array_diff_key($custom_menus, menu_list_system_menus());
+ }
+ foreach ($custom_menus as $menu_name => $menu) {
+ $custom_menus[$menu_name] = t($menu['title']);
+ }
+ asort($custom_menus);
+ }
+ return $custom_menus;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/menu/menu.test b/kolab.org/www/drupal-7.26/modules/menu/menu.test
new file mode 100644
index 0000000..95e0ee9
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/menu/menu.test
@@ -0,0 +1,724 @@
+<?php
+
+/**
+ * @file
+ * Tests for menu.module.
+ */
+
+class MenuTestCase extends DrupalWebTestCase {
+ protected $big_user;
+ protected $std_user;
+ protected $menu;
+ protected $items;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Menu link creation/deletion',
+ 'description' => 'Add a custom menu, add menu links to the custom menu and Navigation menu, check their data, and delete them using the menu module UI.',
+ 'group' => 'Menu'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('menu');
+ // Create users.
+ $this->big_user = $this->drupalCreateUser(array('access administration pages', 'administer blocks', 'administer menu', 'create article content'));
+ $this->std_user = $this->drupalCreateUser(array());
+ }
+
+ /**
+ * Login users, add menus and menu links, and test menu functionality through the admin and user interfaces.
+ */
+ function testMenu() {
+ // Login the user.
+ $this->drupalLogin($this->big_user);
+ $this->items = array();
+
+ // Do standard menu tests.
+ $this->doStandardMenuTests();
+
+ // Do custom menu tests.
+ $this->doCustomMenuTests();
+
+ // Do standard user tests.
+ // Login the user.
+ $this->drupalLogin($this->std_user);
+ $this->verifyAccess(403);
+ foreach ($this->items as $item) {
+ $node = node_load(substr($item['link_path'], 5)); // Paths were set as 'node/$nid'.
+ $this->verifyMenuLink($item, $node);
+ }
+
+ // Login the user.
+ $this->drupalLogin($this->big_user);
+
+ // Delete menu links.
+ foreach ($this->items as $item) {
+ $this->deleteMenuLink($item);
+ }
+
+ // Delete custom menu.
+ $this->deleteCustomMenu($this->menu);
+
+ // Modify and reset a standard menu link.
+ $item = $this->getStandardMenuLink();
+ $old_title = $item['link_title'];
+ $this->modifyMenuLink($item);
+ $item = menu_link_load($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']);
+ $this->assertEqual($description, $saved_item['options']['attributes']['title'], 'Saving an existing link updates the description (title attribute)');
+ $this->resetMenuLink($item, $old_title);
+ }
+
+ /**
+ * Test standard menu functionality using navigation menu.
+ *
+ */
+ function doStandardMenuTests() {
+ $this->doMenuTests();
+ $this->addInvalidMenuLink();
+ }
+
+ /**
+ * Test custom menu functionality using navigation menu.
+ *
+ */
+ function doCustomMenuTests() {
+ $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(hash('sha256', $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->assertRaw($title, '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->assertRaw($new_title, 'Custom menu was edited.');
+ }
+
+ /**
+ * Add custom menu.
+ */
+ function addCustomMenu() {
+ // Add custom menu.
+
+ // Try adding a menu using a menu_name that is too long.
+ $this->drupalGet('admin/structure/menu/add');
+ $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI + 1);
+ $title = $this->randomName(16);
+ $edit = array(
+ 'menu_name' => $menu_name,
+ 'description' => '',
+ 'title' => $title,
+ );
+ $this->drupalPost('admin/structure/menu/add', $edit, t('Save'));
+
+ // Verify that using a menu_name that is too long results in a validation message.
+ $this->assertRaw(t('!name cannot be longer than %max characters but is currently %length characters long.', array(
+ '!name' => t('Menu name'),
+ '%max' => MENU_MAX_MENU_NAME_LENGTH_UI,
+ '%length' => drupal_strlen($menu_name),
+ )));
+
+ // Change the menu_name so it no longer exceeds the maximum length.
+ $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
+ $edit['menu_name'] = $menu_name;
+ $this->drupalPost('admin/structure/menu/add', $edit, t('Save'));
+
+ // Verify that no validation error is given for menu_name length.
+ $this->assertNoRaw(t('!name cannot be longer than %max characters but is currently %length characters long.', array(
+ '!name' => t('Menu name'),
+ '%max' => MENU_MAX_MENU_NAME_LENGTH_UI,
+ '%length' => drupal_strlen($menu_name),
+ )));
+ // Unlike most other modules, there is no confirmation message displayed.
+
+ $this->drupalGet('admin/structure/menu');
+ $this->assertText($title, 'Menu created');
+
+ // Enable the custom menu block.
+ $menu_name = 'menu-' . $menu_name; // Drupal prepends the name with 'menu-'.
+ $edit = array();
+ $edit['blocks[menu_' . $menu_name . '][region]'] = 'sidebar_first';
+ $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+ $this->assertResponse(200);
+ $this->assertText(t('The block settings have been updated.'), 'Custom menu block was enabled');
+
+ return menu_load($menu_name);
+ }
+
+ /**
+ * Delete custom menu.
+ *
+ * @param string $menu_name Custom menu name.
+ */
+ function deleteCustomMenu($menu) {
+ $menu_name = $this->menu['menu_name'];
+ $title = $this->menu['title'];
+
+ // Delete custom menu.
+ $this->drupalPost("admin/structure/menu/manage/$menu_name/delete", array(), t('Delete'));
+ $this->assertResponse(200);
+ $this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $title)), '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, 'All menu links associated to the custom menu were deleted.');
+ }
+
+ /**
+ * Test menu functionality using navigation menu.
+ *
+ */
+ function doMenuTests($menu_name = 'navigation') {
+ // Add nodes to use as links for menu links.
+ $node1 = $this->drupalCreateNode(array('type' => 'article'));
+ $node2 = $this->drupalCreateNode(array('type' => 'article'));
+ $node3 = $this->drupalCreateNode(array('type' => 'article'));
+ $node4 = $this->drupalCreateNode(array('type' => 'article'));
+ $node5 = $this->drupalCreateNode(array('type' => 'article'));
+
+ // Add menu links.
+ $item1 = $this->addMenuLink(0, 'node/' . $node1->nid, $menu_name);
+ $item2 = $this->addMenuLink($item1['mlid'], 'node/' . $node2->nid, $menu_name, FALSE);
+ $item3 = $this->addMenuLink($item2['mlid'], 'node/' . $node3->nid, $menu_name);
+ $this->assertMenuLink($item1['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item1['mlid'], 'p2' => 0));
+ $this->assertMenuLink($item2['mlid'], array('depth' => 2, 'has_children' => 1, 'p1' => $item1['mlid'], 'p2' => $item2['mlid'], 'p3' => 0));
+ $this->assertMenuLink($item3['mlid'], array('depth' => 3, 'has_children' => 0, 'p1' => $item1['mlid'], 'p2' => $item2['mlid'], 'p3' => $item3['mlid'], 'p4' => 0));
+
+ // Verify menu links.
+ $this->verifyMenuLink($item1, $node1);
+ $this->verifyMenuLink($item2, $node2, $item1, $node1);
+ $this->verifyMenuLink($item3, $node3, $item2, $node2);
+
+ // Add more menu links.
+ $item4 = $this->addMenuLink(0, 'node/' . $node4->nid, $menu_name);
+ $item5 = $this->addMenuLink($item4['mlid'], 'node/' . $node5->nid, $menu_name);
+ $this->assertMenuLink($item4['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => 0));
+ $this->assertMenuLink($item5['mlid'], array('depth' => 2, 'has_children' => 0, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => 0));
+
+ // Modify menu links.
+ $this->modifyMenuLink($item1);
+ $this->modifyMenuLink($item2);
+
+ // Toggle menu links.
+ $this->toggleMenuLink($item1);
+ $this->toggleMenuLink($item2);
+
+ // Move link and verify that descendants are updated.
+ $this->moveMenuLink($item2, $item5['mlid'], $menu_name);
+ $this->assertMenuLink($item1['mlid'], array('depth' => 1, 'has_children' => 0, 'p1' => $item1['mlid'], 'p2' => 0));
+ $this->assertMenuLink($item4['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => 0));
+ $this->assertMenuLink($item5['mlid'], array('depth' => 2, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => 0));
+ $this->assertMenuLink($item2['mlid'], array('depth' => 3, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => $item2['mlid'], 'p4' => 0));
+ $this->assertMenuLink($item3['mlid'], array('depth' => 4, 'has_children' => 0, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => $item2['mlid'], 'p4' => $item3['mlid'], 'p5' => 0));
+
+ // Enable a link via the overview form.
+ $this->disableMenuLink($item1);
+ $edit = array();
+
+ // Note in the UI the 'mlid:x[hidden]' form element maps to enabled, or
+ // NOT hidden.
+ $edit['mlid:' . $item1['mlid'] . '[hidden]'] = TRUE;
+ $this->drupalPost('admin/structure/menu/manage/' . $item1['menu_name'], $edit, t('Save configuration'));
+
+ // Verify in the database.
+ $this->assertMenuLink($item1['mlid'], array('hidden' => 0));
+
+ // Save menu links for later tests.
+ $this->items[] = $item1;
+ $this->items[] = $item2;
+ }
+
+ /**
+ * Add and remove a menu link with a query string and fragment.
+ */
+ function testMenuQueryAndFragment() {
+ $this->drupalLogin($this->big_user);
+
+ // Make a path with query and fragment on.
+ $path = 'node?arg1=value1&arg2=value2';
+ $item = $this->addMenuLink(0, $path);
+
+ $this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
+ $this->assertFieldByName('link_path', $path, 'Path is found with both query and fragment.');
+
+ // Now change the path to something without query and fragment.
+ $path = 'node';
+ $this->drupalPost('admin/structure/menu/item/' . $item['mlid'] . '/edit', array('link_path' => $path), t('Save'));
+ $this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
+ $this->assertFieldByName('link_path', $path, 'Path no longer has query or fragment.');
+ }
+
+ /**
+ * Add a menu link using the menu module UI.
+ *
+ * @param integer $plid Parent menu link id.
+ * @param string $link Link path.
+ * @param string $menu_name Menu name.
+ * @return array Menu link created.
+ */
+ function addMenuLink($plid = 0, $link = '<front>', $menu_name = 'navigation', $expanded = TRUE) {
+ // View add menu link page.
+ $this->drupalGet("admin/structure/menu/manage/$menu_name/add");
+ $this->assertResponse(200);
+
+ $title = '!link_' . $this->randomName(16);
+ $edit = array(
+ 'link_path' => $link,
+ 'link_title' => $title,
+ 'description' => '',
+ 'enabled' => TRUE, // Use this to disable the menu and test.
+ 'expanded' => $expanded, // Setting this to true should test whether it works when we do the std_user tests.
+ 'parent' => $menu_name . ':' . $plid,
+ 'weight' => '0',
+ );
+
+ // 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');
+
+ $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));
+
+ return $item;
+ }
+
+ /**
+ * Attempt to add menu link with invalid path or no access permission.
+ *
+ * @param string $menu_name Menu name.
+ */
+ function addInvalidMenuLink($menu_name = 'navigation') {
+ foreach (array('-&-', 'admin/people/permissions', '#') as $link_path) {
+ $edit = array(
+ 'link_path' => $link_path,
+ 'link_title' => 'title',
+ );
+ $this->drupalPost("admin/structure/menu/manage/$menu_name/add", $edit, t('Save'));
+ $this->assertRaw(t("The path '@path' is either invalid or you do not have access to it.", array('@path' => $link_path)), 'Menu link was not created');
+ }
+ }
+
+ /**
+ * Verify a menu link using the menu module UI.
+ *
+ * @param array $item Menu link.
+ * @param object $item_node Menu link content node.
+ * @param array $parent Parent menu link.
+ * @param object $parent_node Parent menu link content node.
+ */
+ function verifyMenuLink($item, $item_node, $parent = NULL, $parent_node = NULL) {
+ // View home page.
+ $this->drupalGet('');
+ $this->assertResponse(200);
+
+ // Verify parent menu link.
+ if (isset($parent)) {
+ // Verify menu link.
+ $title = $parent['link_title'];
+ $this->assertLink($title, 0, 'Parent menu link was displayed');
+
+ // Verify menu link link.
+ $this->clickLink($title);
+ $title = $parent_node->title;
+ $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Parent menu link link target was correct');
+ }
+
+ // Verify menu link.
+ $title = $item['link_title'];
+ $this->assertLink($title, 0, 'Menu link was displayed');
+
+ // Verify menu link link.
+ $this->clickLink($title);
+ $title = $item_node->title;
+ $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Menu link link target was correct');
+ }
+
+ /**
+ * Change the parent of a menu link using the menu module UI.
+ */
+ function moveMenuLink($item, $plid, $menu_name) {
+ $mlid = $item['mlid'];
+
+ $edit = array(
+ 'parent' => $menu_name . ':' . $plid,
+ );
+ $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
+ $this->assertResponse(200);
+ }
+
+ /**
+ * Modify a menu link using the menu module UI.
+ *
+ * @param array $item Menu link passed by reference.
+ */
+ function modifyMenuLink(&$item) {
+ $item['link_title'] = $this->randomName(16);
+
+ $mlid = $item['mlid'];
+ $title = $item['link_title'];
+
+ // Edit menu link.
+ $edit = array();
+ $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.
+
+ // Verify menu link.
+ $this->drupalGet('admin/structure/menu/manage/' . $item['menu_name']);
+ $this->assertText($title, 'Menu link was edited');
+ }
+
+ /**
+ * Reset a standard menu link using the menu module UI.
+ *
+ * @param array $item Menu link.
+ * @param string $old_title Original title for menu link.
+ */
+ function resetMenuLink($item, $old_title) {
+ $mlid = $item['mlid'];
+ $title = $item['link_title'];
+
+ // Reset menu link.
+ $this->drupalPost("admin/structure/menu/item/$mlid/reset", array(), t('Reset'));
+ $this->assertResponse(200);
+ $this->assertRaw(t('The menu link was reset to its default settings.'), 'Menu link was reset');
+
+ // Verify menu link.
+ $this->drupalGet('');
+ $this->assertNoText($title, 'Menu link was reset');
+ $this->assertText($old_title, 'Menu link was reset');
+ }
+
+ /**
+ * Delete a menu link using the menu module UI.
+ *
+ * @param array $item Menu link.
+ */
+ function deleteMenuLink($item) {
+ $mlid = $item['mlid'];
+ $title = $item['link_title'];
+
+ // Delete menu link.
+ $this->drupalPost("admin/structure/menu/item/$mlid/delete", array(), t('Confirm'));
+ $this->assertResponse(200);
+ $this->assertRaw(t('The menu link %title has been deleted.', array('%title' => $title)), 'Menu link was deleted');
+
+ // Verify deletion.
+ $this->drupalGet('');
+ $this->assertNoText($title, 'Menu link was deleted');
+ }
+
+ /**
+ * Alternately disable and enable a menu link.
+ *
+ * @param $item
+ * Menu link.
+ */
+ function toggleMenuLink($item) {
+ $this->disableMenuLink($item);
+
+ // Verify menu link is absent.
+ $this->drupalGet('');
+ $this->assertNoText($item['link_title'], 'Menu link was not displayed');
+ $this->enableMenuLink($item);
+
+ // Verify menu link is displayed.
+ $this->drupalGet('');
+ $this->assertText($item['link_title'], 'Menu link was displayed');
+ }
+
+ /**
+ * Disable a menu link.
+ *
+ * @param $item
+ * Menu link.
+ */
+ function disableMenuLink($item) {
+ $mlid = $item['mlid'];
+ $edit['enabled'] = FALSE;
+ $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
+
+ // Unlike most other modules, there is no confirmation message displayed.
+ // Verify in the database.
+ $this->assertMenuLink($mlid, array('hidden' => 1));
+ }
+
+ /**
+ * Enable a menu link.
+ *
+ * @param $item
+ * Menu link.
+ */
+ function enableMenuLink($item) {
+ $mlid = $item['mlid'];
+ $edit['enabled'] = TRUE;
+ $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
+
+ // Verify in the database.
+ $this->assertMenuLink($mlid, array('hidden' => 0));
+ }
+
+ /**
+ * Fetch the menu item from the database and compare it to the specified
+ * array.
+ *
+ * @param $mlid
+ * Menu item id.
+ * @param $item
+ * Array containing properties to verify.
+ */
+ 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']);
+ if (!empty($options['query'])) {
+ $item['link_path'] .= '?' . drupal_http_build_query($options['query']);
+ }
+ if (!empty($options['fragment'])) {
+ $item['link_path'] .= '#' . $options['fragment'];
+ }
+ foreach ($expected_item as $key => $value) {
+ $this->assertEqual($item[$key], $value, format_string('Parameter %key had expected value.', array('%key' => $key)));
+ }
+ }
+
+ /**
+ * 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();
+ $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);
+ $this->assertTrue((bool) $item, 'Standard menu link was loaded');
+ return $item;
+ }
+
+ /**
+ * Verify the logged in user has the desired access to the various menu nodes.
+ *
+ * @param integer $response HTTP response code.
+ */
+ private function verifyAccess($response = 200) {
+ // View menu help node.
+ $this->drupalGet('admin/help/menu');
+ $this->assertResponse($response);
+ if ($response == 200) {
+ $this->assertText(t('Menu'), 'Menu help was displayed');
+ }
+
+ // View menu build overview node.
+ $this->drupalGet('admin/structure/menu');
+ $this->assertResponse($response);
+ if ($response == 200) {
+ $this->assertText(t('Menus'), 'Menu build overview node was displayed');
+ }
+
+ // View navigation menu customization node.
+ $this->drupalGet('admin/structure/menu/manage/navigation');
+ $this->assertResponse($response);
+ if ($response == 200) {
+ $this->assertText(t('Navigation'), 'Navigation menu node was displayed');
+ }
+
+ // View menu edit node.
+ $item = $this->getStandardMenuLink();
+ $this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
+ $this->assertResponse($response);
+ if ($response == 200) {
+ $this->assertText(t('Edit menu item'), 'Menu edit node was displayed');
+ }
+
+ // View menu settings node.
+ $this->drupalGet('admin/structure/menu/settings');
+ $this->assertResponse($response);
+ if ($response == 200) {
+ $this->assertText(t('Menus'), 'Menu settings node was displayed');
+ }
+
+ // View add menu node.
+ $this->drupalGet('admin/structure/menu/add');
+ $this->assertResponse($response);
+ if ($response == 200) {
+ $this->assertText(t('Menus'), 'Add menu node was displayed');
+ }
+ }
+}
+
+/**
+ * Test menu settings for nodes.
+ */
+class MenuNodeTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Menu settings for nodes',
+ 'description' => 'Add, edit, and delete a node with menu link.',
+ 'group' => 'Menu',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('menu');
+
+ $this->admin_user = $this->drupalCreateUser(array(
+ 'access administration pages',
+ 'administer content types',
+ 'administer menu',
+ 'create page content',
+ 'edit any page content',
+ 'delete any page content',
+ ));
+ $this->drupalLogin($this->admin_user);
+ }
+
+ /**
+ * Test creating, editing, deleting menu links via node form widget.
+ */
+ function testMenuNodeFormWidget() {
+ // Enable Navigation menu as available menu.
+ $edit = array(
+ 'menu_options[navigation]' => 1,
+ );
+ $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
+ // Change default parent item to Navigation menu, so we can assert more
+ // easily.
+ $edit = array(
+ 'menu_parent' => 'navigation:0',
+ );
+ $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
+
+ // Create a node.
+ $node_title = $this->randomName();
+ $language = LANGUAGE_NONE;
+ $edit = array(
+ "title" => $node_title,
+ "body[$language][0][value]" => $this->randomString(),
+ );
+ $this->drupalPost('node/add/page', $edit, t('Save'));
+ $node = $this->drupalGetNodeByTitle($node_title);
+ // Assert that there is no link for the node.
+ $this->drupalGet('');
+ $this->assertNoLink($node_title);
+
+ // Edit the node, enable the menu link setting, but skip the link title.
+ $edit = array(
+ 'menu[enabled]' => 1,
+ );
+ $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
+ // Assert that there is no link for the node.
+ $this->drupalGet('');
+ $this->assertNoLink($node_title);
+
+ // Edit the node and create a menu link.
+ $edit = array(
+ 'menu[enabled]' => 1,
+ 'menu[link_title]' => $node_title,
+ 'menu[weight]' => 17,
+ );
+ $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
+ // Assert that the link exists.
+ $this->drupalGet('');
+ $this->assertLink($node_title);
+
+ $this->drupalGet('node/' . $node->nid . '/edit');
+ $this->assertOptionSelected('edit-menu-weight', 17, 'Menu weight correct in edit form');
+
+ // Edit the node and remove the menu link.
+ $edit = array(
+ 'menu[enabled]' => FALSE,
+ );
+ $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
+ // Assert that there is no link for the node.
+ $this->drupalGet('');
+ $this->assertNoLink($node_title);
+
+ // Add a menu link to the Management menu.
+ $item = array(
+ 'link_path' => 'node/' . $node->nid,
+ 'link_title' => $this->randomName(16),
+ 'menu_name' => 'management',
+ );
+ menu_link_save($item);
+
+ // Assert that disabled Management menu is not shown on the node/$nid/edit page.
+ $this->drupalGet('node/' . $node->nid . '/edit');
+ $this->assertText('Provide a menu link', 'Link in not allowed menu not shown in node edit form');
+ // Assert that the link is still in the management menu after save.
+ $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
+ $link = menu_link_load($item['mlid']);
+ $this->assertTrue($link, 'Link in not allowed menu still exists after saving node');
+
+ // Move the menu link back to the Navigation menu.
+ $item['menu_name'] = 'navigation';
+ menu_link_save($item);
+ // 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(
+ 'link_path' => 'node/'. $child_node->nid,
+ 'link_title' => $this->randomName(16),
+ 'plid' => $item['mlid'],
+ );
+ menu_link_save($child_item);
+ // 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.
+ $this->assertNoOption('edit-menu-parent', 'navigation:'. $item['mlid']);
+ $this->assertNoOption('edit-menu-parent', 'navigation:'. $child_item['mlid']);
+ // Assert that unallowed Management menu is not available in options.
+ $this->assertNoOption('edit-menu-parent', 'management:0');
+ }
+
+ /**
+ * Asserts that a select option in the current page does not exist.
+ *
+ * @param $id
+ * Id of select field to assert.
+ * @param $option
+ * Option to assert.
+ * @param $message
+ * Message to display.
+ * @return
+ * TRUE on pass, FALSE on fail.
+ *
+ * @todo move to simpletest drupal_web_test_case.php.
+ */
+ protected function assertNoOption($id, $option, $message = '') {
+ $selects = $this->xpath('//select[@id=:id]', array(':id' => $id));
+ $options = $this->xpath('//select[@id=:id]//option[@value=:option]', array(':id' => $id, ':option' => $option));
+ return $this->assertTrue(isset($selects[0]) && !isset($options[0]), $message ? $message : t('Option @option for field @id does not exist.', array('@option' => $option, '@id' => $id)), t('Browser'));
+ }
+}