\n";
}
/**
* Generate the HTML output for a single local action link.
*
- * @param $variables
+ * @param $element
* An associative array containing:
- * - link: A menu link array with 'title', 'href', and 'localized_options'
+ * - #link: A menu link array with 'title', 'href', and 'localized_options'
* keys.
*
* @ingroup themeable
*/
function theme_menu_local_action($variables) {
- $link = $variables['link'];
+ $link = $variables['element']['#link'];
return '
';
+ $output[] = $secondary;
}
return $output;
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.533
diff -u -p -r1.533 theme.inc
--- includes/theme.inc 9 Oct 2009 16:33:13 -0000 1.533
+++ includes/theme.inc 10 Oct 2009 17:39:51 -0000
@@ -2081,6 +2081,29 @@ function template_preprocess(&$variables
$variables['attributes_array'] = array();
$variables['title_attributes_array'] = array();
+ // Initialize a variable to hold any attached links. This is populated when
+ // the first item passed to the theme function is a page element that has
+ // links attached to it.
+ $variables['attached_links'] = array();
+ $element = reset($variables);
+ if (is_array($element) && !empty($element['#attached_links'])) {
+ $variables['attached_links'] = $element['#attached_links'];
+ // Add appropriate CSS classes that associate the element with the links
+ // that are attached to it.
+ $attached_link_classes = template_generate_attached_link_classes($element['#attached_links']);
+ if (!empty($attached_link_classes)) {
+ $variables['classes_array'][] = 'attached-links-enabled-region';
+ $variables['classes_array'] = array_merge($variables['classes_array'], $attached_link_classes);
+ }
+ // Wrap the attached links associated with this element in a theme function
+ // that will group them together for display. Templates which render
+ // different sets of attached links separately - for example, by using
+ // render($attached_links['block']) rather than render($attached_links) -
+ // will automatically avoid this wrapper function and therefore will have
+ // complete freedom to display the links anywhere within the page element.
+ $variables['attached_links']['#theme_wrappers'][] = 'attached_links_group';
+ }
+
// Set default variables that depend on the database.
$variables['is_admin'] = FALSE;
$variables['is_front'] = FALSE;
@@ -2112,6 +2135,40 @@ function template_process(&$variables, $
}
/**
+ * Generate a list of relevant CSS classes from an array of attached links.
+ *
+ * The array of attached links is searched recursively for all items of type
+ * 'attached_links', and the 'href' parameter of each link is used to generate
+ * an appropriate CSS class.
+ *
+ * @param $attached_links
+ * An associative array containing the links that will be searched. Usually,
+ * this will be the '#attached_links' property of a page element.
+ * @return
+ * An array of CSS classes generated from the attached links.
+ *
+ * @see system_pre_render_attached_links()
+ */
+function template_generate_attached_link_classes($attached_links) {
+ $classes = array();
+ // If the array itself contains attached links, generate classes from them.
+ if (isset($attached_links['#type']) && $attached_links['#type'] == 'attached_links') {
+ if (isset($attached_links['#links'])) {
+ foreach ($attached_links['#links'] as $link) {
+ $classes[] = drupal_html_class('attached-links-region-for-' . $link['href']);
+ }
+ }
+ }
+ // Otherwise, recursively search the element's children for links.
+ else {
+ foreach (element_children($attached_links) as $group) {
+ $classes = array_merge($classes, template_generate_attached_link_classes($attached_links[$group]));
+ }
+ }
+ return $classes;
+}
+
+/**
* Preprocess variables for html.tpl.php
*
* @see system_elements()
Index: misc/attached_links.css
===================================================================
RCS file: misc/attached_links.css
diff -N misc/attached_links.css
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ misc/attached_links.css 10 Oct 2009 17:44:23 -0000
@@ -0,0 +1,62 @@
+/* $Id$ */
+
+/**
+ * Highlighted regions for attached_links.js.
+ */
+.attached-links-link-icon {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ background: #444 none no-repeat scroll 2px 2px;
+ height: 19px;
+ width: 19px;
+ float: left;
+ display: block;
+ margin-right: 2px;
+}
+
+.attached-links-link-icon-configure {
+ background-image: url(configure.png);
+}
+
+.attached-links-link-icon-delete {
+ background-image: url(delete.png);
+}
+
+.attached-links-link-icon-edit {
+ background-image: url(edit.png);
+}
+
+.attached-links-enabled-region {
+ outline: none;
+ position: relative;
+}
+
+.active-attached-links-region {
+ outline: #000 dashed 2px;
+}
+
+.attached-links-group {
+ position: absolute;
+ right: 0;
+ top: 0;
+ padding: 0;
+ margin: 0;
+}
+
+ul.attached-links {
+ float: right;
+ padding: 0;
+ margin: 0;
+}
+
+ul.attached-links li {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ display: inline;
+ line-height: 100%;
+}
+
+ul.attached-links li a {
+ text-decoration: none;
+}
Index: misc/attached_links.js
===================================================================
RCS file: misc/attached_links.js
diff -N misc/attached_links.js
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ misc/attached_links.js 10 Oct 2009 17:45:59 -0000
@@ -0,0 +1,31 @@
+// $Id$
+(function ($) {
+
+/**
+ * Highlights the region of the page that an attached link is associated with.
+ */
+Drupal.behaviors.attachedLinks = {
+ attach: function (context) {
+ var addHighlight = function () {
+ // If the attached link has a CSS class containing an encoded version of
+ // the URL that the link points to, find the region of the page whose CSS
+ // class contains the same encoded URL.
+ var matches = $(this).attr('class').match(/\battached-links-link-to-(\S+)/);
+ if (matches) {
+ var className = '.attached-links-region-for-' + matches[1];
+ $(className).addClass('active-attached-links-region');
+ $(className).addClass('active-attached-links-link');
+ }
+ };
+
+ var removeHighlight = function () {
+ $('.active-attached-links-region').removeClass('active-attached-links-region');
+ $('.active-attached-links-link').removeClass('active-attached-links-link');
+ };
+
+ // Trigger the behavior when hovering over the link.
+ $('.attached-links-link').hover(addHighlight, removeHighlight);
+ }
+};
+
+})(jQuery);
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.386
diff -u -p -r1.386 block.module
--- modules/block/block.module 10 Oct 2009 21:39:01 -0000 1.386
+++ modules/block/block.module 11 Oct 2009 05:05:48 -0000
@@ -249,6 +249,27 @@ function block_get_blocks_by_region($reg
foreach ($list as $key => $block) {
$build[$key] = $block->content;
unset($block->content);
+
+ // Attach links for this block; skip this for the system main block.
+ // @todo Actually, this is nonsense. The local tasks we render elsewhere
+ // are the system main block's edit links. Right? ;)
+ if ($key != 'system_main') {
+ $build[$key] += array(
+ '#attached_links' => array(
+ '#type' => 'attached_links',
+ '#links' => array(),
+ ),
+ );
+ // Add local tasks for blocks; not overwriting any existing links.
+ // @todo Implement proper path/subject/verb URL pattern for blocks.
+ $build[$key]['#attached_links']['#links'] += array(
+ 'configure' => array(
+ 'href' => "admin/structure/block/configure/{$block->module}/{$block->delta}",
+ 'title' => t('Configure'),
+ ),
+ );
+ }
+
$build[$key] += array(
'#block' => $block,
'#weight' => ++$weight,
Index: modules/block/block.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.tpl.php,v
retrieving revision 1.4
diff -u -p -r1.4 block.tpl.php
--- modules/block/block.tpl.php 11 Sep 2009 06:48:02 -0000 1.4
+++ modules/block/block.tpl.php 10 Oct 2009 17:39:51 -0000
@@ -11,6 +11,12 @@
* - $block->module: Module that generated the block.
* - $block->delta: An ID for the block, unique within each module.
* - $block->region: The block region embedding the current block.
+ * - $attached_links: An array of links that are attached to the block on the
+ * page. Use render($attached_links) to print them all, or print a subset
+ * using render($attached_links['block']), which prints the configuration
+ * links for the block itself, or render($attached_links['block content']),
+ * which prints the links associated with the content that is displayed in
+ * the block.
* - $classes: String of classes that can be used to style contextually through
* CSS. It can be manipulated through the variable $classes_array from
* preprocess functions. The default values can be one or more of the following:
@@ -36,6 +42,11 @@
*/
?>
>
+
+
+
+
+
subject): ?>
>subject ?>
Index: modules/locale/locale.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v
retrieving revision 1.43
diff -u -p -r1.43 locale.test
--- modules/locale/locale.test 11 Oct 2009 03:07:18 -0000 1.43
+++ modules/locale/locale.test 11 Oct 2009 05:05:48 -0000
@@ -1087,7 +1087,7 @@ class LanguageSwitchingFunctionalTest ex
$this->assertText(t('Languages'), t('Language switcher block found.'));
// Assert that only the current language is marked as active.
- list($language_switcher) = $this->xpath('//div[@id="block-locale-language"]');
+ list($language_switcher) = $this->xpath('//div[@id="block-locale-language"]/div[@class="content"]');
$links = array(
'active' => array(),
'inactive' => array(),
@@ -1096,7 +1096,7 @@ class LanguageSwitchingFunctionalTest ex
'active' => array(),
'inactive' => array(),
);
- foreach ($language_switcher->div->ul->li as $link) {
+ foreach ($language_switcher->ul->li as $link) {
$classes = explode(" ", (string) $link['class']);
list($language) = array_intersect($classes, array('en', 'fr'));
if (in_array('active', $classes)) {
Index: modules/menu/menu.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.api.php,v
retrieving revision 1.15
diff -u -p -r1.15 menu.api.php
--- modules/menu/menu.api.php 9 Oct 2009 08:02:24 -0000 1.15
+++ modules/menu/menu.api.php 11 Oct 2009 05:12:43 -0000
@@ -430,5 +430,68 @@ function hook_menu_delete($menu) {
}
/**
+ * Alter tabs and actions displayed on the page before they are rendered.
+ *
+ * This hook is invoked by menu_local_tasks(). The system-determined tabs and
+ * actions are passed in by reference. Additional tabs or actions may be added,
+ * or existing items altered.
+ *
+ * Each tab or action is an associative array containing:
+ * - #theme: The theme function to use to render.
+ * - #link: An associative array containing:
+ * - title: The localized title of the link.
+ * - href: The system path to link to.
+ * - localized_options: An array of options to pass to url().
+ * - #active: Whether the link should be marked as 'active'.
+ *
+ * @param $data
+ * An associative array containing:
+ * - actions: An associative array containing:
+ * - count: The amount of actions determined by the menu system, which can
+ * be ignored.
+ * - output: A list of of actions, each one being an associative array
+ * as described above.
+ * - tabs: An indexed array (list) of tab levels (up to 2 levels), each
+ * containing an associative array:
+ * - count: The amount of tabs determined by the menu system. This value
+ * does not need to be altered if there is more than one tab.
+ * - output: A list of of tabs, each one being an associative array as
+ * described above.
+ */
+function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) {
+ // Add an action linking to node/add to all pages.
+ $data['actions']['output'][] = array(
+ '#theme' => 'menu_local_task',
+ '#link' => array(
+ 'title' => t('Add new content'),
+ 'href' => 'node/add',
+ 'localized_options' => array(
+ 'attributes' => array(
+ 'title' => t('Add new content'),
+ ),
+ ),
+ ),
+ );
+
+ // Add a tab linking to node/add to all pages.
+ $data['tabs'][0]['output'][] = array(
+ '#theme' => 'menu_local_task',
+ '#link' => array(
+ 'title' => t('Example tab'),
+ 'href' => 'node/add',
+ 'localized_options' => array(
+ 'attributes' => array(
+ 'title' => t('Add new content'),
+ ),
+ ),
+ ),
+ // Define whether this link is active. This can be omitted for
+ // implementations that add links to pages outside of the current page
+ // context.
+ '#active' => ($router_item['path'] == $root_path),
+ );
+}
+
+/**
* @} End of "addtogroup hooks".
*/
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.208
diff -u -p -r1.208 menu.module
--- modules/menu/menu.module 11 Oct 2009 03:07:18 -0000 1.208
+++ modules/menu/menu.module 11 Oct 2009 05:05:49 -0000
@@ -383,6 +383,19 @@ function menu_block_view($delta = '') {
$menus = menu_get_menus(FALSE);
$data['subject'] = check_plain($menus[$delta]);
$data['content'] = menu_tree($delta);
+ if (!empty($data['content'])) {
+ // @todo This is how we will do it. 11/10/2009 sun
+ $tasks = menu_local_tasks(0, 'admin/structure/menu/manage/' . $delta);
+ $data['content']['#attached_links'] = array(
+ '#type' => 'attached_links',
+ '#links' => array(
+ 'edit' => array(
+ 'href' => 'admin/structure/menu/manage/' . $delta,
+ 'title' => t('Edit the !menu menu', array('!menu' => drupal_strtolower($menus[$delta]))),
+ ),
+ ),
+ );
+ }
return $data;
}
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1145
diff -u -p -r1.1145 node.module
--- modules/node/node.module 11 Oct 2009 03:07:18 -0000 1.1145
+++ modules/node/node.module 11 Oct 2009 05:05:49 -0000
@@ -1102,10 +1102,26 @@ function node_build($node, $build_mode =
$build = $node->content;
// We don't need duplicate rendering info in node->content.
unset($node->content);
-
+
+ // @todo This is how we will do it. 11/10/2009 sun
+ $tasks = menu_local_tasks(0, 'node/' . $node->nid);
+
$build += array(
'#theme' => 'node',
'#node' => $node,
+ '#attached_links' => array(
+ '#type' => 'attached_links',
+ '#links' => array(
+ 'edit' => array(
+ 'href' => "node/{$node->nid}/edit",
+ 'title' => t('Edit !type !node', array('!type' => drupal_strtolower(node_type_get_name($node)), '!node' => drupal_strtolower($node->title))),
+ ),
+ 'delete' => array(
+ 'href' => "node/{$node->nid}/delete",
+ 'title' => t('Delete !type !node', array('!type' => drupal_strtolower(node_type_get_name($node)), '!node' => drupal_strtolower($node->title))),
+ ),
+ ),
+ ),
'#build_mode' => $build_mode,
);
return $build;
Index: modules/node/node.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.tpl.php,v
retrieving revision 1.23
diff -u -p -r1.23 node.tpl.php
--- modules/node/node.tpl.php 11 Oct 2009 03:07:18 -0000 1.23
+++ modules/node/node.tpl.php 11 Oct 2009 05:05:49 -0000
@@ -18,6 +18,8 @@
* - $node_url: Direct url of the current node.
* - $terms: the themed list of taxonomy term links output from theme_links().
* - $display_submitted: whether submission information should be displayed.
+ * - $attached_links: An array of links that are attached to the node on the
+ * page. Use render($attached_links) to print them all.
* - $classes: String of classes that can be used to style contextually through
* CSS. It can be manipulated through the variable $classes_array from
* preprocess functions. The default values can be one or more of the following:
@@ -74,6 +76,10 @@
+
+
+
+
>a href="">
Index: modules/system/page.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/page.tpl.php,v
retrieving revision 1.36
diff -u -p -r1.36 page.tpl.php
--- modules/system/page.tpl.php 9 Oct 2009 01:00:05 -0000 1.36
+++ modules/system/page.tpl.php 11 Oct 2009 05:13:05 -0000
@@ -31,14 +31,14 @@
* - $secondary_menu (array): An array containing the Secondary menu links for
* the site, if they have been configured.
* - $breadcrumb: The breadcrumb trail for the current page.
- * - $action_links: Actions local to the page, such as 'Add menu' on the menu
- * administration interface.
*
* Page content (in order of occurrence in the default page.tpl.php):
* - $title: The page title, for use in the actual HTML content.
* - $messages: HTML for status and error messages. Should be displayed prominently.
- * - $tabs: Tabs linking to any sub-pages beneath the current page (e.g., the view
- * and edit tabs when displaying a node).
+ * - $tabs (array): Tabs linking to any sub-pages beneath the current page
+ * (e.g., the view and edit tabs when displaying a node).
+ * - $action_links (array): Actions local to the page, such as 'Add menu' on the
+ * menu administration interface.
* - $feed_icons: A string of all feed icons for the current page.
*
* Regions:
@@ -107,9 +107,9 @@
-
+
-
+
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.804
diff -u -p -r1.804 system.module
--- modules/system/system.module 10 Oct 2009 16:48:38 -0000 1.804
+++ modules/system/system.module 11 Oct 2009 00:05:33 -0000
@@ -204,6 +204,9 @@ function system_theme() {
'system_run_cron_image' => array(
'arguments' => array('image_path' => NULL),
),
+ 'attached_links_group' => array(
+ 'arguments' => array('elements' => NULL),
+ ),
));
}
@@ -303,6 +306,16 @@ function system_element_info() {
'#attributes' => array(),
'#items' => array(),
);
+ $types['attached_links'] = array(
+ '#theme' => 'links',
+ '#pre_render' => array('system_pre_render_attached_links'),
+ '#attributes' => array('class' => array('attached-links')),
+ '#links' => array(),
+ '#attached' => array(
+ 'js' => array('misc/attached_links.js'),
+ 'css' => array('misc/attached_links.css'),
+ ),
+ );
// Input elements.
$types['submit'] = array(
@@ -507,6 +520,7 @@ function system_menu() {
'type' => MENU_CALLBACK,
'file' => 'system.admin.inc',
);
+
$items['admin'] = array(
'title' => 'Administer',
'access arguments' => array('access administration pages'),
@@ -1641,6 +1655,19 @@ function system_block_view($delta = '')
if (isset($system_menus[$delta])) {
$block['subject'] = t($system_menus[$delta]);
$block['content'] = menu_tree($delta);
+ // @todo: Should we move this to the menu module? The code is much
+ // cleaner and easier to read when we put it here.
+ if (!empty($block['content']) && module_exists('menu')) {
+ $block['content']['#attached_links'] = array(
+ '#type' => 'attached_links',
+ '#links' => array(
+ 'edit' => array(
+ 'href' => 'admin/structure/menu/manage/' . $delta,
+ 'title' => t('Edit the !menu menu', array('!menu' => drupal_strtolower($block['subject']))),
+ ),
+ ),
+ );
+ }
return $block;
}
break;
@@ -2901,3 +2928,76 @@ function theme_system_run_cron_image($va
return '';
}
+/**
+ * Checks access, adds classes, and performs other actions on attached links.
+ *
+ * This is used as a pre-render function for the #attached_links type.
+ */
+function system_pre_render_attached_links($element) {
+ $transformed_links = array();
+ foreach ($element['#links'] as $type => $link_data) {
+ // Use the menu item to determine access to the link, if an access property
+ // was not already set.
+ if (!isset($link_data['#access'])) {
+ $item = menu_get_item($link_data['href']);
+ $link_data['#access'] = !empty($item['access']);
+ }
+
+ // Do not display this link if the current user does not have access to it,
+ // or if the user is already on the page that is being linked to.
+ if (!$link_data['#access'] || $link_data['href'] == $_GET['q']) {
+ continue;
+ }
+
+ // Get the link title from the menu item if none was set.
+ if (!isset($link_data['title'])) {
+ $item = menu_get_item($link_data['href']);
+ $link_data['title'] = $item['title'];
+ }
+
+ // Define a class that will associate this link with the region of the page
+ // that it is attached to. See template_generate_attached_link_classes().
+ $link_class = implode(' ', array('attached-links-link', drupal_html_class("attached-links-$type-link"), drupal_html_class('attached-links-link-to-' . $link_data['href'])));
+
+ // Add wrapper classes for the icon display.
+ $existing_wrapper_class = !empty($link_data['attributes']['class']) ? $link_data['attributes']['class'] : array();
+ $wrapper_class = array_merge($existing_wrapper_class, array("attached-links-link-icon", "attached-links-link-icon-$type"));
+
+ // Transform and add to the link.
+ $transformed_links[$link_class] = array(
+ 'href' => $link_data['href'],
+ // Refer users back to the current page after they have completed any
+ // action (for example, a form submission) associated with the attached
+ // link.
+ 'query' => drupal_get_destination(),
+ // Prepare the link to be displayed as an icon with the original link
+ // title appearing on hover.
+ 'title' => '',
+ 'attributes' => array(
+ 'class' => $wrapper_class,
+ 'title' => $link_data['title'],
+ ),
+ );
+ }
+
+ // Replace the links data with the recomputed items.
+ $element['#links'] = $transformed_links;
+ return $element;
+}
+
+/**
+ * Theme a group of attached links.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - elements: An associative array containing the properties of the element.
+ * Properties used: #children
+ *
+ * @return
+ * A themed HTML string representing the group of attached links.
+ *
+ * @ingroup themeable
+ */
+function theme_attached_links_group($variables) {
+ return '