? .cvsignore
? edit-shortcuts-59.patch
? nbproject
? toggle-advanced-settings-9.patch
? files/.DS_Store
? modules/.cvsignore
? modules/menu/menu.admin.js
? sites/.cvsignore
? sites/d7sandbox
? sites/drupalhead
? sites/private
Index: modules/menu/menu.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.admin.inc,v
retrieving revision 1.57
diff -u -p -r1.57 menu.admin.inc
--- modules/menu/menu.admin.inc	5 Sep 2009 15:05:03 -0000	1.57
+++ modules/menu/menu.admin.inc	17 Sep 2009 23:36:52 -0000
@@ -10,7 +10,7 @@
  * 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));
+  $result = db_query("SELECT * FROM {menu_custom} WHERE personalized = 0 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) {
@@ -35,6 +35,28 @@ function theme_menu_admin_overview($titl
 }
 
 /**
+ * Menu callback; display the page for editing the menu tree for a given menu.
+ *
+ * @param $menu
+ *   An array representing the menu to be edited.
+ * @return
+ *   A page displaying the form for editing this menu.
+ */
+function menu_customize_menu($menu) {
+  // Cross-link menus with any personalized versions of them that belong to the
+  // current logged-in user.
+  if ($personalized_menu_name = menu_get_personalized_menu_name($menu['menu_name'])) {
+    drupal_set_message(t('You have a personalized version of this menu which you can <a href="@url">edit instead</a>.', array('@url' => url('admin/structure/menu-customize/' . $personalized_menu_name))), 'status', FALSE);
+  }
+  elseif (user_access('administer menu') && ($original_menu = menu_get_original_menu($menu))) {
+    drupal_set_message(t('You are editing a personalized version of the site-wide <a href="@url">%title</a> menu.', array('@url' => url('admin/structure/menu-customize/' . $original_menu['menu_name']), '%title' => $original_menu['title'])), 'status', FALSE);
+  }
+
+  // Display the form for editing the menu tree.
+  return drupal_get_form('menu_overview_form', $menu);
+}
+
+/**
  * Form for editing an entire menu tree at once.
  *
  * Shows for one menu the menu links accessible to the current user and
@@ -61,6 +83,8 @@ function menu_overview_form(&$form_state
   $menu_admin = FALSE;
 
   $form = _menu_overview_tree_form($tree);
+  $form['#attached_js'] = array(drupal_get_path('module', 'menu') . '/menu.admin.js' => array('weight' => JS_DEFAULT - 0.5));
+  
   $form['#menu'] =  $menu;
   if (element_children($form)) {
     $form['submit'] = array(
@@ -321,20 +345,32 @@ function menu_edit_item(&$form_state, $t
     '#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);
+  // Store the parent of this item, if it is in a personalized menu. Switching
+  // this item to a new menu will not be allowed via the UI.
   $default = $item['menu_name'] . ':' . $item['plid'];
-  if (!isset($options[$default])) {
-    $default = 'main-menu:0';
+  $parent_menu = menu_load($item['menu_name']);
+  if ($parent_menu['personalized']) {
+    $form['menu']['parent'] = array(
+      '#type' => 'value',
+      '#value' => $default,
+    );
+  }
+  // Otherwise, generate a list of possible parents (not including this item or
+  // descendants) so the user can choose a new one.
+  else {
+    $options = menu_parent_options(menu_get_menus(), $item);
+    if (!isset($options[$default])) {
+      $default = 'main-menu:0';
+    }
+    $form['menu']['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['menu']['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['menu']['weight'] = array(
     '#type' => 'weight',
     '#title' => t('Weight'),
Index: modules/menu/menu.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.install,v
retrieving revision 1.20
diff -u -p -r1.20 menu.install
--- modules/menu/menu.install	10 Sep 2009 06:38:19 -0000	1.20
+++ modules/menu/menu.install	17 Sep 2009 23:36:52 -0000
@@ -58,6 +58,31 @@ function menu_schema() {
         'not null' => FALSE,
         'description' => 'Menu description.',
       ),
+      'personalized' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny',
+        'description' => 'This field is set to 1 for menus that are personalized for a particular user, and 0 for site-wide menus shared by all users.',
+      ),
+      'uid' => array(
+        'type' => 'int',
+        'not null' => FALSE,
+        'description' => 'The {users}.uid that this menu is personalized for. This field is only relevant if the menu is personalized.',
+      ),
+      'original_menu' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The name of the menu that this menu was cloned from, if there is one.',
+      ),
+    ),
+    'indexes' => array(
+      'menu_custom_personalized' => array('personalized', 'uid', 'original_menu'),
+    ),
+    'foreign keys' => array(
+      'uid' => array('users' => 'uid'),
     ),
     'primary key' => array('menu_name'),
   );
@@ -65,3 +90,24 @@ function menu_schema() {
   return $schema;
 }
 
+/**
+ * @defgroup updates-6.x-to-7.x System updates from 6.x to 7.x
+ * @{
+ */
+
+/**
+ * Add columns to the {menu_custom} table to allow per-user menus.
+ */
+function menu_update_7000() {
+  $ret = array();
+  db_add_field($ret, 'menu_custom', 'personalized', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'tiny'));
+  db_add_field($ret, 'menu_custom', 'uid', array('type' => 'int', 'not null' => FALSE));
+  db_add_field($ret, 'menu_custom', 'original_menu', array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''));
+  db_add_index($ret, 'menu_custom', 'menu_custom_personalized', array('personalized', 'uid', 'original_menu'));
+  return $ret;
+}
+
+/**
+ * @} End of "defgroup updates-6.x-to-7.x"
+ * The next series of updates should start at 8000.
+ */
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.203
diff -u -p -r1.203 menu.module
--- modules/menu/menu.module	5 Sep 2009 15:05:03 -0000	1.203
+++ modules/menu/menu.module	17 Sep 2009 23:36:52 -0000
@@ -80,11 +80,12 @@ function menu_menu() {
   );
   $items['admin/structure/menu-customize/%menu'] = array(
     'title' => 'Customize menu',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('menu_overview_form', 3),
+    'page callback' => 'menu_customize_menu',
+    'page arguments' => array(3),
     'title callback' => 'menu_overview_title',
     'title arguments' => array(3),
-    'access arguments' => array('administer menu'),
+    'access callback' => '_menu_custom_menu_access',
+    'access arguments' => array(3),
     'type' => MENU_CALLBACK,
     'file' => 'menu.admin.inc',
   );
