? .DS_Store ? edit-shortcuts.patch ? head.db ? install_drupal.php ? menu_shortcuts_45.patch ? menu_shortcuts_45.patch.1 ? toggle-advanced-settings.patch ? toolbar.png ? sites/default/files ? sites/default/private ? sites/default/settings.php 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 1 Sep 2009 00:23:29 -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 1 Sep 2009 00:23:29 -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 1 Sep 2009 00:23:29 -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 1 Sep 2009 00:23:29 -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 1 Sep 2009 00:23:29 -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' => '
', + '#markup' => l('' . t('Add to shortcuts') . '', 'admin/settings/shortcuts/add-link', array('query' => http_build_query($query), 'html' => TRUE)), + '#suffix' => '
', + ); + } +} + +/** + * 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,9 @@ function toolbar_preprocess_page(&$vars) if (user_access('access toolbar')) { $vars['classes_array'][] = 'toolbar toolbar-shortcuts'; } + if (user_access('create own shortcuts')) { + $vars['add_to_shortcuts'] = drupal_render($vars['page']['add_to_shortcuts']); + } } /** @@ -96,18 +175,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: modules/toolbar/toolbar.png =================================================================== RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.png,v retrieving revision 1.3 diff -u -p -r1.3 toolbar.png Binary files /tmp/cvsrtn8kT and toolbar.png differ 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 1 Sep 2009 00:23:29 -0000 @@ -18,6 +18,7 @@

+
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 1 Sep 2009 00:23:29 -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; +}