Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.961
diff -u -r1.961 common.inc
--- includes/common.inc	15 Aug 2009 06:20:20 -0000	1.961
+++ includes/common.inc	15 Aug 2009 11:16:16 -0000
@@ -3940,6 +3940,18 @@
     $elements['#theme'] = 'markup';
   }
 
+  // If there is are action 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['#actions'])) {
+    foreach ($elements['#actions'] as $key => $data) {
+      if (!isset($data['access']) && drupal_function_exists('menu_get_item')) {
+        $item = menu_get_item($data['href']);
+        $elements['#actions'][$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.
@@ -4247,6 +4259,9 @@
     'links' => array(
       'arguments' => array('links' => NULL, 'attributes' => array('class' => 'links')),
     ),
+    'action_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.333
diff -u -r1.333 menu.inc
--- includes/menu.inc	11 Aug 2009 17:26:33 -0000	1.333
+++ includes/menu.inc	15 Aug 2009 11:12:58 -0000
@@ -766,7 +766,7 @@
 }
 
 /**
- * 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'
@@ -775,28 +775,30 @@
  * @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_unrendered($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.
  *
  * @param $tree
  *   A data structure representing the tree as returned from menu_tree_data.
  * @return
- *   The rendered HTML of that data structure.
+ *  A structured array representing the menu, suitable for rendering with
+ *  drupal_render().
  */
-function menu_tree_output($tree) {
-  $output = '';
+function menu_tree_unrendered($tree) {
+  $content = '';
   $items = array();
 
   // Pull out just the menu items we are going to render so that we
@@ -819,13 +821,28 @@
     $extra_class = implode(' ', $extra_class);
     $link = theme('menu_item_link', $data['link']);
     if ($data['below']) {
-      $output .= theme('menu_item', $link, $data['link']['has_children'], menu_tree_output($data['below']), $data['link']['in_active_trail'], $extra_class);
+      $content .= theme('menu_item', $link, $data['link']['has_children'], menu_tree_output($data['below']), $data['link']['in_active_trail'], $extra_class);
     }
     else {
-      $output .= theme('menu_item', $link, $data['link']['has_children'], '', $data['link']['in_active_trail'], $extra_class);
+      $content .= theme('menu_item', $link, $data['link']['has_children'], '', $data['link']['in_active_trail'], $extra_class);
     }
   }
-  return $output ? theme('menu_tree', $output) : '';
+  return array(
+    '#theme' => 'menu_tree',
+    '#menu' => $content,
+  );
+}
+
+/**
+ * Returns a rendered menu tree.
+ *
+ * @param $tree
+ *   A data structure representing the tree as returned from menu_tree_data.
+ * @return
+ *   The rendered HTML of that data structure.
+ */
+function menu_tree_output($tree) {
+  return drupal_render(menu_tree_unrendered($tree));
 }
 
 /**
@@ -1258,7 +1275,7 @@
  * @ingroup themeable
  */
 function theme_menu_tree($tree) {
-  return '<ul class="menu">' . $tree . '</ul>';
+  return !empty($tree['#menu']) ? '<ul class="menu">' . $tree['#menu'] . '</ul>' : '';
 }
 
 /**
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.504
diff -u -r1.504 theme.inc
--- includes/theme.inc	13 Aug 2009 03:05:54 -0000	1.504
+++ includes/theme.inc	15 Aug 2009 11:12:55 -0000
@@ -740,16 +740,22 @@
   }
   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;
         $count++;
       }
     }
+    // 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';
@@ -778,7 +784,12 @@
     foreach (array('preprocess functions', 'process functions') as $template_phase) {
       foreach ($info[$template_phase] as $template_function) {
         if (drupal_function_exists($template_function)) {
-          call_user_func_array($template_function, $args);
+          $result = call_user_func_array($template_function, $args);
+          // Allow process functions to abort rendering the item by returning
+          // FALSE.
+          if ($result === FALSE) {
+            return '';
+          }
         }
       }
     }
@@ -1322,6 +1333,36 @@
 }
 
 /**
+ * Return themed action links for an element on a page.
+ *
+ * @param $links
+ *   An array representing the action 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_action_links($links, $element) {
+  if (empty($_SESSION['edit_mode'])) {
+    return;
+  }
+  // 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 = "actions-link actions-$key-link actions-at-" . str_replace('/', '-', $link['href']);
+    $links_array[$class] = $link;
+  }
+  drupal_add_js('misc/actions.js');
+  drupal_add_css('misc/actions.css');
+  return theme('links', $links_array, array('class' => 'action-links'));
+}
+
+/**
  * Return a themed image.
  *
  * @param $path
@@ -1839,6 +1880,72 @@
   // Initialize html class attribute for the current hook.
   $variables['classes_array'] = array($hook);
 
+  // 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_actions'] = FALSE;
+  if (is_array($element) && isset($element['#actions'])) {
+    foreach ($element['#actions'] as $key => $data) {
+      $variables['has_actions'] = $variables['has_actions'] || !empty($data['access']);
+    }
+  }
+  // Do not show edit links when the user is already on the page that is being
+  // linked to.
+  $show_action_links = FALSE;
+  if ($variables['has_actions'] && isset($_GET['q'])) {
+    foreach ($element['#actions'] as $key => $data) {
+      $show_action_links = $show_action_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_actions']) {
+    $variables['classes_array'][] = 'actions-enabled-region';
+    foreach ($element['#actions'] as $key => $data) {
+      $variables['classes_array'][] = 'actions-enabled-at-' . str_replace('/', '-', $data['href']);
+    }
+  }
+  if ($show_action_links) {
+    $available_icons = module_invoke_all('action_links_icons');
+    $text_items = array();
+    $info_items = array();
+    foreach ($element['#actions'] as $key => $data) {
+      if (isset($available_icons[$key])) {
+        $title = isset($data['title']) ? $data['title'] : '';
+        $text_items[$key] = theme('image', $available_icons[$key]['path'], $available_icons[$key]['alt'], $title, array('class' => 'action-link-icon action-link-icon-' . $key));
+        $info_items[$key] = array(
+          'title' => $text_items[$key],
+          'href' => $data['href'],
+          // HTML should always be on for icons, which are images.
+          'html' => TRUE,
+          // Refer users back to the current page after they have completed
+          // their edits.
+          'query' => drupal_get_destination(),
+        );
+      }
+      else {
+        $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' => $text_items[$key],
+          'href' => $data['href'],
+          'html' => isset($data['html']) ? $data['html'] : FALSE,
+          // Refer users back to the current page after they have completed
+          // their edits.
+          'query' => drupal_get_destination(),
+        );
+      }
+    }
+    $variables['action_links_text'] = $text_items;
+    $variables['action_links_info'] = $info_items;
+  }
+  else {
+    $variables['action_links_text'] = array();
+    $variables['action_links_info'] = array();
+  }
+  $variables['action_links'] = $show_action_links ? theme('action_links', $variables['action_links_info'], $element) : '';
+
   // Set default variables that depend on the database.
   $variables['is_admin']            = FALSE;
   $variables['is_front']            = FALSE;
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.359
diff -u -r1.359 block.module
--- modules/block/block.module	15 Aug 2009 06:50:29 -0000	1.359
+++ modules/block/block.module	15 Aug 2009 11:27:46 -0000
@@ -278,8 +278,20 @@
       unset($block->content);
       $build[$key] += array(
         '#block' => $block,
+        '#actions' => array(
+          'configure' => array(
+            'href' => 'admin/structure/block/configure/' . $block->module . '/' . $block->delta,
+            'title' => t('Configure this block'),
+          ),
+        ),
         '#weight' => ++$weight,
       );
+      // Special exception for the main system content block: it's weird and
+      // confusing to show action links for it - what exactly is the user
+      // configuring?
+      if ($block->module == 'system' && $block->delta == 'main') {
+        unset($build[$key]['#actions']['configure']);
+      }
       $build[$key]['#theme_wrappers'][] ='block';
     }
     $build['#sorted'] = TRUE;
@@ -833,6 +845,22 @@
 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;
@@ -841,9 +869,6 @@
   $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.3
diff -u -r1.3 block.tpl.php
--- modules/block/block.tpl.php	12 Jun 2009 09:02:55 -0000	1.3
+++ modules/block/block.tpl.php	15 Aug 2009 11:12:51 -0000
@@ -30,12 +30,26 @@
  * - $logged_in: Flags true when the current user is a logged-in member.
  * - $is_admin: Flags true when the current user is an administrator.
  *
+ * Action variables:
+ * - $has_actions: TRUE when the block is editable by the current user.
+ * - $action_links: Already-themed link(s) for actions that may be taken on the
+ *   block; may be empty.
+ * - $action_links_text: An array of captions for the links to take action on
+ *   the block; may be empty.
+ * - $action_links_info: An array of information describing the links to take
+ *   action on the block; may be empty.
+ *
  * @see template_preprocess()
  * @see template_preprocess_block()
  * @see template_process()
  */
 ?>
 <div id="block-<?php print $block->module . '-' . $block->delta; ?>" class="<?php print $classes; ?>">
+
+<?php if ($action_links): ?>
+  <?php print $action_links; ?>
+<?php endif; ?>
+
 <?php if ($block->subject): ?>
   <h2><?php print $block->subject ?></h2>
 <?php endif;?>
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.195
diff -u -r1.195 menu.module
--- modules/menu/menu.module	20 Jul 2009 18:51:33 -0000	1.195
+++ modules/menu/menu.module	15 Aug 2009 11:12:46 -0000
@@ -147,6 +147,10 @@
       'file' => 'menu.admin.inc',
       'arguments' => array('title' => NULL, 'name' => NULL, 'description' => NULL),
     ),
+    'menu_editable' => array(
+      'arguments' => array('element' => NULL),
+      'template' => 'menu-editable',
+    ),
   );
 }
 
