diff --git a/css/multiversion.toolbar.css b/css/multiversion.toolbar.css index 8900815..ec41cc3 100644 --- a/css/multiversion.toolbar.css +++ b/css/multiversion.toolbar.css @@ -10,3 +10,33 @@ .toolbar .toolbar-bar .multiversion-toolbar-tab.toolbar-tab { float: right; } + +#toolbar-item-workspace-switcher-tray nav { + display: flex; + align-items: center; +} + +#toolbar-item-workspace-switcher-tray.toolbar-tray-vertical nav { + display: flex; + align-items: center; + flex-direction: column; +} + +.toolbar #toolbar-item-workspace-switcher-tray input[type="submit"], +.toolbar #toolbar-item-workspace-switcher-tray input[type="submit"]:hover { + background: transparent; + border: 0; + border-radius: 0; +} + +.toolbar #toolbar-item-workspace-switcher-tray input[type="submit"]:hover { + text-decoration: underline; +} + +.toolbar #toolbar-item-workspace-switcher-tray.toolbar-tray-horizontal a { + border-right: 1px solid darkgray; +} + +.toolbar #toolbar-item-workspace-switcher-tray.toolbar-tray-vertical a { + border-bottom: 1px solid darkgray; +} diff --git a/multiversion.routing.yml b/multiversion.routing.yml index 133cd95..e4657bb 100644 --- a/multiversion.routing.yml +++ b/multiversion.routing.yml @@ -8,6 +8,16 @@ entity.workspace.add: requirements: _permission: 'administer workspaces' +entity.workspace.activate_form: + path: '/admin/structure/workspaces/{workspace}/activate' + defaults: + _title: 'Activate Workspace' + _form: '\Drupal\multiversion\Form\WorkspaceActivateForm' + options: + _admin_route: TRUE + requirements: + _permission: 'administer workspaces' + entity.workspace.add_form: path: '/admin/structure/workspaces/add/{workspace_type}' defaults: diff --git a/multiversion.services.yml b/multiversion.services.yml index a617103..be355d6 100644 --- a/multiversion.services.yml +++ b/multiversion.services.yml @@ -38,7 +38,7 @@ services: - { name: cache.context } multiversion.toolbar: class: Drupal\multiversion\Toolbar - arguments: ['@entity_type.manager', '@workspace.manager'] + arguments: ['@entity_type.manager', '@workspace.manager', '@form_builder'] # @todo: {@link https://www.drupal.org/node/2597414 Simplify the container diff --git a/multiversion_ui/css/multiversion.switcherform.css b/multiversion_ui/css/multiversion.switcherform.css new file mode 100644 index 0000000..3fb9e69 --- /dev/null +++ b/multiversion_ui/css/multiversion.switcherform.css @@ -0,0 +1,4 @@ +form.multiversion-switcher-form { + display: inline; + border: 2px solid red; +} diff --git a/src/Entity/Workspace.php b/src/Entity/Workspace.php index cb38769..308e838 100644 --- a/src/Entity/Workspace.php +++ b/src/Entity/Workspace.php @@ -35,6 +35,7 @@ use Drupal\user\UserInterface; * }, * links = { * "edit-form" = "/admin/structure/workspaces/{workspace}/edit", + * "activate-form" = "/admin/structure/workspaces/{workspace}/activate", * "collection" = "/admin/structure/workspaces" * }, * admin_permission = "administer workspaces", diff --git a/src/Form/WorkspaceActivateForm.php b/src/Form/WorkspaceActivateForm.php new file mode 100644 index 0000000..f73f3fb --- /dev/null +++ b/src/Form/WorkspaceActivateForm.php @@ -0,0 +1,121 @@ +get('workspace.manager'), + $container->get('entity_type.manager') + ); + } + + /** + * Inject dependencies for activating a workspace. + * + * @param \Drupal\multiversion\Workspace\WorkspaceManagerInterface $workspace_manager + * The workspace manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + */ + public function __construct(WorkspaceManagerInterface $workspace_manager, EntityTypeManagerInterface $entity_type_manager) { + $this->workspaceManager = $workspace_manager; + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, Workspace $workspace = NULL) { + $form['workspace_id'] = [ + '#type' => 'hidden', + '#value' => $workspace->id(), + ]; + + $form['instruction'] = [ + '#type' => 'markup', + '#prefix' => '

