Commit 31614ec2 authored by catch's avatar catch
Browse files

Issue #3112783 by amateescu, paulocs, mglaman, guilhermevp, dixon_, Fabianx:...

Issue #3112783 by amateescu, paulocs, mglaman, guilhermevp, dixon_, Fabianx: List and count all changes made in a workspace
parent 831b2081
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
 *   ),
 *   handlers = {
 *     "list_builder" = "\Drupal\workspaces\WorkspaceListBuilder",
 *     "view_builder" = "Drupal\workspaces\WorkspaceViewBuilder",
 *     "access" = "Drupal\workspaces\WorkspaceAccessControlHandler",
 *     "route_provider" = {
 *       "html" = "\Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
@@ -53,6 +54,7 @@
 *     "owner" = "uid",
 *   },
 *   links = {
 *     "canonical" = "/admin/config/workflow/workspaces/manage/{workspace}",
 *     "add-form" = "/admin/config/workflow/workspaces/add",
 *     "edit-form" = "/admin/config/workflow/workspaces/manage/{workspace}/edit",
 *     "delete-form" = "/admin/config/workflow/workspaces/manage/{workspace}/delete",
+27 −0
Original line number Diff line number Diff line
@@ -2,9 +2,11 @@

namespace Drupal\workspaces\Form;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\workspaces\WorkspaceAccessException;
use Drupal\workspaces\WorkspaceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -111,6 +113,31 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    catch (WorkspaceAccessException $e) {
      $this->messenger->addError($this->t('You do not have access to activate the %workspace_label workspace.', ['%workspace_label' => $this->entity->label()]));
    }

    // Redirect to the workspace manage page by default.
    if (!$this->getRequest()->query->has('destination')) {
      $form_state->setRedirectUrl($this->entity->toUrl());
    }
  }

  /**
   * Checks access for the workspace activate form.
   *
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The route match.
   *
   * @return \Drupal\Core\Access\AccessResult
   *   The access result.
   */
  public function checkAccess(RouteMatchInterface $route_match) {
    /** @var \Drupal\workspaces\WorkspaceInterface $workspace */
    $workspace = $route_match->getParameter('workspace');
    $active_workspace = $this->workspaceManager->getActiveWorkspace();

    $access = AccessResult::allowedIf(!$active_workspace || ($active_workspace && $active_workspace->id() != $workspace->id()))
      ->addCacheableDependency($workspace);

    return $access;
  }

}
+11 −3
Original line number Diff line number Diff line
@@ -116,7 +116,9 @@ public function buildRow(EntityInterface $entity) {
      'label' => [
        'data' => [
          '#prefix' => isset($indentation) ? $this->renderer->render($indentation) : '',
          '#markup' => $entity->label(),
          '#type' => 'link',
          '#title' => $entity->label(),
          '#url' => $entity->toUrl(),
        ],
      ],
      'owner' => $entity->getOwner()->getDisplayname(),
@@ -183,6 +185,12 @@ public function getDefaultOperations(EntityInterface $entity) {
      ];
    }

    $operations['manage'] = [
      'title' => $this->t('Manage'),
      'weight' => 5,
      'url' => $entity->toUrl(),
    ];

    return $operations;
  }

