class WorkspaceManager
Same name and namespace in other branches
- 9 core/modules/workspaces/src/WorkspaceManager.php \Drupal\workspaces\WorkspaceManager
- 8.9.x core/modules/workspaces/src/WorkspaceManager.php \Drupal\workspaces\WorkspaceManager
- 10 core/modules/workspaces/src/WorkspaceManager.php \Drupal\workspaces\WorkspaceManager
Provides the workspace manager.
@property iterable $negotiators
Hierarchy
- class \Drupal\workspaces\WorkspaceManager extends \Drupal\workspaces\WorkspaceManagerInterface uses \Drupal\Core\StringTranslation\StringTranslationTrait
Expanded class hierarchy of WorkspaceManager
1 string reference to 'WorkspaceManager'
- workspaces.services.yml in core/
modules/ workspaces/ workspaces.services.yml - core/modules/workspaces/workspaces.services.yml
1 service uses WorkspaceManager
- workspaces.manager in core/
modules/ workspaces/ workspaces.services.yml - Drupal\workspaces\WorkspaceManager
File
-
core/
modules/ workspaces/ src/ WorkspaceManager.php, line 23
Namespace
Drupal\workspacesView source
class WorkspaceManager implements WorkspaceManagerInterface {
use StringTranslationTrait;
/**
* The current active workspace.
*
* The value is either a workspace object, FALSE if there is no active
* workspace, or NULL if the active workspace hasn't been determined yet.
*/
protected WorkspaceInterface|false|null $activeWorkspace = NULL;
/**
* An array of workspace negotiator services.
*
* @todo Remove in drupal:12.0.0.
*/
private array $collectedNegotiators = [];
public function __construct(protected RequestStack $requestStack, protected EntityTypeManagerInterface $entityTypeManager, protected MemoryCacheInterface $entityMemoryCache, protected AccountProxyInterface $currentUser, protected StateInterface $state, #[Autowire(service: 'logger.channel.workspaces')] protected LoggerInterface $logger, #[AutowireIterator(tag: 'workspace_negotiator')] protected $negotiators, protected WorkspaceAssociationInterface $workspaceAssociation, protected WorkspaceInformationInterface $workspaceInfo) {
if ($negotiators instanceof ClassResolverInterface) {
@trigger_error('Passing the \'class_resolver\' service as the 7th argument to ' . __METHOD__ . ' is deprecated in drupal:11.3.0 and is unsupported in drupal:12.0.0. Use autowiring for the \'workspaces.manager\' service instead. See https://www.drupal.org/node/3532939', E_USER_DEPRECATED);
$this->negotiators = $this->collectedNegotiators;
}
}
/**
* {@inheritdoc}
*/
public function hasActiveWorkspace() {
return $this->getActiveWorkspace() !== FALSE;
}
/**
* {@inheritdoc}
*/
public function getActiveWorkspace() {
if (!isset($this->activeWorkspace)) {
$request = $this->requestStack
->getCurrentRequest();
foreach ($this->negotiators as $negotiator) {
if ($negotiator->applies($request)) {
if ($workspace_id = $negotiator->getActiveWorkspaceId($request)) {
/** @var \Drupal\workspaces\WorkspaceInterface $negotiated_workspace */
$negotiated_workspace = $this->entityTypeManager
->getStorage('workspace')
->load($workspace_id);
}
// By default, 'view' access is checked when a workspace is activated,
// but it should also be checked when retrieving the currently active
// workspace.
if (isset($negotiated_workspace) && $negotiated_workspace->access('view')) {
// Notify the negotiator that its workspace has been selected.
$negotiator->setActiveWorkspace($negotiated_workspace);
$active_workspace = $negotiated_workspace;
break;
}
}
}
// If no negotiator was able to provide a valid workspace, default to the
// live version of the site.
$this->activeWorkspace = $active_workspace ?? FALSE;
}
return $this->activeWorkspace;
}
/**
* {@inheritdoc}
*/
public function setActiveWorkspace(WorkspaceInterface $workspace) {
$persist = func_num_args() < 2 || func_get_arg(1);
$this->doSwitchWorkspace($workspace);
// Set the workspace on the first applicable negotiator.
if ($persist) {
$request = $this->requestStack
->getCurrentRequest();
foreach ($this->negotiators as $negotiator) {
if ($negotiator->applies($request)) {
$negotiator->setActiveWorkspace($workspace);
break;
}
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function switchToLive() {
$this->doSwitchWorkspace(NULL);
// Unset the active workspace on all negotiators.
foreach ($this->negotiators as $negotiator) {
$negotiator->unsetActiveWorkspace();
}
return $this;
}
/**
* Switches the current workspace.
*
* @param \Drupal\workspaces\WorkspaceInterface|null $workspace
* The workspace to set as active or NULL to switch out of the currently
* active workspace.
*
* @throws \Drupal\workspaces\WorkspaceAccessException
* Thrown when the current user doesn't have access to view the workspace.
*/
protected function doSwitchWorkspace($workspace) {
// If the current user doesn't have access to view the workspace, they
// shouldn't be allowed to switch to it, except in CLI processes.
if ($workspace && PHP_SAPI !== 'cli' && !$workspace->access('view')) {
$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 ?: FALSE;
// Clear the static entity cache for the supported entity types.
$cache_tags_to_invalidate = [];
foreach (array_keys($this->workspaceInfo
->getSupportedEntityTypes()) as $entity_type_id) {
$this->entityTypeManager
->getStorage($entity_type_id)
->resetCache();
$cache_tags_to_invalidate[] = 'entity.memory_cache:' . $entity_type_id;
}
$this->entityMemoryCache
->invalidateTags($cache_tags_to_invalidate);
// Clear the static cache for path aliases. We can't inject the path alias
// manager service because it would create a circular dependency.
if (\Drupal::hasService('path_alias.manager')) {
\Drupal::service('path_alias.manager')->cacheClear();
}
}
/**
* {@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 executeOutsideWorkspace(callable $function) {
$previous_active_workspace = $this->getActiveWorkspace();
$this->doSwitchWorkspace(NULL);
$result = $function();
$this->doSwitchWorkspace($previous_active_workspace);
return $result;
}
/**
* {@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);
// Get the first deleted workspace from the list and delete the revisions
// associated with it, along with the workspace association records.
$workspace_id = reset($deleted_workspace_ids);
$all_associated_revisions = [];
foreach (array_keys($this->workspaceInfo
->getSupportedEntityTypes()) as $entity_type_id) {
$all_associated_revisions[$entity_type_id] = $this->workspaceAssociation
->getAssociatedRevisions($workspace_id, $entity_type_id);
}
$all_associated_revisions = array_filter($all_associated_revisions);
$count = 1;
foreach ($all_associated_revisions as $entity_type_id => $associated_revisions) {
/** @var \Drupal\Core\Entity\RevisionableStorageInterface $associated_entity_storage */
$associated_entity_storage = $this->entityTypeManager
->getStorage($entity_type_id);
// Sort the associated revisions in reverse ID order, so we can delete the
// most recent revisions first.
krsort($associated_revisions);
// Get a list of default revisions tracked by the given workspace, because
// they need to be handled differently than pending revisions.
$initial_revision_ids = $this->workspaceAssociation
->getAssociatedInitialRevisions($workspace_id, $entity_type_id);
foreach (array_keys($associated_revisions) as $revision_id) {
if ($count > $batch_size) {
continue 2;
}
// If the workspace is tracking the entity's default revision (i.e. the
// entity was created inside that workspace), we need to delete the
// whole entity after all of its pending revisions are gone.
if (isset($initial_revision_ids[$revision_id])) {
$associated_entity_storage->delete([
$associated_entity_storage->load($initial_revision_ids[$revision_id]),
]);
}
else {
// Delete the associated entity revision.
$associated_entity_storage->deleteRevision($revision_id);
}
$count++;
}
}
// The purging operation above might have taken a long time, so we need to
// request a fresh list of tracked entities. If it is empty, we can go ahead
// and remove the deleted workspace ID entry from state.
$has_associated_revisions = FALSE;
foreach (array_keys($this->workspaceInfo
->getSupportedEntityTypes()) as $entity_type_id) {
if (!empty($this->workspaceAssociation
->getAssociatedRevisions($workspace_id, $entity_type_id))) {
$has_associated_revisions = TRUE;
break;
}
}
if (!$has_associated_revisions) {
unset($deleted_workspace_ids[$workspace_id]);
$this->state
->set('workspace.deleted', $deleted_workspace_ids);
// Delete any possible leftover association entries.
$this->workspaceAssociation
->deleteAssociations($workspace_id);
}
}
/**
* Adds a workspace negotiator service.
*
* @param \Drupal\workspaces\Negotiator\WorkspaceNegotiatorInterface $negotiator
* The negotiator to be added.
*
* @todo Remove in drupal:12.0.0.
*
* @internal
*/
public function addNegotiator(WorkspaceNegotiatorInterface $negotiator) : void {
$this->collectedNegotiators[] = $negotiator;
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
StringTranslationTrait::$stringTranslation | protected | property | The string translation service. | 3 | |
StringTranslationTrait::formatPlural | protected | function | Formats a string containing a count of items. | ||
StringTranslationTrait::getNumberOfPlurals | protected | function | Returns the number of plurals supported by a given language. | ||
StringTranslationTrait::getStringTranslation | protected | function | Gets the string translation service. | ||
StringTranslationTrait::setStringTranslation | public | function | Sets the string translation service to use. | 2 | |
StringTranslationTrait::t | protected | function | Translates a string to the current language or to a given language. | 1 | |
WorkspaceManager::$activeWorkspace | protected | property | The current active workspace. | ||
WorkspaceManager::$collectedNegotiators | private | property | An array of workspace negotiator services. | ||
WorkspaceManager::addNegotiator | public | function | Adds a workspace negotiator service. | ||
WorkspaceManager::doSwitchWorkspace | protected | function | Switches the current workspace. | ||
WorkspaceManager::executeInWorkspace | public | function | Executes the given callback function in the context of a workspace. | Overrides WorkspaceManagerInterface::executeInWorkspace | |
WorkspaceManager::executeOutsideWorkspace | public | function | Executes the given callback function without any workspace context. | Overrides WorkspaceManagerInterface::executeOutsideWorkspace | |
WorkspaceManager::getActiveWorkspace | public | function | Gets the active workspace. | Overrides WorkspaceManagerInterface::getActiveWorkspace | |
WorkspaceManager::hasActiveWorkspace | public | function | Determines whether a workspace is active in the current request. | Overrides WorkspaceManagerInterface::hasActiveWorkspace | |
WorkspaceManager::purgeDeletedWorkspacesBatch | public | function | Deletes the revisions associated with deleted workspaces. | Overrides WorkspaceManagerInterface::purgeDeletedWorkspacesBatch | |
WorkspaceManager::setActiveWorkspace | public | function | Sets the active workspace. | Overrides WorkspaceManagerInterface::setActiveWorkspace | |
WorkspaceManager::switchToLive | public | function | Unsets the active workspace. | Overrides WorkspaceManagerInterface::switchToLive | |
WorkspaceManager::__construct | public | function |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.