diff options
author | Dries Buytaert <dries@buytaert.net> | 2009-10-17 00:51:53 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2009-10-17 00:51:53 +0000 |
commit | a613c60fe1cb806d6db2493c5322a4fb08549724 (patch) | |
tree | b1785e2d8ae0a8a27ea055b37392e32f3c173c13 /modules/shortcut/shortcut.module | |
parent | a09822b363813158a6182057917080b22d3ca8f7 (diff) | |
download | brdo-a613c60fe1cb806d6db2493c5322a4fb08549724.tar.gz brdo-a613c60fe1cb806d6db2493c5322a4fb08549724.tar.bz2 |
- Patch #511286 by JacobSingh, David_Rothstein, dmitrig01, Gábor Hojtsy, ksenzee et al: add customizable shortcuts to the toolbar.
Diffstat (limited to 'modules/shortcut/shortcut.module')
-rw-r--r-- | modules/shortcut/shortcut.module | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/modules/shortcut/shortcut.module b/modules/shortcut/shortcut.module new file mode 100644 index 000000000..e69cae7be --- /dev/null +++ b/modules/shortcut/shortcut.module @@ -0,0 +1,557 @@ +<?php +// $Id$ + +/** + * @file + * Allows users to manage customizable lists of shortcut links. + */ + +/** + * The name of the default shortcut set. + * + * This set will be displayed to any user that does not have another set + * assigned. + */ +define('SHORTCUT_DEFAULT_SET_NAME', 'shortcut-set-1'); + +/** + * Implement hook_permission(). + */ +function shortcut_permission() { + return array( + 'administer shortcuts' => array( + 'title' => t('Administer shortcuts'), + 'description' => t('Manage all shortcut and shortcut sets.'), + ), + 'customize shortcut links' => array( + 'title' => t('Customize shortcut links'), + 'description' => t("Edit, add and delete the links in shortcut set the user is using."), + ), + 'switch shortcut sets' => array( + 'title' => t('Choose a different shortcut set'), + 'description' => t('Choose which set of shortcuts are displayed for the user.') + ), + ); +} + +/** + * Implement hook_menu(). + */ +function shortcut_menu() { + $items['admin/config/system/shortcut'] = array( + 'title' => 'Shortcuts', + 'description' => 'List the available shortcut sets and switch between them.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_set_switch'), + 'access arguments' => array('administer shortcuts'), + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/system/shortcut/%shortcut_set'] = array( + 'title' => 'Customize shortcuts', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_set_customize', 4), + 'access callback' => 'shortcut_set_edit_access', + 'access arguments' => array(4), + 'type' => MENU_CALLBACK, + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/system/shortcut/%shortcut_set/add-link'] = array( + 'title' => 'Add shortcut', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_link_add', 4), + 'access callback' => 'shortcut_set_edit_access', + 'access arguments' => array(4), + 'type' => MENU_LOCAL_ACTION, + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/system/shortcut/%shortcut_set/add-link-inline'] = array( + 'title' => 'Add shortcut', + 'page callback' => 'shortcut_link_add_inline', + 'page arguments' => array(4), + 'access callback' => 'shortcut_set_edit_access', + 'access arguments' => array(4), + 'type' => MENU_CALLBACK, + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/system/shortcut/link/%menu_link'] = array( + 'title' => 'Edit shortcut', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_link_edit', 5), + 'access callback' => 'shortcut_link_access', + 'access arguments' => array(5), + 'type' => MENU_CALLBACK, + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/system/shortcut/link/%menu_link/delete'] = array( + 'title' => 'Delete shortcut', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_link_delete', 5), + 'access callback' => 'shortcut_link_access', + 'access arguments' => array(5), + 'type' => MENU_CALLBACK, + 'file' => 'shortcut.admin.inc', + ); + $items['user/%user/shortcuts'] = array( + 'title' => 'Shortcuts', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_set_switch', 1), + 'access callback' => 'shortcut_set_switch_access', + 'access arguments' => array(1), + 'type' => MENU_LOCAL_TASK, + 'file' => 'shortcut.admin.inc', + ); + return $items; +} + +/** + * Implement hook_theme(). + */ +function shortcut_theme() { + return array( + 'shortcut_set_switch' => array( + 'arguments' => array('form' => NULL), + 'file' => 'shortcut.admin.inc', + ), + 'shortcut_set_customize' => array( + 'arguments' => array('form' => NULL), + 'file' => 'shortcut.admin.inc', + ), + ); +} + +/** + * Implement hook_block_info(). + */ +function shortcut_block_info() { + $blocks['shortcuts']['info'] = t('Shortcuts'); + // Shortcut blocks can't be cached because each menu item can have a custom + // access callback. menu.inc manages its own caching. + $blocks['shortcuts']['cache'] = DRUPAL_NO_CACHE; + return $blocks; +} + +/** + * Implement hook_block_view(). + */ +function shortcut_block_view($delta = '') { + if ($delta == 'shortcuts') { + $shortcut_set = shortcut_current_displayed_set(); + $data['subject'] = t('@shortcut_set shortcuts', array('@shortcut_set' => $shortcut_set->title)); + $data['content'] = shortcut_renderable_links($shortcut_set); + return $data; + } +} + +/** + * Access callback for editing a shortcut set. + * + * @param $shortcut_set + * (optional) The shortcut set to be edited. If not set, the current + * displayed shortcut set will be assumed. + * @return + * TRUE if the current user has access to edit the shortcut set, FALSE + * otherwise. + */ +function shortcut_set_edit_access($shortcut_set = NULL) { + // Sufficiently-privileged users can edit their currently displayed shortcut + // set, but not other sets. Shortcut administrators can edit any set. + if (user_access('administer shortcuts')) { + return TRUE; + } + if (user_access('customize shortcut links')) { + return !isset($shortcut_set) || $shortcut_set == shortcut_current_displayed_set(); + } + return FALSE; +} + +/** + * Access callback for switching the shortcut set assigned to a user account. + * + * @param $account + * (optional) The user account whose shortcuts will be switched. If not set, + * the account of the current logged-in user will be assumed. + * @return + * TRUE if the current user has access to switch the shortcut set of the + * provided account, FALSE otherwise. + */ +function shortcut_set_switch_access($account = NULL) { + global $user; + // Sufficiently-privileged users can switch their own shortcut sets, but not + // those of other users. Shortcut administrators can switch any user's set. + return user_access('administer shortcuts') || (user_access('switch shortcut sets') && (!isset($account) || $user->uid == $account->uid)); +} + +/** + * Access callback for editing a link in a shortcut set. + */ +function shortcut_link_access($menu_link) { + // The link must belong to a shortcut set that the current user has access + // to edit. + if ($shortcut_set = shortcut_set_load($menu_link['menu_name'])) { + return shortcut_set_edit_access($shortcut_set); + } + return FALSE; +} + +/** + * Loads the data for a shortcut set. + * + * @param $set_name + * The name of the shortcut set to load. + * @return + * If the shortcut set exists, an object of type stdClass containing the + * following properties: + * - 'set_name': The internal name of the shortcut set. + * - 'title': The title of the shortcut set. + * - 'links': An array of links associated with this shortcut set. + * If the shortcut set does not exist, the function returns FALSE. + */ +function shortcut_set_load($set_name) { + $set = db_select('shortcut_set', 'ss') + ->fields('ss') + ->condition('set_name', $set_name) + ->execute() + ->fetchObject(); + if (!$set) { + return FALSE; + } + $set->links = menu_load_links($set_name); + return $set; +} + +/** + * Saves a shortcut set. + * + * @param $shortcut_set + * An object containing the following properties: + * - 'title': The title of the shortcut set. + * - 'set_name': (optional) The internal name of the shortcut set. If + * omitted, a new shortcut set will be created, and the 'set_name' property + * will be added to the passed-in array. + * - 'links': (optional) An array of menu links to save for the shortcut set. + * Each link is an array containing at least the following keys (which will + * be expanded to fill in other default values after the shortcut set is + * saved): + * - 'link_path': The Drupal path or external path that the link points to. + * - 'link_title': The title of the link. + * Any other keys accepted by menu_link_save() may also be provided. + * @return + * A constant which is either SAVED_NEW or SAVED_UPDATED depending on whether + * a new set was created or an existing one was updated. + * + * @see menu_link_save() + */ +function shortcut_set_save(&$shortcut_set) { + // First save the shortcut set itself. + if (isset($shortcut_set->set_name)) { + $return = drupal_write_record('shortcut_set', $shortcut_set, 'set_name'); + } + else { + $shortcut_set->set_name = shortcut_set_get_unique_name(); + $return = drupal_write_record('shortcut_set', $shortcut_set); + } + // If links were provided for the set, save them, replacing any that were + // there before. + if (isset($shortcut_set->links)) { + menu_delete_links($shortcut_set->set_name); + foreach ($shortcut_set->links as &$link) { + // Do not specifically associate these links with the shortcut module, + // since other modules may make them editable via the menu system. + // However, we do need to specify the correct menu name. + $link['menu_name'] = $shortcut_set->set_name; + menu_link_save($link); + } + // Make sure that we have a return value, since if the links were updated + // but the shortcut set was not, the call to drupal_write_record() above + // would not return an indication that anything had changed. + if (empty($return)) { + $return = SAVED_UPDATED; + } + } + return $return; +} + +/** + * Deletes a shortcut set. + * + * Note that the default set cannot be deleted. + * + * @param $shortcut_set + * An object representing the shortcut set to delete. + * @return + * TRUE if the set was deleted, FALSE otherwise. + */ +function shortcut_set_delete($shortcut_set) { + // Make sure not to delete the default set. + $default_set = shortcut_default_set(); + if ($shortcut_set->set_name == $default_set->set_name) { + return FALSE; + } + // First, delete any user assignments for this set, so that each of these + // users will go back to using whatever default set applies. + db_delete('shortcut_set_users') + ->condition('set_name', $shortcut_set->set_name) + ->execute(); + // Next, delete the menu links for this set. + menu_delete_links($shortcut_set->set_name); + // Finally, delete the set itself. + $deleted = db_delete('shortcut_set') + ->condition('set_name', $shortcut_set->set_name) + ->execute(); + return (bool) $deleted; +} + +/** + * Reset the link weights in a shortcut set to match their current order. + * + * This function can be used, for example, when a new shortcut link is added to + * the set. If the link is added to the end of the array and this function is + * called, it will force that link to display at the end of the list. + * + * @param $shortcut_set + * An object representing a shortcut set. The link weights of the passed-in + * object will be reset as described above. + */ +function shortcut_set_reset_link_weights(&$shortcut_set) { + $weight = -50; + foreach ($shortcut_set->links as &$link) { + $link['weight'] = $weight; + $weight++; + } +} + +/** + * Assign a user to a particular shortcut set. + * + * @param $shortcut_set + * An object representing the shortcut set. + * @param $account + * A user account that will be assigned to use the set. + */ +function shortcut_set_assign_user($shortcut_set, $account) { + db_merge('shortcut_set_users') + ->key(array('uid' => $account->uid)) + ->fields(array('set_name' => $shortcut_set->set_name)) + ->execute(); +} + +/** + * Unassign a user from any shortcut set they may have been assigned to. + * + * The user will go back to using whatever default set applies. + * + * @param $account + * A user account that will be removed from the shortcut set assignment. + * @return + * TRUE if the user was previously assigned to a shortcut set and has been + * successfully removed from it. FALSE if the user was already not assigned + * to any set. + */ +function shortcut_set_unassign_user($account) { + $deleted = db_delete('shortcut_set') + ->condition('uid', $account->uid) + ->execute(); + return (bool) $deleted; +} + +/** + * Returns the current displayed shortcut set for the provided user account. + * + * @param $account + * (optional) The user account whose shortcuts will be returned. Defaults to + * the current logged-in user. + * @return + * An object representing the shortcut set that should be displayed to the + * current user. If the user does not have an explicit shortcut set defined, + * the default set is returned. + */ +function shortcut_current_displayed_set($account = NULL) { + $shortcut_sets = &drupal_static(__FUNCTION__, array()); + global $user; + if (!isset($account)) { + $account = $user; + } + // Try to return a shortcut set from the static cache. + if (isset($shortcut_sets[$account->uid])) { + return $shortcut_sets[$account->uid]; + } + // If none was found, try to find a shortcut set that is explicitly assigned + // to this user. + $query = db_select('shortcut_set', 's'); + $query->fields('s'); + $query->join('shortcut_set_users', 'u', 's.set_name = u.set_name'); + $query->condition('u.uid', $account->uid); + $shortcut_set = $query->execute()->fetchObject(); + // Otherwise, use the default set. + if (!$shortcut_set) { + $shortcut_set = shortcut_default_set($account); + } + $shortcut_sets[$account->uid] = $shortcut_set; + return $shortcut_set; +} + +/** + * Returns the default shortcut set for a given user account. + * + * @param $account + * (optional) The user account whose shortcuts will be returned. Defaults to + * the current logged-in user. + * @return + * An object representing the default shortcut set. + */ +function shortcut_default_set($account = NULL) { + global $user; + if (!isset($account)) { + $account = $user; + } + // Allow modules to return a default shortcut set name. Since we can only + // have one, we allow the last module which returns a valid result to take + // precedence. If no module returns a valid set, fall back on the site-wide + // default. + $shortcut_set_names = array_reverse(array_merge(array(SHORTCUT_DEFAULT_SET_NAME), module_invoke_all('shortcut_default_set', $account))); + foreach ($shortcut_set_names as $name) { + if ($shortcut_set = shortcut_set_load($name)) { + break; + } + } + return $shortcut_set; +} + +/** + * Returns a unique, machine-readable shortcut set name. + */ +function shortcut_set_get_unique_name() { + // Shortcut sets are numbered sequentially, so we keep trying until we find + // one that is available. For better performance, we start with a number + // equal to one more than the current number of shortcut sets, so that if + // no shortcut sets have been deleted from the database, this will + // automatically give us the correct one. + $number = db_query("SELECT COUNT(*) FROM {shortcut_set}")->fetchField() + 1; + do { + $name = shortcut_set_name($number); + $number++; + } while ($shortcut_set = shortcut_set_load($name)); + return $name; +} + +/** + * Returns the name of a shortcut set, based on a provided number. + * + * All shortcut sets have names like "shortcut-set-N" so that they can be + * matched with a properly-namespaced entry in the {menu_links} table. + * + * @param $number + * A number representing the shortcut set whose name should be retrieved. + * @return + * A string representing the expected shortcut name. + */ +function shortcut_set_name($number) { + return "shortcut-set-$number"; +} + +/** + * Returns an array of all shortcut sets, keyed by the set name. + * + * @return + * An array of shortcut sets. Note that only the basic shortcut set + * properties (name and title) are returned by this function, not the list + * of menu links that belong to the set. + */ +function shortcut_sets() { + return db_select('shortcut_set', 'ss') + ->fields('ss') + ->execute() + ->fetchAllAssoc('set_name'); +} + +/** + * Determines if a path corresponds to a valid shortcut link. + * + * @param $path + * The path to the link. + * @return + * TRUE if the shortcut link is valid, FALSE otherwise. Valid links are ones + * that correspond to actual paths on the site. + * + * @see menu_edit_item_validate() + */ +function shortcut_valid_link($path) { + // Do not use URL aliases. + $normal_path = drupal_get_normal_path($path); + if ($path != $normal_path) { + $path = $normal_path; + } + // Only accept links that correspond to valid paths on the site itself. + return !menu_path_is_external($path) && menu_get_item($path); +} + +/** + * Returns an array of shortcut links, suitable for rendering. + * + * @param $shortcut_set + * (optional) An object representing the set whose links will be displayed. + * If not provided, the user's current set will be displayed. + * @return + * An array of shortcut links, in the format returned by the menu system. + * + * @see menu_tree() + */ +function shortcut_renderable_links($shortcut_set = NULL) { + if (!isset($shortcut_set)) { + $shortcut_set = shortcut_current_displayed_set(); + } + return menu_tree($shortcut_set->set_name); +} + +/** + * Implement hook_page_build(). + */ +function shortcut_page_build(&$page) { + if (shortcut_set_edit_access()) { + $link = $_GET['q']; + $query_parameters = drupal_get_query_parameters(); + if (!empty($query_parameters)) { + $link .= '?' . drupal_http_build_query($query_parameters); + } + $query = array( + 'link' => $link, + 'name' => drupal_get_title(), + 'token' => drupal_get_token('shortcut-add-link'), + ); + $query += drupal_get_destination(); + + $shortcut_set = shortcut_current_displayed_set(); + $link_text = shortcut_set_switch_access() ? t('Add to %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->title)) : t('Add to shortcuts'); + $page['add_to_shortcuts'] = array( + '#prefix' => '<div class="add-to-shortcuts">', + '#markup' => l('<span class="icon"></span><span class="text">' . $link_text . '</span>', 'admin/config/system/shortcut/' . $shortcut_set->set_name . '/add-link-inline', array('query' => $query, 'html' => TRUE)), + '#suffix' => '</div>', + ); + } + + $links = shortcut_renderable_links(); + $links['#attached'] = array('css' => array(drupal_get_path('module', 'shortcut') . '/shortcut.css')); + $links['#prefix'] = '<div class="toolbar-shortcuts">'; + $links['#suffix'] = '</div>'; + $shortcut_set = shortcut_current_displayed_set(); + $configure_link = NULL; + if (shortcut_set_edit_access($shortcut_set)) { + $configure_link = array('#markup' => l(t('edit shortcuts'), 'admin/config/system/shortcut/' . $shortcut_set->set_name, array('attributes' => array('id' => 'toolbar-customize')))); + } + + $drawer = array( + 'shortcuts' => $links, + 'configure' => $configure_link, + ); + + $page['toolbar_drawer'] = $drawer; +} + +/** + * Implement hook_preprocess_page(). + */ +function shortcut_preprocess_page(&$variables) { + if (isset($variables['page']['add_to_shortcuts'])) { + $variables['add_to_shortcuts'] = drupal_render($variables['page']['add_to_shortcuts']); + } +} |