@@ -279,7 +287,7 @@ protected function offCanvasRender(array &$build) {
      $build['active_workspace']['label']['manage'] = [
        '#type' => 'link',
        '#title' => $this->t('Manage workspace'),
        '#url' => $active_workspace->toUrl('edit-form'),
        '#url' => $active_workspace->toUrl('canonical'),
        '#attributes' => [
          'class' => ['active-workspace__manage'],
        ],
@@ -331,7 +339,7 @@ protected function offCanvasRender(array &$build) {
        $url = Url::fromRoute('entity.workspace.activate_form', ['workspace' => $id], ['query' => $this->getDestinationArray()]);
        $items[] = [
          '#type' => 'link',
          '#title' => ltrim($row['data']['label']['data']['#markup']),
          '#title' => ltrim($row['data']['label']['data']['#title']),
          '#url' => $url,
          '#attributes' => [
            'class' => ['use-ajax', 'workspaces__item', 'workspaces__item--not-default'],
+172 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\workspaces;

use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityListBuilder;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityViewBuilder;
use Drupal\user\EntityOwnerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a Workspace view builder.
 */
class WorkspaceViewBuilder extends EntityViewBuilder {

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

  /**
   * The workspace association service.
   *
   * @var \Drupal\workspaces\WorkspaceAssociationInterface
   */
  protected $workspaceAssociation;

  /**
   * The date formatter service.
   *
   * @var \Drupal\Core\Datetime\DateFormatterInterface
   */
  protected $dateFormatter;

  /**
   * The entity bundle information service.
   *
   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
   */
  protected $bundleInfo;

  /**
   * {@inheritdoc}
   */
  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
    $instance = parent::createInstance($container, $entity_type);
    $instance->entityTypeManager = $container->get('entity_type.manager');
    $instance->workspaceAssociation = $container->get('workspaces.association');
    $instance->dateFormatter = $container->get('date.formatter');
    $instance->bundleInfo = $container->get('entity_type.bundle.info');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function buildComponents(array &$build, array $entities, array $displays, $view_mode) {
    parent::buildComponents($build, $entities, $displays, $view_mode);
    $bundle_info = $this->bundleInfo->getAllBundleInfo();

    $header = [
      'title' => $this->t('Title'),
      'type' => $this->t('Type'),
      'changed' => $this->t('Last changed'),
      'owner' => $this->t('Author'),
      'operations' => $this->t('Operations'),
    ];
    foreach ($entities as $build_id => $entity) {
      $all_tracked_entities = $this->workspaceAssociation->getTrackedEntities($entity->id());

      $build[$build_id]['changes']['overview'] = [
        '#type' => 'item',
        '#title' => $this->t('Workspace changes'),
      ];

      $build[$build_id]['changes']['list'] = [
        '#type' => 'table',
        '#header' => $header,
        '#empty' => $this->t('This workspace has no changes.'),
      ];

      $changes_count = [];
      foreach ($all_tracked_entities as $entity_type_id => $tracked_entities) {
        // Ensure that newest revisions are displayed at the top.
        krsort($tracked_entities);

        $changes_count[$entity_type_id] = $this->entityTypeManager->getDefinition($entity_type_id)->getCountLabel(count($tracked_entities));

        $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
        if ($this->entityTypeManager->hasHandler($entity_type_id, 'list_builder')) {
          $list_builder = $this->entityTypeManager->getListBuilder($entity_type_id);
        }
        else {
          $list_builder = $this->entityTypeManager->createHandlerInstance(EntityListBuilder::class, $entity_type);
        }

        $revisions = $this->entityTypeManager->getStorage($entity_type_id)->loadMultipleRevisions(array_keys($tracked_entities));

        // Load all users at once.
        $user_ids = [];
        foreach ($revisions as $revision) {
          if ($revision instanceof EntityOwnerInterface) {
            $user_ids[$revision->getOwnerId()] = $revision->getOwnerId();
          }
        }

        if ($user_ids = array_filter($user_ids)) {
          $revision_owners = $this->entityTypeManager->getStorage('user')->loadMultiple($user_ids);
        }

        foreach ($revisions as $revision) {
          if ($revision->getEntityType()->hasLinkTemplate('canonical')) {
            $title = [
              '#type' => 'link',
              '#title' => $revision->label(),
              '#url' => $revision->toUrl(),
            ];
          }
          else {
            $title = ['#markup' => $revision->label()];
          }

          if (count($bundle_info[$entity_type_id]) > 1) {
            $type = [
              '#markup' => $this->t('@entity_type_label: @entity_bundle_label', [
                '@entity_type_label' => $entity_type->getLabel(),
                '@entity_bundle_label' => $bundle_info[$entity_type_id][$revision->bundle()]['label'],
              ]),
            ];
          }
          else {
            $type = ['#markup' => $bundle_info[$entity_type_id][$revision->bundle()]['label']];
          }

          $changed = $revision instanceof EntityChangedInterface
            ? $this->dateFormatter->format($revision->getChangedTime())
            : '';

          if ($revision instanceof EntityOwnerInterface && isset($revision_owners[$revision->getOwnerId()])) {
            $author = [
              '#theme' => 'username',
              '#account' => $revision_owners[$revision->getOwnerId()],
            ];
          }
          else {
            $author = ['#markup' => ''];
          }

          $build[$build_id]['changes']['list'][$entity_type_id . ':' . $revision->id()] = [
            '#entity' => $revision,
            'title' => $title,
            'type' => $type,
            'changed' => ['#markup' => $changed],
            'owner' => $author,
            'operations' => [
              '#type' => 'operations',
              '#links' => $list_builder->getOperations($revision),
            ],
          ];
        }
      }

      if ($changes_count) {
        $build[$build_id]['changes']['overview']['#markup'] = implode(', ', $changes_count);
      }
    }
  }

}
+56 −1
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@

use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;

/**
 * Test the workspace entity.
@@ -14,11 +15,20 @@ class WorkspaceTest extends BrowserTestBase {

  use WorkspaceTestUtilities;
  use ContentTypeCreationTrait;
  use TaxonomyTestTrait;

  /**
   * {@inheritdoc}
   */
  protected static $modules = ['block', 'field_ui', 'node', 'toolbar', 'user', 'workspaces'];
  protected static $modules = [
    'block',
    'field_ui',
    'node',
    'taxonomy',
    'toolbar',
    'user',
    'workspaces',
  ];

  /**
   * {@inheritdoc}
@@ -56,6 +66,8 @@ public function setUp(): void {

    $this->editor1 = $this->drupalCreateUser($permissions);
    $this->editor2 = $this->drupalCreateUser($permissions);

    $this->drupalPlaceBlock('local_actions_block');
  }

  /**
@@ -150,6 +162,49 @@ public function testWorkspaceFormRevisions() {
    $this->assertEquals('2', $stage_workspace->getRevisionId());
  }

  /**
   * Tests the manage workspace page.
   *
   * @group failing
   */
  public function testWorkspaceManagePage() {
    $this->drupalLogin($this->rootUser);
    $this->setupWorkspaceSwitcherBlock();
    $assert_session = $this->assertSession();

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

    $this->switchToWorkspace($test_1);

    // Check that the 'test_1' workspace doesn't contain any changes initially.
    $this->drupalGet($test_1->toUrl()->toString());
    $assert_session->pageTextContains('This workspace has no changes.');

    // Check that the 'Switch to this workspace' action link is not displayed on
    // the manage page of the currently active workspace.
    $assert_session->linkNotExists('Switch to this workspace');
    $this->drupalGet($test_2->toUrl()->toString());
    $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',
    ];
    $this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
    $this->submitForm($edit, 'Save');

    $this->drupalGet($test_1->toUrl()->toString());
    $assert_session->pageTextContains('2 content items, 1 taxonomy term');
    $assert_session->linkExists('Node 1');
    $assert_session->linkExists('Node 2');
    $assert_session->linkExists('Term 1');
  }

  /**
   * Tests adding new fields to workspace entities.
   */
Loading