Issue #2986005 by amateescu, longwave: Add the ability to mark (VBO) actions as workspace-safe
(cherry picked from commit 0a64504771
)
merge-requests/7514/merge
parent
c6bb313951
commit
dfcd148f25
|
@ -5,6 +5,7 @@ namespace Drupal\Core\Entity\Form;
|
|||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\BaseFormIdInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\TypedData\TranslatableInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
@ -17,7 +18,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
/**
|
||||
* Provides an entities deletion confirmation form.
|
||||
*/
|
||||
class DeleteMultipleForm extends ConfirmFormBase implements BaseFormIdInterface {
|
||||
class DeleteMultipleForm extends ConfirmFormBase implements BaseFormIdInterface, WorkspaceDynamicSafeFormInterface {
|
||||
|
||||
use WorkspaceSafeFormTrait;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
|
@ -320,4 +323,11 @@ class DeleteMultipleForm extends ConfirmFormBase implements BaseFormIdInterface
|
|||
return $this->formatPlural($count, "@count item has not been deleted because you do not have the necessary permissions.", "@count items have not been deleted because you do not have the necessary permissions.");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWorkspaceSafeForm(array $form, FormStateInterface $form_state): bool {
|
||||
return $this->isWorkspaceSafeEntityType($this->entityType);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Entity\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\workspaces\WorkspaceInformationInterface;
|
||||
|
||||
/**
|
||||
* Provides helpers for checking whether objects in forms are workspace-safe.
|
||||
*/
|
||||
trait WorkspaceSafeFormTrait {
|
||||
|
||||
/**
|
||||
* The workspace information service.
|
||||
*/
|
||||
protected ?WorkspaceInformationInterface $workspaceInfo = NULL;
|
||||
|
||||
/**
|
||||
* Determines whether an entity used in a form is workspace-safe.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* An entity object.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the entity is workspace-safe, FALSE otherwise.
|
||||
*/
|
||||
protected function isWorkspaceSafeEntity(EntityInterface $entity): bool {
|
||||
if (!\Drupal::hasService('workspaces.information')) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$is_supported = $this->getWorkspaceInfo()->isEntitySupported($entity);
|
||||
$is_ignored = $this->getWorkspaceInfo()->isEntityIgnored($entity);
|
||||
|
||||
return $is_supported || $is_ignored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an entity type used in a form is workspace-safe.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* An entity type object.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the entity type is workspace-safe, FALSE otherwise.
|
||||
*/
|
||||
protected function isWorkspaceSafeEntityType(EntityTypeInterface $entity_type): bool {
|
||||
if (!\Drupal::hasService('workspaces.information')) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$is_supported = $this->getWorkspaceInfo()->isEntityTypeSupported($entity_type);
|
||||
$is_ignored = $this->getWorkspaceInfo()->isEntityTypeIgnored($entity_type);
|
||||
|
||||
return $is_supported || $is_ignored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the workspace information service.
|
||||
*
|
||||
* @return \Drupal\workspaces\WorkspaceInformationInterface
|
||||
* The workspace information service.
|
||||
*/
|
||||
protected function getWorkspaceInfo(): WorkspaceInformationInterface {
|
||||
if (!$this->workspaceInfo) {
|
||||
$this->workspaceInfo = \Drupal::service('workspaces.information');
|
||||
}
|
||||
|
||||
return $this->workspaceInfo;
|
||||
}
|
||||
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace Drupal\Core\Form;
|
|||
*
|
||||
* @see \Drupal\Core\Form\WorkspaceSafeFormInterface
|
||||
*/
|
||||
interface WorkspaceDynamicSafeFormInterface extends FormInterface {
|
||||
interface WorkspaceDynamicSafeFormInterface {
|
||||
|
||||
/**
|
||||
* Determines whether the form is safe to be submitted in a workspace.
|
||||
|
|
|
@ -12,4 +12,4 @@ namespace Drupal\Core\Form;
|
|||
*
|
||||
* @see \Drupal\Core\Form\WorkspaceDynamicSafeFormInterface
|
||||
*/
|
||||
interface WorkspaceSafeFormInterface extends FormInterface {}
|
||||
interface WorkspaceSafeFormInterface {}
|
||||
|
|
|
@ -4,19 +4,16 @@ declare(strict_types=1);
|
|||
|
||||
namespace Drupal\layout_builder\Form;
|
||||
|
||||
use Drupal\Core\Entity\Form\WorkspaceSafeFormTrait as EntityWorkspaceSafeFormTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
use Drupal\workspaces\WorkspaceInformationInterface;
|
||||
|
||||
/**
|
||||
* Provides a trait that marks Layout Builder forms as workspace-safe.
|
||||
*/
|
||||
trait WorkspaceSafeFormTrait {
|
||||
|
||||
/**
|
||||
* The workspace information service.
|
||||
*/
|
||||
protected ?WorkspaceInformationInterface $workspaceInfo = NULL;
|
||||
use EntityWorkspaceSafeFormTrait;
|
||||
|
||||
/**
|
||||
* Determines whether the current form is safe to be submitted in a workspace.
|
||||
|
@ -35,13 +32,9 @@ trait WorkspaceSafeFormTrait {
|
|||
$context_definitions = $section_storage->getContextDefinitions();
|
||||
if (!empty($context_definitions['entity'])) {
|
||||
/** @var \Drupal\Core\Entity\EntityInterface $entity */
|
||||
$entity = $section_storage->getContext('entity')->getContextValue();
|
||||
$supported = $entity && $this->getWorkspaceInfo()->isEntitySupported($entity);
|
||||
$ignored = $entity && $this->getWorkspaceInfo()->isEntityIgnored($entity);
|
||||
$entity = $section_storage->getContextValue('entity');
|
||||
|
||||
if ($supported || $ignored) {
|
||||
return TRUE;
|
||||
}
|
||||
return $this->isWorkspaceSafeEntity($entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,18 +60,4 @@ trait WorkspaceSafeFormTrait {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the workspace information service.
|
||||
*
|
||||
* @return \Drupal\workspaces\WorkspaceInformationInterface
|
||||
* The workspace information service.
|
||||
*/
|
||||
protected function getWorkspaceInfo(): WorkspaceInformationInterface {
|
||||
if (!$this->workspaceInfo) {
|
||||
$this->workspaceInfo = \Drupal::service('workspaces.information');
|
||||
}
|
||||
|
||||
return $this->workspaceInfo;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ use Drupal\Component\Render\MarkupInterface;
|
|||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
|
||||
use Drupal\Core\Form\WorkspaceSafeFormInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\views\Render\ViewsRenderPipelineMarkup;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
@ -109,6 +111,13 @@ class ViewsFormMainForm implements FormInterface, TrustedCallbackInterface {
|
|||
$has_form = FALSE;
|
||||
if (method_exists($field, 'viewsForm')) {
|
||||
$field->viewsForm($form, $form_state);
|
||||
|
||||
// Allow the views form to determine whether it's safe to be submitted
|
||||
// in a workspace.
|
||||
$workspace_safe = $field instanceof WorkspaceSafeFormInterface
|
||||
|| ($field instanceof WorkspaceDynamicSafeFormInterface && $field->isWorkspaceSafeForm($form, $form_state));
|
||||
$form_state->set('workspace_safe', $workspace_safe);
|
||||
|
||||
$has_form = TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@ use Drupal\Core\Cache\CacheableDependencyInterface;
|
|||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\EntityRepositoryInterface;
|
||||
use Drupal\Core\Entity\Form\WorkspaceSafeFormTrait;
|
||||
use Drupal\Core\Entity\RevisionableInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\Routing\RedirectDestinationTrait;
|
||||
|
@ -26,11 +28,12 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
* Defines an actions-based bulk operation form element.
|
||||
*/
|
||||
#[ViewsField("bulk_form")]
|
||||
class BulkForm extends FieldPluginBase implements CacheableDependencyInterface {
|
||||
class BulkForm extends FieldPluginBase implements CacheableDependencyInterface, WorkspaceDynamicSafeFormInterface {
|
||||
|
||||
use RedirectDestinationTrait;
|
||||
use UncacheableFieldHandlerTrait;
|
||||
use EntityTranslationRenderTrait;
|
||||
use WorkspaceSafeFormTrait;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
|
@ -584,4 +587,12 @@ class BulkForm extends FieldPluginBase implements CacheableDependencyInterface {
|
|||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWorkspaceSafeForm(array $form, FormStateInterface $form_state): bool {
|
||||
$entity_type = $this->entityTypeManager->getDefinition($this->getEntityTypeId());
|
||||
return $this->isWorkspaceSafeEntityType($entity_type);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,23 @@ class BulkFormTest extends BrowserTestBase {
|
|||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Log in as a user with 'administer nodes' permission to have access to the
|
||||
// bulk operation.
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'administer nodes',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the bulk form.
|
||||
*/
|
||||
|
@ -68,16 +85,6 @@ class BulkFormTest extends BrowserTestBase {
|
|||
$edit["node_bulk_form[$i]"] = TRUE;
|
||||
}
|
||||
|
||||
// Log in as a user with 'administer nodes' permission to have access to the
|
||||
// bulk operation.
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'administer nodes',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->drupalGet('test_bulk_form');
|
||||
|
||||
// Set all nodes to sticky and check that.
|
||||
|
@ -162,6 +169,11 @@ class BulkFormTest extends BrowserTestBase {
|
|||
$edit = [];
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$edit["node_bulk_form[$i]"] = TRUE;
|
||||
|
||||
// $nodes[0] was unpublished above, so the bulk form displays only
|
||||
// $nodes[1] - $nodes[9]. Remove deleted items from $nodes to prevent
|
||||
// deleting them twice at the end of this test method.
|
||||
unset($nodes[$i + 1]);
|
||||
}
|
||||
$edit += ['action' => 'node_delete_action'];
|
||||
$this->submitForm($edit, 'Apply to selected items');
|
||||
|
@ -185,6 +197,11 @@ class BulkFormTest extends BrowserTestBase {
|
|||
'action' => 'node_delete_action',
|
||||
];
|
||||
$this->submitForm($edit, 'Apply to selected items');
|
||||
|
||||
// Remove deleted items from $nodes to prevent deleting them twice at the
|
||||
// end of this test method.
|
||||
unset($nodes[6]);
|
||||
|
||||
// Make sure we just return to the bulk view with no warnings.
|
||||
$this->assertSession()->addressEquals('test_bulk_form');
|
||||
$this->assertSession()->elementNotExists('xpath', '//div[contains(@class, "messages--status")]');
|
||||
|
@ -201,6 +218,11 @@ class BulkFormTest extends BrowserTestBase {
|
|||
'action' => 'node_delete_action',
|
||||
];
|
||||
$this->submitForm($edit, 'Apply to selected items');
|
||||
|
||||
// Remove deleted items from $nodes to prevent deleting them twice at the
|
||||
// end of this test method.
|
||||
unset($nodes[7], $nodes[8]);
|
||||
|
||||
// Make sure we don't show an action message while we are still on the
|
||||
// confirmation page.
|
||||
$this->assertSession()->elementNotExists('xpath', '//div[contains(@class, "messages--status")]');
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\views_ui;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\WorkspaceSafeFormInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
|
@ -10,7 +11,7 @@ use Drupal\Core\Url;
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
class ViewPreviewForm extends ViewFormBase {
|
||||
class ViewPreviewForm extends ViewFormBase implements WorkspaceSafeFormInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Drupal\Tests\workspaces\Functional;
|
||||
|
||||
use Drupal\Tests\block\Traits\BlockCreationTrait;
|
||||
use Drupal\workspaces\Entity\Handler\IgnoredWorkspaceHandler;
|
||||
use Drupal\workspaces\Entity\Workspace;
|
||||
use Drupal\workspaces\WorkspaceInterface;
|
||||
|
||||
|
@ -202,4 +203,17 @@ trait WorkspaceTestUtilities {
|
|||
return $page->hasContent($label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks an entity type as ignored in a workspace.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type ID.
|
||||
*/
|
||||
protected function ignoreEntityType(string $entity_type_id): void {
|
||||
$entity_type = clone \Drupal::entityTypeManager()->getDefinition($entity_type_id);
|
||||
$entity_type->setHandlerClass('workspace', IgnoredWorkspaceHandler::class);
|
||||
\Drupal::state()->set("$entity_type_id.entity_type", $entity_type);
|
||||
\Drupal::entityTypeManager()->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\workspaces\Functional;
|
||||
|
||||
use Drupal\Tests\views\Functional\BulkFormTest;
|
||||
use Drupal\workspaces\Entity\Workspace;
|
||||
|
||||
/**
|
||||
* Tests the views bulk form in a workspace.
|
||||
*
|
||||
* @group views
|
||||
* @group workspaces
|
||||
*/
|
||||
class WorkspaceViewsBulkFormTest extends BulkFormTest {
|
||||
|
||||
use WorkspaceTestUtilities;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['block', 'workspaces', 'workspaces_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Override the user created in the parent method to add workspaces access.
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'administer nodes',
|
||||
'administer workspaces',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Ensure that all the test methods are executed in the context of a
|
||||
// workspace.
|
||||
$this->setupWorkspaceSwitcherBlock();
|
||||
$stage = Workspace::load('stage');
|
||||
$this->switchToWorkspace($stage);
|
||||
}
|
||||
|
||||
public function testBulkForm() {
|
||||
// Ignore entity types that are not being tested, in order to fully re-use
|
||||
// the parent test method.
|
||||
$this->ignoreEntityType('view');
|
||||
|
||||
parent::testBulkForm();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue