From 16eac3bec5ac39b950a2b1016f2cf7be90a203ec Mon Sep 17 00:00:00 2001
From: webchick <webchick@24967.no-reply.drupal.org>
Date: Tue, 9 Oct 2012 12:49:07 -0700
Subject: [PATCH] Issue #1480854 by tim.plunkett, Noyz, Bojhan, yoroy, kika:
 Convert operation links to '#type' => 'operations'.

---
 core/modules/action/action.admin.inc          | 32 +++++--
 core/modules/aggregator/aggregator.admin.inc  | 51 ++++++++---
 core/modules/ban/ban.admin.inc                | 16 +++-
 core/modules/block/block.admin.inc            | 21 ++---
 .../block-admin-display-form.tpl.php          |  8 +-
 core/modules/book/book.admin.inc              | 44 ++++++++--
 core/modules/comment/comment.admin.inc        | 20 +++--
 core/modules/contact/contact.admin.inc        | 27 ++++--
 core/modules/field_ui/field_ui.admin.inc      | 39 ++++-----
 core/modules/filter/filter.admin.inc          | 23 +++--
 core/modules/image/image.admin.inc            | 49 ++++++++---
 core/modules/language/language.admin.inc      | 87 ++++++++-----------
 core/modules/language/language.module         |  4 -
 core/modules/menu/menu.admin.inc              | 57 ++++++++----
 core/modules/node/content_types.inc           | 41 ++++++---
 core/modules/node/node.pages.inc              | 24 +++--
 core/modules/openid/openid.pages.inc          | 15 +++-
 core/modules/path/path.admin.inc              |  4 +-
 core/modules/shortcut/shortcut.admin.inc      | 45 +++++++---
 core/modules/statistics/statistics.admin.inc  | 47 +++++++---
 core/modules/statistics/statistics.pages.inc  | 55 ++++++++----
 core/modules/system/system.admin.inc          | 56 ++++++++----
 core/modules/taxonomy/taxonomy.admin.inc      | 28 ++++--
 .../modules/translation/translation.pages.inc | 26 ++++--
 core/modules/user/user.admin.inc              | 35 +++++---
 25 files changed, 588 insertions(+), 266 deletions(-)