@@ -97,7 +98,8 @@ function menu_menu() {
     'title' => 'Add link',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('menu_edit_item', 'add', NULL, 3),
-    'access arguments' => array('administer menu'),
+    'access callback' => '_menu_custom_menu_access',
+    'access arguments' => array(3),
     'type' => MENU_LOCAL_ACTION,
     'file' => 'menu.admin.inc',
   );
@@ -105,7 +107,8 @@ function menu_menu() {
     'title' => 'Edit menu',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('menu_edit_menu', 'edit', 3),
-    'access arguments' => array('administer menu'),
+    'access callback' => '_menu_custom_menu_access',
+    'access arguments' => array(3),
     'type' => MENU_LOCAL_TASK,
     'file' => 'menu.admin.inc',
   );
@@ -113,7 +116,8 @@ function menu_menu() {
     'title' => 'Delete menu',
     'page callback' => 'menu_delete_menu_page',
     'page arguments' => array(3),
-    'access arguments' => array('administer menu'),
+    'access callback' => '_menu_custom_menu_access',
+    'access arguments' => array(3),
     'type' => MENU_CALLBACK,
     'file' => 'menu.admin.inc',
   );
@@ -121,7 +125,8 @@ function menu_menu() {
     'title' => 'Edit menu link',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('menu_edit_item', 'edit', 4, NULL),
-    'access arguments' => array('administer menu'),
+    'access callback' => '_menu_custom_item_access',
+    'access arguments' => array(4),
     'type' => MENU_CALLBACK,
     'file' => 'menu.admin.inc',
   );
