bulk screen image

Custom Actions in the Views Bulk Operations Module in Drupal

The Views Bulk Operations module is used when performing bulk operations on results to tables or other items created with Views. In this example, I will show you how to add your own action that will change the state (Workflows) of selected entries.

vbo13

The action is performed on all selected entries and automatically performed in Batch queues so as not to overload the system and not to "crash" the page when the process takes too long. If you know what you are doing and you have reasons for it, you can turn off the queuing option in the field configuration.

VBO installation and configuration

The Views Bulk Operations module can be installed by downloading and unpacking the package in the modules/contrib directory or by using the composer command, which will download the latest stable version of the module.

composer require drupal/views_bulk_operations

After installation, the module does not need to be configured in any way, you just add the appropriate field to the page (or block) created by the Views module.

  • Add a new Content view that creates the page.
  • Set the display format to Table. It is not necessary, the checkboxes on the table simply look more natural
  • Add the title field
  • Add the Views bulk operations field

vbo3

Action configuration for a VBO field

In the field configuration, you can mark which mass actions can be performed on the results. There are over a dozen predefined actions, such as:

  • deleting entries
  • publishing
  • sticking

vbo4

The above list is compatible when the view displays Content. If you add the view that displays Users, you will notice that the list has different actions.

Adding own action to VBO

The goal is to add your own actions on entries (Content) that will change the state (Workflows) of the selected entries.

Entry states are not available immediately after Drupal installation. In order to do this, you need to enable two additional modules that are directly in Drupal, you do not need to additionally download anything.

Go to the page with modules - admin/modules

Enable the modules: Workflows and Content moderation

vbo7

Workflows configuration

For the purposes of this post, I will use automatically-defined states for the Editorial type, which is available immediately after enabling the Content moderation module.

All you need to do is enable a given type of state transition for the selected content types.

  • On the page admin/config/workflow/workflows edit the Editorial type
  • Set the Article content type

vbo9

This operation will add a new field to every add/edit article form.

vbo10

To the view where the VBO field is, add

  • a field displaying the state of a given entry - Content: Moderation state
  • a filter that will limit the display of entries of the Article type

If added is a filter restricting entries to published only Content: Published (= Yes) then remove it. It will block the display of entries that have a state of e.g. Draftor Archived, which are not being published by definition.

Own action code

Create your own module end enable it. In my case, the module's name is d_workflows

In the module's directory create a directory structuresrc/Plugin/Action

Create in it a new class file for the action to change the state of the entry into Published.

Filename: PublishNodeAction.php

/**
 * Content moderation publish node.
 *
 * @Action(
 *   id = "d_workflows_publish_node_action",
 *   label = @Translation("Publish node (moderation_state)"),
 *   type = "node",
 *   confirm = TRUE
 * )
 */

class PublishNodeAction extends ViewsBulkOperationsActionBase {

Class annotation is important here.

You set in it an id for the action – this can be any unique value. It is worth to include in its name the name of the module and what the given action is doing.

Label is the value that will appear in the VBO field configuration

Type is the type of entity, for which the new action will be available. In this case, it is node, but it can be, for example, user, taxonomy_term, or any other.

This class inherits from ViewsBulkOperationsActionBase

It requires declaring two methods:

  • execute() - what is to be executed for each record
  • access() - who can perform the action

Example of a code for the execute() method

/**
 * {@inheritdoc}
 */
public function execute(ContentEntityInterface $entity = NULL) {
  if (!$state = $entity->get('moderation_state')->getString()) {
    return $this->t(':title  - can\'t change state',
      [
        ':title' => $entity->getTitle(),
      ]
    );
  }

  switch ($state) {
    case 'archived':
    case 'draft':
      $entity->set('moderation_state', 'published');
      $entity->save();
      break;
  }

  return $this->t(':title state changed to :state',
    [
      ':title' => $entity->getTitle(),
      ':state' => $entity->get('moderation_state')->getString(),
    ]
  );
}

The first condition checks whether the given entity has the state setting option enabled.

In this case it is only Article. When the action is initiated on entries with no states enabled (empty $entity->get('moderation_state')->getString()) it will be interrupted and an information about it will be displayed.

The condition can be easily extended by e.g. checking the content type for the entity

$entity->bundle()

Further down the code, the current status of the entry is being checked and if the condition is met, the code changes its status into Published.

$entity->set('moderation_state', 'published');

Example of a code for the access() method

  /**
   * {@inheritdoc}
   */
  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
    if ($object instanceof Node) {
      $can_update = $object->access('update', $account, TRUE);
      $can_edit = $object->access('edit', $account, TRUE);

      return $can_edit->andIf($can_update)->isAllowed();
    }

    return FALSE;
  }

In this simple condition, it is being checked whether you are operating on Node type objects and whether the user performing the action has the authorisation to edit and update it. Otherwise, the code will return a FALSE, and the action will not be executed.

The whole PublishNodeAction.php file looks like this:

<?php

namespace Drupal\d_workflows\Plugin\Action;

use Drupal\node\Entity\Node;
use Drupal\views_bulk_operations\Action\ViewsBulkOperationsActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Entity\ContentEntityInterface;

/**
 * Content moderation publish node.
 *
 * @Action(
 *   id = "d_workflows_publish_node_action",
 *   label = @Translation("Publish node (moderation_state)"),
 *   type = "node",
 *   confirm = TRUE
 * )
 */

class PublishNodeAction extends ViewsBulkOperationsActionBase {

  use StringTranslationTrait;

  /**
   * {@inheritdoc}
   */
  public function execute(ContentEntityInterface $entity = NULL) {
    if (!$state = $entity->get('moderation_state')->getString()) {
      return $this->t(':title  - can\'t change state',
        [
          ':title' => $entity->getTitle(),
        ]
      );
    }

    switch ($state) {
      case 'archived':
      case 'draft':
        $entity->set('moderation_state', 'published');
        $entity->save();
        break;
    }

    return $this->t(':title state changed to :state',
      [
        ':title' => $entity->getTitle(),
        ':state' => $entity->get('moderation_state')->getString(),
      ]
    );
  }

  /**
   * {@inheritdoc}
   */
  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
    if ($object instanceof Node) {
      $can_update = $object->access('update', $account, TRUE);
      $can_edit = $object->access('edit', $account, TRUE);

      return $can_edit->andIf($can_update)->isAllowed();
    }

    return FALSE;
  }
}

A new action will be available in the VBO field edit

vbo14

Summary

Experienced Drupal developers as well as Drupal consultants ensure that adding new actions to Views Bulk Operations is simple and comes down to creating an additional class with two methods. Based on this simple example, you can build more complex mass actions on entities of various types.

3. Best practices for software development teams