
| Current Path : /var/www/html/german-vocational.cn/core/modules/workspaces/src/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : /var/www/html/german-vocational.cn/core/modules/workspaces/src/WorkspaceManager.php |
<?php
namespace Drupal\workspaces;
use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface;
use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Provides the workspace manager.
*/
class WorkspaceManager implements WorkspaceManagerInterface {
use StringTranslationTrait;
/**
* An array of entity type IDs that can not belong to a workspace.
*
* By default, only entity types which are revisionable and publishable can
* belong to a workspace.
*
* @var string[]
*/
protected $blacklist = [
'workspace_association' => 'workspace_association',
'workspace' => 'workspace',
];
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity memory cache service.
*
* @var \Drupal\Core\Cache\MemoryCache\MemoryCacheInterface
*/
protected $entityMemoryCache;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* A logger instance.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* The class resolver.
*
* @var \Drupal\Core\DependencyInjection\ClassResolverInterface
*/
protected $classResolver;
/**
* The workspace negotiator service IDs.
*
* @var array
*/
protected $negotiatorIds;
/**
* The current active workspace.
*
* @var \Drupal\workspaces\WorkspaceInterface
*/
protected $activeWorkspace;
/**
* Constructs a new WorkspaceManager.
*
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Cache\MemoryCache\MemoryCacheInterface $entity_memory_cache
* The entity memory cache service.
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
* The current user.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
* The class resolver.
* @param array $negotiator_ids
* The workspace negotiator service IDs.
*/
public function __construct(RequestStack $request_stack, EntityTypeManagerInterface $entity_type_manager, MemoryCacheInterface $entity_memory_cache, AccountProxyInterface $current_user, StateInterface $state, LoggerInterface $logger, ClassResolverInterface $class_resolver, array $negotiator_ids) {
$this->requestStack = $request_stack;
$this->entityTypeManager = $entity_type_manager;
$this->entityMemoryCache = $entity_memory_cache;
$this->currentUser = $current_user;
$this->state = $state;
$this->logger = $logger;
$this->classResolver = $class_resolver;
$this->negotiatorIds = $negotiator_ids;
}
/**
* {@inheritdoc}
*/
public function isEntityTypeSupported(EntityTypeInterface $entity_type) {
// First, check if we already determined whether this entity type is
// supported or not.
if (isset($this->blacklist[$entity_type->id()])) {
return FALSE;
}
if ($entity_type->entityClassImplements(EntityPublishedInterface::class) && $entity_type->isRevisionable()) {
return TRUE;
}
// This entity type can not belong to a workspace, add it to the blacklist.
$this->blacklist[$entity_type->id()] = $entity_type->id();
return FALSE;
}
/**
* {@inheritdoc}
*/
public function getSupportedEntityTypes() {
$entity_types = [];
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
if ($this->isEntityTypeSupported($entity_type)) {
$entity_types[$entity_type_id] = $entity_type;
}
}
return $entity_types;
}
/**
* {@inheritdoc}
*/
public function getActiveWorkspace() {
if (!isset($this->activeWorkspace)) {
$request = $this->requestStack->getCurrentRequest();
foreach ($this->negotiatorIds as $negotiator_id) {
$negotiator = $this->classResolver->getInstanceFromDefinition($negotiator_id);
if ($negotiator->applies($request)) {
if ($this->activeWorkspace = $negotiator->getActiveWorkspace($request)) {
break;
}
}
}
}
// The default workspace negotiator always returns a valid workspace.
return $this->activeWorkspace;
}
/**
* {@inheritdoc}
*/
public function setActiveWorkspace(WorkspaceInterface $workspace) {
$this->doSwitchWorkspace($workspace);
// Set the workspace on the proper negotiator.
$request = $this->requestStack->getCurrentRequest();
foreach ($this->negotiatorIds as $negotiator_id) {
$negotiator = $this->classResolver->getInstanceFromDefinition($negotiator_id);
if ($negotiator->applies($request)) {
$negotiator->setActiveWorkspace($workspace);
break;
}
}
return $this;
}
/**
* Switches the current workspace.
*
* @param \Drupal\workspaces\WorkspaceInterface $workspace
* The workspace to set as active.
*
* @throws \Drupal\workspaces\WorkspaceAccessException
* Thrown when the current user doesn't have access to view the workspace.
*/
protected function doSwitchWorkspace(WorkspaceInterface $workspace) {
// If the current user doesn't have access to view the workspace, they
// shouldn't be allowed to switch to it.
if (!$workspace->access('view') && !$workspace->isDefaultWorkspace()) {
$this->logger->error('Denied access to view workspace %workspace_label for user %uid', [
'%workspace_label' => $workspace->label(),
'%uid' => $this->currentUser->id(),
]);
throw new WorkspaceAccessException('The user does not have permission to view that workspace.');
}
$this->activeWorkspace = $workspace;
// Clear the static entity cache for the supported entity types.
$cache_tags_to_invalidate = array_map(function ($entity_type_id) {
return 'entity.memory_cache:' . $entity_type_id;
}, array_keys($this->getSupportedEntityTypes()));
$this->entityMemoryCache->invalidateTags($cache_tags_to_invalidate);
}
/**
* {@inheritdoc}
*/
public function executeInWorkspace($workspace_id, callable $function) {
/** @var \Drupal\workspaces\WorkspaceInterface $workspace */
$workspace = $this->entityTypeManager->getStorage('workspace')->load($workspace_id);
if (!$workspace) {
throw new \InvalidArgumentException('The ' . $workspace_id . ' workspace does not exist.');
}
$previous_active_workspace = $this->getActiveWorkspace();
$this->doSwitchWorkspace($workspace);
$result = $function();
$this->doSwitchWorkspace($previous_active_workspace);
return $result;
}
/**
* {@inheritdoc}
*/
public function shouldAlterOperations(EntityTypeInterface $entity_type) {
return $this->isEntityTypeSupported($entity_type) && !$this->getActiveWorkspace()->isDefaultWorkspace();
}
/**
* {@inheritdoc}
*/
public function purgeDeletedWorkspacesBatch() {
$deleted_workspace_ids = $this->state->get('workspace.deleted', []);
// Bail out early if there are no workspaces to purge.
if (empty($deleted_workspace_ids)) {
return;
}
$batch_size = Settings::get('entity_update_batch_size', 50);
/** @var \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage */
$workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association');
// Get the first deleted workspace from the list and delete the revisions
// associated with it, along with the workspace_association entries.
$workspace_id = reset($deleted_workspace_ids);
$workspace_association_ids = $this->getWorkspaceAssociationRevisionsToPurge($workspace_id, $batch_size);
if ($workspace_association_ids) {
$workspace_associations = $workspace_association_storage->loadMultipleRevisions(array_keys($workspace_association_ids));
foreach ($workspace_associations as $workspace_association) {
$associated_entity_storage = $this->entityTypeManager->getStorage($workspace_association->target_entity_type_id->value);
// Delete the associated entity revision.
if ($entity = $associated_entity_storage->loadRevision($workspace_association->target_entity_revision_id->value)) {
if ($entity->isDefaultRevision()) {
$entity->delete();
}
else {
$associated_entity_storage->deleteRevision($workspace_association->target_entity_revision_id->value);
}
}
// Delete the workspace_association revision.
if ($workspace_association->isDefaultRevision()) {
$workspace_association->delete();
}
else {
$workspace_association_storage->deleteRevision($workspace_association->getRevisionId());
}
}
}
// The purging operation above might have taken a long time, so we need to
// request a fresh list of workspace association IDs. If it is empty, we can
// go ahead and remove the deleted workspace ID entry from state.
if (!$this->getWorkspaceAssociationRevisionsToPurge($workspace_id, $batch_size)) {
unset($deleted_workspace_ids[$workspace_id]);
$this->state->set('workspace.deleted', $deleted_workspace_ids);
}
}
/**
* Gets a list of workspace association IDs to purge.
*
* @param string $workspace_id
* The ID of the workspace.
* @param int $batch_size
* The maximum number of records that will be purged.
*
* @return array
* An array of workspace association IDs, keyed by their revision IDs.
*/
protected function getWorkspaceAssociationRevisionsToPurge($workspace_id, $batch_size) {
return $this->entityTypeManager->getStorage('workspace_association')
->getQuery()
->allRevisions()
->accessCheck(FALSE)
->condition('workspace', $workspace_id)
->sort('revision_id', 'ASC')
->range(0, $batch_size)
->execute();
}
}