Verified Commit e0539a1b authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #3040258 by amateescu, adityasingh, anushrikumari, larowlan, s_leu,...

Issue #3040258 by amateescu, adityasingh, anushrikumari, larowlan, s_leu, dixon_, catch, atul4drupal, alexpott, Fabianx: Menu link content changes are not visible on non-live workspaces

(cherry picked from commit 96ac98b6)
parent 85b55e17
Loading
Loading
Loading
Loading
+96 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\workspaces;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Menu\MenuTreeStorage as CoreMenuTreeStorage;

/**
 * Overrides the default menu storage to provide workspace-specific menu links.
 *
 * @internal
 */
class WorkspacesMenuTreeStorage extends CoreMenuTreeStorage {

  /**
   * WorkspacesMenuTreeStorage constructor.
   *
   * @param \Drupal\workspaces\WorkspaceManagerInterface $workspaceManager
   *   The workspace manager service.
   * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspaceAssociation
   *   The workspace association service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Core\Database\Connection $connection
   *   A Database connection to use for reading and writing configuration data.
   * @param \Drupal\Core\Cache\CacheBackendInterface $menu_cache_backend
   *   Cache backend instance for the extracted tree data.
   * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_invalidator
   *   The cache tags invalidator.
   * @param string $table
   *   A database table name to store configuration data in.
   * @param array $options
   *   (optional) Any additional database connection options to use in queries.
   */
  public function __construct(
    protected readonly WorkspaceManagerInterface $workspaceManager,
    protected readonly WorkspaceAssociationInterface $workspaceAssociation,
    protected readonly EntityTypeManagerInterface $entityTypeManager,
    Connection $connection,
    CacheBackendInterface $menu_cache_backend,
    CacheTagsInvalidatorInterface $cache_tags_invalidator,
    string $table,
    array $options = []
  ) {
    parent::__construct($connection, $menu_cache_backend, $cache_tags_invalidator, $table, $options);
  }

  /**
   * {@inheritdoc}
   */
  public function loadTreeData($menu_name, MenuTreeParameters $parameters) {
    // Add the active workspace as a menu tree condition parameter in order to
    // include it in the cache ID.
    if ($active_workspace = $this->workspaceManager->getActiveWorkspace()) {
      $parameters->conditions['workspace'] = $active_workspace->id();
    }
    return parent::loadTreeData($menu_name, $parameters);
  }

