commit d676b319d7c4daf744dacf318d31c4b04da20a4a
Author: Bart Feenstra <bart@mynameisbart.com>
Date:   Wed Mar 26 14:15:52 2014 +0100

    foo

diff --git a/core/modules/node/config/views.view.content.yml b/core/modules/node/config/views.view.content.yml
index 074c5b5..95cba6b 100644
--- a/core/modules/node/config/views.view.content.yml
+++ b/core/modules/node/config/views.view.content.yml
@@ -251,54 +251,6 @@ display:
           timezone: ''
           plugin_id: date
           provider: views
-        edit_node:
-          id: edit_node
-          table: node
-          field: edit_node
-          label: ''
-          exclude: true
-          text: Edit
-          plugin_id: node_link_edit
-          provider: node
-        delete_node:
-          id: delete_node
-          table: node
-          field: delete_node
-          label: ''
-          exclude: true
-          text: Delete
-          plugin_id: node_link_delete
-          provider: node
-        translation_link:
-          id: translation_link
-          table: node
-          field: translation_link
-          label: ''
-          exclude: true
-          alter:
-            alter_text: false
-          element_class: ''
-          element_default_classes: true
-          hide_alter_empty: true
-          hide_empty: false
-          empty_zero: false
-          empty: ''
-          text: Translate
-          optional: true
-          plugin_id: content_translation_link
-          provider: content_translation
-        dropbutton:
-          id: dropbutton
-          table: views
-          field: dropbutton
-          label: Operations
-          fields:
-            edit_node: edit_node
-            delete_node: delete_node
-            translation_link: translation_link
-          destination: true
-          plugin_id: dropbutton
-          provider: views
         timestamp:
           id: timestamp
           table: history
@@ -352,6 +304,57 @@ display:
           optional: true
           plugin_id: history_user_timestamp
           provider: history