', + '#markup' => $this->t('Would you like to activate the %workspace workspace?', ['%workspace' => $workspace->label()]), + '#suffix' => '

', + ]; + + $form['submit'] = [ + '#type' => 'submit', + '#value' => 'Activate', + ]; + + $form['#title'] = $this->t('Activate workspace %label', array('%label' => $workspace->label())); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + $id = $form_state->getValue('workspace_id'); + + // Ensure we are given an ID. + if (!$id) { + $form_state->setErrorByName('workspace_id', 'The workspace ID is required.'); + } + + // Ensure the workspace by that id exists. + /** @var WorkspaceInterface $workspace */ + $workspace = $this->entityTypeManager->getStorage('workspace')->load($id); + if (!$workspace) { + $form_state->setErrorByName('workspace_id', 'This workspace no longer exists.'); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $id = $form_state->getValue('workspace_id'); + /** @var WorkspaceInterface $workspace */ + $workspace = $this->entityTypeManager->getStorage('workspace')->load($id); + + $this->workspaceManager->setActiveWorkspace($workspace); + + drupal_set_message($this->t('Now viewing workspace %workspace', ['%workspace' => $workspace->label()])); + } + +} diff --git a/src/Form/WorkspaceSwitcherForm.php b/src/Form/WorkspaceSwitcherForm.php new file mode 100644 index 0000000..cc035b7 --- /dev/null +++ b/src/Form/WorkspaceSwitcherForm.php @@ -0,0 +1,121 @@ +get('workspace.manager'), + $container->get('entity_type.manager') + ); + } + + public function __construct(WorkspaceManagerInterface $workspace_manager, EntityTypeManagerInterface $entity_type_manager) { + $this->workspaceManager = $workspace_manager; + $this->entityTypeManager = $entity_type_manager; + } + + + /** + * @inheritDoc + */ + public function getFormId() { + return 'workspace_switcher_form_' . static::$formCounter++; + } + + /** + * @inheritDoc + */ + public function buildForm(array $form, FormStateInterface $form_state, WorkspaceInterface $workspace = NULL) { + // @todo this form is identical to WorkspaceActivateForm except for this method; can we consolidate forms? + $form['workspace_id'] = [ + '#type' => 'hidden', + '#value' => $workspace->id(), + ]; + + $form['submit'] = [ + '#type' => 'submit', + '#value' => $workspace->label(), + ]; + + // @todo This does not appear to have any effect. I am not sure yet why. + $form['#attached']['library'][] = 'multiversion/switcherform'; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + $id = $form_state->getValue('workspace_id'); + + // Ensure we are given an ID. + if (!$id) { + $form_state->setErrorByName('workspace_id', 'The workspace ID is required.'); + } + + // Ensure the workspace by that id exists. + /** @var WorkspaceInterface $workspace */ + $workspace = $this->entityTypeManager->getStorage('workspace')->load($id); + if (!$workspace) { + $form_state->setErrorByName('workspace_id', 'This workspace no longer exists.'); + } + } + + /** + * @inheritDoc + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $id = $form_state->getValue('workspace_id'); + /** @var WorkspaceInterface $workspace */ + $workspace = $this->entityTypeManager->getStorage('workspace')->load($id); + + $this->workspaceManager->setActiveWorkspace($workspace); + + drupal_set_message($this->t('Now viewing workspace %workspace', ['%workspace' => $workspace->label()])); + } + +} diff --git a/src/Plugin/Block/WorkspaceBlock.php b/src/Plugin/Block/WorkspaceBlock.php deleted file mode 100644 index 7f675bf..0000000 --- a/src/Plugin/Block/WorkspaceBlock.php +++ /dev/null @@ -1,80 +0,0 @@ -workspaceManager = $workspace_manager; - $this->entityTypeManager = $entity_type_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('workspace.manager'), - $container->get('entity_type.manager') - ); - } - - /** - * {@inheritdoc} - */ - public function build() { - $build = array(); - $route_name = \Drupal::service('path.matcher')->isFrontPage() ? '' : ''; - $links = $this->workspaceManager->getWorkspaceSwitchLinks(Url::fromRoute($route_name)); - - if (isset($links)) { - $build = array( - '#theme' => 'links__workspace_block', - '#links' => $links, - '#attributes' => array( - 'class' => array( - 'workspace-switcher', - ), - ), - '#set_active_class' => TRUE, - // @todo: The caching need tests. - '#cache' => [ - 'contexts' => $this->entityTypeManager->getDefinition('workspace')->getListCacheContexts(), - 'tags' => $this->entityTypeManager->getDefinition('workspace')->getListCacheTags(), - ], - ); - } - return $build; - } - -} diff --git a/src/Tests/Views/MultiversionTestBase.php b/src/Tests/Views/MultiversionTestBase.php index 0ecfcd9..4e7bf1c 100644 --- a/src/Tests/Views/MultiversionTestBase.php +++ b/src/Tests/Views/MultiversionTestBase.php @@ -20,7 +20,7 @@ abstract class MultiversionTestBase extends ViewTestBase { * * @var array */ - public static $modules = ['multiversion_test_views']; + public static $modules = ['multiversion_test_views', 'toolbar']; protected function setUp($import_test_views = TRUE) { parent::setUp($import_test_views); diff --git a/src/Tests/Views/WorkspaceTest.php b/src/Tests/Views/WorkspaceTest.php index 1749665..054e118 100644 --- a/src/Tests/Views/WorkspaceTest.php +++ b/src/Tests/Views/WorkspaceTest.php @@ -8,6 +8,7 @@ namespace Drupal\multiversion\Tests\Views; use Drupal\multiversion\Entity\Workspace; +use Drupal\multiversion\Workspace\WorkspaceManagerInterface; /** * Tests the workspace and current_workspace field handlers. @@ -20,45 +21,61 @@ class WorkspaceTest extends MultiversionTestBase { protected $strictConfigSchema = FALSE; /** - * Views used by this test. - * - * @var array + * {@inheritdoc} */ - public static $testViews = ['test_current_workspace']; + protected function setUp() { + parent::setUp(); + + // Create Article node type. + if ($this->profile != 'standard') { + $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + } + } /** - * Tests the workspace filter. + * Test being able to switch between active workspaces. */ public function testWorkspace() { - $admin_user = $this->drupalCreateUser(['bypass node access']); + $admin_user = $this->drupalCreateUser(['bypass node access', 'administer workspaces']); $uid = $admin_user->id(); $this->drupalLogin($admin_user); - // Create two nodes on 'default' workspace. - $node1 = $this->drupalCreateNode(['uid' => $uid]); - $node2 = $this->drupalCreateNode(['uid' => $uid]); + /** @var WorkspaceManagerInterface $workspace_manager */ + $workspace_manager = $this->container->get('workspace.manager'); + + $initial_workspace = $workspace_manager->getActiveWorkspace(); + + // Create a node on 'default' workspace. + $this->drupalPostForm('node/add/article', [ + 'title[0][value]' => 'Initial workspace article', + ], 'Save'); + $node1_url = $this->drupalGetHeader('location', true); // Create a new workspace and switch to it. $new_workspace = Workspace::create(['machine_name' => 'new_workspace', 'label' => 'New Workspace', 'type' => 'basic']); $new_workspace->save(); - \Drupal::service('workspace.manager')->setActiveWorkspace($new_workspace); - - // Create two nodes on 'new_workspace' workspace. - $node3 = $this->drupalCreateNode(['uid' => $uid]); - $node4 = $this->drupalCreateNode(['uid' => $uid]); - - // Test current_workspace filter. - $this->drupalGet('test_current_workspace', ['query' => ['workspace' => $new_workspace->id()]]); - $this->assertNoText($node1->label()); - $this->assertNoText($node2->label()); - $this->assertText($node3->label()); - $this->assertText($node4->label()); - - $this->drupalGet('test_current_workspace', ['query' => ['workspace' => 1]]); - $this->assertText($node1->label()); - $this->assertText($node2->label()); - $this->assertNoText($node3->label()); - $this->assertNoText($node4->label()); + $this->drupalPostForm($new_workspace->url('activate-form'), [], 'Activate'); + + // Create a node on 'new_workspace' workspace. + $this->drupalPostForm('node/add/article', [ + 'title[0][value]' => 'New workspace article', + ], 'Save'); + $node2_url = $this->drupalGetHeader('location', true); + + // Ensure you have access to only active workspace. + $this->drupalGet($node1_url); + $this->assertResponse(404, 'User cannot access content in an inactive workspace.'); + $out = $this->drupalGet($node2_url); + $this->assertResponse(200, 'User can access content in the active workspace.'); + + // Switch back to the initial workspace. + $this->drupalPostForm($initial_workspace->url('activate-form'), [], 'Activate'); + + // Ensure you have access to only active workspace. + $this->drupalGet($node1_url); + $this->assertResponse(200, 'User can access content in the active workspace.'); + $out = $this->drupalGet($node2_url); + $this->assertResponse(404, 'User cannot access content in an inactive workspace.'); } } diff --git a/src/Tests/WorkspaceBlockTest.php b/src/Tests/WorkspaceBlockTest.php deleted file mode 100644 index fec3050..0000000 --- a/src/Tests/WorkspaceBlockTest.php +++ /dev/null @@ -1,91 +0,0 @@ -webUser = $this->drupalCreateUser([ - 'administer blocks', - 'create article content', - 'access administration pages', - 'access content', - ]); - $this->drupalLogin($this->webUser); - $this->drupalPlaceBlock('local_tasks_block'); - } - - public function testBlock() { - $this->drupalPlaceBlock('multiversion_workspace_block', ['region' => 'sidebar_first', 'label' => 'Workspace switcher']); - $this->drupalGet(''); - - // Confirm that the block is being displayed. - $this->assertText('Workspace switcher', t('Block successfully being displayed on the page.')); - $front = Url::fromRoute('')->toString(TRUE)->getGeneratedUrl(); - $this->assertRaw('href="'. $front .'"', 'The id of the default workspace was displayed in the Workspace switcher block as a link.'); - $machine_name = $this->randomMachineName(); - $entity = Workspace::create(['machine_name' => $machine_name, 'label' => $machine_name, 'type' => 'basic']); - $entity->save(); - $id = $entity->id(); - $node = Node::create(['type' => 'article', 'title' => 'Test article']); - $node->save(); - $nid = $node->id(); - $this->drupalGet(''); - $this->assertText('Test article', 'The title of the test article was displayed on the front page.'); - $this->drupalGet("node/$nid"); - $this->assertText('Test article'); - $this->drupalGet(''); - $url = $front . "?workspace=$id"; - $this->assertRaw('href="'. $url .'"', 'The id of the new workspace was displayed in the Workspace switcher block as a link.'); - $this->drupalGet("/node/$nid", ['query' => ['workspace' => $id]]); - $this->assertText('Page not found'); - $this->drupalGet('', ['query' =>['workspace' => 'default']]); - $this->assertText('Test article', 'The title of the test article was displayed on the front page.'); - $this->drupalGet('', ['query' => ['workspace' => $id]]); - $this->drupalGet('/node/add/article'); - $this->assertText('Create Article'); - $this->drupalGet('', ['query' => ['workspace' => $id]]); - $this->assertNoText('Test article', 'The title of the test article was not displayed on the front page after switching the workspace.'); - $entity->delete(); - $this->drupalGet(''); - $this->assertNoText($machine_name, 'The name of the deleted workspace was not displayed in the Workspace switcher block.'); - } - -} diff --git a/src/Tests/WorkspaceTest.php b/src/Tests/WorkspaceTest.php index 58f905f..d9b6a35 100644 --- a/src/Tests/WorkspaceTest.php +++ b/src/Tests/WorkspaceTest.php @@ -14,6 +14,8 @@ use Drupal\multiversion\Entity\WorkspaceInterface; /** * Test the workspace entity. * + * @todo refactor to test the form at /admin/structure/workspaces/{id}/activate + * * @group multiversion */ class WorkspaceTest extends MultiversionWebTestBase { diff --git a/src/Toolbar.php b/src/Toolbar.php index e013d60..5ce2dfc 100644 --- a/src/Toolbar.php +++ b/src/Toolbar.php @@ -4,9 +4,12 @@ namespace Drupal\multiversion; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Form\FormBuilderInterface; +use Drupal\Core\Link; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Url; use Drupal\multiversion\Entity\WorkspaceInterface; +use Drupal\multiversion\Form\WorkspaceSwitcherForm; use Drupal\multiversion\Workspace\WorkspaceManagerInterface; @@ -27,16 +30,25 @@ class Toolbar { protected $workspaceManager; /** + * @var \Drupal\Core\Form\FormBuilderInterface + */ + protected $formBuilder; + + /** * Constructs a new Toolbar. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager service. * @param \Drupal\multiversion\Workspace\WorkspaceManagerInterface $workspace_manager * The workspace manager service. + * @param \Drupal\Core\Form\FormBuilderInterface $form_builder + * The form builder service. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager) { + + public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager, FormBuilderInterface $form_builder) { $this->entityTypeManager = $entity_type_manager; $this->workspaceManager = $workspace_manager; + $this->formBuilder = $form_builder; } /** @@ -67,28 +79,27 @@ class Toolbar { $items['workspace_switcher']['tab'] = [ '#type' => 'link', '#title' => $this->t('Workspaces (@active)', ['@active' => $active->label()]), - // @todo This should likely be something else, but not sure what. - '#url' => Url::fromRoute(''), + '#url' => Url::fromRoute('entity.workspace.collection'), '#attributes' => [ 'title' => $this->t('Switch workspaces'), 'class' => ['toolbar-icon', 'toolbar-icon-multiversion'], ], ]; + $create_link = Link::createFromRoute($this->t('Create new workspace'), 'entity.workspace.add'); + $items['workspace_switcher']['tray'] = [ '#heading' => $this->t('Switch to workspace'), - 'workspace_links' => [ - '#pre_render' => ['multiversion.toolbar:preRenderWorkspaceLinks'], - '#cache' => [ - 'contexts' => $this->entityTypeManager->getDefinition('workspace')->getListCacheContexts(), - 'tags' => $this->entityTypeManager->getDefinition('workspace')->getListCacheTags(), - ], - '#theme' => 'links__toolbar_workspaces', - // This will be filled in during pre-render. - '#links' => [], - '#attributes' => [ - 'class' => ['toolbar-menu'], - ], + '#pre_render' => ['multiversion.toolbar:preRenderWorkspaceSwitcherForms'], + 'create_link' => $create_link->toRenderable(), + // This wil get filled in via pre-render. + 'workspace_forms' => [], + '#cache' => [ + 'contexts' => $this->entityTypeManager->getDefinition('workspace')->getListCacheContexts(), + 'tags' => $this->entityTypeManager->getDefinition('workspace')->getListCacheTags(), + ], + '#attributes' => [ + 'class' => ['toolbar-menu'], ], ]; @@ -96,48 +107,19 @@ class Toolbar { } /** - * Prerender callback; Adds the workspace links to the render array. + * Prerender callback; Adds the workspace switcher forms to the render array. * * @param array $element * * @return array * The modified $element. */ - public function preRenderWorkspaceLinks(array $element) { - $element['#links'] = $this->workspaceLinks(); - - return $element; - } - - /** - * Builds a render array of links to switch to all workspaces. - * - * Note: This is an expensive call so should only be made from within a - * pre-render callback, so it gets cached. - * - * @return array - * A render array of links to switch to each workspace. - */ - protected function workspaceLinks() { - $links = []; - - $links['add'] = [ - 'title' => $this->t('Create new workspace'), - 'url' => Url::fromRoute('entity.workspace.add'), - ]; - - /** @var WorkspaceInterface $workspace */ + public function preRenderWorkspaceSwitcherForms(array $element) { foreach ($this->allWorkspaces() as $workspace) { - $links['workspace_' . $workspace->getMachineName()] = [ - 'title' => $workspace->label(), - 'url' => Url::fromRoute('', ['workspace' => $workspace->id()]), - 'attributes' => [ - 'title' => t('Switch to %workspace workspace', ['%workspace' => $workspace->label()]) - ], - ]; + $element['workspace_forms']['workspace_' . $workspace->getMachineName()] = $this->formBuilder->getForm(WorkspaceSwitcherForm::class, $workspace); } - return $links; + return $element; } /** diff --git a/src/Workspace/SessionWorkspaceNegotiator.php b/src/Workspace/SessionWorkspaceNegotiator.php index e336438..70b5178 100644 --- a/src/Workspace/SessionWorkspaceNegotiator.php +++ b/src/Workspace/SessionWorkspaceNegotiator.php @@ -11,7 +11,7 @@ use Drupal\Core\Url; use Drupal\multiversion\Entity\WorkspaceInterface; use Symfony\Component\HttpFoundation\Request; -class SessionWorkspaceNegotiator extends WorkspaceNegotiatorBase implements WorkspaceSwitcherInterface { +class SessionWorkspaceNegotiator extends WorkspaceNegotiatorBase { /** * {@inheritdoc} @@ -41,42 +41,4 @@ class SessionWorkspaceNegotiator extends WorkspaceNegotiatorBase implements Work return TRUE; } - /** - * {@inheritdoc} - */ - public function getWorkspaceSwitchLinks(Request $request, Url $url) { - $links = array(); - $active_workspace_id = $this->workspaceManager->getActiveWorkspace($request)->id(); - $query = array(); - parse_str($request->getQueryString(), $query); - - // If we have an error on the requested page, set links URL to be . - if (!empty($query['_exception_statuscode'])) { - if (isset($query['workspace'])) { - $query = array( - 'workspace' => $query['workspace'], - ); - } - $url = URL::fromRoute(''); - } - - $workspaces = $this->workspaceManager->loadMultiple(); - ksort($workspaces); - foreach ($workspaces as $workspace) { - // @todo {@link https://www.drupal.org/node/2600382 Access check.} - $workspace_id = $workspace->id(); - $links[$workspace_id] = array( - 'url' => $url, - 'title' => $workspace->label(), - 'query' => $query, - ); - $links[$workspace_id]['query']['workspace'] = $workspace_id; - if ($workspace_id == $active_workspace_id) { - $links[$workspace_id]['attributes']['class'][] = 'session-active'; - } - } - - return $links; - } - } diff --git a/src/Workspace/WorkspaceManager.php b/src/Workspace/WorkspaceManager.php index 4570a47..c33e3ef 100644 --- a/src/Workspace/WorkspaceManager.php +++ b/src/Workspace/WorkspaceManager.php @@ -35,7 +35,8 @@ class WorkspaceManager implements WorkspaceManagerInterface { protected $sortedNegotiators; /** - * @var \Drupal\multiversion\Entity\WorkspaceInterface + * @var \Drupal\multiversion\Entity\WorkspaceInterface $activeWorkspace + * Track the active workspace for performance gain. */ protected $activeWorkspace; @@ -84,21 +85,24 @@ class WorkspaceManager implements WorkspaceManagerInterface { * @todo {@link https://www.drupal.org/node/2600382 Access check.} */ public function getActiveWorkspace() { - if (!isset($this->activeWorkspace)) { - $request = $this->requestStack->getCurrentRequest(); - foreach ($this->getSortedNegotiators() as $negotiator) { - if ($negotiator->applies($request)) { - if ($workspace_id = $negotiator->getWorkspaceId($request)) { - /** @var \Drupal\multiversion\Entity\WorkspaceInterface $workspace */ - if ($workspace = $this->entityManager->getStorage('workspace')->load($workspace_id)) { - $negotiator->persist($workspace); - $this->activeWorkspace = $workspace; - break; - } + // Return the cached value if it is set. + if (isset($this->activeWorkspace)) { + return $this->activeWorkspace; + } + + $this->activeWorkspace = NULL; + $request = $this->requestStack->getCurrentRequest(); + foreach ($this->getSortedNegotiators() as $negotiator) { + if ($negotiator->applies($request)) { + if ($workspace_id = $negotiator->getWorkspaceId($request)) { + if ($active_workspace = $this->load($workspace_id)) { + $this->activeWorkspace = $active_workspace; + break; } } } } + return $this->activeWorkspace; } @@ -106,22 +110,19 @@ class WorkspaceManager implements WorkspaceManagerInterface { * {@inheritdoc} */ public function setActiveWorkspace(WorkspaceInterface $workspace) { - $this->activeWorkspace = $workspace; - return $this; - } + // Unset the cached variable so it can re-populate on get. + unset($this->activeWorkspace); - /** - * {@inheritdoc} - */ - public function getWorkspaceSwitchLinks(Url $url) { + // Set the workspace on the proper negotiator. $request = $this->requestStack->getCurrentRequest(); foreach ($this->getSortedNegotiators() as $negotiator) { - if ($negotiator instanceof WorkspaceSwitcherInterface && $negotiator->applies($request)) { - if ($links = $negotiator->getWorkspaceSwitchLinks($request, $url)) { - return $links; - } + if ($negotiator->applies($request)) { + $negotiator->persist($workspace); + break; } } + + return $this; } /** diff --git a/src/Workspace/WorkspaceManagerInterface.php b/src/Workspace/WorkspaceManagerInterface.php index 17eeb9b..9c75ca4 100644 --- a/src/Workspace/WorkspaceManagerInterface.php +++ b/src/Workspace/WorkspaceManagerInterface.php @@ -44,10 +44,4 @@ interface WorkspaceManagerInterface { */ public function setActiveWorkspace(WorkspaceInterface $workspace); - /** - * @param \Drupal\Core\Url $url - * @return array - */ - public function getWorkspaceSwitchLinks(Url $url); - } diff --git a/src/Workspace/WorkspaceSwitcherInterface.php b/src/Workspace/WorkspaceSwitcherInterface.php deleted file mode 100644 index b36c172..0000000 --- a/src/Workspace/WorkspaceSwitcherInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -getOwner()->getDisplayname(); $type = $entity->get('type')->first()->entity; $row['type'] = $type ? $type->label() : ''; + $active_workspace = $entity->getActiveWorkspaceId(); + $row['status'] = $active_workspace && $active_workspace[0] == $entity->id() ? 'Active' : 'Inactive'; return $row + parent::buildRow($entity); } @@ -46,6 +49,17 @@ class WorkspaceListBuilder extends EntityListBuilder { if (isset($operations['edit'])) { $operations['edit']['query']['destination'] = $entity->url('collection'); } + + $active_workspace = $entity->getActiveWorkspaceId(); + if (!$active_workspace || $entity->id() != $active_workspace[0]) { + $operations['activate'] = array( + 'title' => $this->t('Set Active'), + 'weight' => 20, + // @todo What is the better way to get the form submission to redirect back to the workspaces page? + 'url' => $entity->urlInfo('activate-form', ['query' => ['destination' => '/admin/structure/workspaces']]), + ); + } + return $operations; } diff --git a/tests/src/Unit/SessionWorkspaceNegotiatorTest.php b/tests/src/Unit/SessionWorkspaceNegotiatorTest.php index 8369fa5..1de3bf1 100644 --- a/tests/src/Unit/SessionWorkspaceNegotiatorTest.php +++ b/tests/src/Unit/SessionWorkspaceNegotiatorTest.php @@ -174,67 +174,4 @@ class SessionWorkspaceNegotiatorTest extends UnitTestCase { $this->assertSame(1, $_SESSION['workspace']); } - /** - * Tests the getWorkspaceSwitchLinks() method. - */ - public function testGetWorkspaceSwitchLinks() { - $second_machine_name = $this->values[1]['machine_name']; - $url = Url::fromRoute($this->path); - $expected_links = [ - 1 => [ - 'url' => $url, - 'title' => $this->defaultMachineName, - 'query' => ['workspace' => 1], - 'attributes' => [ - 'class' => ['session-active'], - ], - ], - 2 => [ - 'url' => $url, - 'title' => $second_machine_name, - 'query' => ['workspace' => 2], - ], - ]; - - foreach ($this->values as $key => $value) { - $this->entities[$key]->expects($this->any()) - ->method('id') - ->will($this->returnValue($value['id'])); - $this->entities[$key]->expects($this->any()) - ->method('label') - ->will($this->returnValue($value['label'])); - } - - $this->negotiator = $this->getMock('\Drupal\multiversion\Workspace\SessionWorkspaceNegotiator'); - $this->negotiator->expects($this->any()) - ->method('getActiveWorkspace') - ->with($this->requestStack, $this->entityManager) - ->will($this->returnValue($this->defaultMachineName)); - - $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface'); - $storage->expects($this->any()) - ->method('loadMultiple') - ->with() - ->will($this->returnValue($this->entities)); - - $this->entityManager->expects($this->any()) - ->method('getStorage') - ->with('workspace') - ->will($this->returnValue($storage)); - - $this->workspaceManager->expects($this->any()) - ->method('loadMultiple') - ->with() - ->will($this->returnValue(array($this->entities))); - - $workspace_manager = new WorkspaceManager($this->requestStack, $this->entityManager, $this->cacheRender); - $workspace_manager->addNegotiator($this->workspaceNegotiator, 1); - $workspace_manager->setActiveWorkspace($this->entities[0]); - $negotiator = new SessionWorkspaceNegotiator(); - $negotiator->setWorkspaceManager($workspace_manager); - - $links = $negotiator->getWorkspaceSwitchLinks($this->request, $url); - $this->assertSame($expected_links, $links); - } - } diff --git a/tests/src/Unit/WorkspaceManagerTest.php b/tests/src/Unit/WorkspaceManagerTest.php index e4b0eb1..c59863e 100644 --- a/tests/src/Unit/WorkspaceManagerTest.php +++ b/tests/src/Unit/WorkspaceManagerTest.php @@ -11,6 +11,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Url; use Drupal\Tests\UnitTestCase; use Drupal\multiversion\Workspace\WorkspaceManager; +use Prophecy\Argument; use Symfony\Component\HttpFoundation\Request; /** @@ -181,48 +182,72 @@ class WorkspaceManagerTest extends UnitTestCase { } /** - * Tests the setActiveWorkspace() and getActiveWorkspace() methods. + * Tests that setActiveWorkspace() sets the workspace on the negotiator. */ public function testSetActiveWorkspace() { + // Create the request we will use. + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $this->requestStack->method('getCurrentRequest')->willReturn($request); + + // Create the workspace that we will set. + $workspace = $this->getMockBuilder('Drupal\multiversion\Entity\Workspace') + ->disableOriginalConstructor() + ->getMock(); + + // Spy on the negotiator and stub the applies and persist methods. + $negotiator = $this->prophesize('Drupal\multiversion\Workspace\DefaultWorkspaceNegotiator'); + $negotiator->applies(Argument::any())->willReturn(TRUE); + $negotiator->persist(Argument::any())->will(function(){ return $this; }); + + // Create the workspace manager. $workspace_manager = new WorkspaceManager($this->requestStack, $this->entityManager, $this->cacheRender); - $workspace_manager->setActiveWorkspace($this->entities[0]); - $this->assertSame($this->entities[0], $workspace_manager->getActiveWorkspace()); + $workspace_manager->addNegotiator($negotiator->reveal(), 1); + + // Execute the code under test. + $workspace_manager->setActiveWorkspace($workspace); + + // Ensure persist with the workspace was called on the negotiator. + $negotiator->persist($workspace)->shouldHaveBeenCalled(); } /** - * Tests the getWorkspaceSwitchLinks() method. + * Tests that getActiveWorkspace() gets from the negotiator. */ - public function testGetWorkspaceSwitchLinks() { - $path = ''; - $request = Request::create($path); - $query = array(); - $url = Url::fromRoute(''); - $expected_links = array( - 1 => array( - 'href' => $url, - 'title' => null, - 'query' => $query, - ), - ); - - $this->requestStack->expects($this->once()) - ->method('getCurrentRequest') - ->will($this->returnValue($request)); + public function testGetActiveWorkspace() { + $workspace_id = '123'; + + // Create the request we will use. + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $this->requestStack->method('getCurrentRequest')->willReturn($request); + + // Create the workspace that we will get. + $workspace = $this->getMockBuilder('Drupal\multiversion\Entity\Workspace') + ->disableOriginalConstructor() + ->getMock(); + // Create the negotiator and stub the applies and getWorkspaceId methods. + $negotiator = $this->getMock('Drupal\multiversion\Workspace\DefaultWorkspaceNegotiator'); + $negotiator->method('applies')->willReturn(TRUE); + $negotiator->method('getWorkspaceId')->willReturn($workspace_id); + + // Create the storage and stub the load method. + $storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface'); + $storage->method('load')->with($workspace_id)->willReturn($workspace); + + // Stub the entity manager to return $storage. + $this->entityManager->method('getStorage') + ->with($this->entityTypeId) + ->willReturn($storage); + + // Create the workspace manager with the negotiator. $workspace_manager = new WorkspaceManager($this->requestStack, $this->entityManager, $this->cacheRender); - $workspace_manager->addNegotiator($this->workspaceNegotiators[1][0], 1); + $workspace_manager->addNegotiator($negotiator, 1); + + // Execute the code under test. + $active_workspace = $workspace_manager->getActiveWorkspace(); - $this->workspaceNegotiators[1][0]->expects($this->any()) - ->method('applies') - ->with($request) - ->will($this->returnValue(TRUE)); - $this->workspaceNegotiators[1][0]->expects($this->once()) - ->method('getWorkspaceSwitchLinks') - ->with($request, $url) - ->will($this->returnValue($expected_links)); - - $result_links = $workspace_manager->getWorkspaceSwitchLinks($url); - $this->assertSame($expected_links, $result_links); + // Ensure value is the workspace we stubbed. + $this->assertSame($workspace, $active_workspace); } /**