diff --git a/core/modules/action/action.admin.inc b/core/modules/action/action.admin.inc
index 0d76905b8707..f130111c0652 100644
--- a/core/modules/action/action.admin.inc
+++ b/core/modules/action/action.admin.inc
@@ -29,7 +29,7 @@ function action_admin_manage() {
   $header = array(
     array('data' => t('Action type'), 'field' => 'type'),
     array('data' => t('Label'), 'field' => 'label'),
-    array('data' => $instances_present ? t('Operations') : '', 'colspan' => '2')
+    $instances_present ? t('Operations') : '',
   );
   $query = db_select('actions')
     ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
@@ -41,21 +41,37 @@ function action_admin_manage() {
     ->execute();
 
   foreach ($result as $action) {
+    $row = array();
+    $row[] = $action->type;
+    $row[] = check_plain($action->label);
+    $links = array();
+    if ($action->parameters) {
+      $links['configure'] = array(
+        'title' => t('configure'),
+        'href' => "admin/config/system/actions/configure/$action->aid",
+      );
+      $links['delete'] = array(
+        'title' => t('delete'),
+        'href' => "admin/config/system/actions/delete/$action->aid",
+      );
+    }
     $row[] = array(
-      array('data' => $action->type),
-      array('data' => check_plain($action->label)),
-      array('data' => $action->parameters ? l(t('configure'), "admin/config/system/actions/configure/$action->aid") : ''),
-      array('data' => $action->parameters ? l(t('delete'), "admin/config/system/actions/delete/$action->aid") : '')
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
     );
+
+    $rows[] = $row;
   }
 
-  if ($row) {
+  if ($rows) {
     $pager = theme('pager');
     if (!empty($pager)) {
-      $row[] = array(array('data' => $pager, 'colspan' => '3'));
+      $rows[] = array(array('data' => $pager, 'colspan' => '3'));
     }
     $build['action_header'] = array('#markup' => '<h3>' . t('Available actions:') . '</h3>');
-    $build['action_table'] = array('#markup' => theme('table', array('header' => $header, 'rows' => $row)));
+    $build['action_table'] = array('#markup' => theme('table', array('header' => $header, 'rows' => $rows)));
   }
 
   if ($actions_map) {
diff --git a/core/modules/aggregator/aggregator.admin.inc b/core/modules/aggregator/aggregator.admin.inc
index 718e79cca4de..a859e85def87 100644
--- a/core/modules/aggregator/aggregator.admin.inc
+++ b/core/modules/aggregator/aggregator.admin.inc
@@ -28,18 +28,35 @@ function aggregator_view() {
 
   $output = '<h3>' . t('Feed overview') . '</h3>';
 
-  $header = array(t('Title'), t('Items'), t('Last update'), t('Next update'), array('data' => t('Operations'), 'colspan' => '3'));
+  $header = array(t('Title'), t('Items'), t('Last update'), t('Next update'), t('Operations'));
   $rows = array();
   foreach ($result as $feed) {
-    $rows[] = array(
-      l($feed->title, "aggregator/sources/$feed->fid"),
-      format_plural($feed->items, '1 item', '@count items'),
-      ($feed->checked ? t('@time ago', array('@time' => format_interval(REQUEST_TIME - $feed->checked))) : t('never')),
-      ($feed->checked && $feed->refresh ? t('%time left', array('%time' => format_interval($feed->checked + $feed->refresh - REQUEST_TIME))) : t('never')),
-      l(t('edit'), "admin/config/services/aggregator/edit/feed/$feed->fid"),
-      l(t('remove items'), "admin/config/services/aggregator/remove/$feed->fid"),
-      l(t('update items'), "admin/config/services/aggregator/update/$feed->fid", array('query' => array('token' => drupal_get_token("aggregator/update/$feed->fid")))),
+    $row = array();
+    $row[] = l($feed->title, "aggregator/sources/$feed->fid");
+    $row[] = format_plural($feed->items, '1 item', '@count items');
+    $row[] = ($feed->checked ? t('@time ago', array('@time' => format_interval(REQUEST_TIME - $feed->checked))) : t('never'));
+    $row[] = ($feed->checked && $feed->refresh ? t('%time left', array('%time' => format_interval($feed->checked + $feed->refresh - REQUEST_TIME))) : t('never'));
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('edit'),
+      'href' => "admin/config/services/aggregator/edit/feed/$feed->fid",
     );
+    $links['remove'] = array(
+      'title' => t('remove items'),
+      'href' => "admin/config/services/aggregator/remove/$feed->fid",
+    );
+    $links['update'] = array(
+      'title' => t('update items'),
+      'href' => "admin/config/services/aggregator/update/$feed->fid",
+      'query' => array('token' => drupal_get_token("aggregator/update/$feed->fid")),
+    );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
+    $rows[] = $row;
   }
   $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No feeds available. <a href="@link">Add feed</a>.', array('@link' => url('admin/config/services/aggregator/add/feed')))));
 
@@ -50,7 +67,21 @@ function aggregator_view() {
   $header = array(t('Title'), t('Items'), t('Operations'));
   $rows = array();
   foreach ($result as $category) {
-    $rows[] = array(l($category->title, "aggregator/categories/$category->cid"), format_plural($category->items, '1 item', '@count items'), l(t('edit'), "admin/config/services/aggregator/edit/category/$category->cid"));
+    $row = array();
+    $row[] = l($category->title, "aggregator/categories/$category->cid");
+    $row[] = format_plural($category->items, '1 item', '@count items');
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('edit'),
+      'href' => "admin/config/services/aggregator/edit/category/$category->cid",
+    );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
+    $rows[] = $row;
   }
   $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No categories available. <a href="@link">Add category</a>.', array('@link' => url('admin/config/services/aggregator/add/category')))));
 
diff --git a/core/modules/ban/ban.admin.inc b/core/modules/ban/ban.admin.inc
index d7940f909d93..bccd84eaee6e 100644
--- a/core/modules/ban/ban.admin.inc
+++ b/core/modules/ban/ban.admin.inc
@@ -17,10 +17,20 @@ function ban_admin_page($default_ip = '') {
   $header = array(t('banned IP addresses'), t('Operations'));
   $result = db_query('SELECT * FROM {ban_ip}');
   foreach ($result as $ip) {
-    $rows[] = array(
-      $ip->ip,
-      l(t('delete'), "admin/config/people/ban/delete/$ip->iid"),
+    $row = array();
+    $row[] = $ip->ip;
+    $links = array();
+    $links['delete'] = array(
+      'title' => t('delete'),
+      'href' => "admin/config/people/ban/delete/$ip->iid",
     );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
+    $rows[] = $row;
   }
 
   $build['ban_ip_form'] = drupal_get_form('ban_ip_form', $default_ip);
diff --git a/core/modules/block/block.admin.inc b/core/modules/block/block.admin.inc
index cc4bd716354f..2a17a5521170 100644
--- a/core/modules/block/block.admin.inc
+++ b/core/modules/block/block.admin.inc
@@ -146,18 +146,20 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_r
       '#title' => t('Region for @block block', array('@block' => $block['info'])),
       '#options' => $block_regions,
     );
-    $form['blocks'][$key]['configure'] = array(
-      '#type' => 'link',
-      '#title' => t('configure'),
-      '#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure',
+    $links['configure'] = array(
+      'title' => t('configure'),
+      'href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure',
     );
     if ($block['module'] == 'block') {
-      $form['blocks'][$key]['delete'] = array(
-        '#type' => 'link',
-        '#title' => t('delete'),
-        '#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/delete',
+      $links['delete'] = array(
+        'title' => t('delete'),
+        'href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/delete',
      );
     }
+    $form['blocks'][$key]['operations'] = array(
+      '#type' => 'operations',
+      '#links' => $links,
+    );
   }
   // Do not allow disabling the main system content block when it is present.
   if (isset($form['blocks']['system_main']['region'])) {
@@ -806,8 +808,7 @@ function template_preprocess_block_admin_display_form(&$variables) {
     $variables['block_listing'][$region][$i]->block_title = drupal_render($block['info']);
     $variables['block_listing'][$region][$i]->region_select = drupal_render($block['region']) . drupal_render($block['theme']);
     $variables['block_listing'][$region][$i]->weight_select = drupal_render($block['weight']);
-    $variables['block_listing'][$region][$i]->configure_link = drupal_render($block['configure']);
-    $variables['block_listing'][$region][$i]->delete_link = !empty($block['delete']) ? drupal_render($block['delete']) : '';
+    $variables['block_listing'][$region][$i]->operations = drupal_render($block['operations']);
     $variables['block_listing'][$region][$i]->printed = FALSE;
   }
 
diff --git a/core/modules/block/templates/block-admin-display-form.tpl.php b/core/modules/block/templates/block-admin-display-form.tpl.php
index 172827303020..4fa3e703004f 100644
--- a/core/modules/block/templates/block-admin-display-form.tpl.php
+++ b/core/modules/block/templates/block-admin-display-form.tpl.php
@@ -16,8 +16,7 @@
  * - $data->block_title: Block title.
  * - $data->region_select: Drop-down menu for assigning a region.
  * - $data->weight_select: Drop-down menu for setting weights.
- * - $data->configure_link: Block configuration link.
- * - $data->delete_link: For deleting user added blocks.
+ * - $data->operations: Block operations.
  *
  * @see template_preprocess_block_admin_display_form()
  * @see theme_block_admin_display()
@@ -31,7 +30,7 @@
       <th><?php print t('Block'); ?></th>
       <th><?php print t('Region'); ?></th>
       <th><?php print t('Weight'); ?></th>
-      <th colspan="2"><?php print t('Operations'); ?></th>
+      <th><?php print t('Operations'); ?></th>
     </tr>
   </thead>
   <tbody>
@@ -48,8 +47,7 @@
         <td class="block"><?php print $data->block_title; ?></td>
         <td><?php print $data->region_select; ?></td>
         <td><?php print $data->weight_select; ?></td>
-        <td><?php print $data->configure_link; ?></td>
-        <td><?php print $data->delete_link; ?></td>
+        <td><?php print $data->operations; ?></td>
       </tr>
       <?php $row++; ?>
       <?php endforeach; ?>
diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc
index 5c414e09529f..d4d6f0bfd11f 100644
--- a/core/modules/book/book.admin.inc
+++ b/core/modules/book/book.admin.inc
@@ -19,7 +19,21 @@ function book_admin_overview() {
 
   // Add any recognized books to the table list.
   foreach (book_get_books() as $book) {
-    $rows[] = array(l($book['title'], $book['href'], $book['options']), l(t('edit order and titles'), 'admin/content/book/' . $book['nid']));
+    $row = array(
+      l($book['title'], $book['href'], $book['options']),
+    );
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('edit order and titles'),
+      'href' => 'admin/content/book/' . $book['nid'],
+    );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
+    $rows[] = $row;
   }
 
   return theme('table', array('header' => $headers, 'rows' => $rows, 'empty' => t('No books available.')));
@@ -265,7 +279,7 @@ function theme_book_admin_table($variables) {
   drupal_add_tabledrag('book-outline', 'match', 'parent', 'book-plid', 'book-plid', 'book-mlid', TRUE, MENU_MAX_DEPTH - 2);
   drupal_add_tabledrag('book-outline', 'order', 'sibling', 'book-weight');
 
-  $header = array(t('Title'), t('Weight'), t('Parent'), array('data' => t('Operations'), 'colspan' => '3'));
+  $header = array(t('Title'), t('Weight'), t('Parent'), t('Operations'));
 
   $rows = array();
   $destination = drupal_get_destination();
@@ -283,9 +297,29 @@ function theme_book_admin_table($variables) {
       theme('indentation', array('size' => $form[$key]['depth']['#value'] - 2)) . drupal_render($form[$key]['title']),
       drupal_render($form[$key]['weight']),
       drupal_render($form[$key]['plid']) . drupal_render($form[$key]['mlid']),
-      l(t('view'), $href),
-      $access ? l(t('edit'), 'node/' . $nid . '/edit', array('query' => $destination)) : '&nbsp;',
-      $access ? l(t('delete'), 'node/' . $nid . '/delete', array('query' => $destination) )  : '&nbsp;',
+    );
+    $links = array();
+    $links['view'] = array(
+      'title' => t('view'),
+      'href' => $href,
+    );
+    if ($access) {
+      $links['edit'] = array(
+        'title' => t('edit'),
+        'href' => "node/$nid/edit",
+        'query' => $destination,
+      );
+      $links['delete'] = array(
+        'title' => t('delete'),
+        'href' => "node/$nid/delete",
+        'query' => $destination,
+      );
+    }
+    $data[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
     );
     $row = array('data' => $data);
     if (isset($form[$key]['#attributes'])) {
diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc
index ef8a90bc3340..dcbd1bcad2f1 100644
--- a/core/modules/comment/comment.admin.inc
+++ b/core/modules/comment/comment.admin.inc
@@ -76,7 +76,7 @@ function comment_admin_overview($form, &$form_state, $arg) {
     'author' => array('data' => t('Author'), 'field' => 'name', 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
     'posted_in' => array('data' => t('Posted in'), 'field' => 'node_title', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
     'changed' => array('data' => t('Updated'), 'field' => 'c.changed', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'operations' => array('data' => t('Operations')),
+    'operations' => t('Operations'),
   );
 
   $query = db_select('comment', 'c')
@@ -129,14 +129,16 @@ function comment_admin_overview($form, &$form_state, $arg) {
         ),
       ),
       'changed' => format_date($comment->changed, 'short'),
-      'operations' => array(
-        'data' => array(
-          '#type' => 'link',
-          '#title' => t('edit'),
-          '#href' => 'comment/' . $comment->cid . '/edit',
-          '#options' => array('query' => $destination),
-        ),
-      ),
+    );
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('edit'),
+      'href' => 'comment/' . $comment->cid . '/edit',
+      'query' => $destination,
+    );
+    $options[$comment->cid]['operations']['data'] = array(
+      '#type' => 'operations',
+      '#links' => $links,
     );
   }
 
diff --git a/core/modules/contact/contact.admin.inc b/core/modules/contact/contact.admin.inc
index f6835f9c3ce7..29f02147d670 100644
--- a/core/modules/contact/contact.admin.inc
+++ b/core/modules/contact/contact.admin.inc
@@ -15,7 +15,7 @@ function contact_category_list() {
     t('Category'),
     t('Recipients'),
     t('Selected'),
-    array('data' => t('Operations'), 'colspan' => 2),
+    t('Operations'),
   );
   $rows = array();
 
@@ -30,13 +30,26 @@ function contact_category_list() {
 
   // Loop through the categories and add them to the table.
   foreach ($categories as $category) {
-    $rows[] = array(
-      check_plain($category->category),
-      check_plain($category->recipients),
-      ($category->selected ? t('Yes') : t('No')),
-      l(t('Edit'), 'admin/structure/contact/edit/' . $category->cid),
-      l(t('Delete'), 'admin/structure/contact/delete/' . $category->cid),
+    $row = array();
+    $row[] = check_plain($category->category);
+    $row[] = check_plain($category->recipients);
+    $row[] = ($category->selected ? t('Yes') : t('No'));
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('Edit'),
+      'href' => 'admin/structure/contact/edit/' . $category->cid,
     );
+    $links['delete'] = array(
+      'title' => t('Delete'),
+      'href' => 'admin/structure/contact/delete/' . $category->cid,
+    );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
+    $rows[] = $row;
   }
 
   if (!$rows) {
diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc
index 7b22a0b5ddf7..57f5132ab71b 100644
--- a/core/modules/field_ui/field_ui.admin.inc
+++ b/core/modules/field_ui/field_ui.admin.inc
@@ -333,7 +333,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle
       t('Machine name'),
       t('Field type'),
       t('Widget'),
-      array('data' => t('Operations'), 'colspan' => 2),
+      t('Operations'),
     ),
     '#parent_options' => array(),
     '#regions' => array(
@@ -398,23 +398,25 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle
         '#href' => $admin_field_path . '/widget-type',
         '#options' => array('attributes' => array('title' => t('Change widget type.'))),
       ),
-      'edit' => array(
-        '#type' => 'link',
-        '#title' => t('edit'),
-        '#href' => $admin_field_path,
-        '#options' => array('attributes' => array('title' => t('Edit instance settings.'))),
-      ),
-      'delete' => array(
-        '#type' => 'link',
-        '#title' => t('delete'),
-        '#href' => $admin_field_path . '/delete',
-        '#options' => array('attributes' => array('title' => t('Delete instance.'))),
-      ),
+    );
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('edit'),
+      'href' => $admin_field_path,
+      'attributes' => array('title' => t('Edit instance settings.')),
+    );
+    $links['delete'] = array(
+      'title' => t('delete'),
+      'href' => "$admin_field_path/delete",
+      'attributes' => array('title' => t('Delete instance.')),
+    );
+    $table[$name]['operations']['data'] = array(
+      '#type' => 'operations',
+      '#links' => $links,
     );
 
     if (!empty($instance['locked'])) {
-      $table[$name]['edit'] = array('#value' => t('Locked'));
-      $table[$name]['delete'] = array();
+      $table[$name]['operations'] = array('#value' => t('Locked'));
       $table[$name]['#attributes']['class'][] = 'menu-disabled';
     }
   }
@@ -459,11 +461,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle
         '#markup' => isset($extra_field['description']) ? $extra_field['description'] : '',
         '#cell_attributes' => array('colspan' => 2),
       ),
-      'edit' => array(
-        '#markup' => isset($extra_field['edit']) ? $extra_field['edit'] : '',
-      ),
-      'delete' => array(
-        '#markup' => isset($extra_field['delete']) ? $extra_field['delete'] : '',
+      'operations' => array(
+        '#markup' => '',
       ),
     );
   }
diff --git a/core/modules/filter/filter.admin.inc b/core/modules/filter/filter.admin.inc
index 044fe5854d3d..194a82c8691a 100644
--- a/core/modules/filter/filter.admin.inc
+++ b/core/modules/filter/filter.admin.inc
@@ -19,6 +19,11 @@ function filter_admin_overview($form) {
 
   $form['#tree'] = TRUE;
   foreach ($formats as $id => $format) {
+    $links = array();
+    $links['configure'] = array(
+      'title' => t('configure'),
+      'href' => "admin/config/content/formats/$id",
+    );
     // Check whether this is the fallback text format. This format is available
     // to all roles and cannot be disabled via the admin interface.
     $form['formats'][$id]['#is_fallback'] = ($id == $fallback_format);
@@ -30,10 +35,16 @@ function filter_admin_overview($form) {
       $form['formats'][$id]['name'] = array('#markup' => check_plain($format->name));
       $roles = array_map('check_plain', filter_get_roles_by_format($format));
       $roles_markup = $roles ? implode(', ', $roles) : t('No roles may use this format');
+      $links['disable'] = array(
+        'title' => t('disable'),
+        'href' => "admin/config/content/formats/$id/disable",
+      );
     }
     $form['formats'][$id]['roles'] = array('#markup' => $roles_markup);
-    $form['formats'][$id]['configure'] = array('#type' => 'link', '#title' => t('configure'), '#href' => 'admin/config/content/formats/' . $id);
-    $form['formats'][$id]['disable'] = array('#type' => 'link', '#title' => t('disable'), '#href' => 'admin/config/content/formats/' . $id . '/disable', '#access' => !$form['formats'][$id]['#is_fallback']);
+    $form['formats'][$id]['operations'] = array(
+      '#type' => 'operations',
+      '#links' => $links,
+    );
     $form['formats'][$id]['weight'] = array(
       '#type' => 'weight',
       '#title' => t('Weight for @title', array('@title' => $format->name)),
@@ -78,18 +89,18 @@ function theme_filter_admin_overview($variables) {
   $rows = array();
   foreach (element_children($form['formats']) as $id) {
     $form['formats'][$id]['weight']['#attributes']['class'] = array('text-format-order-weight');
-    $rows[] = array(
+    $row = array(
       'data' => array(
         drupal_render($form['formats'][$id]['name']),
         drupal_render($form['formats'][$id]['roles']),
         drupal_render($form['formats'][$id]['weight']),
-        drupal_render($form['formats'][$id]['configure']),
-        drupal_render($form['formats'][$id]['disable']),
+        drupal_render($form['formats'][$id]['operations']),
       ),
       'class' => array('draggable'),
     );
+    $rows[] = $row;
   }
-  $header = array(t('Name'), t('Roles'), t('Weight'), array('data' => t('Operations'), 'colspan' => 2));
+  $header = array(t('Name'), t('Roles'), t('Weight'), t('Operations'));
   $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'text-format-order')));
   $output .= drupal_render_children($form);
 
diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc
index 8a1e45081496..a2d86af9cf99 100644
--- a/core/modules/image/image.admin.inc
+++ b/core/modules/image/image.admin.inc
@@ -84,6 +84,21 @@ function image_style_form($form, &$form_state, $style) {
         '#default_value' => $effect['weight'],
       );
 
+      $links = array();
+      if (isset($effect['form callback'])) {
+        $links['edit'] = array(
+          'title' => t('edit'),
+          'href' => 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $key,
+        );
+      }
+      $links['delete'] = array(
+        'title' => t('delete'),
+        'href' => 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $key . '/delete',
+      );
+      $form['effects'][$key]['operations'] = array(
+        '#type' => 'operations',
+        '#links' => $links,
+      );
       $form['effects'][$key]['configure'] = array(
         '#type' => 'link',
         '#title' => t('edit'),
@@ -583,19 +598,28 @@ function image_rotate_form($data) {
 function theme_image_style_list($variables) {
   $styles = $variables['styles'];
 
-  $header = array(t('Style name'), array('data' => t('Operations'), 'colspan' => 3));
+  $header = array(t('Style name'), t('Operations'));
   $rows = array();
-  $link_attributes = array(
-    'attributes' => array(
-      'class' => array('image-style-link'),
-    ),
-  );
-
   foreach ($styles as $style) {
     $row = array();
     $row[] = l($style['label'], 'admin/config/media/image-styles/edit/' . $style['name']);
-    $row[] = l(t('edit'), 'admin/config/media/image-styles/edit/' . $style['name'], $link_attributes);
-    $row[] = l(t('delete'), 'admin/config/media/image-styles/delete/' . $style['name'], $link_attributes);
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('edit'),
+      'href' => 'admin/config/media/image-styles/edit/' . $style['name'],
+      'class' => array('image-style-link'),
+    );
+    $links['delete'] = array(
+      'title' => t('delete'),
+      'href' => 'admin/config/media/image-styles/delete/' . $style['name'],
+      'class' => array('image-style-link'),
+    );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
     $rows[] = $row;
   }
 
@@ -629,14 +653,13 @@ function theme_image_style_effects($variables) {
       $summary = drupal_render($form[$key]['summary']);
       $row[] = drupal_render($form[$key]['label']) . (empty($summary) ? '' : ' ' . $summary);
       $row[] = drupal_render($form[$key]['weight']);
-      $row[] = drupal_render($form[$key]['configure']);
-      $row[] = drupal_render($form[$key]['remove']);
+      $row[] = array('data' => $form[$key]['operations']);
     }
     else {
       // Add the row for adding a new image effect.
       $row[] = '<div class="image-style-new">' . drupal_render($form['new']['new']) . drupal_render($form['new']['add']) . '</div>';
       $row[] = drupal_render($form['new']['weight']);
-      $row[] = array('data' => '', 'colspan' => 2);
+      $row[] = '';
     }
 
     $rows[] = array(
@@ -648,7 +671,7 @@ function theme_image_style_effects($variables) {
   $header = array(
     t('Effect'),
     t('Weight'),
-    array('data' => t('Operations'), 'colspan' => 2),
+    t('Operations'),
   );
 
   if (count($rows) == 1 && (!isset($form['new']['#access']) || $form['new']['#access'])) {
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index 1fd971ed9156..b365937a5b28 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -69,26 +69,27 @@ function language_admin_overview_form($form, &$form_state) {
       ),
       '#delta' => 30,
     );
-    $form['languages'][$langcode]['operations'] = array(
-      '#theme_wrappers' => array('language_admin_operations'),
-      '#weight' => 100,
-    );
+    $links = array();
     if (empty($language->locked)) {
-      $form['languages'][$langcode]['operations']['edit'] = array(
-        '#type' => 'link',
-        '#title' => t('edit'),
-        '#href' => 'admin/config/regional/language/edit/' . $langcode,
-      );
-      $form['languages'][$langcode]['operations']['delete'] = array(
-        '#type' => 'link',
-        '#title' => t('delete'),
-        '#href' => 'admin/config/regional/language/delete/' . $langcode,
-        '#access' => $langcode != $default->langcode,
+      $links['edit'] = array(
+        'title' => t('edit'),
+        'href' => 'admin/config/regional/language/edit/' . $langcode,
       );
+      if ($langcode != $default->langcode) {
+        $links['delete'] = array(
+          'title' => t('delete'),
+          'href' => 'admin/config/regional/language/delete/' . $langcode,
+        );
+      }
     }
     else {
       $form['languages'][$langcode]['default']['#attributes']['disabled'] = 'disabled';
     }
+    $form['languages'][$langcode]['operations'] = array(
+      '#type' => 'operations',
+      '#links' => $links,
+      '#weight' => 100,
+    );
   }
 
   $form['actions'] = array('#type' => 'actions');
@@ -100,30 +101,6 @@ function language_admin_overview_form($form, &$form_state) {
   return $form;
 }
 
-/**
- * Returns HTML for operation links in language_admin_overview_form() table.
- *
- * @todo Introduce #type '[table_]operations' or just simply #type 'links'.
- */
-function theme_language_admin_operations($variables) {
-  $links = array();
-  foreach (element_children($variables['elements']) as $key) {
-    // Children are only rendered if the current user has access.
-    if (isset($variables['elements'][$key]['#children'])) {
-      $links[$key] = $variables['elements'][$key]['#children'];
-    }
-  }
-  // If there are links, render a link list.
-  if (!empty($links)) {
-    return theme('item_list__operations', array(
-      'items' => $links,
-      'attributes' => array('class' => array('links', 'inline')),
-    ));
-  }
-  // Otherwise, ensure to produce no output.
-  return '';
-}
-
 /**
  * Returns HTML for the language overview form.
  *
@@ -539,11 +516,17 @@ function language_negotiation_configure_form_table(&$form, $type) {
 
       $config_op = array();
       if (isset($method['config'])) {
-        $config_op = array('#type' => 'link', '#title' => t('Configure'), '#href' => $method['config']);
+        $config_op['configure'] = array(
+          'title' => t('Configure'),
+          'href' => $method['config'],
+        );
         // If there is at least one operation enabled show the operation column.
         $table_form['#show_operations'] = TRUE;
       }
-      $table_form['operation'][$method_id] = $config_op;
+      $table_form['operation'][$method_id] = array(
+       '#type' => 'operations',
+       '#links' => $config_op,
+      );
     }
   }
 
@@ -931,20 +914,26 @@ function language_negotiation_configure_browser_form($form, &$form_state) {
 function theme_language_negotiation_configure_browser_form_table($variables) {
   $form = $variables['form'];
   $rows = array();
-  $link_attributes = array(
-    'attributes' => array(
-      'class' => array('image-style-link'),
-    ),
-  );
   foreach (element_children($form, TRUE) as $key) {
     $row = array();
     $row[] = drupal_render($form[$key]['browser_langcode']);
     $row[] = drupal_render($form[$key]['drupal_langcode']);
-    $row[] = l(t('Delete'), 'admin/config/regional/language/detection/browser/delete/' . $key, $link_attributes);
-
-    $rows[] = array(
-      'data' => $row,
+    $links = array();
+    $links['delete'] = array(
+      'title' => t('Delete'),
+      'href' => "admin/config/regional/language/detection/browser/delete/$key",
+      'attributes' => array(
+        'class' => array('image-style-link'),
+      ),
     );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
+
+    $rows[] = $row;
   }
 
   $header = array(
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index a2d1bacfc961..c0c22b22ceac 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -180,10 +180,6 @@ function language_theme() {
       'render element' => 'form',
       'file' => 'language.admin.inc',
     ),
-    'language_admin_operations' => array(
-      'render element' => 'elements',
-      'file' => 'language.admin.inc',
-    ),
     'language_negotiation_configure_form' => array(
       'render element' => 'form',
     ),
diff --git a/core/modules/menu/menu.admin.inc b/core/modules/menu/menu.admin.inc
index fc38caa27bb1..eeb5c7ff8ae6 100644
--- a/core/modules/menu/menu.admin.inc
+++ b/core/modules/menu/menu.admin.inc
@@ -12,13 +12,30 @@
  */
 function menu_overview_page() {
   $result = db_query("SELECT * FROM {menu_custom} ORDER BY title", array(), array('fetch' => PDO::FETCH_ASSOC));
-  $header = array(t('Title'), array('data' => t('Operations'), 'colspan' => '3'));
+  $header = array(t('Title'), t('Operations'));
   $rows = array();
   foreach ($result as $menu) {
-    $row = array(theme('menu_admin_overview', array('title' => $menu['title'], 'name' => $menu['menu_name'], 'description' => $menu['description'])));
-    $row[] = array('data' => l(t('list links'), 'admin/structure/menu/manage/' . $menu['menu_name']));
-    $row[] = array('data' => l(t('edit menu'), 'admin/structure/menu/manage/' . $menu['menu_name'] . '/edit'));
-    $row[] = array('data' => l(t('add link'), 'admin/structure/menu/manage/' . $menu['menu_name'] . '/add'));
+    $row = array();
+    $row[] = theme('menu_admin_overview', array('title' => $menu['title'], 'name' => $menu['menu_name'], 'description' => $menu['description']));
+    $links = array();
+    $links['list'] = array(
+      'title' => t('list links'),
+      'href' => 'admin/structure/menu/manage/' . $menu['menu_name'],
+    );
+    $links['edit'] = array(
+      'title' => t('edit menu'),
+      'href' => 'admin/structure/menu/manage/' . $menu['menu_name'] . '/edit',
+    );
+    $links['add'] = array(
+      'title' => t('add link'),
+      'href' => 'admin/structure/menu/manage/' . $menu['menu_name'] . '/add',
+    );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
     $rows[] = $row;
   }
 
@@ -135,16 +152,32 @@ function _menu_overview_tree_form($tree, $delta = 50) {
       );
       // Build a list of operations.
       $operations = array();
+      $links = array();
+      $links['edit'] = array(
+        'title' => t('edit'),
+        'href' => 'admin/structure/menu/item/' . $item['mlid'] . '/edit',
+      );
       $operations['edit'] = array('#type' => 'link', '#title' => t('edit'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/edit');
       // Only items created by the menu module can be deleted.
       if ($item['module'] == 'menu' || $item['updated'] == 1) {
+        $links['delete'] = array(
+          'title' => t('delete'),
+          'href' => 'admin/structure/menu/item/' . $item['mlid'] . '/delete',
+        );
         $operations['delete'] = array('#type' => 'link', '#title' => t('delete'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/delete');
       }
       // Set the reset column.
       elseif ($item['module'] == 'system' && $item['customized']) {
+        $links['reset'] = array(
+          'title' => t('reset'),
+          'href' => 'admin/structure/menu/item/' . $item['mlid'] . '/reset',
+        );
         $operations['reset'] = array('#type' => 'link', '#title' => t('reset'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/reset');
       }
-      $form[$mlid]['operations'] = $operations;
+      $form[$mlid]['operations'] = array(
+        '#type' => 'operations',
+        '#links' => $links,
+      );
     }
 
     if ($data['below']) {
@@ -220,21 +253,13 @@ function theme_menu_overview_form($variables) {
     t('Menu link'),
     array('data' => t('Enabled'), 'class' => array('checkbox')),
     t('Weight'),
-    array('data' => t('Operations'), 'colspan' => '3'),
+    t('Operations'),
   );
 
   $rows = array();
   foreach (element_children($form) as $mlid) {
     if (isset($form[$mlid]['hidden'])) {
       $element = &$form[$mlid];
-      // Build a list of operations.
-      $operations = array();
-      foreach (element_children($element['operations']) as $op) {
-        $operations[] = array('data' => drupal_render($element['operations'][$op]), 'class' => array('menu-operations'));
-      }
-      while (count($operations) < 2) {
-        $operations[] = '';
-      }
 
       // Add special classes to be used for tabledrag.js.
       $element['plid']['#attributes']['class'] = array('menu-plid');
@@ -248,7 +273,7 @@ function theme_menu_overview_form($variables) {
       $row[] = theme('indentation', array('size' => $element['#item']['depth'] - 1)) . drupal_render($element['title']);
       $row[] = array('data' => drupal_render($element['hidden']), 'class' => array('checkbox', 'menu-enabled'));
       $row[] = drupal_render($element['weight']) . drupal_render($element['plid']) . drupal_render($element['mlid']);
-      $row = array_merge($row, $operations);
+      $row[] = drupal_render($element['operations']);
 
       $row = array_merge(array('data' => $row), $element['#attributes']);
       $row['class'][] = 'draggable';
diff --git a/core/modules/node/content_types.inc b/core/modules/node/content_types.inc
index 7388ee951b3d..3c8812dc3113 100644
--- a/core/modules/node/content_types.inc
+++ b/core/modules/node/content_types.inc
@@ -17,32 +17,47 @@ function node_overview_types() {
   $types = node_type_get_types();
   $names = node_type_get_names();
   $field_ui = module_exists('field_ui');
-  $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => $field_ui ? '4' : '2'));
+  $header = array(t('Name'), t('Operations'));
   $rows = array();
 
   foreach ($names as $key => $name) {
     $type = $types[$key];
     if (node_hook($type->type, 'form')) {
       $row = array(theme('node_admin_overview', array('name' => $name, 'type' => $type)));
-      // Set the edit column.
-      $row[] = array('data' => l(t('edit'), 'admin/structure/types/manage/' . $type->type));
+      $links['edit'] = array(
+        'title' => t('edit'),
+        'href' => 'admin/structure/types/manage/' . $type->type,
+        'weight' => 0,
+      );
 
       if ($field_ui) {
-        // Manage fields.
-        $row[] = array('data' => l(t('manage fields'), 'admin/structure/types/manage/' . $type->type . '/fields'));
-
-        // Display fields.
-        $row[] = array('data' => l(t('manage display'), 'admin/structure/types/manage/' . $type->type . '/display'));
+        $links['fields'] = array(
+          'title' => t('manage fields'),
+          'href' => 'admin/structure/types/manage/' . $type->type . '/fields',
+          'weight' => 5,
+        );
+        $links['display'] = array(
+          'title' => t('manage display'),
+          'href' => 'admin/structure/types/manage/' . $type->type . '/display',
+          'weight' => 10,
+        );
       }
 
-      // Set the delete column.
       if ($type->custom) {
-        $row[] = array('data' => l(t('delete'), 'admin/structure/types/manage/' . $type->type . '/delete'));
-      }
-      else {
-        $row[] = array('data' => '');
+        $links['delete'] = array(
+          'title' => t('delete'),
+          'href' => 'admin/structure/types/manage/' . $type->type . '/delete',
+          'weight' => 15,
+        );
       }
 
+      $row[] = array(
+        'data' => array(
+          '#type' => 'operations',
+          '#links' => $links,
+        ),
+      );
+
       $rows[] = $row;
     }
   }
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index 1780fb961b23..570ae88b17dc 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -251,7 +251,7 @@ function node_delete_confirm_submit($form, &$form_state) {
 function node_revision_overview($node) {
   drupal_set_title(t('Revisions for %title', array('%title' => $node->label())), PASS_THROUGH);
 
-  $header = array(t('Revision'), array('data' => t('Operations'), 'colspan' => 2));
+  $header = array(t('Revision'), t('Operations'));
 
   $revisions = node_revision_list($node);
 
@@ -266,25 +266,35 @@ function node_revision_overview($node) {
   }
   foreach ($revisions as $revision) {
     $row = array();
-    $operations = array();
-
     if ($revision->current_vid > 0) {
       $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid"), '!username' => theme('username', array('account' => $revision))))
                                . (($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''),
                      'class' => array('revision-current'));
-      $operations[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'), 'colspan' => 2);
+      $row[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'));
     }
     else {
       $row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid/revisions/$revision->vid/view"), '!username' => theme('username', array('account' => $revision))))
                . (($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : '');
       if ($revert_permission) {
-        $operations[] = l(t('revert'), "node/$node->nid/revisions/$revision->vid/revert");
+        $links['revert'] = array(
+          'title' => t('revert'),
+          'href' => "node/$node->nid/revisions/$revision->vid/revert",
+        );
       }
       if ($delete_permission) {
-        $operations[] = l(t('delete'), "node/$node->nid/revisions/$revision->vid/delete");
+        $links['delete'] = array(
+          'title' => t('delete'),
+          'href' => "node/$node->nid/revisions/$revision->vid/delete",
+        );
       }
+      $row[] = array(
+        'data' => array(
+          '#type' => 'operations',
+          '#links' => $links,
+        ),
+      );
     }
-    $rows[] = array_merge($row, $operations);
+    $rows[] = $row;
   }
 
   $build['node_revisions_table'] = array(
diff --git a/core/modules/openid/openid.pages.inc b/core/modules/openid/openid.pages.inc
index ee843e4a1bf5..f54617c60524 100644
--- a/core/modules/openid/openid.pages.inc
+++ b/core/modules/openid/openid.pages.inc
@@ -51,7 +51,20 @@ function openid_user_identities($account) {
 
   $result = db_query("SELECT * FROM {authmap} WHERE module='openid' AND uid=:uid", array(':uid' => $account->uid));
   foreach ($result as $identity) {
-    $rows[] = array(check_plain($identity->authname), l(t('Delete'), 'user/' . $account->uid . '/openid/delete/' . $identity->aid));
+    $row = array();
+    $row[] = check_plain($identity->authname);
+    $links = array();
+    $links['delete'] = array(
+      'title' => t('Delete'),
+      'href' => 'user/' . $account->uid . '/openid/delete/' . $identity->aid,
+    );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
+    $rows[] = $row;
   }
 
   $build['openid_table'] = array(
diff --git a/core/modules/path/path.admin.inc b/core/modules/path/path.admin.inc
index 4188effed9ae..5465141dd227 100644
--- a/core/modules/path/path.admin.inc
+++ b/core/modules/path/path.admin.inc
@@ -25,7 +25,7 @@ function path_admin_overview($keys = NULL) {
   if ($multilanguage) {
     $header[] = array('data' => t('Language'), 'field' => 'langcode');
   }
-  $header[] = array('data' => t('Operations'));
+  $header[] = t('Operations');
 
   $query = db_select('url_alias')
     ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
@@ -68,7 +68,7 @@ function path_admin_overview($keys = NULL) {
     );
     $row['data']['operations'] = array(
       'data' => array(
-        '#theme' => 'links',
+        '#type' => 'operations',
         '#links' => $operations,
         '#attributes' => array('class' => array('links', 'inline', 'nowrap')),
       ),
diff --git a/core/modules/shortcut/shortcut.admin.inc b/core/modules/shortcut/shortcut.admin.inc
index ca5d936c736f..e805acfc014a 100644
--- a/core/modules/shortcut/shortcut.admin.inc
+++ b/core/modules/shortcut/shortcut.admin.inc
@@ -157,21 +157,33 @@ function shortcut_set_switch_submit($form, &$form_state) {
  */
 function shortcut_set_admin() {
   $shortcut_sets = shortcut_sets();
-  $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => 4));
+  $header = array(t('Name'), t('Operations'));
 
   $rows = array();
   foreach ($shortcut_sets as $set) {
     $row = array(
       check_plain($set->title),
-      l(t('list links'), "admin/config/user-interface/shortcut/$set->set_name"),
-      l(t('edit set name'), "admin/config/user-interface/shortcut/$set->set_name/edit"),
+    );
+    $links['list'] = array(
+      'title' => t('list links'),
+      'href' => "admin/config/user-interface/shortcut/$set->set_name",
+    );
+    $links['edit'] = array(
+      'title' => t('list links'),
+      'href' => "admin/config/user-interface/shortcut/$set->set_name/edit",
     );
     if (shortcut_set_delete_access($set)) {
-      $row[] = l(t('delete set'), "admin/config/user-interface/shortcut/$set->set_name/delete");
-    }
-    else {
-      $row[] = '';
+      $links['delete'] = array(
+        'title' => t('delete set'),
+        'href' => "admin/config/user-interface/shortcut/$set->set_name/delete",
+      );
     }
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
 
     $rows[] = $row;
   }
@@ -271,8 +283,18 @@ function shortcut_set_customize($form, &$form_state, $shortcut_set) {
       '#attributes' => array('class' => array('shortcut-weight')),
     );
 
-    $form['shortcuts']['links'][$mlid]['edit']['#markup'] = l(t('edit'), 'admin/config/user-interface/shortcut/link/' . $mlid);
-    $form['shortcuts']['links'][$mlid]['delete']['#markup'] = l(t('delete'), 'admin/config/user-interface/shortcut/link/' . $mlid . '/delete');
+    $links['edit'] = array(
+      'title' => t('edit'),
+      'href' => "admin/config/user-interface/shortcut/link/$mlid",
+    );
+    $links['delete'] = array(
+      'title' => t('delete'),
+      'href' => "admin/config/user-interface/shortcut/link/$mlid/delete",
+    );
+    $form['shortcuts']['links'][$mlid]['operations'] = array(
+      '#type' => 'operations',
+      '#links' => $links,
+    );
   }
 
   $form['actions'] = array(
@@ -322,15 +344,14 @@ function theme_shortcut_set_customize($variables) {
     $row = array();
     $row[] = drupal_render($shortcut['name']);
     $row[] = drupal_render($shortcut['weight']);
-    $row[] = drupal_render($shortcut['edit']);
-    $row[] = drupal_render($shortcut['delete']);
+    $row[] = drupal_render($shortcut['operations']);
     $rows[] = array(
       'data' => $row,
       'class' => array('draggable'),
     );
   }
 
-  $header = array(t('Name'), t('Weight'), array('data' => t('Operations'), 'colspan' => 2));
+  $header = array(t('Name'), t('Weight'), t('Operations'));
   $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'shortcuts'), 'empty' => t('No shortcuts available. <a href="@link">Add a shortcut</a>.', array('@link' => url('admin/config/user-interface/shortcut/' . $form['#shortcut_set_name'] . '/add-link')))));
   $output .= drupal_render($form['actions']);
   $output = drupal_render_children($form) . $output;
diff --git a/core/modules/statistics/statistics.admin.inc b/core/modules/statistics/statistics.admin.inc
index b768e07f8e7b..bec1eb51f831 100644
--- a/core/modules/statistics/statistics.admin.inc
+++ b/core/modules/statistics/statistics.admin.inc
@@ -22,7 +22,7 @@ function statistics_recent_hits() {
     array('data' => t('Timestamp'), 'field' => 'a.timestamp', 'sort' => 'desc'),
     array('data' => t('Page'), 'field' => 'a.path'),
     array('data' => t('User'), 'field' => 'u.name'),
-    array('data' => t('Operations'))
+    t('Operations'),
   );
 
   $query = db_select('accesslog', 'a', array('target' => 'slave'))
@@ -38,11 +38,22 @@ function statistics_recent_hits() {
   $result = $query->execute();
   $rows = array();
   foreach ($result as $log) {
-    $rows[] = array(
-      array('data' => format_date($log->timestamp, 'short'), 'class' => array('nowrap')),
-      _statistics_format_item($log->title, $log->path),
-      theme('username', array('account' => $log)),
-      l(t('details'), "admin/reports/access/$log->aid"));
+    $row = array();
+    $row[] = array('data' => format_date($log->timestamp, 'short'), 'class' => array('nowrap'));
+    $row[] = _statistics_format_item($log->title, $log->path);
+    $row[] = theme('username', array('account' => $log));
+    $links = array();
+    $links['details'] = array(
+      'title' => t('details'),
+      'href' => "admin/reports/access/$log->aid",
+    );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
+    $rows[] = $row;
   }
 
   $build['statistics_table'] = array(
@@ -127,7 +138,7 @@ function statistics_top_visitors() {
     array('data' => t('Hits'), 'field' => 'hits', 'sort' => 'desc'),
     array('data' => t('Visitor'), 'field' => 'u.name'),
     array('data' => t('Total page generation time'), 'field' => 'total'),
-    array('data' => $ban_exists && user_access('ban IP addresses') ? t('Operations') : '', 'colspan' => 2),
+    $ban_exists && user_access('Ban IP addresses') ? t('Operations') : '',
   );
   $query = db_select('accesslog', 'a', array('target' => 'slave'))
     ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
@@ -163,19 +174,33 @@ function statistics_top_visitors() {
   $rows = array();
   $destination = drupal_get_destination();
   foreach ($result as $account) {
-    if ($ban_exists) {
+    $links = array();
+    if ($ban_exists && user_access('ban IP addresses') && !$account->uid) {
       if ($account->iid) {
-        $ban_link = l(t('unban IP address'), "admin/config/people/ban/delete/$account->iid", array('query' => $destination));
+        $links['unban'] = array(
+          'title' => t('unban IP address'),
+          'href' => "admin/config/people/ban/delete/$account->iid",
+          'query' => $destination,
+        );
       }
       else {
-        $ban_link = l(t('ban IP address'), "admin/config/people/ban/$account->hostname", array('query' => $destination));
+        $links['ban'] = array(
+          'title' => t('ban IP address'),
+          'href' => "admin/config/people/ban/$account->hostname",
+          'query' => $destination,
+        );
       }
     }
     $row = array();
     $row[] = $account->hits;
     $row[] = ($account->uid ? theme('username', array('account' => $account)) : $account->hostname);
     $row[] = format_interval(round($account->total / 1000));
-    $row[] = ($ban_exists && user_access('ban IP addresses') && !$account->uid ? $ban_link : '');
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
     $rows[] = $row;
   }
 
diff --git a/core/modules/statistics/statistics.pages.inc b/core/modules/statistics/statistics.pages.inc
index d4ac0f855e06..6cf110cf9788 100644
--- a/core/modules/statistics/statistics.pages.inc
+++ b/core/modules/statistics/statistics.pages.inc
@@ -19,10 +19,11 @@
 function statistics_node_tracker() {
   if ($node = node_load(arg(1))) {
     $header = array(
-        array('data' => t('Time'), 'field' => 'a.timestamp', 'sort' => 'desc'),
-        array('data' => t('Referrer'), 'field' => 'a.url'),
-        array('data' => t('User'), 'field' => 'u.name'),
-        array('data' => t('Operations')));
+      array('data' => t('Time'), 'field' => 'a.timestamp', 'sort' => 'desc'),
+      array('data' => t('Referrer'), 'field' => 'a.url'),
+      array('data' => t('User'), 'field' => 'u.name'),
+      t('Operations'),
+    );
 
     $query = db_select('accesslog', 'a', array('target' => 'slave'))
       ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
@@ -41,12 +42,22 @@ function statistics_node_tracker() {
     $result = $query->execute();
     $rows = array();
     foreach ($result as $log) {
-      $rows[] = array(
-        array('data' => format_date($log->timestamp, 'short'), 'class' => array('nowrap')),
-        _statistics_link($log->url),
-        theme('username', array('account' => $log)),
-        l(t('details'), "admin/reports/access/$log->aid"),
+      $row = array();
+      $row[] = array('data' => format_date($log->timestamp, 'short'), 'class' => array('nowrap'));
+      $row[] = _statistics_link($log->url);
+      $row[] = theme('username', array('account' => $log));
+      $links = array();
+      $links['details'] = array(
+        'title' => t('details'),
+        'href' => "admin/reports/access/$log->aid",
+      );
+      $row[] = array(
+        'data' => array(
+          '#type' => 'operations',
+          '#links' => $links,
+        ),
       );
+      $rows[] = $row;
     }
 
     // Do not use $node->label() here, because $node comes from the database.
@@ -78,9 +89,10 @@ function statistics_user_tracker() {
   if ($account = user_load(arg(1))) {
 
     $header = array(
-        array('data' => t('Timestamp'), 'field' => 'timestamp', 'sort' => 'desc'),
-        array('data' => t('Page'), 'field' => 'path'),
-        array('data' => t('Operations')));
+      array('data' => t('Timestamp'), 'field' => 'timestamp', 'sort' => 'desc'),
+      array('data' => t('Page'), 'field' => 'path'),
+      t('Operations'),
+    );
     $query = db_select('accesslog', 'a', array('target' => 'slave'))
       ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
       ->extend('Drupal\Core\Database\Query\TableSortExtender');
@@ -93,10 +105,21 @@ function statistics_user_tracker() {
     $result = $query->execute();
     $rows = array();
     foreach ($result as $log) {
-      $rows[] = array(
-        array('data' => format_date($log->timestamp, 'short'), 'class' => array('nowrap')),
-        _statistics_format_item($log->title, $log->path),
-        l(t('details'), "admin/reports/access/$log->aid"));
+      $row = array();
+      $row[] = array('data' => format_date($log->timestamp, 'short'), 'class' => array('nowrap'));
+      $row[] = _statistics_format_item($log->title, $log->path);
+      $links = array();
+      $links['details'] = array(
+        'title' => t('details'),
+        'href' => "admin/reports/access/$log->aid",
+      );
+      $row[] = array(
+        'data' => array(
+          '#type' => 'operations',
+          '#links' => $links,
+        ),
+      );
+      $rows[] = $row;
     }
 
     drupal_set_title(user_format_name($account));
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 2d7e1941c81e..3c7a13188436 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1990,15 +1990,19 @@ function system_date_time_settings() {
         '#options' => $choices,
       );
 
+      $links = array();
       // If this isn't a system provided type, allow the user to remove it from
       // the system.
       if ($type_info['locked'] == 0) {
-        $form['formats']['delete']['date_format_' . $type . '_delete'] = array(
-          '#type' => 'link',
-          '#title' => t('delete'),
-          '#href' => 'admin/config/regional/date-time/types/' . $type . '/delete',
+        $links['delete'] = array(
+          'title' => t('delete'),
+          'href' => "admin/config/regional/date-time/types/$type/delete",
         );
       }
+      $form['formats']['operations']["date_format_$type"] = array(
+        '#type' => 'operations',
+        '#links' => $links,
+      );
     }
   }
 
@@ -2026,12 +2030,11 @@ function theme_system_date_time_settings($variables) {
   );
 
   foreach (element_children($form['format']) as $key) {
-    $delete_key = $key . '_delete';
     $row = array();
     $row[] = $form['format'][$key]['#title'];
     $form['format'][$key]['#title_display'] = 'invisible';
     $row[] = array('data' => drupal_render($form['format'][$key]));
-    $row[] = array('data' => drupal_render($form['delete'][$delete_key]));
+    $row[] = drupal_render($form['operations'][$key]);
     $rows[] = $row;
   }
 
@@ -2725,7 +2728,7 @@ function system_delete_date_format_type_form_submit($form, &$form_state) {
  * Displays the date format strings overview page.
  */
 function system_date_time_formats() {
-  $header = array(t('Format'), array('data' => t('Operations'), 'colspan' => '2'));
+  $header = array(t('Format'), t('Operations'));
   $rows = array();
 
   drupal_static_reset('system_get_date_formats');
@@ -2734,8 +2737,21 @@ function system_date_time_formats() {
     foreach ($formats as $format) {
       $row = array();
       $row[] = array('data' => format_date(REQUEST_TIME, 'custom', $format['format']));
-      $row[] = array('data' => l(t('edit'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/edit'));
-      $row[] = array('data' => l(t('delete'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/delete'));
+      $links = array();
+      $links['edit'] = array(
+        'title' => t('edit'),
+        'href' => 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/edit',
+      );
+      $links['delete'] = array(
+        'title' => t('delete'),
+        'href' => 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/delete',
+      );
+      $row[] = array(
+        'data' => array(
+          '#type' => 'operations',
+          '#links' => $links,
+        ),
+      );
       $rows[] = $row;
     }
   }
@@ -2840,17 +2856,27 @@ function system_add_date_formats_form_submit($form, &$form_state) {
  * @see locale_menu()
  */
 function system_date_format_language_overview_page() {
-  $header = array(
-    t('Language'),
-    array('data' => t('Operations'), 'colspan' => '2'),
-  );
+  $header = array(t('Language'), t('Operations'));
 
   $languages = language_list();
   foreach ($languages as $langcode => $language) {
     $row = array();
     $row[] = $language->name;
-    $row[] = l(t('edit'), 'admin/config/regional/date-time/locale/' . $langcode . '/edit');
-    $row[] = l(t('reset'), 'admin/config/regional/date-time/locale/' . $langcode . '/reset');
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('edit'),
+      'href' => "admin/config/regional/date-time/locale/$langcode/edit",
+    );
+    $links['reset'] = array(
+      'title' => t('reset'),
+      'href' => "admin/config/regional/date-time/locale/$langcode/reset",
+    );
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+    );
     $rows[] = $row;
   }
 
diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc
index ecef0b587376..be305f63e4c9 100644
--- a/core/modules/taxonomy/taxonomy.admin.inc
+++ b/core/modules/taxonomy/taxonomy.admin.inc
@@ -28,9 +28,23 @@ function taxonomy_overview_vocabularies($form) {
       '#delta' => 10,
       '#default_value' => $vocabulary->weight,
     );
-    $form[$vocabulary->vid]['edit'] = array('#type' => 'link', '#title' => t('edit vocabulary'), '#href' => "admin/structure/taxonomy/$vocabulary->machine_name/edit");
-    $form[$vocabulary->vid]['list'] = array('#type' => 'link', '#title' => t('list terms'), '#href' => "admin/structure/taxonomy/$vocabulary->machine_name");
-    $form[$vocabulary->vid]['add'] = array('#type' => 'link', '#title' => t('add terms'), '#href' => "admin/structure/taxonomy/$vocabulary->machine_name/add");
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('edit vocabulary'),
+      'href' => "admin/structure/taxonomy/$vocabulary->machine_name/edit",
+    );
+    $links['list'] = array(
+      'title' => t('list terms'),
+      'href' => "admin/structure/taxonomy/$vocabulary->machine_name",
+    );
+    $links['add'] = array(
+      'title' => t('add terms'),
+      'href' => "admin/structure/taxonomy/$vocabulary->machine_name/add",
+    );
+    $form[$vocabulary->vid]['operations'] = array(
+      '#type' => 'operations',
+      '#links' => $links,
+    );
   }
 
   // Only make this form include a submit button and weight if more than one
@@ -85,9 +99,7 @@ function theme_taxonomy_overview_vocabularies($variables) {
         $vocabulary['weight']['#attributes']['class'] = array('vocabulary-weight');
         $row[] = drupal_render($vocabulary['weight']);
       }
-      $row[] = drupal_render($vocabulary['edit']);
-      $row[] = drupal_render($vocabulary['list']);
-      $row[] = drupal_render($vocabulary['add']);
+      $row[] = drupal_render($vocabulary['operations']);
       $rows[] = array('data' => $row, 'class' => array('draggable'));
     }
   }
@@ -97,7 +109,7 @@ function theme_taxonomy_overview_vocabularies($variables) {
     $header[] = t('Weight');
     drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'vocabulary-weight');
   }
-  $header[] = array('data' => t('Operations'), 'colspan' => '3');
+  $header[] = t('Operations');
   return theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No vocabularies available. <a href="@link">Add vocabulary</a>.', array('@link' => url('admin/structure/taxonomy/add'))), 'attributes' => array('id' => 'taxonomy'))) . drupal_render_children($form);
 }
 
@@ -278,7 +290,7 @@ function taxonomy_overview_terms($form, &$form_state, Vocabulary $vocabulary) {
       'delete' => array('title' => t('delete'), 'href' => 'taxonomy/term/' . $term->tid . '/delete', 'query' => drupal_get_destination()),
     );
     $form[$key]['operations'] = array(
-      '#theme' => 'links',
+      '#type' => 'operations',
       '#links' => $operations,
       '#attributes' => array('class' => array('links', 'inline')),
     );
diff --git a/core/modules/translation/translation.pages.inc b/core/modules/translation/translation.pages.inc
index a501870a9bd2..e33f53ecc9e3 100644
--- a/core/modules/translation/translation.pages.inc
+++ b/core/modules/translation/translation.pages.inc
@@ -46,10 +46,13 @@ function translation_node_overview(Node $node) {
       $links = language_negotiation_get_switch_links($type, $path);
       $title = empty($links->links[$langcode]['href']) ? l($translation_node->label(), $path) : l($translation_node->label(), $links->links[$langcode]['href'], $links->links[$langcode]);
       if (node_access('update', $translation_node)) {
-        $text = t('edit');
         $path = 'node/' . $translation_node->nid . '/edit';
         $links = language_negotiation_get_switch_links($type, $path);
-        $options[] = empty($links->links[$langcode]['href']) ? l($text, $path) : l($text, $links->links[$langcode]['href'], $links->links[$langcode]);
+        if (!empty($links->links[$langcode]['href'])) {
+          $options['edit'] = array(
+            'title' => t('edit'),
+          ) + $links->links[$langcode];
+        }
       }
       $status = $translation_node->status ? t('Published') : t('Not published');
       $status .= $translation_node->translate ? ' - <span class="marker">' . t('outdated') . '</span>' : '';
@@ -61,15 +64,28 @@ function translation_node_overview(Node $node) {
       // No such translation in the set yet: help user to create it.
       $title = t('n/a');
       if (node_access('create', $node)) {
-        $text = t('add translation');
         $path = 'node/add/' . $node->type;
         $links = language_negotiation_get_switch_links($type, $path);
         $query = array('query' => array('translation' => $node->nid, 'target' => $langcode));
-        $options[] = empty($links->links[$langcode]['href']) ? l($text, $path, $query) : l($text, $links->links[$langcode]['href'], array_merge_recursive($links->links[$langcode], $query));
+        if (!empty($links->links[$langcode]['href'])) {
+          $options['add'] = array(
+            'title' => t('add translation'),
+          ) + $links->links[$langcode];
+          $options['add'] = array_merge_recursive($options['add'], $query);
+        }
       }
       $status = t('Not translated');
     }
-    $rows[] = array($language_name, $title, $status, implode(" | ", $options));
+    $row[] = $language_name;
+    $row[] = $title;
+    $row[] = $status;
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $options,
+      ),
+    );
+    $rows[] = $row;
   }
 
   drupal_set_title(t('Translations of %title', array('%title' => $node->label())), PASS_THROUGH);
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
index 180b9f1d0778..396f4c0e76c2 100644
--- a/core/modules/user/user.admin.inc
+++ b/core/modules/user/user.admin.inc
@@ -149,7 +149,7 @@ function user_admin_account() {
     'roles' => array('data' => t('Roles'), 'class' => array(RESPONSIVE_PRIORITY_LOW)),
     'member_for' => array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
     'access' => array('data' => t('Last access'), 'field' => 'u.access', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'operations' => array('data' => t('Operations')),
+    'operations' => t('Operations'),
   );
 
   $query = db_select('users', 'u');
@@ -210,7 +210,16 @@ function user_admin_account() {
       'roles' => theme('item_list', array('items' => $users_roles)),
       'member_for' => format_interval(REQUEST_TIME - $account->created),
       'access' =>  $account->access ? t('@time ago', array('@time' => format_interval(REQUEST_TIME - $account->access))) : t('never'),
-      'operations' => array('data' => array('#type' => 'link', '#title' => t('edit'), '#href' => "user/$account->uid/edit", '#options' => array('query' => $destination))),
+    );
+    $links = array();
+    $links['edit'] = array(
+      'title' => t('edit'),
+      'href' => "user/$account->uid/edit",
+      'query' => $destination,
+    );
+    $options[$account->uid]['operations']['data'] = array(
+      '#type' => 'operations',
+      '#links' => $links,
     );
   }
 
@@ -886,15 +895,19 @@ function user_admin_roles($form, $form_state) {
       '#default_value' => $role->weight,
       '#attributes' => array('class' => array('role-weight')),
     );
-    $form['roles'][$role->rid]['edit'] = array(
-      '#type' => 'link',
-      '#title' => t('edit role'),
-      '#href' => 'admin/people/roles/edit/' . $role->rid,
+    $links['edit'] = array(
+      'title' => t('edit role'),
+      'href' => 'admin/people/roles/edit/' . $role->rid,
+      'weight' => 0,
+    );
+    $links['permissions'] = array(
+      'title' => t('edit permissions'),
+      'href' => 'admin/people/permissions/' . $role->rid,
+      'weight' => 5,
     );
-    $form['roles'][$role->rid]['permissions'] = array(
-      '#type' => 'link',
-      '#title' => t('edit permissions'),
-      '#href' => 'admin/people/permissions/' . $role->rid,
+    $form['roles'][$role->rid]['operations'] = array(
+      '#type' => 'operations',
+      '#links' => $links,
     );
   }
 
@@ -946,7 +959,7 @@ function user_admin_roles_order_submit($form, &$form_state) {
 function theme_user_admin_roles($variables) {
   $form = $variables['form'];
 
-  $header = array(t('Name'), t('Weight'), array('data' => t('Operations'), 'colspan' => 2));
+  $header = array(t('Name'), t('Weight'), t('Operations'));
   foreach (element_children($form['roles']) as $rid) {
     $row = array();
     foreach (element_children($form['roles'][$rid]) as $column) {
-- 
GitLab