+        operations:
+          id: operations
+          table: node
+          field: operations
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Operations
+          exclude: 0
+          alter:
+            alter_text: 0
+            text: ''
+            make_link: 0
+            path: ''
+            absolute: 0
+            external: 0
+            replace_spaces: 0
+            path_case: none
+            trim_whitespace: 0
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: 0
+            max_length: ''
+            word_boundary: 1
+            ellipsis: 1
+            more_link: 0
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: 0
+            trim: 0
+            preserve_tags: ''
+            html: 0
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: 1
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: 1
+          empty: ''
+          hide_empty: 0
+          empty_zero: 0
+          hide_alter_empty: 1
+          destination: 1
+          plugin_id: entity_operations
+          provider: views
       filters:
         status_extra:
           id: status_extra
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/EntityOperations.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/EntityOperations.php
new file mode 100644
index 0000000..72f4842
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/EntityOperations.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\field\EntityOperations.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\views\ResultRow;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Renders all operations links for an entity.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @PluginID("entity_operations")
+ */
+class EntityOperations extends FieldPluginBase {
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * Constructor.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param array $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *    The entity manager.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManagerInterface $entity_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+    return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity.manager'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function usesGroupBy() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defineOptions() {
+    $options = parent::defineOptions();
+
+    $options['destination'] = array(
+      'default' => TRUE,
+      'bool' => TRUE,
+    );
+
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildOptionsForm(&$form, &$form_state) {
+    parent::buildOptionsForm($form, $form_state);
+
+    $form['destination'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Include destination'),
+      '#description' => $this->t('Include a <code>destination</code> parameter in the link to return the user to the original view upon completing the link action.'),
+      '#default_value' => $this->options['destination'],
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render(ResultRow $values) {
+    $entity = $this->getEntity($values);
+    $operations = $this->entityManager->getListBuilder($this->getEntityType())->getOperations($entity);
+    if ($this->options['destination']) {
+      foreach ($operations as &$operation) {
+        if (!isset($operation['options']['query'])) {
+          $operation['options']['query'] = array();
+        }
+        $operation['options']['query'] += $this->drupalGetDestination();
+      }
+    }
+    $build = array(
+      '#type' => 'operations',
+      '#links' => $operations,
+    );
+
+    return $build;
+  }
+
+  /**
+   * Wraps drupal_get_destination().
+   */
+  protected function drupalGetDestination() {
+    return drupal_get_destination();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+  }
+}
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/views/field/EntityOperationsUnitTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/views/field/EntityOperationsUnitTest.php
new file mode 100644
index 0000000..df65a81
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/views/field/EntityOperationsUnitTest.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * @file Contains \Drupal\views\Tests\Plugin\views\field\EntityOperationsUnitTest.
+ */
+
+namespace Drupal\views\Tests\Plugin\views\field;
+
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests \Drupal\views\Plugin\views\field\EntityOperations.
+ */
+class EntityOperationsUnitTest extends UnitTestCase {
+
+  /**
+   * The entity manager used for testing.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityManager;
+
+  /**
+   * The plugin under test.
+   *
+   * @var \Drupal\views\Plugin\views\field\EntityOperations|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $plugin;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'description' => '',
+      'name' => '\Drupal\views\Plugin\views\field\EntityOperations',
+      'group' => 'Views Plugins',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
+
+    $configuration = array();
+    $plugin_id = $this->randomName();
+    $plugin_definition = array();
+    $this->plugin = $this->getMockBuilder('\Drupal\views\Plugin\views\field\EntityOperations')
+      ->setConstructorArgs(array($configuration, $plugin_id, $plugin_definition, $this->entityManager))
+      ->setMethods(array('drupalGetDestination', 'getEntity', 't'))
+      ->getMock();
+  }
+
+  /**
+   * Tests usesGroupBy().
+   */
+  public function testUsesGroupBy() {
+    $this->assertFalse($this->plugin->usesGroupBy());
+  }
+
+  /**
+   * Tests buildOptionsForm().
+   */
+  public function testBuildOptionsForm() {
+    $form = array();
+    $form_state = array();
+    $form = $this->plugin->buildOptionsForm($form, $form_state);
+    $this->assertInternalType('array', $form);
+    $this->assertArrayHasKey('destination', $form);
+  }
+
+  /**
+   * Tests defineOptions().
+   */
+  public function testDefineOptions() {
+    $options = $this->plugin->defineOptions();
+    $this->assertInternalType('array', $options);
+    $this->assertArrayHasKey('destination', $options);
+  }
+
+  /**
+   * Tests render().
+   */
+  public function testRenderWithDestination() {
+    $entity = $this->getMockBuilder('\Drupal\user\Entity\Role')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->plugin->expects($this->any())
+      ->method('getEntity')
+      ->will($this->returnValue($entity));
+    $this->plugin->expects($this->once())
+      ->method('drupalGetDestination')
+      ->will($this->returnValue(array()));
+    $this->plugin->options['destination'] = TRUE;
+
+    $result = $this->getMock('\Drupal\views\ResultRow');
+
+    $build = $this->plugin->render($result);
+    $this->assertInternalType('array', $build);
+  }
+
+  /**
+   * Tests render().
+   */
+  public function testRenderWithoutDestination() {
+    $entity = $this->getMockBuilder('\Drupal\user\Entity\Role')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->plugin->expects($this->any())
+      ->method('getEntity')
+      ->will($this->returnValue($entity));
+    $this->plugin->expects($this->never())
+      ->method('drupalGetDestination');
+    $this->plugin->options['destination'] = FALSE;
+
+    $result = $this->getMock('\Drupal\views\ResultRow');
+
+    $build = $this->plugin->render($result);
+    $this->assertInternalType('array', $build);
+  }
+}
diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc
index d11c4d7..5430c19 100644
--- a/core/modules/views/views.views.inc
+++ b/core/modules/views/views.views.inc
@@ -128,6 +128,16 @@ function views_views_data() {
         ),
       );
     }
+
+    if ($entity_type->getBaseTable()) {
+      $data[$entity_type->getBaseTable()]['operations'] = array(
+        'field' => array(
+          'title' => t('Operations links'),
+          'help' => t('Provide links to perform entity operations.'),
+          'id' => 'entity_operations',
+        ),
+      );
+    }
   }
 
   // Registers an action bulk form per entity.
