Index: modules/menu/menu.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.admin.inc,v
retrieving revision 1.56
diff -u -p -r1.56 menu.admin.inc
--- modules/menu/menu.admin.inc	24 Aug 2009 01:49:41 -0000	1.56
+++ modules/menu/menu.admin.inc	2 Sep 2009 03:30:11 -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 uid = 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) {
Index: modules/menu/menu.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.install,v
retrieving revision 1.19
diff -u -p -r1.19 menu.install
--- modules/menu/menu.install	20 Jul 2009 18:51:33 -0000	1.19
+++ modules/menu/menu.install	2 Sep 2009 03:30:11 -0000
@@ -50,6 +50,19 @@ function menu_schema() {
         'default' => '',
         'description' => 'Primary Key: Unique key for menu. This is used as a block delta so length is 32.',
       ),
+      'uid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The {users}.uid that this menu is specific to; if set to 0, then the menu is available to all users.',
+      ),
+      'parent_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.',
+      ),
       'title' => array(
         'type' => 'varchar',
         'length' => 255,
@@ -63,9 +76,35 @@ function menu_schema() {
         'description' => 'Menu description.',
       ),
     ),
+    'indexes' => array(
+      'uid' => array('uid'),
+    ),
+    'foreign keys' => array(
+      'uid' => array('users' => 'uid'),
+    ),
     'primary key' => array('menu_name'),
   );
 
   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', 'uid', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
+  db_add_field($ret, 'menu_custom', 'parent_menu', array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''));
+  db_add_index($ret, 'menu_custom', 'uid', array('uid'));
+  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.202
diff -u -p -r1.202 menu.module
--- modules/menu/menu.module	31 Aug 2009 17:06:09 -0000	1.202
+++ modules/menu/menu.module	2 Sep 2009 03:30:11 -0000
@@ -84,7 +84,8 @@ function menu_menu() {
     'page arguments' => array('menu_overview_form', 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') || (!empty($menu['uid']) && $menu['uid'] == $user->uid);
+}
+
+/**
+ * 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 !empty($menu['uid']) && $menu['uid'] == $user->uid;
+}
+
+/**
  * 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 uid = 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,76 @@ function menu_load($menu_name) {
 }
 
 /**
+ * Clone a menu for the provided user account, if one doesn't already exist.
+ */
+function menu_clone_menu($menu, $account) {
+  if (!empty($account->uid) && $account->uid != $menu['uid']) {
+    // Clone the menu.
+    $original_name = $menu['menu_name'];
+    $menu['menu_name'] = menu_get_unique_name($original_name);
+    $menu['parent_menu'] = $original_name;
+    $menu['uid'] = $account->uid;
+    drupal_write_record('menu_custom', $menu);
+    // Clone each 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()) {
+    // When the menu name doesn't validate, try adding a numerical index to
+    // make it unique.
+    $index = empty($index) ? 1 : $index + 1;
+    // Make sure to keep the menu name under the maximum length.
+    $candidate_name = substr($menu_name, 0, 32 - strlen((string)$index)) . $index;
+  }
+  return $candidate_name;
+}
+
+/**
+ * Returns the name of the user-customized version of a menu, or the original
+ * menu if it has not been customized.
+ */
+function menu_get_user_menu_name($menu_name, $account = NULL) {
+  global $user;
+  if (!isset($account)) {
+    $account = $user;
+  }
+  $customized_menu_name = menu_check_user_menu_name($menu_name, $account);
+  return $customized_menu_name === FALSE ? $menu_name : $customized_menu_name;
+}
+
+/**
+ * Returns the name of the user-customized version of a menu, or FALSE if there
+ * is none.
+ */
+function menu_check_user_menu_name($menu_name, $account = NULL) {
+  global $user;
+  if (!isset($account)) {
+    $account = $user;
+  }
+  // Only logged-in users can have a customized menu.
+  if (!empty($account->uid)) {
+    $customized_menu = db_query('SELECT menu_name FROM {menu_custom} WHERE uid = :uid AND parent_menu = :menu', array(':uid' => $account->uid, ':menu' => $menu_name))->fetchField();
+    if (!empty($customized_menu)) {
+      return $customized_menu;
+    }
+  }
+  return FALSE;
+}
+
+/**
  * Return a list of menu items that are valid possible parents for the given menu item.
  *
  * @param $menus
@@ -477,21 +578,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_user_specific_menus
+ *   If FALSE return only site-wide menus that are not associated with a
+ *   particular user, or if TRUE also include the user-specific 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_user_specific_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_user_specific_menus) {
+    $query->condition('uid', 0);
+  }
   $query->orderBy('title');
 
   return $query->execute()->fetchAllKeyed();
Index: modules/toolbar/toolbar.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.css,v
retrieving revision 1.2
diff -u -p -r1.2 toolbar.css
--- modules/toolbar/toolbar.css	29 Jul 2009 12:28:41 -0000	1.2
+++ modules/toolbar/toolbar.css	2 Sep 2009 03:30:11 -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.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.module,v
retrieving revision 1.9
diff -u -p -r1.9 toolbar.module
--- modules/toolbar/toolbar.module	31 Aug 2009 20:23:36 -0000	1.9
+++ modules/toolbar/toolbar.module	2 Sep 2009 03:30:11 -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('Add items to the personal shortcuts menu'),
+      'description' => t('Users with this permission are able to automatically add items to their personal shortcuts menu.'),
+    ),
   );
 }
 
@@ -39,6 +62,59 @@ 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(),
+    );
+    $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() {
+  global $user;
+  // Clone the menu, if we need to.
+  $menu = menu_load(toolbar_shortcuts_menu_name());
+  $original_menu = $menu['menu_name'];
+  $menu = menu_clone_menu($menu, $user);
+  // Add the requested menu link to the end of the list.
+  $weight = db_query('SELECT MAX(weight) FROM {menu_links} WHERE menu_name = :menu AND plid = 0', array(':menu' => $menu['menu_name']))->fetchField() + 1;
+  $link = array(
+    'mlid' => 0,
+    'plid' => 0,
+    'link_title' => $_GET['name'],
+    'link_path' => $_GET['link'],
+    'menu_name' => $menu['menu_name'],
+    'module' => 'menu',
+    'weight' => $weight,
+  );
+  menu_link_save($link);
+  drupal_set_message(t('Added a shortcut for %title.', array('%title' => $title)));
+  drupal_goto('admin/structure/menu-customize/' . $menu['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());
+  $menu = menu_clone_menu($menu, $user);
+  drupal_goto('admin/structure/menu-customize/' . $menu['menu_name']);
 }
 
 /**
@@ -50,6 +126,7 @@ function toolbar_preprocess_page(&$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']) : '';
 }
 
 /**
@@ -96,18 +173,33 @@ 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 stores toolbar shortcuts for the current
+ * user.
+ */
+function toolbar_shortcuts_menu_name() {
+  return menu_get_user_menu_name('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.3
diff -u -p -r1.3 page.tpl.php
--- themes/seven/page.tpl.php	22 Aug 2009 19:58:28 -0000	1.3
+++ themes/seven/page.tpl.php	2 Sep 2009 03:30:11 -0000
@@ -18,6 +18,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.16
diff -u -p -r1.16 style.css
--- themes/seven/style.css	31 Aug 2009 17:40:04 -0000	1.16
+++ themes/seven/style.css	2 Sep 2009 03:30:11 -0000
@@ -724,3 +724,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;
+}
