moving the onelab www to a fresh location
[plewww.git] / modules / menu.module
1 <?php
2 // $Id: menu.module 144 2007-03-28 07:52:20Z thierry $
3
4 /**
5  * @file
6  * Allows administrators to customize the site navigation menu.
7  */
8
9 /**
10  * Implementation of hook_help().
11  */
12 function menu_help($section) {
13   switch ($section) {
14     case 'admin/help#menu':
15       $output = t('<p>Menus are a collection of links (menu items) used to navigate a website. The menu module provides an interface to control and customize the powerful menu system that comes with Drupal.  Menus are primarily displayed as a hierarchical list of links using Drupal\'s highly flexible <a href="%admin-block">blocks</a> feature. Each menu automatically creates a block of the same name. By default, new menu items are placed inside a built-in menu labelled %navigation, but administrators can also create custom menus.</p>
16 <p>Drupal themes generally provide out-of-the-box support for two menus commonly labelled %primary-links and %secondary-links. These are sets of links which are usually displayed in the header or footer of each page (depending on the currently active theme). Any menu can be designated as the primary or secondary links menu via the <a href="%menu-settings">menu settings page</a>.</p>
17 Menu administration tabs:
18 <ul>
19   <li>On the administer menu page, administrators can "edit" to change the title, description, parent or weight of a menu item. Under the "operations" column, click on "enable/disable" to toggle a menu item on or off. Only menu items which are enabled are displayed in the corresponding menu block. Note that the default menu items generated by the menu module cannot be deleted, only disabled.</li>
20   <li>Use the "add menu" tab to submit a title for a new custom menu. Once submitted, the menu will appear in a list toward the bottom of the administer menu page underneath the main navigation menu. Under the menu name there will be links to edit or delete the menu, and a link to add new items to the menu.</li>
21   <li>Use the "add menu item" tab to create new links in either the navigation or a custom menu (such as a primary/secondary links menu). Select the parent item to place the new link within an existing menu structure. For top level menu items, choose the name of the menu in which the link is to be added.</li>
22 </ul>', array('%navigation' => theme('placeholder', 'Navigation'), '%primary-links' => theme('placeholder', 'primary links'), '%secondary-links' => theme('placeholder', 'secondary links'), '%admin-block' => url('admin/block'), '%menu-settings' => url('admin/settings/menu')));
23       $output .= t('<p>You can</p>
24 <ul>
25   <li>administer menus at <a href="%admin-menu">administer &gt;&gt; menus</a>.</li>
26   <li>add a menu at <a href="%admin-menu-menu-add">administer &gt;&gt; menus &gt;&gt; add menu</a>.</li>
27   <li>add a menu item at <a href="%admin-menu-item-add">administer &gt;&gt; menus &gt;&gt; add menu item</a>.</li>
28   <li>modify menu settings (in particular, to specify a menu to use for primary or secondary links) at <a href="%admin-settings-menus">administer &gt;&gt; settings &gt;&gt; menus</a>.</li>
29   <li>manage menu blocks at <a href="%admin-block">administer &gt;&gt; blocks</a>.</li>
30 </ul>
31 ', array('%admin-menu' => url('admin/menu'), '%admin-block' => url('admin/block'), '%admin-menu-menu-add' => url('admin/menu/menu/add'), '%admin-menu-item-add' => url('admin/menu/item/add'), '%admin-settings-menus' => url('admin/settings/menu')));
32       $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%menu">Menu page</a>.', array('%menu' => 'http://drupal.org/handbook/modules/menu/')) .'</p>';
33       return $output;
34     case 'admin/modules#description':
35       return t('Allows administrators to customize the site navigation menu.');
36     case 'admin/menu':
37       return '<p>'. t('Menus are a collection of links (menu items) used to navigate a website. The list(s) below display the currently available menus along with their menu items. Select an operation from the list to manage each menu or menu item.', array('%admin-settings-menus' => url('admin/settings/menu'), '%admin-block'=>url('admin/block'))) .'</p>';
38     case 'admin/menu/menu/add':
39       return '<p>'. t('Enter the name for your new menu. Remember to enable the newly created block in the <a href="%blocks">blocks administration page</a>.', array('%blocks' => url('admin/block'))) .'</p>';
40     case 'admin/menu/item/add':
41       return '<p>'. t('Enter the title, path, position and the weight for your new menu item.') .'</p>';
42   }
43 }
44
45 /**
46  * Implementation of hook_menu().
47  */
48 function menu_menu($may_cache) {
49   $items = array();
50
51   if ($may_cache) {
52     $items[] = array('path' => 'admin/menu',
53       'title' => t('menus'),
54       'callback' => 'menu_overview',
55       'access' => user_access('administer menu'));
56     $items[] = array('path' => 'admin/menu/list',
57       'title' => t('list'),
58       'type' => MENU_DEFAULT_LOCAL_TASK,
59       'weight' => -10);
60
61     $items[] = array('path' => 'admin/menu/item/add',
62       'title' => t('add menu item'),
63       'callback' => 'menu_edit_item_form',
64       'access' => user_access('administer menu'),
65       'type' => MENU_LOCAL_TASK);
66     $items[] = array('path' => 'admin/menu/item/edit',
67       'title' => t('edit menu item'),
68       'callback' => 'menu_edit_item_form',
69       'access' => user_access('administer menu'),
70       'type' => MENU_CALLBACK);
71     $items[] = array('path' => 'admin/menu/item/reset',
72       'title' => t('reset menu item'),
73       'callback' => 'menu_reset_item',
74       'access' => user_access('administer menu'),
75       'type' => MENU_CALLBACK);
76     $items[] = array('path' => 'admin/menu/item/disable',
77       'title' => t('disable menu item'),
78       'callback' => 'menu_disable_item',
79       'access' => user_access('administer menu'),
80       'type' => MENU_CALLBACK);
81     $items[] = array('path' => 'admin/menu/item/delete',
82       'title' => t('delete menu item'),
83       'callback' => 'menu_item_delete_form',
84       'access' => user_access('administer menu'),
85       'type' => MENU_CALLBACK);
86
87     $items[] = array('path' => 'admin/menu/menu/add',
88       'title' => t('add menu'),
89       'callback' => 'menu_edit_menu_form',
90       'access' => user_access('administer menu'),
91       'type' => MENU_LOCAL_TASK);
92     $items[] = array('path' => 'admin/menu/menu/edit',
93       'title' => t('edit menu'),
94       'callback' => 'menu_edit_menu_form',
95       'access' => user_access('administer menu'),
96       'type' => MENU_CALLBACK);
97     $items[] = array('path' => 'admin/menu/menu/delete',
98       'title' => t('delete menu'),
99       'callback' => 'menu_item_delete_form',
100       'access' => user_access('administer menu'),
101       'type' => MENU_CALLBACK);
102
103     $items[] = array('path' => 'admin/settings/menu',
104       'title' => t('menus'),
105       'callback' => 'menu_configure');
106   }
107
108   return $items;
109 }
110
111 /**
112  * Implementation of hook_block().
113  */
114 function menu_block($op = 'list', $delta = 0) {
115   if ($op == 'list') {
116     $blocks = array();
117     $root_menus = menu_get_root_menus();
118     foreach ($root_menus as $mid => $title) {
119       // Default "Navigation" block is handled by user.module.
120       if ($mid != 1) {
121         $blocks[$mid]['info'] = check_plain($title);
122       }
123     }
124     return $blocks;
125   }
126   else if ($op == 'view') {
127     $item = menu_get_item($delta);
128     $data['subject'] = check_plain($item['title']);
129     $data['content'] = theme('menu_tree', $delta);
130     return $data;
131   }
132 }
133
134 /**
135  * Implementation of hook_nodeapi().
136  */
137 function menu_nodeapi(&$node, $op) {
138
139   if (user_access('administer menu')) {
140     switch ($op) {
141       case 'insert':
142       case 'update':
143         if ($node->menu['delete']) {
144           menu_node_form_delete($node);
145           menu_rebuild();
146         }
147         elseif ($node->menu['title']) {
148           $node->menu['path'] = ($node->menu['path']) ? $node->menu['path'] : "node/$node->nid";
149           menu_edit_item_save($node->menu);
150           menu_rebuild();
151         }
152         break;
153
154       case 'delete':
155         menu_node_form_delete($node);
156         menu_rebuild();
157         break;
158     }
159   }
160 }
161
162 /**
163  * Implementation of hook_perm().
164  */
165 function menu_perm() {
166   return array('administer menu');
167 }
168
169 /**
170  * Implementation of hook_form_alter().
171  * Add menu item fields to the node form.
172  */
173 function menu_form_alter($form_id, &$form) {
174   if (user_access('administer menu') && isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
175     $edit = isset($_POST['edit']) ? $_POST['edit'] : '';
176     $edit['nid'] = $form['nid']['#value'];
177
178     $item = array();
179     if ($edit['nid'] > 0) {
180       $item = db_fetch_array(db_query("SELECT * FROM {menu} WHERE path = 'node/%d'", $edit['nid']));
181       if (is_array($edit['menu'])) {
182         $item = !is_array($item) ? $edit['menu'] : (($_POST['op'] == t('Preview')) ? array_merge($item, $edit['menu']) : array_merge($edit['menu'], $item));
183       }
184     }
185
186     $form['menu'] = array('#type' => 'fieldset',
187       '#title' => t('Menu settings'),
188       '#collapsible' => TRUE,
189       '#collapsed' => empty($item['title']),
190       '#tree' => TRUE,
191       '#weight' => 30,
192     );
193
194     $form['menu']['title'] = array('#type' => 'textfield',
195       '#title' => t('Title'),
196       '#default_value' => $item['title'],
197       '#description' => t('The name to display for this link.'),
198     );
199
200     $form['menu']['description'] = array('#type' => 'textfield',
201       '#title' => t('Description'),
202       '#default_value' => $item['description'],
203       '#description' => t('The description displayed when hovering over a menu item.'),
204     );
205
206     // Generate a list of possible parents.
207     $options = menu_parent_options($item['mid'], variable_get('menu_parent_items', 0));
208
209     $form['menu']['pid'] = array('#type' => 'select',
210       '#title' => t('Parent item'),
211       '#default_value' => $item['pid'],
212       '#options' => $options,
213     );
214
215     $form['menu']['path'] = array('#type' => 'hidden',
216       '#value' => $item['path'],
217     );
218
219     $form['menu']['weight'] = array('#type' => 'weight',
220       '#title' => t('Weight'),
221       '#default_value' => $item['weight'],
222       '#delta' => 10,
223       '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
224     );
225
226     $form['menu']['mid'] = array('#type' => 'hidden',
227       '#value' => $item['mid'] ? $item['mid'] : 0,
228     );
229
230     $form['menu']['type'] = array('#type' => 'hidden',
231       '#value' => $item['type'] ? $item['type'] : MENU_CUSTOM_ITEM,
232     );
233
234     if ($item['mid'] > 0) {
235       $form['menu']['delete'] = array('#type' => 'checkbox',
236         '#title' => t('Check to delete this menu item.'),
237         '#default_value' => $item['delete'],
238       );
239
240       $form['menu']['advanced'] = array('#type' => 'item',
241         '#value' => t('You may also <a href="%edit">edit the advanced settings</a> for this menu item.', array('%edit' => url("admin/menu/item/edit/{$item['mid']}"))),
242       );
243     }
244   }
245 }
246
247 /**
248  * Menu callback; presents menu configuration options.
249  */
250 function menu_configure() {
251   $menu = menu_get_menu();
252   $root_menus = menu_get_root_menus();
253
254   $primary_options = $root_menus;
255   $primary_options[0] = t('No primary links');
256
257   $form['settings_links'] = array('#type' => 'fieldset',
258     '#title' => t('Primary and secondary links settings'),
259   );
260
261   $form['settings_links']['intro'] = array('#type' => 'item',
262     '#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 <a href="%menu">menu administration</a> page or from a built-in list of menu items such as the navigation menu links.', array('%menu' => url('admin/menu'))),
263   );
264
265   $form['settings_links']['menu_primary_menu'] = array('#type' => 'select',
266     '#title' => t('Menu containing primary links'),
267     '#default_value' => variable_get('menu_primary_menu', 0),
268     '#options' => $primary_options,
269   );
270
271   $secondary_options = $root_menus;
272   $secondary_options[0] = t('No secondary links');
273
274   $form['settings_links']['menu_secondary_menu'] = array('#type' => 'select',
275     '#title' => t('Menu containing secondary links'),
276     '#default_value' => variable_get('menu_secondary_menu', 0),
277     '#options' => $secondary_options,
278     '#description' => t('If you select the same menu as primary links then secondary links will display the appropriate second level of your navigation hierarchy.'),
279   );
280
281   $form['settings_authoring'] = array('#type' => 'fieldset',
282     '#title' => t('Post authoring form settings'),
283   );
284
285   $form['settings_authoring']['intro'] = array('#type' => 'item',
286     '#value' => t('The menu module allows on-the-fly creation of menu links in the post authoring forms. The following option limits the menus in which a new link may be added. For 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.'),
287   );
288
289   $authoring_options = $root_menus;
290   $authoring_options[0] = t('Show all menus');
291
292   $form['settings_authoring']['menu_parent_items'] = array('#type' => 'select',
293     '#title' => t('Restrict parent items to'),
294     '#default_value' => variable_get('menu_parent_items', 0),
295     '#options' => $authoring_options,
296     '#description' => t('Choose the menu to be made available in the post authoring form. Only this menu item and its children will be shown.'),
297    );
298
299   return system_settings_form('menu_configure', $form);
300 }
301
302 /**
303  * Menu callback; handle the adding/editing of a new menu.
304  */
305 function menu_edit_menu_form($mid = 0) {
306   if (arg(3) == 'edit') {
307     if (!($item = db_fetch_array(db_query('SELECT * FROM {menu} WHERE mid = %d', $mid)))) {
308       drupal_not_found();
309       return;
310     }
311   }
312   else {
313     $item = array('mid' => 0, 'pid' => 0, 'path' => '', 'weight' => 0, 'type' => MENU_CUSTOM_MENU);
314   }
315   $form['title'] = array('#type' => 'textfield',
316     '#title' => t('Title'),
317     '#default_value' => $item['title'],
318     '#description' => t('The name of the menu.'),
319     '#required' => TRUE,
320   );
321   $form['mid'] = array('#type' => 'value', '#value' => $item['mid']);
322   $form['pid'] = array('#type' => 'value', '#value' => $item['pid']);
323   $form['path'] = array('#type' => 'value', '#value' => $item['path']);
324   $form['weight'] = array('#type' => 'value', '#value' => $item['weight']);
325   $form['type'] = array('#type' => 'value', '#value' => $item['type']);
326   $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
327
328   // Reuse the submit function of menu_edit_item_form.
329   return drupal_get_form('menu_edit_menu_form', $form, 'menu_edit_item_form');
330 }
331
332 /**
333  * Present the menu item editing form.
334  */
335 function menu_edit_item_form($mid = 0) {
336   if (arg(3) == 'edit') {
337     if (!($item = db_fetch_array(db_query('SELECT * FROM {menu} WHERE mid = %d', $mid)))) {
338       drupal_not_found();
339       return;
340     }
341   }
342   else {
343     // This is an add form.
344     // The mid argument (if set) will be the default pid to use.
345     // Otherwise, we default to the "Navigation" menu (pid #1).
346     $default_pid = $mid ? $mid : 1;
347     $item = array('mid' => 0, 'pid' => $default_pid, 'weight' => 0, 'type' => MENU_CUSTOM_ITEM);
348   }
349
350   $form['title'] = array('#type' => 'textfield',
351     '#title' => t('Title'),
352     '#default_value' => $item['title'],
353     '#description' => t('The name of the menu item.'),
354     '#required' => TRUE,
355   );
356   $form['description'] = array('#type' => 'textfield',
357     '#title' => t('Description'),
358     '#default_value' => $item['description'],
359     '#description' => t('The description displayed when hovering over a menu item.'),
360   );
361
362   if ($item['type'] & MENU_CREATED_BY_ADMIN) {
363     $form['path'] = array('#type' => 'textfield',
364       '#title' => t('Path'),
365       '#default_value' => $item['path'],
366       '#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' => theme('placeholder', '<front>'), '%add-node' => theme('placeholder', 'node/add'), '%drupal' => theme('placeholder', 'http://drupal.org'))),
367       '#required' => TRUE,
368     );
369   }
370   else {
371     $form['_path'] = array('#type' => 'item',
372       '#title' => t('Path'),
373       '#description' => l($item['path'], $item['path']),
374     );
375     $form['path'] = array('#type' => 'value', '#value' => $item['path']);
376   }
377
378   $expanded = $item['type'] & MENU_EXPANDED ? 1 : 0;
379   $form['expanded'] = array('#type' => 'checkbox',
380     '#title' => t('Expanded'),
381     '#default_value' => $expanded,
382     '#description' => t('If selected and this menu item has children, the menu will always appear expanded.'),
383   );
384
385   // Generate a list of possible parents (not including this item or descendants).
386   $options = menu_parent_options($item['mid']);
387   $form['pid'] = array('#type' => 'select',
388     '#title' => t('Parent item'),
389     '#default_value' => $item['pid'],
390     '#options' => $options,
391   );
392   $form['weight'] = array('#type' => 'weight',
393     '#title' => t('Weight'),
394     '#default_value' => $item['weight'],
395     '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
396   );
397
398   // Always enable menu items (but not menus) when editing them.
399   if (!($item['type'] & MENU_IS_ROOT)) {
400     $item['type'] |= MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB;
401   }
402
403   $form['type'] = array('#type' => 'value', '#value' => $item['type']);
404   $form['mid'] = array('#type' => 'value', '#value' => $item['mid']);
405   $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
406
407   return drupal_get_form('menu_edit_item_form', $form);
408 }
409
410 /**
411  * Process menu and menu item add/edit form submissions.
412  */
413 function menu_edit_item_form_submit($form_id, $form_values) {
414   menu_edit_item_save($form_values);
415   return 'admin/menu';
416 }
417
418 /**
419  * Menu callback; delete a single custom item.
420  */
421 function menu_item_delete_form($mid) {
422   if (!($menu = db_fetch_object(db_query('SELECT type, title FROM {menu} WHERE mid = %d', $mid)))) {
423     drupal_not_found();
424     return;
425   }
426
427   $form['mid'] = array('#type' => 'value', '#value' => $mid);
428   $form['type'] = array('#type' => 'value', '#value' => $menu->type);
429   $form['title'] = array('#type' => 'value', '#value' => $menu->title);
430
431   if ($menu->type & MENU_IS_ROOT) {
432     $message = t('Are you sure you want to delete the menu %item?', array('%item' => theme('placeholder', $menu->title)));
433   }
434   else {
435     $message = t('Are you sure you want to delete the custom menu item %item?', array('%item' => theme('placeholder', $menu->title)));
436   }
437
438   return confirm_form('menu_confirm_delete_form', $form, $message, 'admin/menu', t('This action cannot be undone.'), t('Delete'));
439 }
440
441 /**
442  * Process menu delete form submissions.
443  */
444 function menu_confirm_delete_form_submit($form_id, $form_values) {
445   menu_delete_item($form_values['mid']);
446
447   $t_args = array('%title' => theme('placeholder', $form_values['title']));
448   if ($form_values['type'] & MENU_IS_ROOT) {
449     drupal_set_message(t('The menu %title has been deleted.', $t_args));
450     watchdog('menu', t('Deleted menu %title.', $t_args), WATCHDOG_NOTICE);
451   }
452   else {
453     drupal_set_message(t('The menu item %title has been deleted.', $t_args));
454     watchdog('menu', t('Deleted menu item %title.', $t_args), WATCHDOG_NOTICE);
455   }
456
457   return 'admin/menu';
458 }
459
460 /**
461  * Menu callback; reset a single modified item.
462  */
463 function menu_reset_item($mid) {
464   if (isset($mid) && $title = db_result(db_query('SELECT title FROM {menu} WHERE mid = %d', $mid))) {
465     $form['mid'] = array('#type' => 'value', '#value' => $mid);
466     return confirm_form('menu_reset_item_form', $form, t('Are you sure you want to reset the item %item to its default values?', array('%item' => theme('placeholder', $title))), 'admin/menu', t('Any customizations will be lost. This action cannot be undone.'), t('Reset'));
467   }
468   else {
469     drupal_not_found();
470   }
471 }
472
473 /**
474  * Process menu reset item form submissions.
475  */
476 function menu_reset_item_form_submit($form_id, $form_values) {
477   menu_delete_item($form_values['mid']);
478   drupal_set_message(t('The menu item was reset to its default settings.'));
479
480   return 'admin/menu';
481 }
482
483 /**
484  * Menu callback; hide a menu item.
485  */
486 function menu_disable_item($mid) {
487   $item = menu_get_item($mid);
488   $type = $item['type'];
489   $type &= ~MENU_VISIBLE_IN_TREE;
490   $type &= ~MENU_VISIBLE_IN_BREADCRUMB;
491   $type |= MENU_MODIFIED_BY_ADMIN;
492   db_query('UPDATE {menu} SET type = %d WHERE mid = %d', $type, $mid);
493   drupal_set_message(t('The menu item has been disabled.'));
494   drupal_goto('admin/menu');
495 }
496
497 /**
498  * Menu callback; present the main menu management page.
499  */
500 function menu_overview() {
501   menu_rebuild();
502
503   return menu_overview_tree();
504 }
505
506 /**
507  * Save changes to a menu item into the database.
508  *
509  * @return mid
510  */
511 function menu_edit_item_save($edit) {
512   if ($edit['expanded']) {
513     $edit['type'] |= MENU_EXPANDED;
514   }
515   else {
516     $edit['type'] &= ~MENU_EXPANDED;
517   }
518
519   $edit['type'] = $edit['type'] | MENU_MODIFIED_BY_ADMIN;
520
521   $status = menu_save_item($edit);
522
523   $t_args = array('%title' => theme('placeholder', $edit['title']));
524   if ($status == SAVED_UPDATED) {
525     drupal_set_message(t('The menu item %title has been updated.', $t_args));
526   }
527   elseif ($status == SAVED_NEW) {
528     drupal_set_message(t('The menu item %title has been added.', $t_args));
529     watchdog('menu', t('Added menu item %title.', $t_args), WATCHDOG_NOTICE, l(t('view'), 'admin/menu'));
530   }
531   return $edit['mid'];
532 }
533
534 /**
535  * Save a menu item to the database.
536  *
537  * @param $item
538  *   The menu item to be saved. This is passed by reference, so that the newly
539  *   generated $item['mid'] can be accessed after an insert takes place.
540  *
541  * @return $status
542  *   The operation that was performed in saving. Either SAVED_NEW (if a new
543  *   menu item was created), or SAVED_UPDATED (if an existing menu item was
544  *   updated).
545  */
546 function menu_save_item(&$item) {
547   $existing_item = NULL;
548
549   // Check that the item already exists in the menu tree, if $item['mid'] is
550   // specified.
551   if (isset($item['mid'])) {
552     $existing_item = menu_get_item($item['mid']);
553   }
554
555   if ($item['mid'] && !empty($existing_item)) {
556     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']);
557     return SAVED_UPDATED;
558   }
559   else {
560     $item['mid'] = db_next_id('{menu}_mid');
561     // Check explicitly for mid <= 2. If the database was improperly prefixed,
562     // this would cause a nasty infinite loop or duplicate mid errors.
563     // TODO: have automatic prefixing through an installer to prevent this.
564     while ($item['mid'] <= 2) {
565       $item['mid'] = db_next_id('{menu}_mid');
566     }
567     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']);
568     return SAVED_NEW;
569   }
570 }
571
572 /**
573  * Delete a menu item from the database. If $item['mid'] is specified, then
574  * this is used to find the existing item; otherwise, $item['path'] is used.
575  *
576  * @param $item
577  *   The menu item to be deleted.
578  */
579 function menu_delete_item($item) {
580   if (!is_array($item)) {
581     $item = array('mid' => $item);
582   }
583
584   if ($item['mid']) {
585     db_query('DELETE FROM {menu} WHERE mid = %d', $item['mid']);
586   }
587   elseif ($item['path']) {
588     db_query("DELETE FROM {menu} WHERE path = '%s'", $item['path']);
589   }
590 }
591
592 /**
593  * Present the menu tree, rendered along with links to edit menu items.
594  */
595 function menu_overview_tree() {
596   $menu = menu_get_menu();
597   $root_menus = menu_get_root_menus();
598   $header = array(t('Menu item'), t('Expanded'), array('data' => t('Operations'), 'colspan' => '3'));
599   $output = '';
600
601   foreach ($root_menus as $mid => $title) {
602     $operations = array();
603     if ($menu['items'][$mid]['type'] & MENU_MODIFIABLE_BY_ADMIN) {
604       $operations[] = l(t('edit'), 'admin/menu/menu/edit/'. $mid);
605     }
606     if ($menu['items'][$mid]['type'] & MENU_CREATED_BY_ADMIN) {
607       $operations[] = l(t('delete'), 'admin/menu/menu/delete/'. $mid);
608     }
609     $operations[] = l(t('add item'), 'admin/menu/item/add/'. $mid);
610     $table = theme('item_list', $operations);
611     $table .= theme('table', $header, menu_overview_tree_rows($mid));
612     $output .= theme('box', check_plain($title), $table);
613   }
614   return $output;
615 }
616
617 function menu_overview_tree_rows($pid = 0, $depth = 0) {
618   $parent_item = menu_get_item($pid);
619   $rows = array();
620
621   if (isset($parent_item) && isset($parent_item['children'])) {
622     usort($parent_item['children'], '_menu_sort');
623     foreach ($parent_item['children'] as $mid) {
624       $item = menu_get_item($mid);
625       // Populate the title field.
626       $title = '';
627       if ($pid == 0) {
628         // Top-level items are menu names, and don't have an associated path.
629         $title .= check_plain($item['title']);
630       }
631       else {
632         $title .= l($item['title'], $item['path']);
633       }
634       if ($depth > 0) {
635         $title = '-&nbsp;'. $title;
636       }
637       for ($i = 1; $i < $depth; $i++) {
638         $title = '&nbsp;&nbsp;'. $title;
639       }
640
641       // Populate the operations field.
642       $operations = array();
643       if (!($item['type'] & MENU_MODIFIABLE_BY_ADMIN)) {
644         $operations[] = array('data' => t('locked'), 'colspan' => '3', 'align' => 'center');
645       }
646       else {
647         // Set the edit column.
648         if ($item['type'] & (MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IF_HAS_CHILDREN)) {
649           $operations[] = array('data' => l(t('edit'), 'admin/menu/item/edit/'. $mid));
650         }
651         else {
652           $operations[] = array('data' => '');
653         }
654
655         // Set the disable column.
656         if ($item['type'] & (MENU_IS_ROOT | MENU_VISIBLE_IF_HAS_CHILDREN)) {
657           // Disabling entire menus is done from block admin page.
658           // MENU_VISIBLE_IF_HAS_CHILDREN menus are always enabled so hide this operation.
659           $operations[] = array('data' => '');
660         }
661         else if ($item['type'] & MENU_VISIBLE_IN_TREE) {
662           $operations[] = array('data' => l(t('disable'), 'admin/menu/item/disable/'. $mid));
663         }
664         else {
665           $operations[] = array('data' => l(t('enable'), 'admin/menu/item/edit/'. $mid));
666         }
667
668         // Set the reset column.
669         if ($item['type'] & MENU_CREATED_BY_ADMIN) {
670           $operations[] = array('data' => l(t('delete'), 'admin/menu/item/delete/'. $mid));
671         }
672         else if ($item['type'] & MENU_MODIFIED_BY_ADMIN) {
673           $operations[] = array('data' => l(t('reset'), 'admin/menu/item/reset/'. $mid));
674         }
675         else {
676           $operations[] = array('data' => '');
677         }
678       }
679
680       // Call out disabled items.
681       if ($item['type'] & (MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IF_HAS_CHILDREN)) {
682         $class = 'menu-enabled';
683       }
684       else {
685         $title .= ' ('. t('disabled') .')';
686         $class = 'menu-disabled';
687       }
688
689       if ($item['type'] & (MENU_MODIFIABLE_BY_ADMIN | MENU_VISIBLE_IN_TREE)) {
690         $row = array(array('data' => $title, 'class' => $class), array('data' => ($item['children'] ? (($item['type'] & MENU_EXPANDED) ? t('Yes') : t('No')) : ''), 'class' => $class));
691         foreach ($operations as $operation) {
692           $operation['class'] = $class;
693           $row[] = $operation;
694         }
695         $rows[] = $row;
696         $rows = array_merge($rows, menu_overview_tree_rows($mid, $depth + 1));
697       }
698       else {
699         // Skip items that are hidden and locked; admins will never care about them.
700         $rows = array_merge($rows, menu_overview_tree_rows($mid, $depth));
701       }
702     }
703   }
704
705   return $rows;
706 }
707
708 /**
709  * Return a list of menu items that are valid possible parents for the
710  * given menu item. The list excludes the given item and its children.
711  *
712  * @param $mid
713  *   The menu item id for which to generate a list of parents.
714  *   If $mid == 0 then the complete tree is returned.
715  * @param $pid
716  *   The menu item id of the menu item at which to start the tree.
717  *   If $pid > 0 then this item will be included in the tree.
718  * @param $depth
719  *   The current depth in the tree - used when recursing to indent the tree.
720  * @return
721  *   An array of menu titles keyed on the mid.
722  */
723 function menu_parent_options($mid, $pid = 0, $depth = 0) {
724   $options = array();
725
726   if (!($parent_item = menu_get_item($pid))) {
727     return $options;
728   }
729
730   // Exclude $mid and its children from the list unless $mid is 0.
731   if ($mid && $mid == $pid) {
732     return $options;
733   }
734
735   // Add the current $pid to the list.
736   if ($pid > 0 && ($parent_item['type'] & (MENU_MODIFIABLE_BY_ADMIN | MENU_IS_ROOT))) {
737     $title = ' '. $parent_item['title'];
738     for ($i = 0; $i < $depth; $i++) {
739       $title = '--'. $title;
740     }
741     if (!($parent_item['type'] & MENU_VISIBLE_IN_TREE)) {
742       $title .= ' ('. t('disabled') .')';
743     }
744     $options[$pid] = $title;
745     $depth ++;
746   }
747
748   // Add children of $pid to the list recursively.
749   if ($parent_item['children']) {
750     usort($parent_item['children'], '_menu_sort');
751     foreach ($parent_item['children'] as $child) {
752       $options += menu_parent_options($mid, $child, $depth);
753     }
754   }
755
756   return $options;
757 }
758
759 /**
760  * Remove the menu item.
761  */
762 function menu_node_form_delete($node) {
763   menu_delete_item(array('path' => 'node/'. $node->nid));
764 }