From f3cd2da113853bef53b733a09a92e59c04d5ec51 Mon Sep 17 00:00:00 2001 From: Steven Wittens Date: Fri, 6 Apr 2007 04:39:51 +0000 Subject: #130991: Custom menu items, part 1 --- modules/menu/menu.module | 997 +++++++++++++++++++++++------------------------ 1 file changed, 482 insertions(+), 515 deletions(-) (limited to 'modules/menu/menu.module') diff --git a/modules/menu/menu.module b/modules/menu/menu.module index 3a4eae773..b5661b77c 100644 --- a/modules/menu/menu.module +++ b/modules/menu/menu.module @@ -31,54 +31,70 @@ Menu administration tabs: } } +/** + * Implementation of hook_perm(). + */ +function menu_perm() { + return array('administer menu'); +} + /** * Implementation of hook_menu(). */ function menu_menu() { - return; - $items = array(); - - if ($may_cache) { - $items[] = array('path' => 'admin/build/menu', - 'title' => t('Menus'), - 'description' => t("Control your site's navigation menu, primary links and secondary links. as well as rename and reorganize menu items."), - 'callback' => 'menu_overview', - 'access' => user_access('administer menu')); - $items[] = array('path' => 'admin/build/menu/list', - 'title' => t('List'), - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10); - - $items[] = array('path' => 'admin/build/menu/item/add', - 'title' => t('Add menu item'), - 'callback' => 'drupal_get_form', - 'callback arguments' => array('menu_edit_item_form', 'add'), - 'access' => user_access('administer menu'), - 'type' => MENU_LOCAL_TASK); - $items[] = array('path' => 'admin/build/menu/item/edit', - 'title' => t('Edit menu item'), - 'callback' => 'drupal_get_form', - 'callback arguments' => array('menu_edit_item_form', 'edit'), - 'access' => user_access('administer menu'), - 'type' => MENU_CALLBACK); - $items[] = array('path' => 'admin/build/menu/item/reset', - 'title' => t('Reset menu item'), - 'callback' => 'drupal_get_form', - 'callback arguments' => array('menu_reset_item'), - 'access' => user_access('administer menu'), - 'type' => MENU_CALLBACK); - $items[] = array('path' => 'admin/build/menu/item/disable', - 'title' => t('Disable menu item'), - 'callback' => 'menu_disable_item', - 'access' => user_access('administer menu'), - 'type' => MENU_CALLBACK); - $items[] = array('path' => 'admin/build/menu/item/delete', - 'title' => t('Delete menu item'), - 'callback' => 'drupal_get_form', - 'callback arguments' => array('menu_item_delete_form'), - 'access' => user_access('administer menu'), - 'type' => MENU_CALLBACK); - + $items['admin/build/menu'] = array( + 'title' => t('Menus'), + 'description' => t("Control your site's navigation menu, primary links and secondary links. as well as rename and reorganize menu items."), + 'page callback' => 'menu_overview', + 'access callback' => 'user_access', + 'access arguments' => array('administer menu'), + ); + $items['admin/build/menu/list'] = array( + 'title' => t('List'), + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + $items['admin/build/menu/item/disable'] = array( + 'title' => t('Disable menu item'), + 'page callback' => 'menu_flip_item', + 'page arguments' => array('0'), + 'type' => MENU_CALLBACK, + ); + $items['admin/build/menu/item/enable'] = array( + 'title' => t('Enable menu item'), + 'page callback' => 'menu_flip_item', + 'page arguments' => array('1'), + 'type' => MENU_CALLBACK, + ); + $items['admin/build/menu/item/add'] = array( + 'title' => t('Add menu item'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('menu_edit_item_form', 'add'), + 'parent' => 'admin/build/menu', + 'type' => MENU_LOCAL_TASK); + $items['admin/build/menu/item/edit'] = array( + 'title' => t('Edit menu item'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('menu_edit_item_form', 'edit'), + 'type' => MENU_CALLBACK); + $items['admin/build/menu/item/reset'] = array( + 'title' => t('Reset menu item'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('menu_reset_item'), + 'type' => MENU_CALLBACK); + $items['admin/build/menu/item/delete'] = array( + 'title' => t('Delete menu item'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('menu_item_delete_form'), + 'type' => MENU_CALLBACK); + $result = db_query('SELECT * FROM {menu_edit} WHERE admin = 1'); + while ($item = db_fetch_array($result)) { + $item['access callback'] = 1; + $item['access inherited'] = TRUE; + $items[$item['path']] = $item; + } + return $items; +/* $items[] = array('path' => 'admin/build/menu/menu/add', 'title' => t('Add menu'), 'callback' => 'drupal_get_form', @@ -105,230 +121,105 @@ function menu_menu() { 'type' => MENU_LOCAL_TASK, 'weight' => 5, ); - } - - return $items; +*/ } /** - * Implementation of hook_block(). + * Implementation of hook_menu_alter. */ -function menu_block($op = 'list', $delta = 0) { - if ($op == 'list') { - $blocks = array(); - $root_menus = menu_get_root_menus(); - foreach ($root_menus as $mid => $title) { - // Default "Navigation" block is handled by user.module. - if ($mid != 1) { - $blocks[$mid]['info'] = $title; - } +function menu_menu_alter(&$menu, $phase) { + if ($phase == MENU_ALTER_PREPROCESSED) { + $result = db_query('SELECT * FROM {menu_edit} me WHERE admin = 0'); + while ($item = db_fetch_array($result)) { + $menu[$item['path']] = $item + $menu[$item['path']]; } - return $blocks; - } - else if ($op == 'view') { - $item = menu_get_item($delta); - $data['subject'] = check_plain($item['title']); - $data['content'] = theme('menu_tree', $delta); - return $data; } } /** - * Implementation of hook_nodeapi(). + * Menu callback which displays every menu element accessible to the current + * user and the relevant operations. */ -function menu_nodeapi(&$node, $op) { - - if (user_access('administer menu')) { - switch ($op) { - case 'insert': - case 'update': - if ($node->menu['delete']) { - menu_node_form_delete($node); - menu_rebuild(); - } - elseif ($node->menu['title']) { - $node->menu['path'] = ($node->menu['path']) ? $node->menu['path'] : "node/$node->nid"; - menu_edit_item_save($node->menu); - menu_rebuild(); - } - break; - - case 'delete': - menu_node_form_delete($node); - menu_rebuild(); - break; +function menu_overview() { + $header = array(t('Menu item'), t('Expanded'), array('data' => t('Operations'), 'colspan' => '3')); + $result = db_query('SELECT m.*, me.disabled FROM {menu} m LEFT JOIN {menu_edit} me ON m.path = me.path WHERE visible = 1 OR (disabled = 1 AND admin = 0) ORDER BY mleft'); + $map = arg(); + $rows = array(); + while ($item = db_fetch_object($result)) { + _menu_translate($item, $map, MENU_RENDER_LINK); + if (!$item->access) { + continue; } - } -} - -/** - * Implementation of hook_perm(). - */ -function menu_perm() { - return array('administer menu'); -} - -/** - * Implementation of hook_form_alter(). - * Add menu item fields to the node form. - */ -function menu_form_alter(&$form, $form_id) { - if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { - $item = array(); - if ($form['nid']['#value'] > 0) { - $item = db_fetch_array(db_query("SELECT * FROM {menu} WHERE path = 'node/%d'", $form['nid']['#value'])); - if (isset($form['#post']['menu']) && is_array($form['#post']['menu'])) { - $item = !is_array($item) ? $form['#post']['menu'] : (($form['#post']['op'] == t('Preview')) ? array_merge($item, $form['#post']['menu']) : array_merge($form['#post']['menu'], $item)); - } + $title = str_repeat('  ', $item->depth - 1) . ($item->depth > 1 ? '- ' : ''); + $title .= l($item->title, $item->path, (array)$item); + // Populate the operations field. + $operations = array(); + // Set the edit column. + $operations[] = array('data' => l(t('edit'), 'admin/build/menu/item/edit/'. $item->mid)); + if ($item->disabled) { + $title .= ' ('. t('disabled') .')'; + $class = 'menu-disabled'; + $operations[] = array('data' => l(t('enable'), 'admin/build/menu/item/enable/'. $item->mid)); } - - $form['menu'] = array('#type' => 'fieldset', - '#title' => t('Menu settings'), - '#access' => user_access('administer menu'), - '#collapsible' => TRUE, - '#collapsed' => empty($item['title']), - '#tree' => TRUE, - '#weight' => 30, - ); - - $form['menu']['title'] = array('#type' => 'textfield', - '#title' => t('Title'), - '#default_value' => $item['title'], - '#description' => t('The name to display for this menu link.'), - ); - - $form['menu']['description'] = array('#type' => 'textfield', - '#title' => t('Description'), - '#default_value' => $item['description'], - '#description' => t('The description displayed when hovering over a menu item.'), - ); - - // Generate a list of possible parents. - $options = menu_parent_options($item['mid'], variable_get('menu_parent_items', 0)); - - $form['menu']['pid'] = array('#type' => 'select', - '#title' => t('Parent item'), - '#default_value' => $item['pid'], - '#options' => $options, - ); - - $form['menu']['path'] = array('#type' => 'hidden', - '#value' => $item['path'], - ); - - $form['menu']['weight'] = array('#type' => 'weight', - '#title' => t('Weight'), - '#default_value' => $item['weight'], - '#delta' => 10, - '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'), - ); - - $form['menu']['mid'] = array('#type' => 'hidden', - '#value' => $item['mid'] ? $item['mid'] : 0, - ); - - $form['menu']['type'] = array('#type' => 'hidden', - '#value' => $item['type'] ? $item['type'] : MENU_CUSTOM_ITEM, + else { + $class = 'menu-enabled'; + $operations[] = array('data' => l(t('disable'), 'admin/build/menu/item/disable/'. $item->mid)); + } + // Set the reset column. + if ($item->type & MENU_CREATED_BY_ADMIN) { + $operations[] = array('data' => l(t('delete'), 'admin/build/menu/item/delete/'. $item->mid)); + } + else if ($item->type & MENU_MODIFIED_BY_ADMIN) { + $operations[] = array('data' => l(t('reset'), 'admin/build/menu/item/reset/'. $item->mid)); + } + else { + $operations[] = array('data' => ''); + } + $row = array( + array('data' => $title, 'class' => $class), + array('data' => ($item->has_children ? (($item->type & MENU_EXPANDED) ? t('Yes') : t('No')) : ''), 'class' => $class), ); - - if ($item['mid'] > 0) { - $form['menu']['delete'] = array('#type' => 'checkbox', - '#title' => t('Check to delete this menu item.'), - '#default_value' => $item['delete'], - ); - - $form['menu']['advanced'] = array('#type' => 'item', - '#value' => t('You may also edit the advanced settings for this menu item.', array('@edit' => url("admin/build/menu/item/edit/{$item['mid']}"))), - ); + foreach ($operations as $operation) { + $operation['class'] = $class; + $row[] = $operation; } + $rows[] = $row; } + return theme('table', $header, $rows); } - -/** - * Menu callback; presents menu configuration options. - */ -function menu_configure() { - $root_menus = menu_get_root_menus(); - - $primary_options = $root_menus; - $primary_options[0] = t('No primary links'); - - $form['settings_links'] = array('#type' => 'fieldset', - '#title' => t('Primary and secondary links settings'), - ); - - $form['settings_links']['intro'] = array('#type' => 'item', - '#value' => t('Primary and secondary links provide a navigational menu system which usually (depending on your theme) appears at the top-right of the browser window. The links displayed can be generated either from a custom list created via the menu administration page or from a built-in list of menu items such as the navigation menu links.', array('@menu' => url('admin/build/menu'))), - ); - - $form['settings_links']['menu_primary_menu'] = array('#type' => 'select', - '#title' => t('Menu containing primary links'), - '#default_value' => variable_get('menu_primary_menu', 0), - '#options' => $primary_options, - ); - - $secondary_options = $root_menus; - $secondary_options[0] = t('No secondary links'); - - $form['settings_links']['menu_secondary_menu'] = array('#type' => 'select', - '#title' => t('Menu containing secondary links'), - '#default_value' => variable_get('menu_secondary_menu', 0), - '#options' => $secondary_options, - '#description' => t('If you select the same menu as primary links then secondary links will display the appropriate second level of your navigation hierarchy.'), - ); - - $form['settings_authoring'] = array('#type' => 'fieldset', - '#title' => t('Content authoring form settings'), - ); - - $form['settings_authoring']['intro'] = array('#type' => 'item', - '#value' => t('The menu module allows on-the-fly creation of menu links in the content authoring forms. The following option limits the menus in which a new link may be added. E.g., this can be used to force new menu items to be created in the primary links menu or to hide admin menu items.'), - ); - - $authoring_options = $root_menus; - $authoring_options[0] = t('Show all menus'); - - $form['settings_authoring']['menu_parent_items'] = array('#type' => 'select', - '#title' => t('Restrict parent items to'), - '#default_value' => variable_get('menu_parent_items', 0), - '#options' => $authoring_options, - '#description' => t('Choose the menu to be made available in the content authoring form. Only this menu item and its children will be shown.'), - ); - - return system_settings_form($form); -} - + /** - * Menu callback; handle the adding/editing of a new menu. + * Menu callback; enable/disable a menu item. + * + * @param $visible + * 0 to remove from the menu tree. 1 to make the item and its children + * reappear in menu tree. + * @param $mid + * mid of the menu item. + * @param $path + * Optional. Path of the menu item. Only considered if mid is NULL. */ -function menu_edit_menu_form($type, $mid = 0) { - if ($type == 'edit') { - if (!($item = db_fetch_array(db_query('SELECT * FROM {menu} WHERE mid = %d', $mid)))) { - drupal_not_found(); - return; +function menu_flip_item($visible, $mid, $path = NULL) { + if (isset($mid)) { + $parent = menu_get_item_by_mid($mid); + } + elseif (isset($path)) { + $parent = menu_get_item($path); + } + if (isset($parent) && $parent->access) { + $result = db_query('SELECT * FROM {menu} WHERE %d <= mleft AND mright <= %d', $parent->mleft, $parent->mright); + while ($item = db_fetch_object($result)) { + $update_result = db_query("UPDATE {menu_edit} SET disabled = %d WHERE path = '%s'", !$visible, $item->path); + if (!db_affected_rows($update_result)) { + db_query("INSERT INTO {menu_edit} (parent, path, title, description, weight, type, admin, disabled) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d, %d)", $item->parent, $item->path, $item->title, $item->description, 0, $item->type, 0, !$visible); + } + } + menu_rebuild(); + if (isset($mid)) { + drupal_set_message($visible ? t('The menu item has been enabled.') : t('The menu item has been disabled.')); + drupal_goto('admin/build/menu'); } } - else { - $item = array('mid' => 0, 'pid' => 0, 'path' => '', 'weight' => 0, 'type' => MENU_CUSTOM_MENU); - } - $form['title'] = array('#type' => 'textfield', - '#title' => t('Title'), - '#default_value' => $item['title'], - '#description' => t('The name of the menu.'), - '#required' => TRUE, - ); - $form['mid'] = array('#type' => 'value', '#value' => $item['mid']); - $form['pid'] = array('#type' => 'value', '#value' => $item['pid']); - $form['path'] = array('#type' => 'value', '#value' => $item['path']); - $form['weight'] = array('#type' => 'value', '#value' => $item['weight']); - $form['type'] = array('#type' => 'value', '#value' => $item['type']); - $form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); - // Reuse the submit function of menu_edit_item_form. - $form['#submit']['menu_edit_item_form_submit'] = array(); - $form['#validate']['menu_edit_item_form_validate'] = array(); - $form['#theme'] = 'menu_edit_item_form'; - - return $form; } /** @@ -336,7 +227,7 @@ function menu_edit_menu_form($type, $mid = 0) { */ function menu_edit_item_form($type, $mid = 0) { if ($type == 'edit') { - if (!($item = db_fetch_array(db_query('SELECT * FROM {menu} WHERE mid = %d', $mid)))) { + if (!($item = menu_get_item_by_mid($mid))) { drupal_not_found(); return; } @@ -344,71 +235,90 @@ function menu_edit_item_form($type, $mid = 0) { else { // This is an add form. // The mid argument (if set) will be the default pid to use. - // Otherwise, we default to the "Navigation" menu (pid #1). - $default_pid = $mid ? $mid : 1; - $item = array('mid' => 0, 'pid' => $default_pid, 'weight' => 0, 'type' => MENU_CUSTOM_ITEM); + $item = (object)array('mid' => 0, 'pid' => $mid ? $mid : 0, 'weight' => 0, 'type' => MENU_CUSTOM_ITEM, 'title' => '', 'description' => '', 'path' => ''); } $form['title'] = array('#type' => 'textfield', '#title' => t('Title'), - '#default_value' => $item['title'], + '#default_value' => $item->title, '#description' => t('The name of the menu item.'), '#required' => TRUE, ); - $form['description'] = array('#type' => 'textfield', + $form['description'] = array( + '#type' => 'textfield', '#title' => t('Description'), - '#default_value' => $item['description'], + '#default_value' => $item->description, '#description' => t('The description displayed when hovering over a menu item.'), ); - if ($item['type'] & MENU_CREATED_BY_ADMIN) { - $form['path'] = array('#type' => 'textfield', + if ($item->type & MENU_CREATED_BY_ADMIN) { + $form['path'] = array( + '#type' => 'textfield', '#title' => t('Path'), - '#default_value' => $item['path'], + '#default_value' => $item->path, '#description' => t('The path this menu item links to. 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' => '', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')), '#required' => TRUE, ); } else { - $form['_path'] = array('#type' => 'item', + $form['_path'] = array( + '#type' => 'item', '#title' => t('Path'), - '#description' => l($item['path'], $item['path']), + '#description' => l($item->path, $item->link_path, (array)$item), ); - $form['path'] = array('#type' => 'value', '#value' => $item['path']); } + $form['original_path'] = array('#type' => 'value', '#value' => $item->path); - $expanded = $item['type'] & MENU_EXPANDED ? 1 : 0; - $form['expanded'] = array('#type' => 'checkbox', + $form['expanded'] = array( + '#type' => 'checkbox', '#title' => t('Expanded'), - '#default_value' => $expanded, + '#default_value' => $item->type & MENU_EXPANDED ? 1 : 0, '#description' => t('If selected and this menu item has children, the menu will always appear expanded.'), ); // Generate a list of possible parents (not including this item or descendants). - $options = menu_parent_options($item['mid']); - $form['pid'] = array('#type' => 'select', + $options = menu_parent_options($item->mid); + $form['pid'] = array( + '#type' => 'select', '#title' => t('Parent item'), - '#default_value' => $item['pid'], + '#default_value' => $item->pid, '#options' => $options, ); - $form['weight'] = array('#type' => 'weight', + $form['weight'] = array( + '#type' => 'weight', '#title' => t('Weight'), - '#default_value' => $item['weight'], + '#default_value' => isset($item->weight) ? $item->weight : 0, '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'), ); // Always enable menu items (but not menus) when editing them. - if (!($item['type'] & MENU_IS_ROOT)) { - $item['type'] |= MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB; + if (!($item->type & MENU_IS_ROOT)) { + $item->type |= MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB; } - $form['type'] = array('#type' => 'value', '#value' => $item['type']); - $form['mid'] = array('#type' => 'value', '#value' => $item['mid']); + $form['type'] = array('#type' => 'value', '#value' => $item->type); $form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); return $form; } +function menu_edit_item_form_validate($form_id, $form_values) { + if (isset($form_values['path'])) { + $path = $form_values['path']; + // Skip external links. + $colonpos = strpos($path, ':'); + if ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos))) { + return; + } + $item = menu_get_item($path); + if (!$item || !$item->access) { + form_set_error('path', t('This path is either invalid or you do not have access to it')); + } + elseif ($path != $form_values['original_path'] && $item->path == $path) { + form_set_error('path', t('There is already a menu item pointing to this path.')); + } + } +} /** * Process menu and menu item add/edit form submissions. */ @@ -418,15 +328,137 @@ function menu_edit_item_form_submit($form_id, $form_values) { } /** - * Menu callback; delete a single custom item. - */ -function menu_item_delete_form($mid) { - if (!($menu = db_fetch_object(db_query('SELECT type, title FROM {menu} WHERE mid = %d', $mid)))) { + * Return a list of menu items that are valid possible parents for the + * given menu item. The list excludes the given item and its children. + * + * @param $mid + * The menu item id for which to generate a list of parents. + * If $mid == 0 then the complete tree is returned. + * @param $pid + * The menu item id of the menu item at which to start the tree. + * If $pid > 0 then this item will be included in the tree. + * @param $depth + * The current depth in the tree - used when recursing to indent the tree. + * @return + * An array of menu titles keyed on the mid. + */ +function menu_parent_options($mid, $pid = 0, $depth = 0) { + $options = array(0 => t('Root')); + // Exclude $mid and its children from the list unless $mid is 0. + if ($mid && $mid == $pid) { + return $options; + } + $sql = 'SELECT m.*, me.disabled FROM {menu} m LEFT JOIN {menu_edit} me ON m.path = me.path WHERE (m.visible = 1 OR (me.disabled = 1 AND me.admin = 0))'; + if (!$mid) { + $params = array(); + } + elseif ($item = menu_get_item_by_mid($mid)) { + $sql .=' AND (mleft < %d OR %d < mright)'; + $params = array($item->mleft, $item->mright); + } + else { + return $options; + } + if ($parent_item = menu_get_item_by_mid($pid)) { + $sql .= ' AND %d <= mleft AND %d <= mright'; + $params[] = $parent_item->mleft; + $params[] = $parent_item->mright; + } + $sql .= ' ORDER BY mleft'; + $result = db_query($sql, $params); + $map = arg(); + + while ($item = db_fetch_object($result)) { + _menu_translate($item, $map, MENU_RENDER_LINK); + $title = str_repeat('--', $item->depth) .' '. $item->title; + if ($item->disabled) { + $title .= ' ('. t('disabled') .')'; + } + $options[$item->mid] = $title; + } + return $options; +} + +/** + * Save changes to a menu item into the database. + * + * @return mid + */ +function menu_edit_item_save($edit) { + if (isset($edit['expanded'])) { + if ($edit['expanded']) { + $edit['type'] |= MENU_EXPANDED; + } + else { + $edit['type'] &= ~MENU_EXPANDED; + } + } + + $edit['type'] = $edit['type'] | MENU_MODIFIED_BY_ADMIN; + + $parent = $edit['pid'] ? db_result(db_query('SELECT path FROM {menu} WHERE mid = %d', $edit['pid'])) : ''; + $t_args = array('%title' => $edit['title']); + if (!empty($edit['original_path']) && db_num_rows(db_query("SELECT * FROM {menu_edit} WHERE path='%s'", $edit['original_path']))) { + db_query("UPDATE {menu_edit} SET parent = '%s', title = '%s', description = '%s', weight = %d, type = %d, path = '%s' WHERE path = '%s'", $parent, $edit['title'], $edit['description'], $edit['weight'], $edit['type'], isset($edit['path']) ? $edit['path'] : $edit['original_path'], $edit['original_path']); + } + else { + db_query("INSERT INTO {menu_edit} (parent, path, title, description, weight, type, admin) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $parent, isset($edit['path']) ? $edit['path'] : $edit['original_path'], $edit['title'], $edit['description'], $edit['weight'], $edit['type'], isset($edit['path'])); + } + watchdog('menu', t('Saved menu item %title.', $t_args), WATCHDOG_NOTICE, l(t('view'), 'admin/build/menu')); + drupal_set_message(t('The menu item %title has been saved.', $t_args)); + menu_rebuild(); +} + +/** + * Remove the menu item. + */ +function menu_node_form_delete($node) { + menu_delete_item(array('path' => 'node/'. $node->nid)); +} + +/** + * Menu callback; handle the adding/editing of a new menu. + */ +function menu_edit_menu_form($type, $mid = 0) { + if ($type == 'edit') { + if (!$item = menu_get_item_by_mid($mid)) { + drupal_not_found(); + return; + } + } + else { + $item = array('mid' => 0, 'pid' => 0, 'path' => '', 'weight' => 0, 'type' => MENU_CUSTOM_MENU); + } + $form['title'] = array('#type' => 'textfield', + '#title' => t('Title'), + '#default_value' => $item['title'], + '#description' => t('The name of the menu.'), + '#required' => TRUE, + ); + $form['mid'] = array('#type' => 'value', '#value' => $item['mid']); + $form['pid'] = array('#type' => 'value', '#value' => $item['pid']); + $form['path'] = array('#type' => 'value', '#value' => $item['path']); + $form['weight'] = array('#type' => 'value', '#value' => $item['weight']); + $form['type'] = array('#type' => 'value', '#value' => $item['type']); + $form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); + // Reuse the submit function of menu_edit_item_form. + $form['#submit']['menu_edit_item_form_submit'] = array(); + $form['#validate']['menu_edit_item_form_validate'] = array(); + $form['#theme'] = 'menu_edit_item_form'; + + return $form; +} + +/** + * Menu callback; delete a single custom item. + */ +function menu_item_delete_form($mid) { + if (!($menu = db_fetch_object(db_query('SELECT type, path, title FROM {menu} WHERE mid = %d', $mid)))) { drupal_not_found(); return; } - $form['mid'] = array('#type' => 'value', '#value' => $mid); + $form['path'] = array('#type' => 'value', '#value' => $menu->path); $form['type'] = array('#type' => 'value', '#value' => $menu->type); $form['title'] = array('#type' => 'value', '#value' => $menu->title); @@ -444,7 +476,7 @@ function menu_item_delete_form($mid) { * Process menu delete form submissions. */ function menu_item_delete_form_submit($form_id, $form_values) { - menu_delete_item($form_values['mid']); + menu_delete_item($form_values['path']); $t_args = array('%title' => $form_values['title']); if ($form_values['type'] & MENU_IS_ROOT) { @@ -463,9 +495,9 @@ function menu_item_delete_form_submit($form_id, $form_values) { * Menu callback; reset a single modified item. */ function menu_reset_item($mid) { - if (isset($mid) && $title = db_result(db_query('SELECT title FROM {menu} WHERE mid = %d', $mid))) { - $form['mid'] = array('#type' => 'value', '#value' => $mid); - return confirm_form($form, t('Are you sure you want to reset the item %item to its default values?', array('%item' => $title)), 'admin/build/menu', t('Any customizations will be lost. This action cannot be undone.'), t('Reset')); + if (isset($mid) && $item = db_fetch_object(db_query('SELECT path, title FROM {menu} WHERE mid = %d', $mid))) { + $form['mid'] = array('#type' => 'value', '#value' => $item->path); + return confirm_form($form, t('Are you sure you want to reset the item %item to its default values?', array('%item' => $item->title)), 'admin/build/menu', t('Any customizations will be lost. This action cannot be undone.'), t('Reset')); } else { drupal_not_found(); @@ -483,120 +515,204 @@ function menu_reset_item_submit($form_id, $form_values) { } /** - * Menu callback; hide a menu item. - */ -function menu_disable_item($mid) { - $item = menu_get_item($mid); - $type = $item['type']; - $type &= ~MENU_VISIBLE_IN_TREE; - $type &= ~MENU_VISIBLE_IN_BREADCRUMB; - $type |= MENU_MODIFIED_BY_ADMIN; - db_query('UPDATE {menu} SET type = %d WHERE mid = %d', $type, $mid); - drupal_set_message(t('The menu item has been disabled.')); - drupal_goto('admin/build/menu'); -} - -/** - * Menu callback; present the main menu management page. + * Delete a menu item from the database. If $item['mid'] is specified, then + * this is used to find the existing item; otherwise, $item['path'] is used. + * + * @param $item + * The path to the menu item to be deleted. */ -function menu_overview() { +function menu_delete_item($path) { + db_query("DELETE FROM {menu_edit} WHERE path = '%s'", $path); menu_rebuild(); - - return menu_overview_tree(); } +// Conversion ends here. + /** - * Save changes to a menu item into the database. - * - * @return mid + * Implementation of hook_block(). */ -function menu_edit_item_save($edit) { - if (isset($edit['expanded'])) { - if ($edit['expanded']) { - $edit['type'] |= MENU_EXPANDED; - } - else { - $edit['type'] &= ~MENU_EXPANDED; +function _menu_block($op = 'list', $delta = 0) { + if ($op == 'list') { + $blocks = array(); + $root_menus = menu_get_root_menus(); + foreach ($root_menus as $mid => $title) { + // Default "Navigation" block is handled by user.module. + if ($mid != 1) { + $blocks[$mid]['info'] = $title; + } } + return $blocks; } + else if ($op == 'view') { + $item = menu_get_item($delta); + $data['subject'] = check_plain($item['title']); + $data['content'] = theme('menu_tree', $delta); + return $data; + } +} - $edit['type'] = $edit['type'] | MENU_MODIFIED_BY_ADMIN; +/** + * Implementation of hook_nodeapi(). + */ +function _menu_nodeapi(&$node, $op) { - $status = menu_save_item($edit); + if (user_access('administer menu')) { + switch ($op) { + case 'insert': + case 'update': + if ($node->menu['delete']) { + menu_node_form_delete($node); + menu_rebuild(); + } + elseif ($node->menu['title']) { + $node->menu['path'] = ($node->menu['path']) ? $node->menu['path'] : "node/$node->nid"; + menu_edit_item_save($node->menu); + menu_rebuild(); + } + break; - $t_args = array('%title' => $edit['title']); - if ($status == SAVED_UPDATED) { - drupal_set_message(t('The menu item %title has been updated.', $t_args)); - } - elseif ($status == SAVED_NEW) { - drupal_set_message(t('The menu item %title has been added.', $t_args)); - watchdog('menu', t('Added menu item %title.', $t_args), WATCHDOG_NOTICE, l(t('view'), 'admin/build/menu')); + case 'delete': + menu_node_form_delete($node); + menu_rebuild(); + break; + } } - return $edit['mid']; } /** - * Save a menu item to the database. - * - * @param $item - * The menu item to be saved. This is passed by reference, so that the newly - * generated $item['mid'] can be accessed after an insert takes place. - * - * @return $status - * The operation that was performed in saving. Either SAVED_NEW (if a new - * menu item was created), or SAVED_UPDATED (if an existing menu item was - * updated). + * Implementation of hook_form_alter(). + * Add menu item fields to the node form. */ -function menu_save_item(&$item) { - $existing_item = NULL; +function _menu_form_alter(&$form, $form_id) { + if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { + $item = array(); + if ($form['nid']['#value'] > 0) { + $item = db_fetch_array(db_query("SELECT * FROM {menu} WHERE path = 'node/%d'", $form['nid']['#value'])); + if (isset($form['#post']['menu']) && is_array($form['#post']['menu'])) { + $item = !is_array($item) ? $form['#post']['menu'] : (($form['#post']['op'] == t('Preview')) ? array_merge($item, $form['#post']['menu']) : array_merge($form['#post']['menu'], $item)); + } + } - // Check that the item already exists in the menu tree, if $item['mid'] is - // specified. - if (isset($item['mid'])) { - $existing_item = menu_get_item($item['mid']); - } + $form['menu'] = array('#type' => 'fieldset', + '#title' => t('Menu settings'), + '#access' => user_access('administer menu'), + '#collapsible' => TRUE, + '#collapsed' => empty($item['title']), + '#tree' => TRUE, + '#weight' => 30, + ); - if ($item['mid'] && !empty($existing_item)) { - db_query("UPDATE {menu} SET pid = %d, path = '%s', title = '%s', description = '%s', weight = %d, type = %d WHERE mid = %d", $item['pid'], $item['path'], $item['title'], $item['description'], $item['weight'], $item['type'], $item['mid']); - return SAVED_UPDATED; - } - else { - $item['mid'] = db_next_id('{menu}_mid'); - // Check explicitly for mid <= 2. If the database was improperly prefixed, - // this would cause a nasty infinite loop or duplicate mid errors. - // TODO: have automatic prefixing through an installer to prevent this. - while ($item['mid'] <= 2) { - $item['mid'] = db_next_id('{menu}_mid'); + $form['menu']['title'] = array('#type' => 'textfield', + '#title' => t('Title'), + '#default_value' => $item['title'], + '#description' => t('The name to display for this menu link.'), + ); + + $form['menu']['description'] = array('#type' => 'textfield', + '#title' => t('Description'), + '#default_value' => $item['description'], + '#description' => t('The description displayed when hovering over a menu item.'), + ); + + // Generate a list of possible parents. + $options = menu_parent_options($item['mid'], variable_get('menu_parent_items', 0)); + + $form['menu']['pid'] = array('#type' => 'select', + '#title' => t('Parent item'), + '#default_value' => $item['pid'], + '#options' => $options, + ); + + $form['menu']['path'] = array('#type' => 'hidden', + '#value' => $item['path'], + ); + + $form['menu']['weight'] = array('#type' => 'weight', + '#title' => t('Weight'), + '#default_value' => $item['weight'], + '#delta' => 10, + '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'), + ); + + $form['menu']['mid'] = array('#type' => 'hidden', + '#value' => $item['mid'] ? $item['mid'] : 0, + ); + + $form['menu']['type'] = array('#type' => 'hidden', + '#value' => $item['type'] ? $item['type'] : MENU_CUSTOM_ITEM, + ); + + if ($item['mid'] > 0) { + $form['menu']['delete'] = array('#type' => 'checkbox', + '#title' => t('Check to delete this menu item.'), + '#default_value' => $item['delete'], + ); + + $form['menu']['advanced'] = array('#type' => 'item', + '#value' => t('You may also edit the advanced settings for this menu item.', array('@edit' => url("admin/build/menu/item/edit/{$item['mid']}"))), + ); } - db_query("INSERT INTO {menu} (mid, pid, path, title, description, weight, type) VALUES (%d, %d, '%s', '%s', '%s', %d, %d)", $item['mid'], $item['pid'], $item['path'], $item['title'], $item['description'], $item['weight'], $item['type']); - return SAVED_NEW; } } /** - * Delete a menu item from the database. If $item['mid'] is specified, then - * this is used to find the existing item; otherwise, $item['path'] is used. - * - * @param $item - * The menu item to be deleted. + * Menu callback; presents menu configuration options. */ -function menu_delete_item($item) { - if (!is_array($item)) { - $item = array('mid' => $item); - } +function menu_configure() { + $root_menus = menu_get_root_menus(); - if ($item['mid']) { - db_query('DELETE FROM {menu} WHERE mid = %d', $item['mid']); - } - elseif ($item['path']) { - db_query("DELETE FROM {menu} WHERE path = '%s'", $item['path']); - } + $primary_options = $root_menus; + $primary_options[0] = t('No primary links'); + + $form['settings_links'] = array('#type' => 'fieldset', + '#title' => t('Primary and secondary links settings'), + ); + + $form['settings_links']['intro'] = array('#type' => 'item', + '#value' => t('Primary and secondary links provide a navigational menu system which usually (depending on your theme) appears at the top-right of the browser window. The links displayed can be generated either from a custom list created via the menu administration page or from a built-in list of menu items such as the navigation menu links.', array('@menu' => url('admin/build/menu'))), + ); + + $form['settings_links']['menu_primary_menu'] = array('#type' => 'select', + '#title' => t('Menu containing primary links'), + '#default_value' => variable_get('menu_primary_menu', 0), + '#options' => $primary_options, + ); + + $secondary_options = $root_menus; + $secondary_options[0] = t('No secondary links'); + + $form['settings_links']['menu_secondary_menu'] = array('#type' => 'select', + '#title' => t('Menu containing secondary links'), + '#default_value' => variable_get('menu_secondary_menu', 0), + '#options' => $secondary_options, + '#description' => t('If you select the same menu as primary links then secondary links will display the appropriate second level of your navigation hierarchy.'), + ); + + $form['settings_authoring'] = array('#type' => 'fieldset', + '#title' => t('Content authoring form settings'), + ); + + $form['settings_authoring']['intro'] = array('#type' => 'item', + '#value' => t('The menu module allows on-the-fly creation of menu links in the content authoring forms. The following option limits the menus in which a new link may be added. E.g., this can be used to force new menu items to be created in the primary links menu or to hide admin menu items.'), + ); + + $authoring_options = $root_menus; + $authoring_options[0] = t('Show all menus'); + + $form['settings_authoring']['menu_parent_items'] = array('#type' => 'select', + '#title' => t('Restrict parent items to'), + '#default_value' => variable_get('menu_parent_items', 0), + '#options' => $authoring_options, + '#description' => t('Choose the menu to be made available in the content authoring form. Only this menu item and its children will be shown.'), + ); + + return system_settings_form($form); } /** * Present the menu tree, rendered along with links to edit menu items. */ -function menu_overview_tree() { +function _menu_overview_tree() { $menu = menu_get_menu(); $root_menus = menu_get_root_menus(); $header = array(t('Menu item'), t('Expanded'), array('data' => t('Operations'), 'colspan' => '3')); @@ -621,152 +737,3 @@ function menu_overview_tree() { } return $output; } - -function menu_overview_tree_rows($pid = 0, $depth = 0) { - $parent_item = menu_get_item($pid); - $rows = array(); - - if (isset($parent_item) && isset($parent_item['children'])) { - usort($parent_item['children'], '_menu_sort'); - foreach ($parent_item['children'] as $mid) { - $item = menu_get_item($mid); - // Populate the title field. - $title = ''; - if ($pid == 0) { - // Top-level items are menu names, and don't have an associated path. - $title .= check_plain($item['title']); - } - else { - $title .= l($item['title'], $item['path']); - } - if ($depth > 0) { - $title = '- '. $title; - } - for ($i = 1; $i < $depth; $i++) { - $title = '  '. $title; - } - - // Populate the operations field. - $operations = array(); - if (!($item['type'] & MENU_MODIFIABLE_BY_ADMIN)) { - $operations[] = array('data' => t('locked'), 'colspan' => '3', 'align' => 'center'); - } - else { - // Set the edit column. - if ($item['type'] & (MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IF_HAS_CHILDREN)) { - $operations[] = array('data' => l(t('edit'), 'admin/build/menu/item/edit/'. $mid)); - } - else { - $operations[] = array('data' => ''); - } - - // Set the disable column. - if ($item['type'] & (MENU_IS_ROOT | MENU_VISIBLE_IF_HAS_CHILDREN)) { - // Disabling entire menus is done from block admin page. - // MENU_VISIBLE_IF_HAS_CHILDREN menus are always enabled so hide this operation. - $operations[] = array('data' => ''); - } - else if ($item['type'] & MENU_VISIBLE_IN_TREE) { - $operations[] = array('data' => l(t('disable'), 'admin/build/menu/item/disable/'. $mid)); - } - else { - $operations[] = array('data' => l(t('enable'), 'admin/build/menu/item/edit/'. $mid)); - } - - // Set the reset column. - if ($item['type'] & MENU_CREATED_BY_ADMIN) { - $operations[] = array('data' => l(t('delete'), 'admin/build/menu/item/delete/'. $mid)); - } - else if ($item['type'] & MENU_MODIFIED_BY_ADMIN) { - $operations[] = array('data' => l(t('reset'), 'admin/build/menu/item/reset/'. $mid)); - } - else { - $operations[] = array('data' => ''); - } - } - - // Call out disabled items. - if ($item['type'] & (MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IF_HAS_CHILDREN)) { - $class = 'menu-enabled'; - } - else { - $title .= ' ('. t('disabled') .')'; - $class = 'menu-disabled'; - } - - if ($item['type'] & (MENU_MODIFIABLE_BY_ADMIN | MENU_VISIBLE_IN_TREE)) { - $row = array(array('data' => $title, 'class' => $class), array('data' => ($item['children'] ? (($item['type'] & MENU_EXPANDED) ? t('Yes') : t('No')) : ''), 'class' => $class)); - foreach ($operations as $operation) { - $operation['class'] = $class; - $row[] = $operation; - } - $rows[] = $row; - $rows = array_merge($rows, menu_overview_tree_rows($mid, $depth + 1)); - } - else { - // Skip items that are hidden and locked; admins will never care about them. - $rows = array_merge($rows, menu_overview_tree_rows($mid, $depth)); - } - } - } - - return $rows; -} - -/** - * Return a list of menu items that are valid possible parents for the - * given menu item. The list excludes the given item and its children. - * - * @param $mid - * The menu item id for which to generate a list of parents. - * If $mid == 0 then the complete tree is returned. - * @param $pid - * The menu item id of the menu item at which to start the tree. - * If $pid > 0 then this item will be included in the tree. - * @param $depth - * The current depth in the tree - used when recursing to indent the tree. - * @return - * An array of menu titles keyed on the mid. - */ -function menu_parent_options($mid, $pid = 0, $depth = 0) { - $options = array(); - - if (!($parent_item = menu_get_item($pid))) { - return $options; - } - - // Exclude $mid and its children from the list unless $mid is 0. - if ($mid && $mid == $pid) { - return $options; - } - - // Add the current $pid to the list. - if ($pid > 0 && ($parent_item['type'] & (MENU_MODIFIABLE_BY_ADMIN | MENU_IS_ROOT))) { - $title = ' '. $parent_item['title']; - for ($i = 0; $i < $depth; $i++) { - $title = '--'. $title; - } - if (!($parent_item['type'] & MENU_VISIBLE_IN_TREE)) { - $title .= ' ('. t('disabled') .')'; - } - $options[$pid] = $title; - $depth ++; - } - - // Add children of $pid to the list recursively. - if (isset($parent_item['children'])) { - usort($parent_item['children'], '_menu_sort'); - foreach ($parent_item['children'] as $child) { - $options += menu_parent_options($mid, $child, $depth); - } - } - - return $options; -} - -/** - * Remove the menu item. - */ -function menu_node_form_delete($node) { - menu_delete_item(array('path' => 'node/'. $node->nid)); -} -- cgit v1.2.3