  /**
   * {@inheritdoc}
   */
  protected function loadLinks($menu_name, MenuTreeParameters $parameters) {
    $links = parent::loadLinks($menu_name, $parameters);

    // Replace the menu link plugin definitions with workspace-specific ones.
    if ($active_workspace = $this->workspaceManager->getActiveWorkspace()) {
      $tracked_revisions = $this->workspaceAssociation->getTrackedEntities($active_workspace->id());
      if (isset($tracked_revisions['menu_link_content'])) {
        /** @var \Drupal\menu_link_content\MenuLinkContentInterface[] $workspace_revisions */
        $workspace_revisions = $this->entityTypeManager->getStorage('menu_link_content')->loadMultipleRevisions(array_keys($tracked_revisions['menu_link_content']));
        foreach ($workspace_revisions as $workspace_revision) {
          if (isset($links[$workspace_revision->getPluginId()])) {
            $pending_plugin_definition = $workspace_revision->getPluginDefinition();
            $links[$workspace_revision->getPluginId()] = [
              'title' => serialize($pending_plugin_definition['title']),
              'description' => serialize($pending_plugin_definition['description']),
              'enabled' => (string) $pending_plugin_definition['enabled'],
              'url' => $pending_plugin_definition['url'],
              'route_name' => $pending_plugin_definition['route_name'],
              'route_parameters' => serialize($pending_plugin_definition['route_parameters']),
              'options' => serialize($pending_plugin_definition['options']),
            ] + $links[$workspace_revision->getPluginId()];
          }
        }
      }
    }

    return $links;
  }

}
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ public function testBypassOwnWorkspace() {

    // Create a new user that should be able to edit anything in the Bears
    // workspace.
    $this->switchToLive();
    $lombardi = $this->drupalCreateUser(array_merge($permissions, ['view any workspace']));
    $this->drupalLogin($lombardi);
    $this->switchToWorkspace($bears);
+119 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\workspaces\Functional;

use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\Tests\BrowserTestBase;
use Drupal\workspaces\Entity\Workspace;

/**
 * Tests workspace integration for custom menu links.
 *
 * @group workspaces
 * @group menu_link_content
 */
class WorkspaceMenuLinkContentIntegrationTest extends BrowserTestBase {

  use WorkspaceTestUtilities;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'block',
    'menu_link_content',
    'node',
    'workspaces',
  ];

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $permissions = [
      'access administration pages',
      'administer site configuration',
      'administer workspaces',
    ];
    $this->drupalLogin($this->drupalCreateUser($permissions));
    $this->drupalPlaceBlock('system_menu_block:main');
  }

  /**
   * Tests custom menu links in non-default workspaces.
   */
  public function testWorkspacesWithCustomMenuLinks() {
    $stage = Workspace::load('stage');

    $this->setupWorkspaceSwitcherBlock();

    $default_title = 'default';
    $default_link = '#live';
    $menu_link_content = MenuLinkContent::create([
      'title' => $default_title,
      'menu_name' => 'main',
      'link' => [['uri' => 'internal:/' . $default_link]],
    ]);
    $menu_link_content->save();

    $pending_title = 'pending';
    $pending_link = 'http://example.com';
    $this->switchToWorkspace($stage);
    $menu_link_content->set('title', $pending_title);
    $menu_link_content->set('link', [['uri' => $pending_link]]);
    $menu_link_content->save();

    $this->drupalGet('');
    $assert_session = $this->assertSession();
    $assert_session->linkExists($pending_title);
    $assert_session->linkByHrefExists($pending_link);

    // Add a new menu link in the Stage workspace.
    $menu_link_content = MenuLinkContent::create([
      'title' => 'stage link',
      'menu_name' => 'main',
      'link' => [['uri' => 'internal:/#stage']],
    ]);
    $menu_link_content->save();

    $this->drupalGet('');
    $assert_session->linkExists('stage link');
    $assert_session->linkByHrefExists('#stage');

    // Switch back to the Live workspace and check that the menu link has the
    // default values.
    $this->switchToLive();
    $this->drupalGet('');
    $assert_session->linkExists($default_title);
    $assert_session->linkByHrefExists($default_link);
    $assert_session->linkNotExists($pending_title);
    $assert_session->linkByHrefNotExists($pending_link);
    $assert_session->linkNotExists('stage link');
    $assert_session->linkByHrefNotExists('#stage');

    // Publish the workspace and check that the menu link has been updated.
    $stage->publish();
    $this->drupalGet('');
    $assert_session->linkNotExists($default_title);
    $assert_session->linkByHrefNotExists($default_link);
    $assert_session->linkExists($pending_title);
    $assert_session->linkByHrefExists($pending_link);
    $assert_session->linkExists('stage link');
    $assert_session->linkByHrefExists('#stage');
  }

}
+3 −2
Original line number Diff line number Diff line
@@ -172,6 +172,9 @@ public function testWorkspaceManagePage() {
    $this->setupWorkspaceSwitcherBlock();
    $assert_session = $this->assertSession();

    $this->drupalCreateContentType(['type' => 'test', 'label' => 'Test']);
    $vocabulary = $this->createVocabulary();

    $test_1 = $this->createWorkspaceThroughUi('Test 1', 'test_1');
    $test_2 = $this->createWorkspaceThroughUi('Test 2', 'test_2');

@@ -188,10 +191,8 @@ public function testWorkspaceManagePage() {
    $assert_session->linkExists('Switch to this workspace');

    // Create some test content.
    $this->drupalCreateContentType(['type' => 'test', 'label' => 'Test']);
    $this->createNodeThroughUi('Node 1', 'test');
    $this->createNodeThroughUi('Node 2', 'test');
    $vocabulary = $this->createVocabulary();
    $edit = [
      'name[0][value]' => 'Term 1',
    ];
+4 −0
Original line number Diff line number Diff line
@@ -107,6 +107,8 @@ protected function switchToWorkspace(WorkspaceInterface $workspace) {
    $session->buttonExists('Activate');
    $this->submitForm(['workspace_id' => $workspace->id()], 'Activate');
    $session->pageTextContains($workspace->label() . ' is now the active workspace.');
    // Keep the test runner in sync with the system under test.
    \Drupal::service('workspaces.manager')->setActiveWorkspace($workspace);
  }

  /**
@@ -120,6 +122,8 @@ protected function switchToLive() {
    $session = $this->assertSession();
    $this->submitForm([], 'Switch to Live');
    $session->pageTextContains('You are now viewing the live version of the site.');
    // Keep the test runner in sync with the system under test.
    \Drupal::service('workspaces.manager')->switchToLive();
  }

  /**
Loading