You are here

multiversion.module in Multiversion 8

Same filename and directory in other branches
  1. 8.2 multiversion.module

File

multiversion.module
View source
<?php

use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Utility\Error;
use Drupal\multiversion\Entity\Storage\ContentEntityStorageInterface;
use Drupal\multiversion\Entity\WorkspaceInterface;
use Drupal\multiversion\MultiversionFieldItemList;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\ViewExecutable;
use Drupal\Core\Entity\EntityInterface;
use Drupal\multiversion\Entity\Workspace;

/**
 * Implements hook_module_implements_alter().
 */
function multiversion_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'entity_type_alter') {
    $group = $implementations['multiversion'];
    unset($implementations['multiversion']);
    $implementations = [
      'multiversion' => $group,
    ] + $implementations;
  }
  if ($hook == 'field_info_alter') {
    $group = $implementations['multiversion'];
    unset($implementations['multiversion']);
    $implementations['multiversion'] = $group;
  }
}

/**
 * Implements hook_entity_type_alter().
 *
 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
 */
function multiversion_entity_type_alter(array &$entity_types) {

  /** @var \Drupal\multiversion\MultiversionManagerInterface $manager */
  $manager = \Drupal::service('multiversion.manager');
  foreach ($entity_types as $entity_type) {
    if ($manager
      ->allowToAlter($entity_type)) {

      // Make all content entity types revisionable.
      if (!$entity_type
        ->isRevisionable()) {

        // We only need to set the revision key to make an entity type
        // revisionable. The table names will be handled by the storage class.
        // @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout
        $keys = $entity_type
          ->getKeys();
        $keys['revision'] = 'revision_id';
        $entity_type
          ->set('entity_keys', $keys);
        if ($entity_type
          ->getRevisionTable() === null) {
          $entity_type
            ->set('revision_table', $entity_type
            ->id() . '_revision');
        }
        if ($entity_type
          ->getRevisionDataTable() === null) {
          $entity_type
            ->set('revision_data_table', $entity_type
            ->id() . '_field_revision');
        }
      }
      $namespace = 'Drupal\\multiversion\\Entity\\Storage\\Sql';
      $original_storage_class = $entity_type
        ->getHandlerClass('storage');
      $entity_type
        ->setHandlerClass('original_storage', $original_storage_class);
      switch ($entity_type
        ->id()) {
        case 'node':
          $entity_type
            ->setHandlerClass('storage', "{$namespace}\\NodeStorage");
          break;
        case 'taxonomy_term':
          $entity_type
            ->setHandlerClass('storage', "{$namespace}\\TermStorage");
          break;
        case 'comment':
          $entity_type
            ->setHandlerClass('storage', "{$namespace}\\CommentStorage");
          break;
        case 'menu_link_content':
          $entity_type
            ->setClass('Drupal\\multiversion\\Entity\\MenuLinkContent');
          $entity_type
            ->setHandlerClass('storage', "{$namespace}\\MenuLinkContentStorage");
          break;
        case 'file':
          $entity_type
            ->setHandlerClass('storage', "{$namespace}\\FileStorage");
          break;
        case 'media':
          $entity_type
            ->setHandlerClass('storage', "{$namespace}\\MediaStorage");
          break;
        case 'paragraph':
          $entity_type
            ->setClass('Drupal\\multiversion\\Entity\\Paragraph');
          $entity_type
            ->setHandlerClass('storage', "{$namespace}\\ContentEntityStorage");
          break;
        case 'poll':
          $entity_type
            ->setHandlerClass('storage', "{$namespace}\\PollStorage");
          break;
        case 'crop':
          $entity_type
            ->setHandlerClass('storage', "{$namespace}\\CropStorage");
          break;
        case 'redirect':
          $entity_type
            ->setHandlerClass('storage_schema', 'Drupal\\multiversion\\Redirect\\RedirectStorageSchema');
          if (in_array($original_storage_class, [
            NULL,
            'Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage',
          ])) {
            $entity_type
              ->setHandlerClass('storage', "{$namespace}\\ContentEntityStorage");
          }
          break;
        default:

          // We can only override the storage handler for entity types we know
          // what to expect of.
          if (in_array($original_storage_class, [
            NULL,
            'Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage',
          ])) {
            $entity_type
              ->setHandlerClass('storage', "{$namespace}\\ContentEntityStorage");
          }
          break;
      }
    }
  }
  if (isset($entity_types['block_content']) && $manager
    ->allowToAlter($entity_types['block_content'])) {
    $entity_types['block']
      ->setHandlerClass('storage', 'Drupal\\multiversion\\Entity\\Storage\\Sql\\BlockStorage');
  }
}