@@ -291,6 +295,32 @@
 }
 
 /**
+ * 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]['content']['#theme'])) {
+        $block = $blocks[$id]['#block'];
+        $content = &$blocks[$id]['content'];
+        if ($content['#theme'] == 'menu_tree' && !empty($content['#menu'])) {
+          // 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['#actions']['edit'] = array(
+            'href' => 'admin/structure/menu-customize/' . $block->delta,
+            'title' => t('Edit this menu'),
+          );
+        }
+      }
+    }
+  }
+}
+
+/**
  * Implement hook_node_insert().
  */
 function menu_node_insert($node) {
@@ -486,3 +516,11 @@
 
   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.1099
diff -u -r1.1099 node.module
--- modules/node/node.module	14 Aug 2009 13:53:01 -0000	1.1099
+++ modules/node/node.module	15 Aug 2009 11:12:39 -0000
@@ -1094,6 +1094,16 @@
   $build += array(
     '#theme' => 'node',
     '#node' => $node,
+    '#actions' => array(
+      'edit' => array(
+        'href' => 'node/' . $node->nid . '/edit',
+        'title' => t('Edit this @type', array('@type' => drupal_strtolower(node_type_get_name($node)))),
+      ),
+      'delete' => array(
+        'href' => 'node/' . $node->nid . '/delete',
+        'title' => t('Delete this @type', array('@type' => drupal_strtolower(node_type_get_name($node)))),
+      ),
+    ),
     '#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.19
diff -u -r1.19 node.tpl.php
--- modules/node/node.tpl.php	6 Aug 2009 05:05:59 -0000	1.19
+++ modules/node/node.tpl.php	15 Aug 2009 11:12:36 -0000
@@ -45,6 +45,15 @@
  *   teaser listings.
  * - $id: Position of the node. Increments each time it's output.
  *
+ * Action links variables:
+ * - $has_actions: TRUE when the node is editable by the current user.
+ * - $action_links: Already-themed links to the actioni pages for the node; may
+ *   be empty.
+ * - $action_links_text: An array of caption for the action links of the node;
+ *   may be empty.
+ * - $action_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').
@@ -68,6 +77,10 @@
 
   <?php print $user_picture; ?>
 
+  <?php if (!$page && $action_links): ?>
+    <?php print $action_links; ?>
+  <?php endif; ?>
+
   <?php if (!$page): ?>
     <h2><a href="<?php print $node_url; ?>"><?php print $title; ?></a></h2>
   <?php endif; ?>
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.61
diff -u -r1.61 system.api.php
--- modules/system/system.api.php	12 Aug 2009 12:36:04 -0000	1.61
+++ modules/system/system.api.php	15 Aug 2009 11:12:33 -0000
@@ -12,6 +12,24 @@
  */
 
 /**
+ * Register icons to be used for the action links on in-page items; core
+ * registers "configure", "delete", and "edit".
+ *
+ * @return
+ *   An array of icons to be used for the action links on inline items; each
+ *   icon must specify a 'path', the path to the image, and an 'alt', the text
+ *   to display if the image cannot be.
+ */
+function hook_action_links_icons() {
+  $icons['rearrange'] = array(
+    'path' => drupal_get_path('module', 'hook') . '/rearrange.png',
+    'alt'  => t('Rearrange'),
+  );
+
+  return $icons;
+}
+
+/**
  * Perform periodic actions.
  *
  * Modules that require to schedule some commands to be executed at regular
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.747
diff -u -r1.747 system.module
--- modules/system/system.module	15 Aug 2009 06:26:28 -0000	1.747
+++ modules/system/system.module	15 Aug 2009 11:35:49 -0000
@@ -255,6 +255,28 @@
 }
 
 /**
+ * Implement hook_action_links_icons().
+ */
+function system_action_links_icons() {
+  $icons['configure'] = array(
+    'path' => 'misc/configure.png',
+    'alt'  => t('Configure'),
+  );
+
+  $icons['delete'] = array(
+    'path' => 'misc/delete.png',
+    'alt' => t('Delete'),
+  );
+
+  $icons['edit'] = array(
+    'path' => 'misc/edit.png',
+    'alt'  => t('Edit'),
+  );
+
+  return $icons;
+}
+
+/**
  * Implement hook_elements().
  */
 function system_elements() {
@@ -488,6 +510,12 @@
     'access callback' => TRUE,
     'type' => MENU_CALLBACK,
   );
+  $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'),
@@ -1188,6 +1216,13 @@
 }
 
 /**
+ * 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) {
@@ -2931,6 +2966,20 @@
 }
 
 /**
+ * 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'] : '<front>';
+  drupal_goto($referer);
+}
+
+/**
  * Format the Powered by Drupal text.
  *
  * @ingroup themeable
Index: modules/toolbar/toolbar.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.install,v
retrieving revision 1.3
diff -u -r1.3 toolbar.install
--- modules/toolbar/toolbar.install	30 Jul 2009 19:24:21 -0000	1.3
+++ modules/toolbar/toolbar.install	15 Aug 2009 11:12:30 -0000
@@ -28,7 +28,14 @@
     'node/add' => 'Add',
     'admin/content' => 'Find content',
     'admin' => 'Dashboard',
+    'edit-mode/toggle' => '',
   );
+
+  db_delete('menu_links')
+    ->condition('link_path', 'edit-mode/toggle')
+    ->condition('menu_name', 'main-menu')
+    ->execute();
+
   $weight = -20;
   foreach ($items as $path => $title) {
     $link = array(
Index: profiles/default/default.profile
===================================================================
RCS file: /cvs/drupal/drupal/profiles/default/default.profile,v
retrieving revision 1.62
diff -u -r1.62 default.profile
--- profiles/default/default.profile	3 Aug 2009 03:04:34 -0000	1.62
+++ profiles/default/default.profile	15 Aug 2009 11:12:28 -0000
@@ -216,8 +216,12 @@
   menu_rebuild();
 
   // Save some default links.
-  $link = array('link_path' => 'admin/structure/menu-customize/main-menu/add', 'link_title' => 'Add a main menu link', 'menu_name' => 'main-menu');
-  menu_link_save($link);
+  $links = array();
+  $links[] = array('link_path' => 'edit-mode/toggle', 'link_title' => '', 'menu_name' => 'main-menu');
+  $links[] = array('link_path' => 'admin/structure/menu-customize/main-menu/add', 'link_title' => 'Add a main menu link', 'menu_name' => 'main-menu');
+  foreach ($links as $link) {
+    menu_link_save($link);
+  }
 
   // Enable the admin theme.
   db_update('system')
Index: themes/garland/block.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/block.tpl.php,v
retrieving revision 1.7
diff -u -r1.7 block.tpl.php
--- themes/garland/block.tpl.php	12 Jun 2009 09:02:55 -0000	1.7
+++ themes/garland/block.tpl.php	15 Aug 2009 11:12:25 -0000
@@ -3,9 +3,14 @@
 ?>
 <div id="block-<?php print $block->module . '-' . $block->delta; ?>" class="<?php print $classes; ?> clearfix">
 
+<?php if ($action_links): ?>
+  <?php print $action_links; ?>
+<?php endif; ?>
+
 <?php if (!empty($block->subject)): ?>
   <h2><?php print $block->subject ?></h2>
 <?php endif;?>
 
   <div class="content"><?php print $content ?></div>
+
 </div>
Index: themes/garland/node.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/node.tpl.php,v
retrieving revision 1.14
diff -u -r1.14 node.tpl.php
--- themes/garland/node.tpl.php	6 Aug 2009 05:06:00 -0000	1.14
+++ themes/garland/node.tpl.php	15 Aug 2009 11:12:21 -0000
@@ -3,6 +3,10 @@
 ?>
 <div id="node-<?php print $node->nid; ?>" class="<?php print $classes; ?>">
 
+  <?php if (!$page && $action_links): ?>
+    <?php print $action_links; ?>
+  <?php endif; ?>
+
   <?php print $user_picture; ?>
 
   <?php if (!$page): ?>
Index: themes/garland/style.css
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/style.css,v
retrieving revision 1.62
diff -u -r1.62 style.css
--- themes/garland/style.css	6 Aug 2009 05:06:00 -0000	1.62
+++ themes/garland/style.css	15 Aug 2009 11:12:15 -0000
@@ -84,7 +84,7 @@
   background: #5294c1;
 }
 
-ul, .block ul, ol {
+ul, .block ul.menu, ol {
   margin: 0.5em 0 1em;
   padding: 0 0 0 1.5em; /* LTR */
 }
