composer update

This commit is contained in:
Oliver Davies 2019-01-24 08:00:03 +00:00
parent f6abc3dce2
commit 71dfaca858
1753 changed files with 45274 additions and 14619 deletions

View file

@ -70,4 +70,18 @@ class EntityTypeInfo implements ContainerInjectionInterface {
}
}
/**
* Alters field plugin definitions.
*
* @param array[] $definitions
* An array of field plugin definitions.
*
* @see hook_field_info_alter()
*/
public function fieldInfoAlter(&$definitions) {
if (isset($definitions['entity_reference'])) {
$definitions['entity_reference']['constraints']['EntityReferenceSupportedNewEntities'] = [];
}
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\workspaces\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* The entity reference supported new entities constraint.
*
* @Constraint(
* id = "EntityReferenceSupportedNewEntities",
* label = @Translation("Entity Reference Supported New Entities", context = "Validation"),
* )
*/
class EntityReferenceSupportedNewEntitiesConstraint extends Constraint {
/**
* The default violation message.
*
* @var string
*/
public $message = '%collection_label can only be created in the default workspace.';
}

View file

@ -0,0 +1,65 @@
<?php
namespace Drupal\workspaces\Plugin\Validation\Constraint;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\workspaces\WorkspaceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Checks if new entities created for entity reference fields are supported.
*/
class EntityReferenceSupportedNewEntitiesConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
/**
* The workspace manager.
*
* @var \Drupal\workspaces\WorkspaceManagerInterface
*/
protected $workspaceManager;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Creates a new EntityReferenceSupportedNewEntitiesConstraintValidator instance.
*/
public function __construct(WorkspaceManagerInterface $workspaceManager, EntityTypeManagerInterface $entityTypeManager) {
$this->workspaceManager = $workspaceManager;
$this->entityTypeManager = $entityTypeManager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('workspaces.manager'),
$container->get('entity_type.manager')
);
}
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
if ($this->workspaceManager->getActiveWorkspace()->isDefaultWorkspace()) {
return;
}
$target_entity_type_id = $value->getFieldDefinition()->getFieldStorageDefinition()->getSetting('target_type');
$target_entity_type = $this->entityTypeManager->getDefinition($target_entity_type_id);
if ($value->hasNewEntity() && !$this->workspaceManager->isEntityTypeSupported($target_entity_type)) {
$this->context->addViolation($constraint->message, ['%collection_label' => $target_entity_type->getCollectionLabel()]);
}
}
}

View file

