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 25 Sep 2009 11:38:59 -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 '
' . $tree . '
';
+ return !empty($tree) ? '
' . $tree . '
' : '';
}
/**
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.526
diff -u -p -u -p -r1.526 theme.inc
--- includes/theme.inc 21 Sep 2009 06:36:54 -0000 1.526
+++ includes/theme.inc 25 Sep 2009 11:39:00 -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 '';
+ }
}
}
}
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 25 Sep 2009 11:39:00 -0000
@@ -0,0 +1,58 @@
+/* $Id$ */
+
+/*
+** Highlighted regions for attached_links.js.
+*/
+.attached-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;
+}
+
+.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: dashed #000000 2px;
+}
+
+ul.attached-links {
+ position: absolute;
+ right: 0;
+ top: 0;
+ 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 25 Sep 2009 11:39:00 -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 = '.attached-links-enabled-at-' + matches[1];
+ $(class).addClass('active-attached-links-region');
+ $(this).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');
+ };
+
+ $('.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.379
diff -u -p -u -p -r1.379 block.module
--- modules/block/block.module 22 Sep 2009 07:50:15 -0000 1.379
+++ modules/block/block.module 25 Sep 2009 11:39:00 -0000
@@ -233,8 +233,23 @@ function block_get_blocks_by_region($reg
unset($block->content);
$build[$key] += array(
'#block' => $block,
+ 'links' => array(
+ '#type' => 'attached_links',
+ '#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]['#attached_links']['configure']);
+ }
$build[$key]['#theme_wrappers'][] ='block';
}
$build['#sorted'] = TRUE;
@@ -768,6 +783,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;
@@ -776,9 +807,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 25 Sep 2009 11:39:00 -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.
+ * - $attached_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()
*/
?>
>
+
+
+
+
+
subject): ?>
>subject ?>
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.204
diff -u -p -u -p -r1.204 menu.module
--- modules/menu/menu.module 18 Sep 2009 00:04:22 -0000 1.204
+++ modules/menu/menu.module 25 Sep 2009 11:39:00 -0000
@@ -157,6 +157,10 @@ function menu_theme() {
'file' => 'menu.admin.inc',
'arguments' => array('title' => NULL, 'name' => NULL, 'description' => NULL),
),
+ 'menu_editable' => array(
+ 'arguments' => array('element' => NULL),
+ 'template' => 'menu-editable',
+ ),
);
}
@@ -301,6 +305,32 @@ function menu_block_view($delta = '') {
}
/**
+ * Implement hook_page_alter().
+ */
+function menu_page_alter(&$page) {
+ // Search for every non-empty menu block on the page.
+ // TODO: This works, but cries out for something like hook_block_alter()...
+ foreach (element_children($page) as $region) {
+ $blocks = &$page[$region];
+ foreach (element_children($blocks) as $id) {
+ if (isset($blocks[$id]['#theme_wrappers']) && isset($blocks[$id]['#block'])) {
+ $block = $blocks[$id]['#block'];
+ $content = &$blocks[$id];
+ if (isset($content['#theme_wrappers']) && in_array('menu_tree', $content['#theme_wrappers'])) {
+ // If we have a non-empty menu tree, wrap it in a theme function
+ // that will display an edit link.
+ $content['#theme_wrappers'] += array('menu_editable');
+ $content['links']['#links']['edit'] = array(
+ 'href' => 'admin/structure/menu-customize/' . $block->delta,
+ 'title' => t('Edit the @menu menu', array('@menu' => drupal_strtolower($content['#block']->subject))),
+ );
+ }
+ }
+ }
+ }
+}
+
+/**
* Implement hook_node_insert().
*/
function menu_node_insert($node) {
@@ -496,3 +526,12 @@ function menu_get_menus($all = TRUE) {
return $query->execute()->fetchAllKeyed();
}
+
+
+/**
+ * Process variables for menu-editable.tpl.php.
+ */
+function template_preprocess_menu_editable(&$variables) {
+ // Create the $content variable that templates expect.
+ $variables['content'] = $variables['element']['#children'];
+}
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1131
diff -u -p -u -p -r1.1131 node.module
--- modules/node/node.module 23 Sep 2009 10:41:55 -0000 1.1131
+++ modules/node/node.module 25 Sep 2009 11:39:00 -0000
@@ -1034,6 +1034,19 @@ function node_build($node, $build_mode =
$build += array(
'#theme' => 'node',
'#node' => $node,
+ '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.22
diff -u -p -u -p -r1.22 node.tpl.php
--- modules/node/node.tpl.php 11 Sep 2009 06:48:03 -0000 1.22
+++ modules/node/node.tpl.php 25 Sep 2009 11:39:00 -0000
@@ -44,6 +44,15 @@
* teaser listings.
* - $id: Position of the node. Increments each time it's output.
*
+ * Admin links variables:
+ * - $has_attached_links: TRUE when the node is editable by the current user.
+ * - $attached_links: Already-themed links to the admin links pages for the node; may
+ * be empty.
+ * - $attached_links_text: An array of caption for the admin links of the node;
+ * may be empty.
+ * - $attached_links_info: An array of information describing the links to take
+ * action on the node; may be empty.
+ *
* Node status variables:
* - $build_mode: Build mode, e.g. 'full', 'teaser'...
* - $teaser: Flag for the teaser state (shortcut for $build_mode == 'teaser').
@@ -74,6 +83,10 @@
+
+
+
+
>
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.795
diff -u -p -u -p -r1.795 system.module
--- modules/system/system.module 22 Sep 2009 15:26:46 -0000 1.795
+++ modules/system/system.module 25 Sep 2009 11:39:00 -0000
@@ -460,6 +460,17 @@ function system_element_info() {
'#theme' => array('hidden'),
);
+ // Attached links
+ $types['attached_links'] = array(
+ '#theme' => 'links',
+ '#pre_render' => array('edit_mode_pre_render_links'),
+ '#attributes' => array('class' => 'attached-links'),
+ '#attached' => array(
+ 'js' => 'misc/attached_links.js',
+ 'css' => 'misc/attached_links.css',
+ ),
+ );
+
return $types;
}
@@ -503,6 +514,14 @@ function system_menu() {
'type' => MENU_CALLBACK,
'file' => 'system.admin.inc',
);
+
+ $items['edit-mode/toggle'] = array(
+ 'title callback' => 'edit_mode_toggle_title',
+ 'page callback' => 'edit_mode_toggle_page',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
$items['admin'] = array(
'title' => 'Administer',
'access arguments' => array('access administration pages'),
@@ -1306,6 +1325,13 @@ function blocked_ip_load($iid) {
}
/**
+ * Menu item title callback.
+ */
+function edit_mode_toggle_title() {
+ return empty($_SESSION['edit_mode']) ? t('Enable edit mode') : t('Disable edit mode');
+}
+
+/**
* Menu item access callback - only admin or enabled themes can be accessed.
*/
function _system_themes_access($theme) {
@@ -2686,6 +2712,20 @@ function system_timezone($abbreviation =
}
/**
+ * Menu callback; toggle the global edit mode and redirect.
+ */
+function edit_mode_toggle_page() {
+ if (!empty($_SESSION['edit_mode'])) {
+ unset($_SESSION['edit_mode']);
+ }
+ else {
+ $_SESSION['edit_mode'] = 1;
+ }
+ $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
+ drupal_goto($referer);
+}
+
+/**
* Format the Powered by Drupal text.
*
* @ingroup themeable
@@ -2885,3 +2925,68 @@ function theme_system_run_cron_image($im
return '';
}
+/**
+ * Pre-render function for attached_links.
+ */
+function edit_mode_pre_render_links(&$element) {
+ if (empty($_SESSION['edit_mode']) || empty($element['#links'])) {
+ // Remove this element when not in edit mode.
+ $element = array();
+ return;
+ }
+
+ $has_links = FALSE;
+ foreach ($element['#links'] as $key => $data) {
+ if (!isset($data['access']) && function_exists('menu_get_item')) {
+ $item = menu_get_item($data['href']);
+ $element['#links'][$key]['access'] = !empty($item['access']);
+ }
+ $has_links = $has_links || !empty($element['#links'][$key]['access']);
+ }
+
+ // Do not show edit links when the user is already on the page that is
+ // being linked to.
+ $show_links = FALSE;
+ if ($has_links && isset($_GET['q'])) {
+ foreach ($element['#links'] as $key => $data) {
+ $show_links = $show_links || ($data['href'] != $_GET['q']);
+ }
+ }
+
+ /*// Add appropriate CSS classes that define this element as editable and
+ // associate it with its edit link.
+ // @todo expose $classes_array as actual variables.
+ if ($has_links) {
+ $variables['classes_array'][] = 'attached-links-enabled-region';
+ foreach ($links as $key => $data) {
+ $variables['classes_array'][] = 'attached-links-enabled-at-' . str_replace('/', '-', $data['href']);
+ }
+ }*/
+
+ if ($show_links) {
+ $text_items = array();
+ $info_items = array();
+ foreach ($element['#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["attached-links-link attached-links-$key-link attached-links-at-" . str_replace('/', '-', $data['href'])] = 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' => 'attached-links-link attached-links-link-' . $key,
+ 'title' => $text_items[$key],
+ ),
+ );
+ }
+ // Replace the links data with the recomputed items.
+ $element['#links'] = $info_items;
+ }
+ else {
+ $element['#links'] = array();
+ }
+
+}
Index: modules/toolbar/toolbar.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.install,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 toolbar.install
--- modules/toolbar/toolbar.install 31 Aug 2009 17:09:01 -0000 1.4
+++ modules/toolbar/toolbar.install 25 Sep 2009 11:39:00 -0000
@@ -28,7 +28,10 @@ function toolbar_install() {
'node/add' => 'Add content',
'admin/content' => 'Find content',
'admin' => 'Dashboard',
+ // No title, so the title callback prevails.
+ 'edit-mode/toggle' => '',
);
+
$weight = -20;
foreach ($items as $path => $title) {
$link = array(
@@ -37,7 +40,11 @@ function toolbar_install() {
'link_path' => $path,
'router_path' => $path,
'menu_name' => 'admin_shortcuts',
- 'module' => 'menu',
+ // Saved as links with the toolbar module which makes them impossible
+ // to delete on the user interface. This is important for items like
+ // the edit toggle which cannot be figured out easily from elsewhere.
+ // The items can be disabled still of course.
+ 'module' => 'toolbar',
'weight' => $weight,
);
Index: modules/toolbar/toolbar.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.module,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 toolbar.module
--- modules/toolbar/toolbar.module 15 Sep 2009 20:50:48 -0000 1.13
+++ modules/toolbar/toolbar.module 25 Sep 2009 11:39:00 -0000
@@ -151,8 +151,12 @@ function toolbar_menu_navigation_links($
$link['href'] = $item['link']['href'];
// Add icon placeholder.
$link['title'] = '' . $item['link']['title'];
- // Add admin link ID and to-overlay class for the overlay.
- $link['attributes'] = array('id' => 'toolbar-link-' . $id, 'class' => array('to-overlay'));
+ $classes = array();
+ if ($link['href'] != 'edit-mode/toggle') {
+ $classes = array('class' => array('to-overlay'));
+ }
+ // Add admin link ID and optionaly the to-overlay class for the overlay.
+ $link['attributes'] = array('id' => 'toolbar-link-' . $id) + $classes;
$link['html'] = TRUE;
$class = ' path-' . $id;
Index: themes/garland/block.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/block.tpl.php,v
retrieving revision 1.9
diff -u -p -u -p -r1.9 block.tpl.php
--- themes/garland/block.tpl.php 11 Sep 2009 06:48:03 -0000 1.9
+++ themes/garland/block.tpl.php 25 Sep 2009 11:39:00 -0000
@@ -3,6 +3,10 @@
?>