@@ -290,6 +290,10 @@
   margin-bottom: 2.5em;
 }
 
+.edit-link {
+  font-size: 0.8em;
+}
+
 /**
  * Layout
  */
@@ -809,6 +813,21 @@
 }
 
 /**
+ * Edit icons.
+ */
+.active-actions-region {
+  outline: solid #027AC6 2px;
+}
+
+ul.action-links li a img {
+  background-color: #027AC6;
+}
+
+ul.action-links li a:hover img {
+  background-color: #0062A0;
+}
+
+/**
  * Collapsible fieldsets
  */
 fieldset {
--- misc/actions.css
+++ misc/actions.css
@@ -0,0 +1,37 @@
+/* $Id$ */
+
+/*
+** Highlighted regions for actions.js
+*/
+.active-actions-region {
+  outline: solid #000000 2px;
+}
+
+.actions-enabled-region {
+  position: relative;
+}
+
+ul.action-links {
+  position: absolute;
+  right: 0;
+  top: 0;
+  padding: 0;
+  margin: 0 4px 0 0;
+}
+
+ul.action-links li {
+  padding: 0;
+  margin: 0;
+  list-style: none;
+  display: inline;
+}
+
+ul.action-links li a img {
+  background-color: #000000;
+  margin-right: -4px;
+  padding: 1px;
+}
+
+ul.action-links li a:hover img {
+  background-color: #404040;
+}

--- misc/actions.js
+++ misc/actions.js
@@ -0,0 +1,27 @@
+// $Id$
+(function ($) {
+
+/**
+ * Highlights the actions 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 match = $('.actions-enabled-at-' + matches[1]);
+        match.addClass('active-actions-region');
+        $(this).addClass('active-actions-link');
+      }
+    };
+
+    var removeHighlight = function () {
+      $('.active-actions-region').removeClass('active-actions-region');
+      $('.active-actions-link').removeClass('active-actions-link');
+    };
+
+    $('.action-links li').hover(addHighlight, removeHighlight);
+  }
+};
+
+})(jQuery);

--- modules/menu/menu-editable.tpl.php
+++ modules/menu/menu-editable.tpl.php
@@ -0,0 +1,23 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Default theme implementation to display an editable menu.
+ *
+ * Available variables:
+ * - $content: Rendered menu.
+ * - $classes: String of classes to be used.
+ * - $action_links: Links to actions that can be taken on this menu, such as
+ *   "edit".
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_menu_editable()
+ * @see template_process()
+ */
+?>
+<div class="<?php print $classes; ?>">
+  <?php print $action_links; ?>
+
+  <?php print $content; ?>
+</div>