/**
 * Implements hook_entity_base_field_info().
 *
 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
 * @return \Drupal\Core\Field\BaseFieldDefinition[]
 */
function multiversion_entity_base_field_info(EntityTypeInterface $entity_type) {

  /** @var \Drupal\multiversion\MultiversionManagerInterface $manager */
  $manager = \Drupal::service('multiversion.manager');
  if ($manager
    ->allowToAlter($entity_type)) {
    $fields = [];

    // Get the minor version only from the \Drupal::VERSION string.
    $minor_version = substr(\Drupal::VERSION, 0, 3);

    // @todo: Alter the entity label field to make it revisionable.
    // In some scenarios where's in a state of limbo where we've already
    // altered and enabled the entity type but we're given an old entity type
    // definition for this hook and we get an empty revision key. However,
    // these are always the entity types that Multiversion has enabled revisions
    // on, so we can assume the same name of the revision key.
    $revision_key = $entity_type
      ->getKey('revision') ?: 'revision_id';

    // This will essentially overwrite the revision field definition but also
    // ensure that entity types that we enabled revisions for get a revision
    // field definition of a type that we expect.
    $fields[$revision_key] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Revision ID'))
      ->setDescription(t('The local revision ID of the entity.'))
      ->setReadOnly(TRUE)
      ->setSetting('unsigned', TRUE);

    // Add the revision_default field on 8.5 or higher.
    if (version_compare($minor_version, '8.5', '>=')) {
      $fields['revision_default'] = BaseFieldDefinition::create('boolean')
        ->setLabel(t('Default revision'))
        ->setDescription(t('A flag indicating whether this was a default revision when it was saved.'))
        ->setStorageRequired(TRUE)
        ->setTranslatable(FALSE)
        ->setRevisionable(TRUE)
        ->setInitialValue(TRUE);
    }

    // This field shouldn't really be revisionable since all revisions for an
    // entity will only ever exist in one and the same workspace. But we mark
    // this as revisionable to make the storage query more performance because
    // then we don't need to join the data table (which it isn't by default).
    if ($entity_type
      ->get('workspace') !== FALSE) {
      $fields['workspace'] = BaseFieldDefinition::create('workspace_reference')
        ->setLabel(t('Workspace reference'))
        ->setDescription(t('The workspace this entity belongs to.'))
        ->setSetting('target_type', 'workspace')
        ->setRevisionable(FALSE)
        ->setTranslatable(FALSE)
        ->setCardinality(1)
        ->setReadOnly(TRUE);
    }
    $fields['_deleted'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Deleted flag'))
      ->setDescription(t('Indicates if the entity is flagged as deleted or not.'))
      ->setRevisionable(TRUE)
      ->setTranslatable(FALSE)
      ->setDefaultValue(FALSE)
      ->setCardinality(1);
    $fields['_rev'] = BaseFieldDefinition::create('revision_token')
      ->setLabel(t('Revision token'))
      ->setDescription(t('The token for this entity revision.'))
      ->setRevisionable(TRUE)
      ->setTranslatable(FALSE)
      ->setCardinality(1)
      ->setReadOnly(TRUE);

    // Add the 'revision_translation_affected' field if needed. Limit this to
    // Drupal version 8.4 and higher.
    if (version_compare($minor_version, '8.4', '>=') && $entity_type
      ->isTranslatable()) {
      $fields[$entity_type
        ->getKey('revision_translation_affected')] = BaseFieldDefinition::create('boolean')
        ->setName($entity_type
        ->getKey('revision_translation_affected'))
        ->setTargetEntityTypeId($entity_type
        ->id())
        ->setTargetBundle(NULL)
        ->setLabel(new TranslatableMarkup('Revision translation affected'))
        ->setDescription(new TranslatableMarkup('Indicates if the last edit of a translation belongs to current revision.'))
        ->setReadOnly(TRUE)
        ->setRevisionable(TRUE)
        ->setTranslatable(TRUE);
    }
    return $fields;
  }
}

/**
 * Implements hook_data_type_info_alter().
 */
function multiversion_data_type_info_alter(&$info) {
  $info['entity_reference']['class'] = '\\Drupal\\multiversion\\EntityReference';
}

/**
 * Implements hook_field_info_alter().
 */
function multiversion_field_info_alter(&$info) {
  if (\Drupal::state()
    ->get('multiversion_uninstalling', FALSE)) {
    return;
  }
  $info['uuid']['class'] = '\\Drupal\\multiversion\\Field\\UuidItem';
  $info['entity_reference']['class'] = '\\Drupal\\multiversion\\EntityReferenceItem';
  $info['file']['class'] = '\\Drupal\\multiversion\\FileItem';
  $info['image']['class'] = '\\Drupal\\multiversion\\ImageItem';
  if (isset($info['entity_reference_revisions'])) {
    $info['entity_reference_revisions']['class'] = '\\Drupal\\multiversion\\EntityReferenceRevisionsItem';
  }
  if (\Drupal::moduleHandler()
    ->moduleExists('pathauto')) {
    $info['path']['list_class'] = MultiversionFieldItemList::class;
  }
}

/**
 * Implements hook_entity_base_field_info_alter().
 *
 * @param array $fields
 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
 */
function multiversion_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {

  /** @var \Drupal\multiversion\MultiversionManagerInterface $manager */
  $manager = \Drupal::service('multiversion.manager');
  if ($manager
    ->allowToAlter($entity_type)) {
    $exclude_fields = [
      $entity_type
        ->getKey('id'),
      $entity_type
        ->getKey('revision'),
      $entity_type
        ->getKey('uuid'),
      $entity_type
        ->getKey('bundle'),
      $entity_type
        ->getKey('langcode'),
      'workspace',
      '_deleted',
      '_rev',
    ];
    if ($entity_type
      ->id() == 'comment') {
      $exclude_fields[] = 'comment_type';
    }
    foreach ($fields as $key => $field) {
      if (!in_array($key, $exclude_fields)) {
        $field
          ->setRevisionable(TRUE);
      }
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_update().
 */
function multiversion_workspace_update(EntityInterface $entity) {

  /** @var \Drupal\multiversion\Entity\WorkspaceInterface $entity */
  if (!$entity
    ->isPublished() && $entity->original
    ->isPublished()) {
    $default_workspace = \Drupal::getContainer()
      ->getParameter('workspace.default');
    \Drupal::service('workspace.manager')
      ->setActiveWorkspace(Workspace::load($default_workspace));
  }
}

/**
 * Implements hook_ENTITY_TYPE_presave().
 */
function multiversion_workspace_presave(EntityInterface $entity) {
  if ($entity
    ->isDefaultWorkspace() && !$entity
    ->isPublished() && $entity->original
    ->isPublished()) {
    throw new Exception('The default workspace cannot be archived.');
  }
}

/**
 * Implements hook_ENTITY_TYPE_update().
 */
function multiversion_paragraph_update(EntityInterface $entity) {

  // Update the target_revision_id field value for the parent entity if it
  // changed (for example when saving a revision after a stub revision during
  // replication).
  $parent = $entity
    ->getParentEntity();
  $entity_revision_parent_field_name = $entity
    ->getEntityType()
    ->get('entity_revision_parent_field_name_field');
  if ($parent instanceof ContentEntityInterface && !empty($entity->workspace) && $entity_revision_parent_field_name && !empty($entity->{$entity_revision_parent_field_name}->value) && ($entity->_rev->is_stub || !empty($entity->original) && $entity->original->_rev->is_stub)) {
    $parent_field_name = $entity->{$entity_revision_parent_field_name}->value;
    $storage = \Drupal::entityTypeManager()
      ->getStorage($parent
      ->getEntityTypeId());
    $storage
      ->resetCache([
      $parent
        ->id(),
    ]);

    // Load the most recent version of the parent.
    $parent = $storage
      ->load($parent
      ->id());
    if ($parent && !empty($parent->{$parent_field_name})) {
      $save_parent = FALSE;
      $values = $parent->{$parent_field_name}
        ->getValue();
      $updated_values = [];
      foreach ($values as $delta => $value) {
        $updated_values[$delta] = $value;
        if ($value['target_id'] == $entity
          ->id() && $value['target_revision_id'] != $entity
          ->getRevisionId()) {
          $updated_values[$delta]['target_revision_id'] = $entity
            ->getRevisionId();
          $save_parent = TRUE;
        }
      }
      if ($save_parent) {
        $parent->{$parent_field_name}
          ->setValue($updated_values);
        try {
          $storage
            ->saveWithoutForcingNewRevision($parent);
          $storage
            ->resetCache([
            $parent
              ->id(),
          ]);
        } catch (\Exception $e) {
          $details = t('Failed to save parent entity with UUID: %uuid_parent for paragraph entity with UUID: %uuid_paragraph.', [
            '%uuid_parent' => $parent
              ->uuid(),
            '%uuid_paragraph' => $entity
              ->uuid(),
          ]);
          \Drupal::logger('Multiversion')
            ->error('%type: @message in %function (line %line of %file). ' . $details, Error::decodeException($e));
        }
      }
    }
  }
}

/**
 * Implements hook_views_data_alter().
 */
function multiversion_views_data_alter(array &$data) {
  foreach ($data as $key => $item) {

    // Set standard handler for _rev field.
    if (isset($data[$key]['_rev'])) {
      $data[$key]['_rev']['field']['id'] = 'standard';
    }
    if (isset($data[$key]['_deleted'])) {

      // Use status = 1 instead of status <> 0 in WHERE statement.
      $data[$key]['_deleted']['filter']['use_equal'] = TRUE;
    }

    // Add a new filter that filters content by current active workspace.
    if (isset($data[$key]['workspace'])) {
      $data[$key]['current_workspace'] = [
        'title' => t('Current workspace'),
        'help' => t('Filters content by current active workspace.'),
        'filter' => [
          'field' => 'workspace',
          'id' => 'current_workspace',
          'label' => t('Current workspace'),
        ],
      ];

      // Unset the 'Workspace reference' filter because users are not allowed to
      // filter by a specific workspace, other than current active workspace.
      // To filter by current active workspace will be used the 'Current workspace'
      // filter.
      unset($data[$key]['workspace']);
    }
  }
}

/**
 * Implements hook_views_post_execute().
 */
function multiversion_views_post_execute(ViewExecutable $view) {

  // Add deleted entities if we have rows for them.
  // When we want to get deleted entities using the _deleted field, entities
  // should be loaded with
  // \Drupal::entityManager()->getTypeStorage($entity_type)->loadDeleted($id) or
  // \Drupal::entityManager()->getTypeStorage($entity_type)->loadMultipleDeleted($ids),
  // otherwise the _entity field in the view result rows will be null.
  $base_field = $view->storage
    ->get('base_field');
  $table_info = $view->query
    ->getEntityTableInfo();
  $content_type_info = array_column($table_info, 'entity_type');
  if (is_array($view->result) && ($content_type = reset($content_type_info))) {
    $manager = \Drupal::service('multiversion.manager');
    $storage = \Drupal::entityTypeManager()
      ->getStorage($content_type);
    if ($storage instanceof ContentEntityStorageInterface && $manager
      ->allowToAlter($storage
      ->getEntityType())) {
      $ids = [];
      foreach ($view->result as $index => $row) {
        if (empty($row->_entity) && !empty($row->{$base_field})) {
          $ids[$index] = $row->{$base_field};
        }
      }
      $entities = $storage
        ->loadMultipleDeleted($ids);
      foreach ($view->result as $index => $row) {
        if (empty($row->_entity) && !empty($row->{$base_field}) && isset($entities[$row->{$base_field}])) {
          $view->result[$index]->_entity = $entities[$row->{$base_field}];
        }
        elseif (empty($row->_entity)) {
          unset($view->result[$index]);
        }
      }
    }
  }
}

/**
 * Implements hook_views_query_alter().
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object about to be processed.
 * @param QueryPluginBase $query
 *   The query plugin object for the query.
 */
function multiversion_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {

  // Add a new filter for default core views, it will filter deleted content.
  $views_ids = [
    'content',
    'frontpage',
    'comments_recent',
    'content_recent',
    'taxonomy_term',
    'glossary',
    'archive',
    'block_content',
    'poll_admin',
    'poll_list',
    'media_library',
  ];
  if (in_array($view
    ->id(), $views_ids)) {

    /** @var \Drupal\multiversion\MultiversionManagerInterface $manager */
    $manager = \Drupal::service('multiversion.manager');
    $entity_type = $view
      ->getBaseEntityType();
    if (!$manager
      ->isEnabledEntityType($entity_type)) {
      return;
    }
    $base_table = $view->storage
      ->get('base_table');
    $view->query->where[1]['conditions'][] = [
      'field' => $base_table . '._deleted',
      'value' => FALSE,
      'operator' => '=',
    ];
    $view->query->where[1]['conditions'][] = [
      'field' => $base_table . '.workspace',
      'value' => multiversion_get_active_workspace_id(),
      'operator' => '=',
    ];
  }
}

/**
 * Implements hook_element_info_alter().
 */
function multiversion_element_info_alter(array &$types) {
  foreach ($types as &$type) {
    if (!isset($type['#pre_render'])) {
      $type['#pre_render'] = [];
    }
    $type['#pre_render'][] = 'multiversion_element_pre_render';
  }
}

/**
 * Element pre-render callback.
 */
function multiversion_element_pre_render($element) {
  if (isset($element['#cache'])) {
    if (!isset($element['#cache']['contexts'])) {
      $element['#cache']['contexts'] = [];
    }
    $element['#cache']['contexts'] = Cache::mergeContexts($element['#cache']['contexts'], [
      'workspace',
    ]);
  }
  return $element;
}

/**
 * Callback for getting the active workspace ID.
 */
function multiversion_get_active_workspace_id() {
  return \Drupal::service('multiversion.manager')
    ->getActiveWorkspaceId();
}

/**
 * URI callback for the workspace entity type.
 */
function multiversion_workspace_uri(WorkspaceInterface $entity) {
  return $entity
    ->id();
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function multiversion_form_node_type_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {

  // Users don't have the option to disable revisions when using Multiversion.
  // @todo: {@link https://www.drupal.org/node/2597393 See if there's a way
  // to just disable this particular option.}
  unset($form['workflow']['options']['#options']['revision']);
}

/**
 * Implements hook_form_alter().
 */
function multiversion_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if (isset($form['revision']['#group']) && $form['revision']['#group'] == 'revision_information') {

    // Users don't have the option to disable revisions when using Multiversion.
    $form['revision']['#default_value'] = TRUE;
    $form['revision']['#disabled'] = TRUE;
  }
}

/**
 * Prepares a file destination directory.
 *
 * If the directory doesn't exist it tries to create it, if the directory is not
 * writable it tries to make it writable. In case it can't create the directory
 * or make it writable, logs the error message and returns FALSE.
 * When the directory exists and it is writable returns TRUE.
 *
 * @param string $destination
 *
 * @return bool
 */
function multiversion_prepare_file_destination($destination) {
  $dirname = \Drupal::service('file_system')
    ->dirname($destination);
  return file_prepare_directory($dirname, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY);
}

/**
 * Implements hook_menu_links_discovered_alter().
 */
function multiversion_menu_links_discovered_alter(&$links) {

  // Get all custom menu links and set links with the correct ID.
  // The ID format now will be 'menu_link_content:ENTITY_UUID:ENTITY_ID' - we
  // need to change it because we need new entry in the menu_tree table for the
  // same link on different workspaces.
  // The old ID format is 'menu_link_content:ENTITY_UUID'.
  if (\Drupal::moduleHandler()
    ->moduleExists('menu_link_content')) {
    $storage = \Drupal::entityTypeManager()
      ->getStorage('menu_link_content');
    if (\Drupal::service('multiversion.manager')
      ->isEnabledEntityType($storage
      ->getEntityType())) {
      $workspaces = Workspace::loadMultiple();
      foreach ($workspaces as $workspace_id => $workspace) {
        $storage
          ->useWorkspace($workspace_id);
        $menu_link_content_entities = $storage
          ->loadMultiple();
        $new_ids = [];
        $links_to_purge = [];

        /** @var \Drupal\menu_link_content\MenuLinkContentInterface $menu_link_content */
        foreach ($menu_link_content_entities as $menu_link_content) {

          // Unset links with old ID format.
          $uuid = $menu_link_content
            ->uuid();
          $old_id = "menu_link_content:{$uuid}";
          $new_id = "{$old_id}:" . $menu_link_content
            ->id();
          if (isset($links[$old_id])) {
            $links_to_purge[] = $old_id;
            unset($links[$old_id]);
          }
          $new_ids[$old_id] = $new_id;
          if (!isset($links[$new_id])) {
            $links[$new_id] = $menu_link_content
              ->getPluginDefinition();
          }

          // Set a new plugin class tha will handle new ID format.
          $links[$new_id]['class'] = 'Drupal\\multiversion\\Plugin\\Menu\\MenuLinkContent';
        }
        if ($links_to_purge) {
          \Drupal::service('menu.tree_storage')
            ->purgeMultiple($links_to_purge);
        }
        foreach ($links as $id => $link) {
          if (!empty($link['parent']) && in_array($link['parent'], array_keys($new_ids))) {
            $links[$id]['parent'] = $new_ids[$link['parent']];
          }
        }
        $storage
          ->useWorkspace(NULL);
      }
    }
  }
}

/**
 * Implements hook_modules_installed().
 */
function multiversion_modules_installed($modules) {

  // Enable entity types provided by installed modules and supported by
  // Multiversion.
  $entity_type_manager = \Drupal::entityTypeManager();
  $supported_entity_types = \Drupal::configFactory()
    ->getEditable('multiversion.settings')
    ->get('supported_entity_types');
  $supported_entity_types = $supported_entity_types ?: [];
  $entities_to_enable = [];
  foreach ($supported_entity_types as $entity_type_id) {
    $entity_type = $entity_type_manager
      ->getDefinition($entity_type_id, FALSE);
    if (!empty($entity_type) && in_array($entity_type
      ->getProvider(), $modules)) {
      $entities_to_enable[$entity_type_id] = $entity_type;
    }
  }
  if (!empty($entities_to_enable)) {
    \Drupal::service('multiversion.manager')
      ->enableEntityTypes($entities_to_enable);
  }
}

/**
 * Implements hook_search_plugin_alter().
 */
function multiversion_search_plugin_alter(array &$definitions) {
  if (isset($definitions['node_search'])) {
    $definitions['node_search']['class'] = 'Drupal\\multiversion\\Entity\\Search\\NodeSearch';
  }
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Adds the workspace as a class to the body.
 */
function multiversion_preprocess_html(&$variables) {
  $active_workspace = \Drupal::service('workspace.manager')
    ->getActiveWorkspace();
  if ($active_workspace && ($machine_name = $active_workspace
    ->getMachineName())) {

    // Add a new body class with the active workspace.
    $variables['attributes']['class'][] = 'workspace-' . $machine_name;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters the 'revision_overview_form' for provided by Diff module.
 */
function multiversion_form_revision_overview_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (isset($form['node_revisions_table'])) {
    foreach ($form['node_revisions_table'] as $key => $revision_info) {
      if (empty($revision_info['operations']) || !is_array($revision_info['operations'])) {
        continue;
      }
      if (isset($revision_info['operations']['#links']['delete'])) {
        unset($form['node_revisions_table'][$key]['operations']['#links']['delete']);
      }
    }
  }
}

/**
 * Add workspace field in url_alias table.
 *
 * @param bool $install
 *
 * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
 */
function _multiversion_add_workspace_field_in_url_alias_table($install = FALSE) {
  $database = \Drupal::database();
  $schema = $database
    ->schema();
  $table = 'url_alias';
  $field = 'workspace';
  if (!$schema
    ->fieldExists($table, $field)) {
    $spec = [
      'description' => 'The workspace the alias belongs to.',
      'type' => 'int',
      'unsigned' => TRUE,
      'default' => $install ? 1 : 0,
      'size' => 'normal',
    ];
    $schema
      ->addField($table, $field, $spec);
  }
  $table_data = $database
    ->select($table)
    ->fields($table)
    ->execute()
    ->fetchAll();
  $entity_type_manager = \Drupal::entityTypeManager();
  $entity_type_manager
    ->clearCachedDefinitions();
  $entity_ids = [];
  foreach ($table_data as $row) {
    $row_data = (array) $row;
    $source_elements = explode('/', $row_data['source']);
    if (!empty($source_elements[1]) && !empty($source_elements[2]) && is_numeric($source_elements[2])) {
      if ($entity_type_manager
        ->getDefinition($source_elements[1], FALSE)) {
        $entity_ids[$source_elements[1]][$row_data['pid']] = $source_elements[2];
      }
    }
  }
  $workspaces = Workspace::loadMultiple();
  foreach ($workspaces as $workspace_id => $workspace) {
    foreach ($entity_ids as $entity_type_id => $ids) {
      $storage = $entity_type_manager
        ->getStorage($entity_type_id);
      if ($storage instanceof ContentEntityStorageInterface) {
        $storage
          ->useWorkspace($workspace_id);
        $entities = $storage
          ->loadMultiple($ids);
        $storage
          ->useWorkspace(NULL);
      }
      if (empty($entities)) {
        continue;
      }
      foreach ($ids as $pid => $entity_id) {
        if (in_array($entity_id, array_keys($entities))) {
          $database
            ->update($table)
            ->fields([
            $field => $workspace_id,
          ])
            ->condition('pid', $pid)
            ->execute();
        }
      }
    }
  }
}

Functions

Namesort descending Description
multiversion_data_type_info_alter Implements hook_data_type_info_alter().
multiversion_element_info_alter Implements hook_element_info_alter().
multiversion_element_pre_render Element pre-render callback.
multiversion_entity_base_field_info Implements hook_entity_base_field_info().
multiversion_entity_base_field_info_alter Implements hook_entity_base_field_info_alter().
multiversion_entity_type_alter Implements hook_entity_type_alter().
multiversion_field_info_alter Implements hook_field_info_alter().
multiversion_form_alter Implements hook_form_alter().
multiversion_form_node_type_edit_form_alter Implements hook_form_FORM_ID_alter().
multiversion_form_revision_overview_form_alter Implements hook_form_FORM_ID_alter().
multiversion_get_active_workspace_id Callback for getting the active workspace ID.
multiversion_menu_links_discovered_alter Implements hook_menu_links_discovered_alter().
multiversion_modules_installed Implements hook_modules_installed().
multiversion_module_implements_alter Implements hook_module_implements_alter().
multiversion_paragraph_update Implements hook_ENTITY_TYPE_update().
multiversion_prepare_file_destination Prepares a file destination directory.
multiversion_preprocess_html Implements hook_preprocess_HOOK().
multiversion_search_plugin_alter Implements hook_search_plugin_alter().
multiversion_views_data_alter Implements hook_views_data_alter().
multiversion_views_post_execute Implements hook_views_post_execute().
multiversion_views_query_alter Implements hook_views_query_alter().
multiversion_workspace_presave Implements hook_ENTITY_TYPE_presave().
multiversion_workspace_update Implements hook_ENTITY_TYPE_update().
multiversion_workspace_uri URI callback for the workspace entity type.
_multiversion_add_workspace_field_in_url_alias_table Add workspace field in url_alias table.