Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1220
diff -u -p -r1.1220 common.inc
--- includes/common.inc	15 Sep 2010 04:34:26 -0000	1.1220
+++ includes/common.inc	15 Sep 2010 15:56:17 -0000
@@ -4943,6 +4943,25 @@ function drupal_pre_render_conditional_c
 }
 
 /**
+ * Pre-render function for rendering groupable elements together.
+ */
+function drupal_pre_render_groups($elements) {
+  // Go through each element, and ensure that when it is rendered, by default
+  // it will not have its own group wrapper. This allows the elements to be
+  // properly displayed together as a single group, assuming the functions they
+  // are themed with support this concept. Note that we only support altering
+  // one level of nesting in this array; any elements located deeper down will
+  // not be affected by this function.
+  foreach (element_children($elements) as $group) {
+    if (!isset($elements[$group]['#wrap_elements']) && empty($elements[$group]['#printed'])) {
+      $elements[$group]['#wrap_elements'] = FALSE;
+    }
+  }
+
+  return $elements;
+}
+
+/**
  * #pre_render callback to render a link into #markup.
  *
  * Doing so during pre_render gives modules a chance to alter the link parts.
@@ -5803,7 +5822,10 @@ function drupal_common_theme() {
       'variables' => array('text' => NULL, 'path' => NULL, 'options' => array()),
     ),
     'links' => array(
-      'variables' => array('links' => NULL, 'attributes' => array('class' => array('links')), 'heading' => array()),
+      'variables' => array('links' => NULL, 'attributes' => array('class' => array('links')), 'heading' => array(), 'children' => NULL, 'wrap_elements' => TRUE),
+    ),
+    'grouped_element' => array(
+      'render element' => 'element',
     ),
     'image' => array(
       // HTML 4 and XHTML 1.0 always require an alt attribute. The HTML 5 draft
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.611
diff -u -p -r1.611 theme.inc
--- includes/theme.inc	14 Sep 2010 21:42:05 -0000	1.611
+++ includes/theme.inc	15 Sep 2010 15:56:17 -0000
@@ -1395,17 +1395,32 @@ function theme_link($variables) {
  *     screen-reader and keyboard only users to navigate to or skip the links.
  *     See http://juicystudio.com/article/screen-readers-display-none.php
  *     and http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
+ *   - children: An optional string representing the rendered HTML of any
+ *     children to append to the end of the list of links.
+ *   - wrap_elements: A boolean indicating whether the provided links should be
+ *     wrapped so as to form a complete group. For example, in the default
+ *     implementation, in which the links are themed as an unordered list,
+ *     setting 'wrap_elements' to FALSE causes the concatenated <li> elements
+ *     to be displayed, but without being wrapped in <ul> tags, and with the
+ *     'attributes' and 'heading' elements of the variables array ignored.
+ *     Defaults to TRUE.
  */
 function theme_links($variables) {
   $links = $variables['links'];
   $attributes = $variables['attributes'];
   $heading = $variables['heading'];
+  $children = $variables['children'];
+  $wrap_elements = $variables['wrap_elements'];
   global $language_url;
   $output = '';
 
-  if (count($links) > 0) {
-    $output = '';
+  // Do not return anything if there are no links to display.
+  $num_links = count($links);
+  if ($num_links == 0 && empty($children)) {
+    return $output;
+  }
 
+  if ($wrap_elements) {
     // Treat the heading first if it is present to prepend it to the
     // list of links.
     if (!empty($heading)) {
@@ -1426,8 +1441,9 @@ function theme_links($variables) {
     }
 
     $output .= '<ul' . drupal_attributes($attributes) . '>';
+  }
 
-    $num_links = count($links);
+  if ($num_links) {
     $i = 1;
 
     foreach ($links as $key => $link) {
@@ -1465,7 +1481,14 @@ function theme_links($variables) {
       $i++;
       $output .= "</li>\n";
     }
+  }
 
+  // Append the HTML from any children to the end of the list of links.
+  if (!empty($children)) {
+    $output .= $children;
+  }
+
+  if ($wrap_elements) {
     $output .= '</ul>';
   }
 
@@ -1473,6 +1496,24 @@ function theme_links($variables) {
 }
 
 /**
+ * Returns HTML for an element whose children represent groups.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #theme_callback
+ */
+function theme_grouped_element($variables) {
+  $element = $variables['element'];
+
+  // First render any children, to pass along to the theme function.
+  $element['#children'] = drupal_render_children($element);
+
+  // Now return the themed result.
+  return theme($element['#theme_callback'], $element);
+}
+
+/**
  * Returns HTML for an image.
  *
  * @param $variables
Index: modules/blog/blog.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/blog/blog.module,v
retrieving revision 1.360
diff -u -p -r1.360 blog.module
--- modules/blog/blog.module	30 Aug 2010 05:58:46 -0000	1.360
+++ modules/blog/blog.module	15 Sep 2010 15:56:17 -0000
@@ -80,11 +80,16 @@ function blog_view($node, $view_mode) {
 function blog_node_view($node, $view_mode) {
   if ($view_mode != 'rss') {
     if ($node->type == 'blog' && (arg(0) != 'blog' || arg(1) != $node->uid)) {
-      $node->content['links']['#links']['blog_usernames_blog'] = array(
+      $links['blog_usernames_blog'] = array(
         'title' => t("!username's blog", array('!username' => format_username($node))),
         'href' => "blog/$node->uid",
         'attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => format_username($node)))),
       );
+      $node->content['links']['blog'] = array(
+        '#theme' => 'links__node__blog',
+        '#links' => $links,
+        '#attributes' => array('class' => array('links', 'inline')),
+      );
     }
   }
 }
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.552
diff -u -p -r1.552 book.module
--- modules/book/book.module	9 Sep 2010 23:01:48 -0000	1.552
+++ modules/book/book.module	15 Sep 2010 15:56:17 -0000
@@ -112,7 +112,11 @@ function book_node_view_link($node, $vie
   }
 
   if (!empty($links)) {
-    $node->content['links']['#links'] = array_merge($node->content['links']['#links'], $links);
+    $node->content['links']['book'] = array(
+      '#theme' => 'links__node__book',
+      '#links' => $links,
+      '#attributes' => array('class' => array('links', 'inline')),
+    );
   }
 }
 
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.898
diff -u -p -r1.898 comment.module
--- modules/comment/comment.module	13 Sep 2010 05:52:18 -0000	1.898
+++ modules/comment/comment.module	15 Sep 2010 15:56:18 -0000
@@ -682,7 +682,11 @@ function comment_node_view($node, $view_
       $links['comment_forbidden']['html'] = TRUE;
     }
 
-    $node->content['links']['#links'] = array_merge($node->content['links']['#links'], $links);
+    $node->content['links']['comment'] = array(
+      '#theme' => 'links__node__comment',
+      '#links' => $links,
+      '#attributes' => array('class' => array('links', 'inline')),
+    );
 
     // Only append comments when we are building a node on its own node detail
     // page. We compare $node and $page_node to ensure that comments are not
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1298
diff -u -p -r1.1298 node.module
--- modules/node/node.module	11 Sep 2010 06:03:11 -0000	1.1298
+++ modules/node/node.module	15 Sep 2010 15:56:18 -0000
@@ -1286,6 +1286,13 @@ function node_build_content($node, $view
   entity_prepare_view('node', array($node->nid => $node));
   $node->content += field_attach_view('node', $node, $view_mode);
 
+  // Prepare an array to hold the groups of links.
+  $node->content['links'] = array(
+    '#type' => 'grouped_element',
+    '#theme_callback' => 'links__node',
+    '#attributes' => array('class' => array('links', 'inline')),
+  );
+
   // Always display a read more link on teasers because we have no way
   // to know when a teaser view is different than a full view.
   $links = array();
@@ -1296,8 +1303,8 @@ function node_build_content($node, $view
       'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title))
     );
   }
-  $node->content['links'] = array(
-    '#theme' => 'links__node',
+  $node->content['links']['node'] = array(
+    '#theme' => 'links__node__node',
     '#links' => $links,
     '#attributes' => array('class' => array('links', 'inline')),
   );
Index: modules/rdf/rdf.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/rdf/rdf.module,v
retrieving revision 1.45
diff -u -p -r1.45 rdf.module
--- modules/rdf/rdf.module	9 Sep 2010 20:22:00 -0000	1.45
+++ modules/rdf/rdf.module	15 Sep 2010 15:56:18 -0000
@@ -500,7 +500,7 @@ function rdf_preprocess_node(&$variables
   // Adds RDFa markup annotating the number of comments a node has.
   if (isset($variables['node']->comment_count) && !empty($variables['node']->rdf_mapping['comment_count']['predicates'])) {
     // Annotates the 'x comments' link in teaser view.
-    if (isset($variables['content']['links']['#links']['comment-comments'])) {
+    if (isset($variables['content']['links']['comment']['#links']['comment-comments'])) {
       $comment_count_attributes['property'] = $variables['node']->rdf_mapping['comment_count']['predicates'];
       $comment_count_attributes['content'] = $variables['node']->comment_count;
       $comment_count_attributes['datatype'] = $variables['node']->rdf_mapping['comment_count']['datatype'];
@@ -510,7 +510,7 @@ function rdf_preprocess_node(&$variables
       // we set an empty rel attribute which triggers rule number 5. See
       // http://www.w3.org/TR/rdfa-syntax/#sec_5.5.
       $comment_count_attributes['rel'] = '';
-      $variables['content']['links']['#links']['comment-comments']['attributes'] += $comment_count_attributes;
+      $variables['content']['links']['comment']['#links']['comment-comments']['attributes'] += $comment_count_attributes;
     }
     // In full node view, the number of comments is not displayed by
     // node.tpl.php so it is expressed in RDFa in the <head> tag.
Index: modules/statistics/statistics.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics/statistics.module,v
retrieving revision 1.338
diff -u -p -r1.338 statistics.module
--- modules/statistics/statistics.module	30 Aug 2010 05:58:46 -0000	1.338
+++ modules/statistics/statistics.module	15 Sep 2010 15:56:18 -0000
@@ -114,12 +114,19 @@ function statistics_permission() {
  */
 function statistics_node_view($node, $view_mode) {
   if ($view_mode != 'rss') {
+    $links = array();
     if (user_access('view post access counter')) {
       $statistics = statistics_get($node->nid);
       if ($statistics) {
-        $node->content['links']['#links']['statistics_counter']['title'] = format_plural($statistics['totalcount'], '1 read', '@count reads');
+        $links['statistics_counter']['title'] = format_plural($statistics['totalcount'], '1 read', '@count reads');
       }
     }
+
+    $node->content['links']['statistics'] = array(
+      '#theme' => 'links__node__statistics',
+      '#links' => $links,
+      '#attributes' => array('class' => array('links', 'inline')),
+    );
   }
 }
 
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.960
diff -u -p -r1.960 system.module
--- modules/system/system.module	11 Sep 2010 06:03:12 -0000	1.960
+++ modules/system/system.module	15 Sep 2010 15:56:19 -0000
@@ -484,6 +484,12 @@ function system_element_info() {
     '#theme' => 'hidden',
   );
 
+  // Miscellaneous elements.
+  $types['grouped_element'] = array(
+    '#pre_render' => array('drupal_pre_render_groups'),
+    '#theme' => 'grouped_element',
+  );
+
   return $types;
 }
 
Index: modules/translation/translation.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.module,v
retrieving revision 1.84
diff -u -p -r1.84 translation.module
--- modules/translation/translation.module	9 Sep 2010 23:01:48 -0000	1.84
+++ modules/translation/translation.module	15 Sep 2010 15:56:19 -0000
@@ -188,7 +188,11 @@ function translation_node_view($node, $v
       $links = $links->links;
       // Do not show link to the same node.
       unset($links[$node->language]);
-      $node->content['links']['#links'] = array_merge($node->content['links']['#links'], $links);
+      $node->content['links']['translation'] = array(
+        '#theme' => 'links__node__translation',
+        '#links' => $links,
+        '#attributes' => array('class' => array('links', 'inline')),
+      );
     }
   }
 }
