Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.357
diff -u -p -r1.357 menu.inc
--- includes/menu.inc	17 Oct 2009 11:39:15 -0000	1.357
+++ includes/menu.inc	1 Nov 2009 01:10:19 -0000
@@ -1796,16 +1796,26 @@ function menu_local_tasks($level = 0) {
  * If the path "node/123" is passed to this function, then it will return the
  * links for 'edit' and 'report-as-spam'.
  *
+ * @param $module
+ *   The name of the implementing module. This is used to prefix the key for
+ *   each contextual link, which is transformed into a CSS class during
+ *   rendering by theme_links(). For example, if $module is 'block' and the
+ *   retrieved local task path argument is 'edit', then the resulting CSS class
+ *   will be 'block-edit'.
  * @param $path
- *   The menu router path of the object to retrieve local tasks for, for example
- *   "node/123" or "admin/structure/menu/manage/[menu_name]".
+ *   The static menu router path of the object to retrieve local tasks for, for
+ *   example 'node' or 'admin/structure/block/manage'.
+ * @param $args
+ *   A list of of dynamic path arguments to append to $path to form the fully-
+ *   qualified menu router path, for example array(123) for a certain node or
+ *   array('system', 'navigation') for a certain block.
  *
  * @return
  *   A list of menu router items that are local tasks for the passed in path.
  *
  * @see system_preprocess()
  */
-function menu_contextual_links($parent_path, $args) {
+function menu_contextual_links($module, $parent_path, $args) {
   static $path_empty = array();
 
   $links = array();
@@ -1851,12 +1861,9 @@ function menu_contextual_links($parent_p
     if (!$item['access']) {
       continue;
     }
-    // All contextual links are keyed by the actual "task" path argument. The
-    // menu system does not allow for two local tasks with the same name, and
-    // since the key is also used as CSS class for the link item, which may be
-    // styled as icon, it wouldn't make sense to display the same icon for
-    // different tasks.
-    $links[$key] = $item;
+    // All contextual links are keyed by the actual "task" path argument,
+    // prefixed with the name of implementing module.
+    $links[$module . '-' . $key] = $item;
   }
 
   // Allow modules to alter contextual links.
Index: includes/update.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/update.inc,v
retrieving revision 1.18
diff -u -p -r1.18 update.inc
--- includes/update.inc	24 Oct 2009 23:19:21 -0000	1.18
+++ includes/update.inc	1 Nov 2009 00:17:57 -0000
@@ -140,6 +140,8 @@ function update_fix_d7_requirements() {
       'not null' => TRUE,
       'default' => 0,
     ));
+    db_drop_index('menu_router', 'tab_parent');
+    db_add_index('menu_router', 'tab_parent', array(array('tab_parent', 64), 'weight', 'title'));
     db_add_field('menu_router', 'theme_callback', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
     db_add_field('menu_router', 'theme_arguments', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
 
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.394
diff -u -p -r1.394 block.module
--- modules/block/block.module	23 Oct 2009 22:24:11 -0000	1.394
+++ modules/block/block.module	1 Nov 2009 00:17:57 -0000
@@ -283,7 +283,7 @@ function _block_get_renderable_array($li
 
     // Add contextual links for this block; skipping the system main block.
     if ($key != 'system_main') {
-      $build[$key]['#contextual_links']['block'] = menu_contextual_links('admin/structure/block/manage', array($block->module, $block->delta));
+      $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($block->module, $block->delta));
     }
 
     $build[$key] += array(
Index: modules/comment/comment.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.api.php,v
retrieving revision 1.12
diff -u -p -r1.12 comment.api.php
--- modules/comment/comment.api.php	10 Oct 2009 13:37:09 -0000	1.12
+++ modules/comment/comment.api.php	1 Nov 2009 00:17:57 -0000
@@ -73,6 +73,34 @@ function hook_comment_view($comment) {
 }
 
 /**
+ * The comment was built, the module may modify the structured content.
+ *
+ * This hook is called after the content has been assembled in a structured array
+ * and may be used for doing processing which requires that the complete comment
+ * content structure has been built.
+ *
+ * If the module wishes to act on the rendered HTML of the comment rather than the
+ * structured content array, it may use this hook to add a #post_render callback.
+ * Alternatively, it could also implement hook_preprocess_comment(). See
+ * drupal_render() and theme() documentation respectively for details.
+ *
+ * @param $build
+ *   A renderable array representing the comment.
+ *
+ * @see comment_build()
+ */
+function hook_comment_build_alter($build) {
+  // Check for the existence of a field added by another module.
+  if ($build['#build_mode'] == 'full' && isset($build['an_additional_field'])) {
+    // Change its weight.
+    $build['an_additional_field']['#weight'] = -10;
+  }
+
+  // Add a #post_render callback to act on the rendered HTML of the comment.
+  $build['#post_render'][] = 'my_module_comment_post_render';
+}
+
+/**
  * The comment is being published by the moderator.
  *
  * @param $comment
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.796
diff -u -p -r1.796 comment.module
--- modules/comment/comment.module	23 Oct 2009 22:24:12 -0000	1.796
+++ modules/comment/comment.module	1 Nov 2009 00:17:57 -0000
@@ -797,8 +797,6 @@ function comment_build($comment, $node, 
     '#node' => $node,
     '#build_mode' => $build_mode,
   );
-  // Add contextual links for this comment.
-  $build['#contextual_links']['comment'] = menu_contextual_links('comment', array($comment->cid));
 
   $prefix = '';
   $is_threaded = isset($comment->divs) && variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED) == COMMENT_MODE_THREADED;
@@ -822,6 +820,9 @@ function comment_build($comment, $node, 
     $build['#suffix'] = str_repeat('</div>', $comment->divs_final);
   }
 
+  // Allow modules to modify the structured comment.
+  drupal_alter('comment_build', $build);
+
   return $build;
 }
 
@@ -861,9 +862,6 @@ function comment_build_content($comment,
 
   // Allow modules to make their own additions to the comment.
   module_invoke_all('comment_view', $comment, $build_mode);
-
-  // Allow modules to modify the structured comment.
-  drupal_alter('comment_build', $comment, $build_mode);
 }
 
 /**
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.213
diff -u -p -r1.213 menu.module
--- modules/menu/menu.module	23 Oct 2009 22:24:16 -0000	1.213
+++ modules/menu/menu.module	1 Nov 2009 00:17:57 -0000
@@ -427,7 +427,7 @@ function menu_block_view($delta = '') {
   $data['content'] = menu_tree($delta);
   // Add contextual links for this block.
   if (!empty($data['content'])) {
-    $data['content']['#contextual_links']['menu'] = menu_contextual_links('admin/structure/menu/manage', array($delta));
+    $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($delta));
   }
   return $data;
 }
@@ -440,7 +440,7 @@ function menu_block_view_alter(&$data, $
   if ($block->module == 'system' && !empty($data['content'])) {
     $system_menus = menu_list_system_menus();
     if (isset($system_menus[$block->delta])) {
-      $data['content']['#contextual_links']['menu'] = menu_contextual_links('admin/structure/menu/manage', array($block->delta));
+      $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($block->delta));
     }
   }
 }
Index: modules/node/node.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.api.php,v
retrieving revision 1.41
diff -u -p -r1.41 node.api.php
--- modules/node/node.api.php	24 Oct 2009 11:49:52 -0000	1.41
+++ modules/node/node.api.php	1 Nov 2009 00:17:57 -0000
@@ -542,7 +542,7 @@ function hook_node_view($node, $build_mo
 /**
  * The node content was built, the module may modify the structured content.
  *
- * This hook is called after the content has been assembled in $node->content
+ * This hook is called after the content has been assembled in a structured array
  * and may be used for doing processing which requires that the complete node
  * content structure has been built.
  *
@@ -551,20 +551,20 @@ function hook_node_view($node, $build_mo
  * Alternatively, it could also implement hook_preprocess_node(). See
  * drupal_render() and theme() documentation respectively for details.
  *
- * @param $node
- *   The node the action is being performed on.
- * @param $build_mode
- *   The $build_mode parameter from node_build().
+ * @param $build
+ *   A renderable array representing the node content.
+ *
+ * @see node_build()
  */
-function hook_node_build_alter($node, $build_mode) {
+function hook_node_build_alter($build) {
   // Check for the existence of a field added by another module.
-  if (isset($node->content['an_additional_field'])) {
+  if ($build['#build_mode'] == 'full' && isset($build['an_additional_field'])) {
     // Change its weight.
-    $node->content['an_additional_field']['#weight'] = -10;
+    $build['an_additional_field']['#weight'] = -10;
   }
 
   // Add a #post_render callback to act on the rendered HTML of the node.
-  $node->content['#post_render'][] = 'my_module_node_post_render';
+  $build['#post_render'][] = 'my_module_node_post_render';
 }
 
 /**
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1156
diff -u -p -r1.1156 node.module
--- modules/node/node.module	31 Oct 2009 17:57:49 -0000	1.1156
+++ modules/node/node.module	1 Nov 2009 00:17:57 -0000
@@ -1148,7 +1148,10 @@ function node_build($node, $build_mode =
     '#build_mode' => $build_mode,
   );
   // Add contextual links for this node.
-  $build['#contextual_links']['node'] = menu_contextual_links('node', array($node->nid));
+  $build['#contextual_links']['node'] = array('node', array($node->nid));
+
+  // Allow modules to modify the structured node.
+  drupal_alter('node_build', $build);
 
   return $build;
 }
@@ -1210,9 +1213,6 @@ function node_build_content($node, $buil
 
   // Allow modules to make their own additions to the node.
   module_invoke_all('node_view', $node, $build_mode);
-
-  // Allow modules to modify the structured node.
-  drupal_alter('node_build', $node, $build_mode);
 }
 
 /**
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.415
diff -u -p -r1.415 system.install
--- modules/system/system.install	24 Oct 2009 23:19:21 -0000	1.415
+++ modules/system/system.install	1 Nov 2009 00:17:57 -0000
@@ -1097,7 +1097,7 @@ function system_schema() {
     ),
     'indexes' => array(
       'fit' => array('fit'),
-      'tab_parent' => array('tab_parent'),
+      'tab_parent' => array(array('tab_parent', 64), 'weight', 'title'),
       'tab_root_weight_title' => array(array('tab_root', 64), 'weight', 'title'),
     ),
     'primary key' => array('path'),
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.829
diff -u -p -r1.829 system.module
--- modules/system/system.module	30 Oct 2009 22:33:35 -0000	1.829
+++ modules/system/system.module	1 Nov 2009 01:10:35 -0000
@@ -225,6 +225,10 @@ function system_permission() {
       'title' => t('Access administration pages'),
       'description' => t('View the administration panel and browse the help system.'),
     ),
+    'access contextual links' => array(
+      'title' => t('Access contextual links'),
+      'description' => t('Use administrative links associated with items on a page.'),
+    ),
     'access site in maintenance mode' => array(
       'title' => t('Access site in maintenance mode'),
       'description' => t('Log in when the site is in maintenance mode.'),
@@ -3491,17 +3495,24 @@ function theme_confirm_form($variables) 
 
 /**
  * Template variable preprocessor for contextual links.
+ *
+ * @see system_build_contextual_links()
  */
 function system_preprocess(&$variables, $hook) {
   static $hooks;
 
+  // Initialize contextual links template variable.
+  $variables['contextual_links'] = array();
+
+  // Nothing to do here if the user is not permitted to access contextual links.
+  if (!user_access('access contextual links')) {
+    return;
+  }
+
   if (!isset($hooks)) {
     $hooks = theme_get_registry();
   }
 
-  // Initialize contextual links template variable.
-  $variables['contextual_links'] = array();
-
   // Determine the primary theme function argument.
   if (isset($hooks[$hook]['variables'])) {
     $keys = array_keys($hooks[$hook]['variables']);
@@ -3514,7 +3525,7 @@ function system_preprocess(&$variables, 
     $element = $variables[$key];
   }
 
-  if (isset($element) && is_array($element) && isset($element['#contextual_links'])) {
+  if (isset($element) && is_array($element) && !empty($element['#contextual_links'])) {
     $variables['contextual_links'] = system_build_contextual_links($element);
     if (!empty($variables['contextual_links'])) {
       $variables['classes_array'][] = 'contextual-links-region';
@@ -3526,21 +3537,32 @@ function system_preprocess(&$variables, 
  * Build a renderable array for contextual links.
  *
  * @param $element
- *   A renderable array containing a #contextual_links property.
+ *   A renderable array containing a #contextual_links property, which is a
+ *   keyed array. Each key is the name of the implementing module, and each
+ *   value is an array that forms the function arguments for
+ *   menu_contextual_links(). For example:
+ *   @code
+ *     array('#contextual_links' => array(
+ *       'block' => array('admin/structure/block/manage', array('system', 'navigation')),
+ *       'menu' => array('admin/structure/menu/manage', array('navigation')),
+ *     ))
+ *   @endcode
  *
  * @return
  *   A renderable array representing contextual links.
+ *
+ * @see menu_contextual_links()
  */
 function system_build_contextual_links($element) {
   static $destination;
 
-  // Transform contextual links into parameters suitable for theme_link().
-  $items = call_user_func_array('array_merge_recursive', $element['#contextual_links']);
-  $build = array();
-  if (empty($items)) {
-    return $build;
+  // Retrieve contextual menu links.
+  $items = array();
+  foreach ($element['#contextual_links'] as $module => $args) {
+    $items += menu_contextual_links($module, $args[0], $args[1]);
   }
 
+  // Transform contextual links into parameters suitable for theme_link().
   if (!isset($destination)) {
     $destination = drupal_get_destination();
   }
@@ -3559,6 +3581,7 @@ function system_build_contextual_links($
     $item['localized_options']['query'] += $destination;
     $links[$class] += $item['localized_options'];
   }
+  $build = array();
   if ($links) {
     $build = array(
       '#theme' => 'links',
Index: modules/user/user.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.api.php,v
retrieving revision 1.14
diff -u -p -r1.14 user.api.php
--- modules/user/user.api.php	10 Oct 2009 16:48:39 -0000	1.14
+++ modules/user/user.api.php	1 Nov 2009 00:52:35 -0000
@@ -327,6 +327,34 @@ function hook_user_view($account) {
 }
 
 /**
+ * The user was built, the module may modify the structured content.
+ *
+ * This hook is called after the content has been assembled in a structured array
+ * and may be used for doing processing which requires that the complete user
+ * content structure has been built.
+ *
+ * If the module wishes to act on the rendered HTML of the user rather than the
+ * structured content array, it may use this hook to add a #post_render callback.
+ * Alternatively, it could also implement hook_preprocess_user_profile(). See
+ * drupal_render() and theme() documentation respectively for details.
+ *
+ * @param $build
+ *   A renderable array representing the user.
+ *
+ * @see user_build()
+ */
+function hook_user_build_alter($build) {
+  // Check for the existence of a field added by another module.
+  if (isset($build['an_additional_field'])) {
+    // Change its weight.
+    $build['an_additional_field']['#weight'] = -10;
+  }
+
+  // Add a #post_render callback to act on the rendered HTML of the user.
+  $build['#post_render'][] = 'my_module_comment_post_render';
+}
+
+/**
  * Inform other modules that a user role has been added.
  *
  * Modules implementing this hook can act on the user role object when saved to
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1074
diff -u -p -r1.1074 user.module
--- modules/user/user.module	31 Oct 2009 18:04:01 -0000	1.1074
+++ modules/user/user.module	1 Nov 2009 00:48:14 -0000
@@ -2101,6 +2101,9 @@ function user_build($account) {
     '#account' => $account,
   );
 
+  // Allow modules to modify the structured user.
+  drupal_alter('user_build', $build);
+
   return $build;
 }
 
@@ -2109,7 +2112,6 @@ function user_build($account) {
  *
  * @param $account
  *   A user object.
- *
  */
 function user_build_content($account) {
   $account->content = array();