@@ -129,7 +134,8 @@ function menu_menu() {
     'title' => 'Reset menu link',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('menu_reset_item_confirm', 4),
-    'access arguments' => array('administer menu'),
+    'access callback' => '_menu_custom_item_access',
+    'access arguments' => array(4),
     'type' => MENU_CALLBACK,
     'file' => 'menu.admin.inc',
   );
@@ -137,7 +143,8 @@ function menu_menu() {
     'title' => 'Delete menu link',
     'page callback' => 'menu_item_delete_page',
     'page arguments' => array(4),
-    'access arguments' => array('administer menu'),
+    'access callback' => '_menu_custom_item_access',
+    'access arguments' => array(4),
     'type' => MENU_CALLBACK,
     'file' => 'menu.admin.inc',
   );
@@ -145,6 +152,30 @@ function menu_menu() {
 }
 
 /**
+ * Access callback for custom menus.
+ */
+function _menu_custom_menu_access($menu) {
+  global $user;
+  // Only menu administrators and the owner of the menu can access it.
+  return user_access('administer menu') || menu_is_personalized_for_user($menu, $user);
+}
+
+/**
+ * Access callback for custom menu links.
+ */
+function _menu_custom_item_access($menu_link) {
+  global $user;
+  // Menu administrators can access all links.
+  if (user_access('administer menu')) {
+    return TRUE;
+  }
+  // Otherwise, only the owner of the menu that the link belongs to can access
+  // it.
+  $menu = menu_load($menu_link['menu_name']);
+  return menu_is_personalized_for_user($menu, $user);
+}
+
+/**
  * Implement hook_theme().
  */
 function menu_theme() {
@@ -170,7 +201,7 @@ function menu_enable() {
   $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-customize/%';
   $base_link['module'] = 'menu';
-  $result = db_query("SELECT * FROM {menu_custom}", array(), array('fetch' => PDO::FETCH_ASSOC));
+  $result = db_query("SELECT * FROM {menu_custom} WHERE personalized = 0", 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;
@@ -204,6 +235,186 @@ function menu_load($menu_name) {
 }
 
 /**
+ * Determines if a menu is personalized for a particular user.
+ *
+ * @param $menu
+ *   An array representing the menu to check.
+ * @param $account
+ *   The user account to check.
+ * @return
+ *   TRUE if the menu is personalized for this user, or FALSE if not.
+ */
+function menu_is_personalized_for_user($menu, $account) {
+  return $menu['personalized'] && $menu['uid'] == $account->uid;
+}
+
+/**
+ * Save a link to the personalized version of a menu for a particular user.
+ *
+ * @param $menu_link
+ *   An array of information about the menu link to save, containing at least
+ *   the keys 'link_title' and 'link_path'. Other keys will be filled in when
+ *   the menu link is saved.
+ * @param $menu_name
+ *   The name of an existing menu. If this menu is already a personalized menu
+ *   associated with the user, the link will be saved there; otherwise, the
+ *   menu will first be cloned to create a personalized version, and the link
+ *   will be saved to the new menu.
+ * @param $account
+ *   The user for whom the menu link will be saved. Defaults to the current
+ *   logged-in user.
+ * @return
+ *   The mlid of the saved menu link, or FALSE if the menu link could not be
+ *   saved.
+ */
+function menu_save_link_to_personalized_menu(&$menu_link, $menu_name, $account = NULL) {
+  global $user;
+  if (!isset($account)) {
+    $account = $user;
+  }
+
+  // If the provided menu doesn't exist, bail out right away.
+  $menu = menu_load($menu_name);
+  if (empty($menu)) {
+    return FALSE;
+  }
+
+  // Clone the menu if it is not already personalized for this user.
+  if (!menu_is_personalized_for_user($menu, $account)) {
+    $menu = menu_clone_menu($menu, $account);
+  }
+
+  // Require that a new menu item be created, and that it be placed in the
+  // correct menu.
+  $menu_link['mlid'] = 0;
+  $menu_link['menu_name'] = $menu['menu_name'];
+
+  // Add some default values to the requested menu link.
+  $menu_link += array(
+    'plid' => 0,
+    'module' => 'menu',
+  );
+
+  // Put the new link at the end of the list by default.
+  if (!isset($menu_link['weight'])) {
+    $menu_link['weight'] = (int)db_query('SELECT MAX(weight) FROM {menu_links} WHERE menu_name = :menu AND plid = :plid', array(':menu' => $menu['menu_name'], ':plid' => $menu_link['plid']))->fetchField() + 1;
+  }
+
+  // Save the link and return the result.
+  return menu_link_save($menu_link);
+}
+
+/**
+ * Clones a menu for the provided user account.
+ *
+ * @param $menu
+ *   An array representing the menu to clone.
+ * @param $account
+ *   The user for whom the menu will be cloned. Defaults to the current
+ *   logged-in user.
+ * @return
+ *   An array representing the newly-cloned menu.
+ */
+function menu_clone_menu($menu, $account = NULL) {
+  global $user;
+  if (!isset($account)) {
+    $account = $user;
+  }
+
+  // First clone the menu.
+  $original_name = $menu['menu_name'];
+  $menu['menu_name'] = menu_get_unique_name($original_name);
+  $menu['original_menu'] = $original_name;
+  $menu['uid'] = $account->uid;
+  $menu['personalized'] = 1;
+  drupal_write_record('menu_custom', $menu);
+
+  // Then clone each individual menu link.
+  $menu_links = db_query("SELECT * FROM {menu_links} WHERE menu_name = :menu", array(':menu' => $original_name))->fetchAll(PDO::FETCH_ASSOC);
+  foreach ($menu_links as $menu_link) {
+    unset($menu_link['mlid']);
+    $menu_link['menu_name'] = $menu['menu_name'];
+    $menu_link['options'] = unserialize($menu_link['options']);
+    menu_link_save($menu_link);
+  }
+
+  return $menu;
+}
+
+/**
+ * Returns a unique, machine-readable menu name, based on the provided name.
+ */
+function menu_get_unique_name($menu_name) {
+  $candidate_name = $menu_name;
+  while ($existing_name = db_query('SELECT menu_name FROM {menu_custom} WHERE menu_name = :menu', array(':menu' => $candidate_name))->fetchField()) {
+    // If the menu name already exists, try adding a continually incrementing
+    // numerical index to make it unique.
+    $index = empty($index) ? 1 : $index + 1;
+    // Make sure to keep the menu name under the maximum length.
+    if (!isset($maximum_length)) {
+      $schema = drupal_get_schema('menu_custom');
+      $maximum_length = $schema['fields']['menu_name']['length'];
+    }
+    $candidate_name = substr($menu_name, 0, $maximum_length - strlen((string)$index) - 1) . '-' . $index;
+  }
+  return $candidate_name;
+}
+
+/**
+ * Returns the name of the most appropriate version of a menu for a given user.
+ *
+ * @param $menu_name
+ *   The name of a menu to check.
+ * @param $account
+ *   The user account to check. Defaults to the current logged-in user.
+ * @return
+ *   If the menu has a version personalized for the provided user, the name of
+ *   that personalized menu will be returned. Otherwise, the original menu that
+ *   was passed in will be returned unchanged.
+ */
+function menu_get_menu_name_for_user($menu_name, $account = NULL) {
+  global $user;
+  if (!isset($account)) {
+    $account = $user;
+  }
+  $personalized_menu_name = menu_get_personalized_menu_name($menu_name, $account);
+  return !empty($personalized_menu_name) ? $personalized_menu_name : $menu_name;
+}
+
+/**
+ * Returns the name of the personalized version of a menu, if there is one.
+ *
+ * @param $menu_name
+ *   The name of the menu to check.
+ * @param $account
+ *   The user account to check. Defaults to the current logged-in user.
+ * @return
+ *   The name of the version of the menu that is personalized for this user, or
+ *   FALSE if there is none.
+ */
+function menu_get_personalized_menu_name($menu_name, $account = NULL) {
+  global $user;
+  if (!isset($account)) {
+    $account = $user;
+  }
+  $personalized_menu = db_query('SELECT menu_name FROM {menu_custom} WHERE personalized = 1 AND uid = :uid AND original_menu = :menu', array(':uid' => $account->uid, ':menu' => $menu_name))->fetchField();
+  return $personalized_menu;
+}
+
+/**
+ * Returns an array representing the original version of a menu, if one exists.
+ *
+ * @param $menu
+ *   An array representing the menu to check.
+ * @return
+ *   An array representing the original menu that this menu was cloned from, or
+ *   FALSE if there is none.
+ */
+function menu_get_original_menu($menu) {
+  return !empty($menu['original_menu']) ? menu_load($menu['original_menu']) : FALSE;
+}
+
+/**
  * Return a list of menu items that are valid possible parents for the given menu item.
  *
  * @param $menus
@@ -359,6 +570,7 @@ function menu_node_delete($node) {
  * Implement hook_node_prepare().
  */
 function menu_node_prepare($node) {
+  global $user;
   if (empty($node->menu)) {
     // Prepare the node for the edit form so that $node->menu always exists.
     $menu_name = variable_get('menu_default_node_menu', 'main-menu');
@@ -370,10 +582,13 @@ function menu_node_prepare($node) {
         ':menu_name' => $menu_name,
       ), 0, 1)
       ->fetchField();
-      // Check all menus if a link does not exist in the default menu.
+      // Check all menus if a link does not exist in the default menu. Give
+      // priority to site-wide menus over personalized ones, and never display
+      // another user's personalized menu.
       if (!$mlid) {
-        $mlid = db_query_range("SELECT mlid FROM {menu_links} WHERE link_path = :path AND module = 'menu' ORDER BY mlid ASC", array(
+        $mlid = db_query_range("SELECT ml.mlid FROM {menu_links} ml INNER JOIN {menu_custom} mc ON ml.menu_name = mc.menu_name WHERE ml.link_path = :path AND ml.module = 'menu' AND (mc.personalized = 0 OR mc.uid = :uid) ORDER BY mc.personalized, ml.mlid ASC", array(
           ':path' => 'node/' . $node->nid,
+          ':uid' => $user->uid,
         ), 0, 1)
         ->fetchField();
       }
@@ -443,20 +658,32 @@ function menu_form_alter(&$form, $form_s
       '#description' => t('The link text corresponding to this item that should appear in the menu. Leave blank if you do not wish to add this post to the menu.'),
       '#required' => FALSE,
     );
-    // Generate a list of possible parents (not including this item or descendants).
-    $options = menu_parent_options(menu_get_menus(), $item);
+    // Store the parent of this item, if it is in a personalized menu.
+    // Switching this item to a new menu will not be allowed via the UI.
     $default = $item['menu_name'] . ':' . $item['plid'];
-    if (!isset($options[$default])) {
-      $default = 'main-menu:0';
+    $menu = menu_load($item['menu_name']);
+    if ($menu['personalized']) {
+      $form['menu']['parent'] = array(
+        '#type' => 'value',
+        '#value' => $default,
+      );
+    }
+    // Otherwise, generate a list of possible parents (not including this item
+    // or descendants) so the user can choose a new one.
+    else {
+      $options = menu_parent_options(menu_get_menus(), $item);
+      if (!isset($options[$default])) {
+        $default = 'main-menu:0';
+      }
+      $form['menu']['parent'] = array(
+        '#type' => 'select',
+        '#title' => t('Parent item'),
+        '#default_value' => $default,
+        '#options' => $options,
+        '#description' => t('The maximum depth for an item and all its children is fixed at !maxdepth. Some menu items 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['menu']['parent'] = array(
-      '#type' => 'select',
-      '#title' => t('Parent item'),
-      '#default_value' => $default,
-      '#options' => $options,
-      '#description' => t('The maximum depth for an item and all its children is fixed at !maxdepth. Some menu items 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['#submit'][] = 'menu_node_form_submit';
 
     $form['menu']['weight'] = array(
@@ -479,21 +706,27 @@ function menu_node_form_submit($form, &$
 /**
  * Return an associative array of the custom menus names.
  *
- * @param $all
+ * @param $include_system_menus
  *   If FALSE return only user-added menus, or if TRUE also include
  *   the menus defined by the system.
+ * @param $include_personalized_menus
+ *   If FALSE return only site-wide menus that are not personalized for a
+ *   particular user, or if TRUE also include the personalized menus.
  * @return
  *   An array with the machine-readable names as the keys, and human-readable
  *   titles as the values.
  */
-function menu_get_menus($all = TRUE) {
+function menu_get_menus($include_system_menus = TRUE, $include_personalized_menus = FALSE) {
   $system_menus = array_keys(menu_list_system_menus());
   $query = db_select('menu_custom');
   $query->addField('menu_custom', 'menu_name', 'menu_name');
   $query->addField('menu_custom', 'title', 'title');
-  if (!$all) {
+  if (!$include_system_menus) {
     $query->condition('menu_name', $system_menus, 'NOT IN');
   }
+  if (!$include_personalized_menus) {
+    $query->condition('personalized', 0);
+  }
   $query->orderBy('title');
 
   return $query->execute()->fetchAllKeyed();
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.790
diff -u -p -r1.790 system.module
--- modules/system/system.module	15 Sep 2009 17:10:39 -0000	1.790
+++ modules/system/system.module	17 Sep 2009 23:36:52 -0000
@@ -587,20 +587,13 @@ function system_menu() {
     );
   }
 
-  // Configuration and modules.
+  // Modules and configuration.
   $items['admin/config'] = array(
-    'title' => 'Configuration and modules',
+    'title' => 'Modules and configuration',
     'page callback' => 'system_admin_config_page',
     'access arguments' => array('access administration pages'),
     'file' => 'system.admin.inc',
   );
-  $items['admin/config/config'] = array(
-    'title' => 'Configuration',
-    'access arguments' => array('administer site configuration'),
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => -10,
-    'file' => 'system.admin.inc',
-  );
   $items['admin/config/modules'] = array(
     'title' => 'Modules',
     'description' => 'Enable or disable add-on modules for your site.',
@@ -609,8 +602,16 @@ function system_menu() {
     'access arguments' => array('administer site configuration'),
     'file' => 'system.admin.inc',
     'type' => MENU_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/config/config'] = array(
+    'title' => 'Configuration',
+    'access arguments' => array('administer site configuration'),
+    'type' => MENU_DEFAULT_LOCAL_TASK,
     'weight' => 10,
+    'file' => 'system.admin.inc',
   );
+  
   $items['admin/config/modules/list'] = array(
     'title' => 'List',
     'type' => MENU_DEFAULT_LOCAL_TASK,
Index: modules/toolbar/toolbar.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.css,v
retrieving revision 1.4
diff -u -p -r1.4 toolbar.css
--- modules/toolbar/toolbar.css	15 Sep 2009 03:38:06 -0000	1.4
+++ modules/toolbar/toolbar.css	17 Sep 2009 23:36:53 -0000
@@ -161,6 +161,49 @@ div#toolbar div.toolbar-shortcuts span.i
   -webkit-border-radius: 5px;
 }
 
+div#toolbar a#toolbar-customize {
+  float: right;
+}
+
+div.toolbar-add-to-shortcuts a {
+  min-width: 12px;
+  height: 12px;
+  background: url(toolbar.png) no-repeat -50px -60px;
+  display: block;
+  text-decoration: none;
+}
+
+div.toolbar-add-to-shortcuts a span.icon {
+  display: block;
+  width: 12px;
+  background: url(toolbar.png) no-repeat -50px -60px;
+  height: 12px;
+  float: left;
+}
+
+div.toolbar-add-to-shortcuts a:hover span.icon {
+  background-position: -50px -72px;
+}
+
+div.toolbar-add-to-shortcuts a span.text {
+  float: left;
+  display: none;
+}
+
+div.toolbar-add-to-shortcuts a:hover span.text {
+  font-size: 10px;
+  line-height: 12px;
+  color: #fff;
+  background-color: #5f605b;
+  display: block;
+  padding-right: 6px;
+  cursor: pointer;
+  -moz-border-radius-topright: 5px;
+  -moz-border-radius-bottomright: 5px;
+  -webkit-border-radius-topright: 5px;
+  -webkit-border-radius-bottomright: 5px;
+}
+
 /**
  * IE 6 Fixes.
  *
Index: modules/toolbar/toolbar.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.install,v
retrieving revision 1.4
diff -u -p -r1.4 toolbar.install
--- modules/toolbar/toolbar.install	31 Aug 2009 17:09:01 -0000	1.4
+++ modules/toolbar/toolbar.install	17 Sep 2009 23:36:53 -0000
@@ -16,9 +16,9 @@ function toolbar_install() {
   $t = get_t();
   $query = db_insert('menu_custom')
     ->fields(array(
-      'menu_name' => 'admin_shortcuts',
+      'menu_name' => 'admin-shortcuts',
       'title' => $t('Administration shortcuts'),
-      'description' => $t('The <em>Admininstration shortcuts</em> menu contains commonly used links for administrative tasks.')
+      'description' => $t('The <em>Administration shortcuts</em> menu contains commonly used links for administrative tasks.')
     ))
     ->execute();
 
@@ -36,7 +36,7 @@ function toolbar_install() {
       'link_title' => $title,
       'link_path' => $path,
       'router_path' => $path,
-      'menu_name' => 'admin_shortcuts',
+      'menu_name' => 'admin-shortcuts',
       'module' => 'menu',
       'weight' => $weight,
     );
Index: modules/toolbar/toolbar.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.module,v
retrieving revision 1.13
diff -u -p -r1.13 toolbar.module
--- modules/toolbar/toolbar.module	15 Sep 2009 20:50:48 -0000	1.13
+++ modules/toolbar/toolbar.module	17 Sep 2009 23:36:53 -0000
@@ -7,6 +7,25 @@
  */
 
 /**
+ * Implement hook_menu().
+ */
+function toolbar_menu() {
+  $items = array();
+  $items['admin/settings/shortcuts/add-link'] = array(
+    'title' => 'Add link',
+    'page callback' => 'toolbar_shortcut_add_link',
+    'access arguments' => array('create own shortcuts'),
+    'type' => MENU_CALLBACK,
+  );
+  $items['admin/settings/shortcuts/edit'] = array(
+    'page callback' => 'toolbar_shortcut_edit_links',
+    'access arguments' => array('create own shortcuts'),
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
  * Implementation of hook_permission().
  */
 function toolbar_permission() {
@@ -15,6 +34,10 @@ function toolbar_permission() {
       'title' => t('Access administration toolbar'),
       'description' => t('Access the persistent administration toolbar displayed on all pages.'),
     ),
+    'create own shortcuts' => array(
+      'title' => t('Create own shortcuts'),
+      'description' => t('Users with this permission are able to automatically add items to their personal shortcuts menu.'),
+    ),
   );
 }
 
@@ -39,6 +62,53 @@ function toolbar_page_build(&$page) {
   if (user_access('access toolbar')) {
     $page['page_top']['toolbar'] = toolbar_build();
   }
+  if (user_access('create own shortcuts')) {
+    // $_GET['q'] is the unaliased version.
+    $get = $_GET;
+    unset($get['q']);
+    $link = $_GET['q'];
+    if (!empty($get)) {
+      $link .= '?' . http_build_query($get);
+    }
+    $query = array(
+      'link' => $link,
+      'name' => drupal_get_title(),
+      'token' => drupal_get_token('toolbar-add-to-shortcuts'),
+    );
+    $page['add_to_shortcuts'] = array(
+      '#prefix' => '<div class="toolbar-add-to-shortcuts">',
+      '#markup' => l('<span class="icon"></span><span class="text">' . t('Add to shortcuts') . '</span>', 'admin/settings/shortcuts/add-link', array('query' => http_build_query($query), 'html' => TRUE)),
+      '#suffix' => '</div>',
+    );
+  }
+}
+
+/**
+ * Add a link to the shortcuts menu.
+ */
+function toolbar_shortcut_add_link() {
+  if (isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'toolbar-add-to-shortcuts')) {
+    $link = array(
+      'link_title' => $_GET['name'],
+      'link_path' => $_GET['link'],
+    );
+    if (menu_save_link_to_personalized_menu($link, toolbar_shortcuts_menu_name())) {
+      drupal_set_message(t('Added a shortcut for %title.', array('%title' => $link['link_title'])));
+      drupal_goto('admin/structure/menu-customize/' . $link['menu_name']);
+    }
+  }
+}
+
+/**
+ * Edit the shortcuts menu for the current user, creating a new one if needed.
+ */
+function toolbar_shortcut_edit_links() {
+  global $user;
+  $menu = menu_load(toolbar_shortcuts_menu_name());
+  if (!menu_is_personalized_for_user($menu, $user)) {
+    $menu = menu_clone_menu($menu, $user);
+  }
+  drupal_goto('admin/structure/menu-customize/' . $menu['menu_name']);
 }
 
 /**
@@ -50,6 +120,7 @@ function toolbar_preprocess_html(&$vars)
   if (user_access('access toolbar')) {
     $vars['classes_array'][] = 'toolbar toolbar-shortcuts';
   }
+  $vars['add_to_shortcuts'] = !empty($vars['page']['add_to_shortcuts']) ? drupal_render($vars['page']['add_to_shortcuts']) : '';
 }
 
 /**
@@ -102,18 +173,32 @@ function toolbar_build() {
   );
 
   // Add convenience shortcut links.
-  $shortcuts = menu_tree_all_data('admin_shortcuts');
+  $shortcuts = menu_tree_all_data(toolbar_shortcuts_menu_name());
   $shortcuts = toolbar_menu_navigation_links($shortcuts);
   $build['toolbar_shortcuts'] = array(
-    '#theme' => 'links',
-    '#links' => $shortcuts,
-    '#attributes' => array('id' => 'toolbar-shortcuts'),
+    'shortcuts' => array(
+      '#theme' => 'links',
+      '#links' => $shortcuts,
+      '#attributes' => array('id' => 'toolbar-shortcuts'),
+    ),
   );
+  if (user_access('create own shortcuts')) {
+    $build['toolbar_shortcuts']['configure'] = array(
+      '#markup' => l(t('edit shortcuts'), 'admin/settings/shortcuts/edit', array('attributes' => array('id' => 'toolbar-customize'))),
+    );
+  }
 
   return $build;
 }
 
 /**
+ * Returns the name of the menu that has the current user's toolbar shortcuts.
+ */
+function toolbar_shortcuts_menu_name() {
+  return menu_get_menu_name_for_user('admin-shortcuts');
+}
+
+/**
  * Get only the top level items below the 'admin' path.
  */
 function toolbar_get_menu_tree() {
Index: themes/seven/page.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/seven/page.tpl.php,v
retrieving revision 1.4
diff -u -p -r1.4 page.tpl.php
--- themes/seven/page.tpl.php	15 Sep 2009 17:10:39 -0000	1.4
+++ themes/seven/page.tpl.php	17 Sep 2009 23:36:53 -0000
@@ -4,6 +4,7 @@
   <div id="branding" class="clearfix">
     <?php print $breadcrumb; ?>
     <?php if ($title): ?><h1 class="page-title"><?php print $title; ?></h1><?php endif; ?>
+    <?php if ($add_to_shortcuts): ?><?php print $add_to_shortcuts; ?><?php endif; ?>
     <?php if ($primary_local_tasks): ?><ul class="tabs primary"><?php print $primary_local_tasks; ?></ul><?php endif; ?>
   </div>
 
Index: themes/seven/style.css
===================================================================
RCS file: /cvs/drupal/drupal/themes/seven/style.css,v
retrieving revision 1.18
diff -u -p -r1.18 style.css
--- themes/seven/style.css	11 Sep 2009 13:48:44 -0000	1.18
+++ themes/seven/style.css	17 Sep 2009 23:36:53 -0000
@@ -735,3 +735,10 @@ body.overlay #page {
 body.overlay #block-system-main {
   padding: 20px;
 }
+
+/* Shortcut theming */
+div.toolbar-add-to-shortcuts {
+  float: left;
+  margin-left: 6px;
+  margin-top: 6px;
+}