@ -2,6 +2,7 @@
namespace Drupal\workspaces;
use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface;
use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityTypeInterface;
@ -47,6 +48,13 @@ class WorkspaceManager implements WorkspaceManagerInterface {
*/
protected $entityTypeManager;
/**
* The entity memory cache service.
*
* @var \Drupal\Core\Cache\MemoryCache\MemoryCacheInterface
*/
protected $entityMemoryCache;
/**
* The current user.
*
@ -96,6 +104,8 @@ class WorkspaceManager implements WorkspaceManagerInterface {
* 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
@ -107,9 +117,10 @@ class WorkspaceManager implements WorkspaceManagerInterface {
* @param array $negotiator_ids
* The workspace negotiator service IDs.
*/
public function __construct(RequestStack $request_stack, EntityTypeManagerInterface $entity_type_manager, AccountProxyInterface $current_user, StateInterface $state, LoggerInterface $logger, ClassResolverInterface $class_resolver, array $negotiator_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;
@ -167,6 +178,31 @@ class WorkspaceManager implements WorkspaceManagerInterface {
* {@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()) {
@ -179,22 +215,30 @@ class WorkspaceManager implements WorkspaceManagerInterface {
$this->activeWorkspace = $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;
}
// 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.');
}
$supported_entity_types = $this->getSupportedEntityTypes();
foreach ($supported_entity_types as $supported_entity_type) {
$this->entityTypeManager->getStorage($supported_entity_type->id())->resetCache();
}
$previous_active_workspace = $this->getActiveWorkspace();
$this->doSwitchWorkspace($workspace);
$result = $function();
$this->doSwitchWorkspace($previous_active_workspace);
return $this;
return $result;
}
/**

View file

@ -49,6 +49,19 @@ interface WorkspaceManagerInterface {
*/
public function setActiveWorkspace(WorkspaceInterface $workspace);
/**
* Executes the given callback function in the context of a workspace.
*
* @param string $workspace_id
* The ID of a workspace.
* @param callable $function
* The callback to be executed.
*
* @return mixed
* The callable's return value.
*/
public function executeInWorkspace($workspace_id, callable $function);
/**
* Determines whether runtime entity operations should be altered.
*

View file

@ -16,7 +16,7 @@ class WorkspaceTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['workspaces'];
public static $modules = ['workspaces', 'toolbar'];
/**
* A test user.
@ -43,6 +43,8 @@ class WorkspaceTest extends BrowserTestBase {
'create workspace',
'edit own workspace',
'edit any workspace',
'view own workspace',
'access toolbar',
];
$this->editor1 = $this->drupalCreateUser($permissions);
@ -69,6 +71,36 @@ class WorkspaceTest extends BrowserTestBase {
$page->hasContent("This value is not valid");
}
/**
* Test that the toolbar correctly shows the active workspace.
*/
public function testWorkspaceToolbar() {
$this->drupalLogin($this->editor1);
$this->drupalPostForm('/admin/config/workflow/workspaces/add', [
'id' => 'test_workspace',
'label' => 'Test workspace',
], 'Save');
// Activate the test workspace.
$this->drupalPostForm('/admin/config/workflow/workspaces/manage/test_workspace/activate', [], 'Confirm');
$this->drupalGet('<front>');
$page = $this->getSession()->getPage();
// Toolbar should show the correct label.
$this->assertTrue($page->hasLink('Test workspace'));
// Change the workspace label.
$this->drupalPostForm('/admin/config/workflow/workspaces/manage/test_workspace/edit', [
'label' => 'New name',
], 'Save');
$this->drupalGet('<front>');
$page = $this->getSession()->getPage();
// Toolbar should show the new label.
$this->assertTrue($page->hasLink('New name'));
}
/**
* Test changing the owner of a workspace.
*/

View file

@ -0,0 +1,81 @@
<?php
namespace Drupal\Tests\workspaces\Kernel;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestMulRevPub;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\user\Traits\UserCreationTrait;
/**
* @coversDefaultClass \Drupal\workspaces\Plugin\Validation\Constraint\EntityReferenceSupportedNewEntitiesConstraintValidator
* @group workspaces
*/
class EntityReferenceSupportedNewEntitiesConstraintValidatorTest extends KernelTestBase {
use UserCreationTrait;
use WorkspaceTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'user',
'workspaces',
'entity_test',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
$this->installSchema('system', ['sequences']);
$this->createUser();
$fields['supported_reference'] = BaseFieldDefinition::create('entity_reference')->setSetting('target_type', 'entity_test_mulrevpub');
$fields['unsupported_reference'] = BaseFieldDefinition::create('entity_reference')->setSetting('target_type', 'entity_test');
$this->container->get('state')->set('entity_test_mulrevpub.additional_base_field_definitions', $fields);
$this->installEntitySchema('entity_test_mulrevpub');
$this->initializeWorkspacesModule();
}
/**
* @covers ::validate
*/
public function testNewEntitiesAllowedInDefaultWorkspace() {
$entity = EntityTestMulRevPub::create([
'unsupported_reference' => [
'entity' => EntityTest::create([]),
],
'supported_reference' => [
'entity' => EntityTest::create([]),
],
]);
$this->assertCount(0, $entity->validate());
}
/**
* @covers ::validate
*/
public function testNewEntitiesForbiddenInNonDefaultWorkspace() {
$this->switchToWorkspace('stage');
$entity = EntityTestMulRevPub::create([
'unsupported_reference' => [
'entity' => EntityTest::create([]),
],
'supported_reference' => [
'entity' => EntityTestMulRevPub::create([]),
],
]);
$violations = $entity->validate();
$this->assertCount(1, $violations);
$this->assertEquals('<em class="placeholder">Test entity entities</em> can only be created in the default workspace.', $violations[0]->getMessage());
}
}

View file

@ -14,7 +14,6 @@ use Drupal\Tests\node\Traits\NodeCreationTrait;
use Drupal\Tests\user\Traits\UserCreationTrait;
use Drupal\views\Tests\ViewResultAssertionTrait;
use Drupal\views\Views;
use Drupal\workspaces\Entity\Workspace;
/**
* Tests a complete deployment scenario across different workspaces.
@ -28,6 +27,7 @@ class WorkspaceIntegrationTest extends KernelTestBase {
use NodeCreationTrait;
use UserCreationTrait;
use ViewResultAssertionTrait;
use WorkspaceTestTrait;
/**
* The entity type manager.
@ -36,13 +36,6 @@ class WorkspaceIntegrationTest extends KernelTestBase {
*/
protected $entityTypeManager;
/**
* An array of test workspaces, keyed by workspace ID.
*
* @var \Drupal\workspaces\WorkspaceInterface[]
*/
protected $workspaces = [];
/**
* Creation timestamp that should be incremented for each new entity.
*
@ -93,34 +86,6 @@ class WorkspaceIntegrationTest extends KernelTestBase {
$this->createNode(['title' => 'live - 2 - r2 - unpublished', 'created' => $this->createdTimestamp++, 'status' => FALSE]);
}
/**
* Enables the Workspaces module and creates two workspaces.
*/
protected function initializeWorkspacesModule() {
// Enable the Workspaces module here instead of the static::$modules array
// so we can test it with default content.
$this->enableModules(['workspaces']);
$this->container = \Drupal::getContainer();
$this->entityTypeManager = \Drupal::entityTypeManager();
$this->installEntitySchema('workspace');
$this->installEntitySchema('workspace_association');
// Create two workspaces by default, 'live' and 'stage'.
$this->workspaces['live'] = Workspace::create(['id' => 'live']);
$this->workspaces['live']->save();
$this->workspaces['stage'] = Workspace::create(['id' => 'stage']);
$this->workspaces['stage']->save();
$permissions = [
'administer nodes',
'create workspace',
'edit any workspace',
'view any workspace',
];
$this->setCurrentUser($this->createUser($permissions));
}
/**
* Tests various scenarios for creating and deploying content in workspaces.
*/
@ -492,6 +457,57 @@ class WorkspaceIntegrationTest extends KernelTestBase {
$entity_test->delete();
}
/**
* @covers \Drupal\workspaces\WorkspaceManager::executeInWorkspace
*/
public function testExecuteInWorkspaceContext() {
$this->initializeWorkspacesModule();
// Create an entity in the default workspace.
$this->switchToWorkspace('live');
$node = $this->createNode([
'title' => 'live node 1',
]);
$node->save();
// Switch to the 'stage' workspace and change some values for the referenced
// entities.
$this->switchToWorkspace('stage');
$node->title->value = 'stage node 1';
$node->save();
// Switch back to the default workspace and run the baseline assertions.
$this->switchToWorkspace('live');
$storage = $this->entityTypeManager->getStorage('node');
$this->assertEquals('live', $this->workspaceManager->getActiveWorkspace()->id());
$live_node = $storage->loadUnchanged($node->id());
$this->assertEquals('live node 1', $live_node->title->value);
$result = $storage->getQuery()
->condition('title', 'live node 1')
->execute();
$this->assertEquals([$live_node->getRevisionId() => $node->id()], $result);
// Try the same assertions in the context of the 'stage' workspace.
$this->workspaceManager->executeInWorkspace('stage', function () use ($node, $storage) {
$this->assertEquals('stage', $this->workspaceManager->getActiveWorkspace()->id());
$stage_node = $storage->loadUnchanged($node->id());
$this->assertEquals('stage node 1', $stage_node->title->value);
$result = $storage->getQuery()
->condition('title', 'stage node 1')
->execute();
$this->assertEquals([$stage_node->getRevisionId() => $stage_node->id()], $result);
});
// Check that the 'stage' workspace was not persisted by the workspace
// manager.
$this->assertEquals('live', $this->workspaceManager->getActiveWorkspace()->id());
}
/**
* Checks entity load, entity queries and views results for a test scenario.
*
@ -681,18 +697,6 @@ class WorkspaceIntegrationTest extends KernelTestBase {
}
}
/**
* Sets a given workspace as active.
*
* @param string $workspace_id
* The ID of the workspace to switch to.
*/
protected function switchToWorkspace($workspace_id) {
// Switch the test runner's context to the specified workspace.
$workspace = $this->entityTypeManager->getStorage('workspace')->load($workspace_id);
\Drupal::service('workspaces.manager')->setActiveWorkspace($workspace);
}
/**
* Flattens the expectations array defined by testWorkspaces().
*

View file

@ -0,0 +1,67 @@
<?php
namespace Drupal\Tests\workspaces\Kernel;
use Drupal\workspaces\Entity\Workspace;
/**
* A trait with common workspaces testing functionality.
*/
trait WorkspaceTestTrait {
/**
* The workspaces manager.
*
* @var \Drupal\workspaces\WorkspaceManagerInterface
*/
protected $workspaceManager;
/**
* An array of test workspaces, keyed by workspace ID.
*
* @var \Drupal\workspaces\WorkspaceInterface[]
*/
protected $workspaces = [];
/**
* Enables the Workspaces module and creates two workspaces.
*/
protected function initializeWorkspacesModule() {
// Enable the Workspaces module here instead of the static::$modules array
// so we can test it with default content.
$this->enableModules(['workspaces']);
$this->container = \Drupal::getContainer();
$this->entityTypeManager = \Drupal::entityTypeManager();
$this->workspaceManager = \Drupal::service('workspaces.manager');
$this->installEntitySchema('workspace');
$this->installEntitySchema('workspace_association');
// Create two workspaces by default, 'live' and 'stage'.
$this->workspaces['live'] = Workspace::create(['id' => 'live']);
$this->workspaces['live']->save();
$this->workspaces['stage'] = Workspace::create(['id' => 'stage']);
$this->workspaces['stage']->save();
$permissions = array_intersect([
'administer nodes',
'create workspace',
'edit any workspace',
'view any workspace',
], array_keys($this->container->get('user.permissions')->getPermissions()));
$this->setCurrentUser($this->createUser($permissions));
}
/**
* Sets a given workspace as active.
*
* @param string $workspace_id
* The ID of the workspace to switch to.
*/
protected function switchToWorkspace($workspace_id) {
// Switch the test runner's context to the specified workspace.
$workspace = $this->entityTypeManager->getStorage('workspace')->load($workspace_id);
\Drupal::service('workspaces.manager')->setActiveWorkspace($workspace);
}
}

View file

@ -56,6 +56,15 @@ function workspaces_form_alter(&$form, FormStateInterface $form_state, $form_id)
->formAlter($form, $form_state, $form_id);
}
/**
* Implements hook_field_info_alter().
*/
function workspaces_field_info_alter(&$definitions) {
\Drupal::service('class_resolver')
->getInstanceFromDefinition(EntityTypeInfo::class)
->fieldInfoAlter($definitions);
}
/**
* Implements hook_entity_load().
*/
@ -154,15 +163,15 @@ function workspaces_toolbar() {
$current_user = \Drupal::currentUser();
if (!$current_user->hasPermission('administer workspaces')
|| !$current_user->hasPermission('view own workspace')
|| !$current_user->hasPermission('view any workspace')) {
&& !$current_user->hasPermission('view own workspace')
&& !$current_user->hasPermission('view any workspace')) {
return $items;
}
/** @var \Drupal\workspaces\WorkspaceInterface $active_workspace */
$active_workspace = \Drupal::service('workspaces.manager')->getActiveWorkspace();
$items['workspace'] = [
$items['workspace'] += [
'#type' => 'toolbar_item',
'tab' => [
'#type' => 'link',
@ -180,6 +189,7 @@ function workspaces_toolbar() {
],
]),
],
'#cache' => ['tags' => $active_workspace->getCacheTags()],
],
'#wrapper_attributes' => [
'class' => ['workspaces-toolbar-tab'],

View file

@ -1,7 +1,7 @@
services:
workspaces.manager:
class: Drupal\workspaces\WorkspaceManager
arguments: ['@request_stack', '@entity_type.manager', '@current_user', '@state', '@logger.channel.workspaces', '@class_resolver']
arguments: ['@request_stack', '@entity_type.manager', '@entity.memory_cache', '@current_user', '@state', '@logger.channel.workspaces', '@class_resolver']
tags:
- { name: service_id_collector, tag: workspace_negotiator }
workspaces.operation_factory: