View source
<?php
namespace Drupal\multiversion;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Database\Connection;
use Drupal\multiversion\Event\MultiversionManagerEvent;
use Drupal\multiversion\Event\MultiversionManagerEvents;
use Drupal\multiversion\Workspace\WorkspaceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\Serializer;
class MultiversionManager implements MultiversionManagerInterface, ContainerAwareInterface {
use ContainerAwareTrait;
const TO_TMP = 'to_tmp';
const FROM_TMP = 'from_tmp';
const OP_ENABLE = 'enable';
const OP_DISABLE = 'disable';
protected $workspaceManager;
protected $serializer;
protected $entityTypeManager;
protected $state;
protected $languageManager;
protected $cache;
protected $connection;
protected $entityFieldManager;
protected $lastSequenceId;
protected $eventDispatcher;
public function __construct(WorkspaceManagerInterface $workspace_manager, Serializer $serializer, EntityTypeManagerInterface $entity_type_manager, StateInterface $state, LanguageManagerInterface $language_manager, CacheBackendInterface $cache, Connection $connection, EntityFieldManagerInterface $entity_field_manager, EventDispatcherInterface $event_dispatcher) {
$this->workspaceManager = $workspace_manager;
$this->serializer = $serializer;
$this->entityTypeManager = $entity_type_manager;
$this->state = $state;
$this->languageManager = $language_manager;
$this->cache = $cache;
$this->connection = $connection;
$this->entityFieldManager = $entity_field_manager;
$this->eventDispatcher = $event_dispatcher;
}
public static function enableMigrationIsActive($status = NULL) {
static $cache = FALSE;
if ($status !== NULL) {
$cache = $status;
}
return $cache;
}
public static function disableMigrationIsActive($status = NULL) {
static $cache = FALSE;
if ($status !== NULL) {
$cache = $status;
}
return $cache;
}
public function getActiveWorkspaceId() {
return $this->workspaceManager
->getActiveWorkspaceId();
}
public function setActiveWorkspaceId($id) {
$workspace = $this->workspaceManager
->load($id);
return $this->workspaceManager
->setActiveWorkspace($workspace);
}
public function newSequenceId() {
$this->lastSequenceId = (int) (microtime(TRUE) * 1000000);
return $this->lastSequenceId;
}
public function lastSequenceId() {
return $this->lastSequenceId;
}
public function isSupportedEntityType(EntityTypeInterface $entity_type) {
$supported_entity_types = \Drupal::config('multiversion.settings')
->get('supported_entity_types') ?: [];
if (empty($supported_entity_types)) {
return FALSE;
}
if (!in_array($entity_type
->id(), $supported_entity_types)) {
return FALSE;
}
return $entity_type instanceof ContentEntityTypeInterface;
}
public function getSupportedEntityTypes() {
$entity_types = [];
foreach ($this->entityTypeManager
->getDefinitions() as $entity_type_id => $entity_type) {
if ($this
->isSupportedEntityType($entity_type)) {
$entity_types[$entity_type
->id()] = $entity_type;
}
}
return $entity_types;
}
public function isEnabledEntityType(EntityTypeInterface $entity_type) {
if ($this
->isSupportedEntityType($entity_type)) {
$entity_type_id = $entity_type
->id();
$migration_done = $this->state
->get("multiversion.migration_done.{$entity_type_id}", FALSE);
$enabled_entity_types = \Drupal::config('multiversion.settings')
->get('enabled_entity_types') ?: [];
if ($migration_done && in_array($entity_type_id, $enabled_entity_types)) {
return TRUE;
}
}
return FALSE;
}
public function allowToAlter(EntityTypeInterface $entity_type) {
$supported_entity_types = \Drupal::config('multiversion.settings')
->get('supported_entity_types') ?: [];
$id = $entity_type
->id();
$enable_migration = self::enableMigrationIsActive();
$disable_migration = self::disableMigrationIsActive();
if (!in_array($id, $supported_entity_types)) {
return FALSE;
}
if (is_array($disable_migration) && in_array($id, $disable_migration)) {
return FALSE;
}
if (is_array($enable_migration) && in_array($id, $enable_migration)) {
return TRUE;
}
return $this
->isEnabledEntityType($entity_type);
}
public function getEnabledEntityTypes() {
$entity_types = [];
foreach ($this
->getSupportedEntityTypes() as $entity_type_id => $entity_type) {
if ($this
->isEnabledEntityType($entity_type)) {
$entity_types[$entity_type_id] = $entity_type;
}
}
return $entity_types;
}
public function enableEntityTypes($entity_types_to_enable = NULL) {
$entity_types = $entity_types_to_enable !== NULL ? $entity_types_to_enable : $this
->getSupportedEntityTypes();
$enabled_entity_types = \Drupal::config('multiversion.settings')
->get('enabled_entity_types') ?: [];
if (empty($entity_types)) {
return $this;
}
$migration = $this
->createMigration();
$migration
->installDependencies();
$this->eventDispatcher
->dispatch(MultiversionManagerEvents::PRE_MIGRATE, new MultiversionManagerEvent($entity_types, self::OP_ENABLE));
$has_data = $this
->prepareContentForMigration($entity_types, $migration, self::OP_ENABLE);
\Drupal::entityTypeManager()
->clearCachedDefinitions();
\Drupal::service('entity_field.manager')
->clearCachedFieldDefinitions();
self::enableMigrationIsActive(array_keys($entity_types));
$migration
->applyNewStorage(array_keys($entity_types));
if ($entity_types_to_enable !== NULL) {
$updated_entity_types = [];
foreach ($entity_types as $entity_type_id => $entity_type) {
$updated_entity_types[$entity_type_id] = $this->entityTypeManager
->getStorage($entity_type_id)
->getEntityType();
}
}
else {
$updated_entity_types = $this
->getSupportedEntityTypes();
}
$this->state
->set('comment.maintain_entity_statistics', FALSE);
\Drupal::state()
->resetCache();
foreach ($updated_entity_types as $entity_type_id => $entity_type) {
if ($has_data[$entity_type_id]) {
$field_map = $migration
->getFieldMap($entity_type, self::OP_ENABLE, self::FROM_TMP);
$migration
->migrateContentFromTemp($entity_type, $field_map);
$migration
->cleanupMigration($entity_type_id . '__' . self::TO_TMP);
$migration
->cleanupMigration($entity_type_id . '__' . self::FROM_TMP);
}
$this->state
->set("multiversion.migration_done.{$entity_type_id}", TRUE);
}
foreach ($entity_types as $entity_type_id => $entity_type) {
$enabled = $this->state
->get("multiversion.migration_done.{$entity_type_id}", FALSE);
if (!in_array($entity_type_id, $enabled_entity_types) && $enabled) {
$enabled_entity_types[] = $entity_type_id;
}
}
\Drupal::configFactory()
->getEditable('multiversion.settings')
->set('enabled_entity_types', $enabled_entity_types)
->save();
$this->state
->set('comment.maintain_entity_statistics', TRUE);
$migration
->uninstallDependencies();
self::enableMigrationIsActive(FALSE);
$this->state
->set('multiversion.migration_done', TRUE);
$this->eventDispatcher
->dispatch(MultiversionManagerEvents::POST_MIGRATE, new MultiversionManagerEvent($entity_types, self::OP_ENABLE));
\Drupal::state()
->resetCache();
return $this;
}
public function disableEntityTypes($entity_types_to_disable = NULL) {
$entity_types = $entity_types_to_disable !== NULL ? $entity_types_to_disable : $this
->getEnabledEntityTypes();
$migration = $this
->createMigration();
$migration
->installDependencies();
$this->eventDispatcher
->dispatch(MultiversionManagerEvents::PRE_MIGRATE, new MultiversionManagerEvent($entity_types, self::OP_DISABLE));
$has_data = $this
->prepareContentForMigration($entity_types, $migration, self::OP_DISABLE);
if (empty($entity_types)) {
return $this;
}
if ($entity_types_to_disable === NULL) {
$this->entityTypeManager
->clearCachedDefinitions();
$update_manager = \Drupal::entityDefinitionUpdateManager();
foreach ($this->entityTypeManager
->getDefinitions() as $entity_type) {
if ($entity_type
->entityClassImplements(FieldableEntityInterface::class)) {
$entity_type_id = $entity_type
->id();
$revision_key = $entity_type
->getKey('revision');
$storage = $this->entityTypeManager
->getStorage($entity_type_id);
foreach ($this->entityFieldManager
->getFieldStorageDefinitions($entity_type_id) as $storage_definition) {
if ($storage_definition
->getProvider() == 'multiversion' && !$storage
->countFieldData($storage_definition, TRUE) && $storage_definition
->getName() != $revision_key) {
$update_manager
->uninstallFieldStorageDefinition($storage_definition);
}
}
}
}
}
$enabled_entity_types = \Drupal::config('multiversion.settings')
->get('enabled_entity_types') ?: [];
foreach ($entity_types as $entity_type_id => $entity_type) {
if (($key = array_search($entity_type_id, $enabled_entity_types)) !== FALSE) {
unset($enabled_entity_types[$key]);
}
}
if ($entity_types_to_disable === NULL) {
$enabled_entity_types = [];
}
\Drupal::configFactory()
->getEditable('multiversion.settings')
->set('enabled_entity_types', $enabled_entity_types)
->save();
\Drupal::entityTypeManager()
->clearCachedDefinitions();
\Drupal::service('entity_field.manager')
->clearCachedFieldDefinitions();
self::disableMigrationIsActive(array_keys($entity_types));
$migration
->applyNewStorage(array_keys($entity_types));
$this->state
->set('comment.maintain_entity_statistics', FALSE);
\Drupal::state()
->resetCache();
$updated_entity_types = [];
foreach ($entity_types as $entity_type_id => $entity_type) {
$updated_entity_types[$entity_type_id] = $this->entityTypeManager
->getStorage($entity_type_id)
->getEntityType();
}
foreach ($updated_entity_types as $entity_type_id => $entity_type) {
$base_table = $entity_type
->getBaseTable();
$uuid_key = $entity_type
->getKey('uuid');
$this->connection
->schema()
->dropUniqueKey($base_table, $entity_type_id . '_field__' . $uuid_key . '__value');
if ($has_data[$entity_type_id]) {
$field_map = $migration
->getFieldMap($entity_type, self::OP_DISABLE, self::FROM_TMP);
$migration
->migrateContentFromTemp($entity_type, $field_map);
$migration
->cleanupMigration($entity_type_id . '__' . self::TO_TMP);
$migration
->cleanupMigration($entity_type_id . '__' . self::FROM_TMP);
}
$this->state
->delete("multiversion.migration_done.{$entity_type_id}");
}
$this->state
->set('comment.maintain_entity_statistics', TRUE);
$migration
->uninstallDependencies();
self::disableMigrationIsActive(FALSE);
$this->state
->delete('multiversion.migration_done');
$this->eventDispatcher
->dispatch(MultiversionManagerEvents::POST_MIGRATE, new MultiversionManagerEvent($entity_types, self::OP_DISABLE));
return $this;
}
public function newRevisionId(ContentEntityInterface $entity, $index = 0) {
$deleted = $entity->_deleted->value;
$old_rev = $entity->_rev->value;
$normalized_entity = $this->serializer
->normalize($entity, NULL, [
'new_revision_id' => TRUE,
]);
$this
->filterNormalizedEntity($normalized_entity);
return $index + 1 . '-' . md5($this
->termToBinary([
$deleted,
0,
$old_rev,
$normalized_entity,
[],
]));
}
protected function filterNormalizedEntity(&$normalized_entity) {
foreach ($normalized_entity as $key => &$value) {
if ($key[0] == '_') {
unset($normalized_entity[$key]);
}
elseif (is_array($value)) {
$this
->filterNormalizedEntity($value);
}
}
}
protected function termToBinary(array $term) {
return $this->serializer
->serialize($term, 'json');
}
protected function createMigration() {
return MultiversionMigration::create($this->container, $this->entityTypeManager, $this->entityFieldManager);
}
protected function prepareContentForMigration($entity_types, MultiversionMigrationInterface $migration, $op) {
$has_data = [];
foreach ($entity_types as $entity_type_id => $entity_type) {
$storage = $this->entityTypeManager
->getStorage($entity_type_id);
$has_data[$entity_type_id] = FALSE;
try {
if ($storage
->hasData()) {
$has_data[$entity_type_id] = TRUE;
}
} catch (\Exception $e) {
unset($entity_types[$entity_type_id]);
}
if ($has_data[$entity_type_id]) {
$field_map = $migration
->getFieldMap($entity_type, $op, self::TO_TMP);
$migration
->migrateContentToTemp($storage
->getEntityType(), $field_map);
}
}
foreach ($entity_types as $entity_type_id => $entity_type) {
if ($has_data[$entity_type_id] === TRUE) {
$storage = $this->entityTypeManager
->getStorage($entity_type_id);
$migration
->emptyOldStorage($storage);
}
}
return $has_data;
}
}