Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.426
diff -u -9 -p -r1.426 form.inc
--- includes/form.inc	8 Jan 2010 06:36:34 -0000	1.426
+++ includes/form.inc	20 Jan 2010 17:55:47 -0000
@@ -2500,27 +2500,39 @@ function theme_tableselect($variables) {
       if (isset($value['#attributes'])) {
         $row += $value['#attributes'];
       }
       // Render the checkbox / radio element.
       $row['data'][] = drupal_render($element[$key]);
 
       // As theme_table only maps header and row columns by order, create the
       // correct order by iterating over the header fields.
       foreach ($element['#header'] as $fieldname => $title) {
-        $row['data'][] = $element['#options'][$key][$fieldname];
+        if (isset($element['#options'][$key][$fieldname])) {
+          $row['data'][] = $element['#options'][$key][$fieldname];
+        }
+        else {
+          $row['data'][] = '';
+        }
       }
       $rows[] = $row;
     }
     // Add an empty header or a "Select all" checkbox to provide room for the
     // checkboxes/radios in the first table column.
     $first_col = $element['#js_select'] ? array(theme('table_select_header_cell')) : array('');
     $header = array_merge($first_col, $header);
   }
+  // A header cell with colspan == 0 is used to name a column following another
+  // header cells with colspan > 1, and should not be displayed.
+  foreach ($header as $fieldname => $title) {
+    if (is_array($header[$fieldname]) && isset($header[$fieldname]['colspan']) && $header[$fieldname]['colspan'] == 0) {
+      unset($header[$fieldname]);
+    }
+  }
   return theme('table', array('header' => $header, 'rows' => $rows, 'empty' => $element['#empty'], 'attributes' => $element['#attributes']));
 }
 
 /**
  * Create the correct amount of checkbox or radio elements to populate the table.
  *
  * @param $element
  *   An associative array containing the properties and children of the
  *   tableselect element.
Index: modules/node/node.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.admin.inc,v
retrieving revision 1.88
diff -u -9 -p -r1.88 node.admin.inc
--- modules/node/node.admin.inc	13 Jan 2010 23:30:43 -0000	1.88
+++ modules/node/node.admin.inc	20 Jan 2010 17:55:47 -0000
@@ -426,19 +426,20 @@ function node_admin_nodes() {
     'title' => array('data' => t('Title'), 'field' => 'n.title'),
     'type' => array('data' => t('Type'), 'field' => 'n.type'),
     'author' => t('Author'),
     'status' => array('data' => t('Status'), 'field' => 'n.status'),
     'changed' => array('data' => t('Updated'), 'field' => 'n.changed', 'sort' => 'desc')
   );
   if ($multilanguage) {
     $header['language'] = array('data' => t('Language'), 'field' => 'n.language');
   }
-  $header['operations'] = array('data' => t('Operations'));
+  $header['edit'] = array('data' => t('Operations'), 'colspan' => 2);
+  $header['delete'] = array('colspan' => 0);
 
   $query = db_select('node', 'n')->extend('PagerDefault')->extend('TableSort');
   node_build_filter_query($query);
 
   if (!user_access('bypass node access')) {
     // If the user is able to view their own unpublished nodes, allow them
     // to see these in addition to published nodes. Check that they actually
     // have some unpublished nodes to view before adding the condition.
     if (user_access('view own unpublished content') && $own_unpublished = db_query('SELECT nid FROM {node} WHERE uid = :uid AND status = :status', array(':uid' => $GLOBALS['user']->uid, ':status' => 0))->fetchCol()) {
@@ -478,54 +479,35 @@ function node_admin_nodes() {
       ),
       'type' => check_plain(node_type_get_name($node)),
       'author' => theme('username', array('account' => $node)),
       'status' => $node->status ? t('published') : t('not published'),
       'changed' => format_date($node->changed, 'short'),
     );
     if ($multilanguage) {
       $options[$node->nid]['language'] = $node->language == LANGUAGE_NONE ? t('Language neutral') : t($languages[$node->language]->name);
     }
-    // Build a list of all the accessible operations for the current node.
-    $operations = array();
     if (node_access('update', $node)) {
-      $operations['edit'] = array(
-        'title' => t('edit'),
-        'href' => 'node/' . $node->nid . '/edit',
-        'query' => $destination,
-      );
-    }
-    if (node_access('delete', $node)) {
-      $operations['delete'] = array(
-        'title' => t('delete'),
-        'href' => 'node/' . $node->nid . '/delete',
-        'query' => $destination,
-      );
-    }
-    $options[$node->nid]['operations'] = array();
-    if (count($operations) > 1) {
-      // Render an unordered list of operations links.
-      $options[$node->nid]['operations'] = array(
+      $options[$node->nid]['edit'] = array(
         'data' => array(
-          '#theme' => 'links__node_operations',
-          '#links' => $operations,
-          '#attributes' => array('class' => array('links', 'inline')),
+          '#type' => 'link',
+          '#title' => t('edit'),
+          '#href' => 'node/' . $node->nid . '/edit',
+          '#options' => array('query' => $destination),
         ),
       );
     }
-    elseif (!empty($operations)) {
-      // Render the first and only operation as a link.
-      $link = reset($operations);
-      $options[$node->nid]['operations'] = array(
+    if (node_access('delete', $node)) {
+      $options[$node->nid]['delete'] = array(
         'data' => array(
           '#type' => 'link',
-          '#title' => $link['title'],
-          '#href' => $link['href'],
-          '#options' => array('query' => $link['query']),
+          '#title' => t('delete'),
+          '#href' => 'node/' . $node->nid . '/delete',
+          '#options' => array('query' => $destination),
         ),
       );
     }
   }
 
   // Only use a tableselect when the current user is able to perform any
   // operations.
   if ($admin_access) {
     $form['nodes'] = array(
