Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.992 diff -u -p -u -p -r1.992 common.inc --- includes/common.inc 18 Sep 2009 10:54:20 -0000 1.992 +++ includes/common.inc 18 Sep 2009 13:33:38 -0000 @@ -4151,6 +4151,18 @@ function drupal_render(&$elements) { $elements['#theme'] = 'markup'; } + // If there are admin links associated with this element but the access + // property has not been set, use the menu callback to determine whether or + // not it is editable by the current user. + if (isset($elements['#admin_links'])) { + foreach ($elements['#admin_links'] as $key => $data) { + if (!isset($data['access']) && function_exists('menu_get_item')) { + $item = menu_get_item($data['href']); + $elements['#admin_links'][$key]['access'] = !empty($item['access']); + } + } + } + // Make any final changes to the element before it is rendered. This means // that the $element or the children can be altered or corrected before the // element is rendered into the final text. @@ -4562,6 +4574,9 @@ function drupal_common_theme() { 'links' => array( 'arguments' => array('links' => NULL, 'attributes' => array('class' => array('links')), 'heading' => array()), ), + 'admin_links' => array( + 'arguments' => array('link' => NULL, 'element' => NULL), + ), 'image' => array( 'arguments' => array('path' => NULL, 'alt' => '', 'title' => '', 'attributes' => array(), 'getsize' => TRUE), ), Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.346 diff -u -p -u -p -r1.346 menu.inc --- includes/menu.inc 18 Sep 2009 10:54:20 -0000 1.346 +++ includes/menu.inc 18 Sep 2009 13:33:38 -0000 @@ -789,7 +789,7 @@ function menu_get_object($type = 'node', } /** - * Render a menu tree based on the current path. + * Returns a menu tree based on the current path. * * The tree is expanded based on the current path and dynamic paths are also * changed according to the defined to_arg functions (for example the 'My account' @@ -798,20 +798,21 @@ function menu_get_object($type = 'node', * @param $menu_name * The name of the menu. * @return - * The rendered HTML of that menu on the current page. + * An array representing the menu on the current page, suitable for + * rendering with drupal_render(). */ function menu_tree($menu_name) { - $menu_output = &drupal_static(__FUNCTION__, array()); + $menu_tree = &drupal_static(__FUNCTION__, array()); - if (!isset($menu_output[$menu_name])) { + if (!isset($menu_tree[$menu_name])) { $tree = menu_tree_page_data($menu_name); - $menu_output[$menu_name] = menu_tree_output($tree); + $menu_tree[$menu_name] = menu_tree_output($tree); } - return $menu_output[$menu_name]; + return $menu_tree[$menu_name]; } /** - * Returns a rendered menu tree. + * Returns an array representing a menu, suitable for rendering. * * The menu item's LI element is given one of the following classes: * - expanded: The menu item is showing its submenu. @@ -1305,7 +1306,7 @@ function template_preprocess_menu_tree(& * @ingroup themeable */ function theme_menu_tree($tree) { - return '
'; + return !empty($tree) ? ' ' : ''; } /** Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.525 diff -u -p -u -p -r1.525 theme.inc --- includes/theme.inc 15 Sep 2009 20:03:18 -0000 1.525 +++ includes/theme.inc 18 Sep 2009 13:33:38 -0000 @@ -847,10 +847,12 @@ function theme() { } else { // The theme call is a template. - $variables = array( - 'template_files' => array() - ); + $variables = array(); if (!empty($info['arguments'])) { + // Populate the variables with arguments passed to the theme function. + // Note that the first argument may be treated specially by template + // preprocess functions, so it must go into the variables array before + // anything else does. $count = 0; foreach ($info['arguments'] as $name => $default) { $variables[$name] = isset($args[$count]) ? $args[$count] : $default; @@ -858,6 +860,11 @@ function theme() { } } + // Add an empty array of template files as a default; preprocess functions + // will be able to modify this. We include it last, as per the comment + // above. + $variables += array('template_files' => array()); + // default render function and extension. $render_function = 'theme_render_template'; $extension = '.tpl.php'; @@ -885,7 +892,12 @@ function theme() { if (!empty($info[$phase])) { foreach ($info[$phase] as $processor_function) { if (function_exists($processor_function)) { - call_user_func_array($processor_function, $args); + $result = call_user_func_array($processor_function, $args); + // Allow process functions to abort rendering the item by returning + // FALSE. + if ($result === FALSE) { + return ''; + } } } } @@ -1477,6 +1489,33 @@ function theme_links($links, $attributes } /** + * Return themed admin links for an element on a page. + * + * @param $links + * An array representing the admin links to be themed, in the format + * expected by theme_links(); e.g., the array can have keys including + * 'title', 'href', 'html', etc. + * @param $element + * An array representing the editable page element. + * @return + * A themed HTML string representing the edit link. + * + * @see theme_links() + */ +function theme_admin_links($links, $element) { + // Construct a CSS class which associates this edit link with the region on + // the page that it edits. + $links_array = array(); + foreach ($links as $key => $link) { + $class = "admin-links-link admin-links-$key-link admin-links-at-" . str_replace('/', '-', $link['href']); + $links_array[$class] = $link; + } + drupal_add_js('misc/admin_links.js'); + drupal_add_css('misc/admin_links.css'); + return theme('links', $links_array, array('class' => 'admin-links')); +} + +/** * Return a themed image. * * @param $path @@ -2051,6 +2090,63 @@ function template_preprocess(&$variables $variables['attributes_array'] = array(); $variables['title_attributes_array'] = array(); + // Only theme admin links if edit mode is enabled. + if (!empty($_SESSION['edit_mode'])) { + // If the first item passed to the theme function is an editable page + // element which the current user has access to, populate useful variables + // for constructing edit links and other editable markup. + $element = reset($variables); + $variables['has_admin_links'] = FALSE; + if (is_array($element) && isset($element['#admin_links'])) { + foreach ($element['#admin_links'] as $key => $data) { + $variables['has_admin_links'] = $variables['has_admin_links'] || !empty($data['access']); + } + } + // Do not show edit links when the user is already on the page that is + // being linked to. + $show_admin_links = FALSE; + if ($variables['has_admin_links'] && isset($_GET['q'])) { + foreach ($element['#admin_links'] as $key => $data) { + $show_admin_links = $show_admin_links || ($data['href'] != $_GET['q']); + } + } + // Add appropriate CSS classes that define this element as editable and + // associate it with its edit link. + if ($variables['has_admin_links']) { + $variables['classes_array'][] = 'admin-links-enabled-region'; + foreach ($element['#admin_links'] as $key => $data) { + $variables['classes_array'][] = 'admin-links-enabled-at-' . str_replace('/', '-', $data['href']); + } + } + if ($show_admin_links) { + $text_items = array(); + $info_items = array(); + foreach ($element['#admin_links'] as $key => $data) { + $check_plain = isset($data['attributes']['html']) ? !$data['attributes']['html'] : TRUE; + $text_items[$key] = $check_plain ? check_plain($data['title']) : $data['title']; + $info_items[$key] = array( + 'title' => ' ', + 'href' => $data['href'], + 'html' => TRUE, + // Refer users back to the current page after they have completed + // their edits. + 'query' => drupal_get_destination(), + 'attributes' => array( + 'class' => 'admin-links-link admin-links-link-' . $key, + 'title' => $text_items[$key], + ), + ); + } + $variables['admin_links'] = theme('admin_links', $info_items, $element); + } + else { + $variables['admin_links'] = ''; + } + } + else { + $variables['admin_links'] = ''; + } + // Set default variables that depend on the database. $variables['is_admin'] = FALSE; $variables['is_front'] = FALSE; Index: misc/admin_links.css =================================================================== RCS file: misc/admin_links.css diff -N misc/admin_links.css --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ misc/admin_links.css 18 Sep 2009 13:33:38 -0000 @@ -0,0 +1,58 @@ +/* $Id$ */ + +/* +** Highlighted regions for admin_links.js. +*/ +.admin-links-link-icon { + -moz-border-radius-bottomleft: 3px; + -moz-border-radius-bottomright: 3px; + -moz-border-radius-topleft: 3px; + -moz-border-radius-topright: 3px; + background: #444444 none no-repeat scroll 2px 2px; + height: 19px; + width: 19px; + float: left; + display: block; + margin-right: 2px; +} + +.admin-links-link-icon-configure { + background-image: url(configure.png); +} + +.admin-links-link-icon-delete { + background-image: url(delete.png); +} + +.admin-links-link-icon-edit { + background-image: url(edit.png); +} + +.admin-links-enabled-region { + outline: none; + position: relative; +} + +.active-admin-links-region { + outline: dashed #000000 2px; +} + +ul.admin-links { + position: absolute; + right: 0; + top: 0; + padding: 0; + margin: 0; +} + +ul.admin-links li { + padding: 0; + margin: 0; + list-style: none; + display: inline; + line-height: 100%; +} + +ul.admin-links li a { + text-decoration: none; +} Index: misc/admin_links.js =================================================================== RCS file: misc/admin_links.js diff -N misc/admin_links.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ misc/admin_links.js 18 Sep 2009 13:33:38 -0000 @@ -0,0 +1,27 @@ +// $Id$ +(function ($) { + +/** + * Highlights the admin links region when hovering over an edit link. + */ +Drupal.behaviors.highlightEditableRegion = { + attach: function (context) { + var addHighlight = function () { + var matches = $(this).attr('class').match(/[^ ]*-at-(\S+)/); + if (matches) { + var class = '.admin-links-enabled-at-' + matches[1]; + $(class).addClass('active-admin-links-region'); + $(this).addClass('active-admin-links-link'); + } + }; + + var removeHighlight = function () { + $('.active-admin-links-region').removeClass('active-admin-links-region'); + $('.active-admin-links-link').removeClass('active-admin-links-link'); + }; + + $('.admin-links-link').hover(addHighlight, removeHighlight); + } +}; + +})(jQuery); Index: modules/block/block.module =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.module,v retrieving revision 1.377 diff -u -p -u -p -r1.377 block.module --- modules/block/block.module 18 Sep 2009 00:04:22 -0000 1.377 +++ modules/block/block.module 18 Sep 2009 13:33:38 -0000 @@ -233,8 +233,20 @@ function block_get_blocks_by_region($reg unset($block->content); $build[$key] += array( '#block' => $block, + '#admin_links' => array( + 'configure' => array( + 'href' => 'admin/structure/block/configure/' . $block->module . '/' . $block->delta, + 'title' => !empty($block->subject) ? t('Configure the @block block', array('@block' => drupal_strtolower($block->subject))) : t('Configure this block'), + ), + ), '#weight' => ++$weight, ); + // Special exception for the main system content block: it's weird and + // confusing to show admin links for it - what exactly is the user + // configuring? + if ($block->module == 'system' && $block->delta == 'main') { + unset($build[$key]['#admin_links']['configure']); + } $build[$key]['#theme_wrappers'][] ='block'; } $build['#sorted'] = TRUE; @@ -767,6 +779,22 @@ function block_flush_caches() { function template_preprocess_block(&$variables) { $block_counter = &drupal_static(__FUNCTION__, array()); $variables['block'] = $variables['elements']['#block']; + + // Create the $content variable that templates expect. If the block is + // empty, do not show it. + $variables['content'] = $variables['elements']['#children']; + if (empty($variables['content'])) { + // TODO: This is necessary to match the behavior in _block_render_blocks() + // when $block->content contains HTML output rather than a structured + // array. The inconsistency is ugly, though. It also means that the array + // of blocks per region inserted into the page via block_page_alter() can + // contain a larger number of blocks than actually will display on the + // page. (This part actually might not be bad, though; it means that other + // modules have a chance to insert content into these "empty" blocks and + // therefore make them appear after all, if they want to.) + return FALSE; + } + // All blocks get an independent counter for each region. if (!isset($block_counter[$variables['block']->region])) { $block_counter[$variables['block']->region] = 1; @@ -775,9 +803,6 @@ function template_preprocess_block(&$var $variables['block_zebra'] = ($block_counter[$variables['block']->region] % 2) ? 'odd' : 'even'; $variables['block_id'] = $block_counter[$variables['block']->region]++; - // Create the $content variable that templates expect. - $variables['content'] = $variables['elements']['#children']; - $variables['classes_array'][] = 'block-' . $variables['block']->module; $variables['template_files'][] = 'block-' . $variables['block']->region; Index: modules/block/block.tpl.php =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.tpl.php,v retrieving revision 1.4 diff -u -p -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 18 Sep 2009 13:33:38 -0000 @@ -30,12 +30,22 @@ * - $logged_in: Flags true when the current user is a logged-in member. * - $is_admin: Flags true when the current user is an administrator. * + * Admin links variables: + * - $has_admin_lins: TRUE when the block is editable by the current user. + * - $admin_links: Already-themed link(s) for admin links that may be taken on the + * block; may be empty. + * * @see template_preprocess() * @see template_preprocess_block() * @see template_process() */ ?>