Issue #3208390 by amateescu, s_leu, Fabianx, tim.plunkett, xjm, smustgrave, alexpott, catch, joachim, EclipseGc: Add an API for allowing modules to mark their forms as workspace-safe

merge-requests/7012/head^2
catch 2024-04-21 09:19:06 +01:00
parent cbad32ab34
commit 51f471d8fb
23 changed files with 102 additions and 50 deletions

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Drupal\Core\Form;
/**
* Defines an interface for forms that can be workspace-safe.
*
* This interface should be used by forms that have to determine whether they're
* workspace-safe based on dynamic criteria.
*
* @see \Drupal\Core\Form\WorkspaceSafeFormInterface
*/
interface WorkspaceDynamicSafeFormInterface extends FormInterface {
/**
* Determines whether the form is safe to be submitted in a workspace.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return bool
* TRUE if the form is workspace-safe, FALSE otherwise.
*/
public function isWorkspaceSafeForm(array $form, FormStateInterface $form_state): bool;
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Drupal\Core\Form;
/**
* Defines an interface for forms that are safe to be submitted in a workspace.
*
* A form is considered workspace-safe if its submission has no impact on the
* Live site.
*
* @see \Drupal\Core\Form\WorkspaceDynamicSafeFormInterface
*/
interface WorkspaceSafeFormInterface extends FormInterface {}

View File

@ -11,6 +11,7 @@ use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState; use Drupal\Core\Form\SubformState;
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
use Drupal\Core\Plugin\Context\ContextRepositoryInterface; use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait; use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Plugin\ContextAwarePluginInterface;
@ -29,7 +30,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @internal * @internal
* Form classes are internal. * Form classes are internal.
*/ */
abstract class ConfigureBlockFormBase extends FormBase implements BaseFormIdInterface { abstract class ConfigureBlockFormBase extends FormBase implements BaseFormIdInterface, WorkspaceDynamicSafeFormInterface {
use AjaxFormHelperTrait; use AjaxFormHelperTrait;
use ContextAwarePluginAssignmentTrait; use ContextAwarePluginAssignmentTrait;
@ -164,7 +165,6 @@ abstract class ConfigureBlockFormBase extends FormBase implements BaseFormIdInte
$this->delta = $delta; $this->delta = $delta;
$this->uuid = $component->getUuid(); $this->uuid = $component->getUuid();
$this->block = $component->getPlugin(); $this->block = $component->getPlugin();
$this->markWorkspaceSafe($form_state);
$form_state->setTemporaryValue('gathered_contexts', $this->getPopulatedContexts($section_storage)); $form_state->setTemporaryValue('gathered_contexts', $this->getPopulatedContexts($section_storage));

View File

@ -7,6 +7,7 @@ use Drupal\Core\Ajax\AjaxFormHelperTrait;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState; use Drupal\Core\Form\SubformState;
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
use Drupal\Core\Layout\LayoutInterface; use Drupal\Core\Layout\LayoutInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Plugin\PluginFormFactoryInterface; use Drupal\Core\Plugin\PluginFormFactoryInterface;
@ -26,7 +27,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @internal * @internal
* Form classes are internal. * Form classes are internal.
*/ */
class ConfigureSectionForm extends FormBase { class ConfigureSectionForm extends FormBase implements WorkspaceDynamicSafeFormInterface {
use AjaxFormHelperTrait; use AjaxFormHelperTrait;
use LayoutBuilderContextTrait; use LayoutBuilderContextTrait;
@ -128,7 +129,6 @@ class ConfigureSectionForm extends FormBase {
$this->delta = $delta; $this->delta = $delta;
$this->isUpdate = is_null($plugin_id); $this->isUpdate = is_null($plugin_id);
$this->pluginId = $plugin_id; $this->pluginId = $plugin_id;
$this->markWorkspaceSafe($form_state);
$section = $this->getCurrentSection(); $section = $this->getCurrentSection();

View File

@ -4,6 +4,7 @@ namespace Drupal\layout_builder\Form;
use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Messenger\MessengerInterface;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Drupal\layout_builder\SectionStorageInterface; use Drupal\layout_builder\SectionStorageInterface;
@ -15,7 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @internal * @internal
* Form classes are internal. * Form classes are internal.
*/ */
class DiscardLayoutChangesForm extends ConfirmFormBase { class DiscardLayoutChangesForm extends ConfirmFormBase implements WorkspaceDynamicSafeFormInterface {
use WorkspaceSafeFormTrait; use WorkspaceSafeFormTrait;
@ -89,7 +90,6 @@ class DiscardLayoutChangesForm extends ConfirmFormBase {
*/ */
public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL) { public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL) {
$this->sectionStorage = $section_storage; $this->sectionStorage = $section_storage;
$this->markWorkspaceSafe($form_state);
// Mark this as an administrative page for JavaScript ("Back to site" link). // Mark this as an administrative page for JavaScript ("Back to site" link).
$form['#attached']['drupalSettings']['path']['currentPathIsAdmin'] = TRUE; $form['#attached']['drupalSettings']['path']['currentPathIsAdmin'] = TRUE;
return parent::buildForm($form, $form_state); return parent::buildForm($form, $form_state);

View File

@ -4,6 +4,7 @@ namespace Drupal\layout_builder\Form;
use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Messenger\MessengerInterface;
use Drupal\layout_builder\DefaultsSectionStorageInterface; use Drupal\layout_builder\DefaultsSectionStorageInterface;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
@ -16,7 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @internal * @internal
* Form classes are internal. * Form classes are internal.
*/ */
class LayoutBuilderDisableForm extends ConfirmFormBase { class LayoutBuilderDisableForm extends ConfirmFormBase implements WorkspaceDynamicSafeFormInterface {
use WorkspaceSafeFormTrait; use WorkspaceSafeFormTrait;
@ -94,7 +95,6 @@ class LayoutBuilderDisableForm extends ConfirmFormBase {
} }
$this->sectionStorage = $section_storage; $this->sectionStorage = $section_storage;
$this->markWorkspaceSafe($form_state);
// Mark this as an administrative page for JavaScript ("Back to site" link). // Mark this as an administrative page for JavaScript ("Back to site" link).
$form['#attached']['drupalSettings']['path']['currentPathIsAdmin'] = TRUE; $form['#attached']['drupalSettings']['path']['currentPathIsAdmin'] = TRUE;
return parent::buildForm($form, $form_state); return parent::buildForm($form, $form_state);

View File

@ -5,6 +5,7 @@ namespace Drupal\layout_builder\Form;
use Drupal\Core\Ajax\AjaxFormHelperTrait; use Drupal\Core\Ajax\AjaxFormHelperTrait;
use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
use Drupal\layout_builder\Controller\LayoutRebuildTrait; use Drupal\layout_builder\Controller\LayoutRebuildTrait;
use Drupal\layout_builder\LayoutBuilderHighlightTrait; use Drupal\layout_builder\LayoutBuilderHighlightTrait;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
@ -17,7 +18,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @internal * @internal
* Form classes are internal. * Form classes are internal.
*/ */
abstract class LayoutRebuildConfirmFormBase extends ConfirmFormBase { abstract class LayoutRebuildConfirmFormBase extends ConfirmFormBase implements WorkspaceDynamicSafeFormInterface {
use AjaxFormHelperTrait; use AjaxFormHelperTrait;
use LayoutBuilderHighlightTrait; use LayoutBuilderHighlightTrait;
@ -78,7 +79,6 @@ abstract class LayoutRebuildConfirmFormBase extends ConfirmFormBase {
$this->sectionStorage = $section_storage; $this->sectionStorage = $section_storage;
$this->delta = $delta; $this->delta = $delta;
$this->markWorkspaceSafe($form_state);
$form = parent::buildForm($form, $form_state); $form = parent::buildForm($form, $form_state);
if ($this->isAjax()) { if ($this->isAjax()) {

View File

@ -5,6 +5,7 @@ namespace Drupal\layout_builder\Form;
use Drupal\Core\Ajax\AjaxFormHelperTrait; use Drupal\Core\Ajax\AjaxFormHelperTrait;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
use Drupal\layout_builder\Context\LayoutBuilderContextTrait; use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
use Drupal\layout_builder\Controller\LayoutRebuildTrait; use Drupal\layout_builder\Controller\LayoutRebuildTrait;
use Drupal\layout_builder\LayoutBuilderHighlightTrait; use Drupal\layout_builder\LayoutBuilderHighlightTrait;
@ -18,7 +19,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @internal * @internal
* Form classes are internal. * Form classes are internal.
*/ */
class MoveBlockForm extends FormBase { class MoveBlockForm extends FormBase implements WorkspaceDynamicSafeFormInterface {
use AjaxFormHelperTrait; use AjaxFormHelperTrait;
use LayoutBuilderContextTrait; use LayoutBuilderContextTrait;
@ -118,7 +119,6 @@ class MoveBlockForm extends FormBase {
$this->delta = $delta; $this->delta = $delta;
$this->uuid = $uuid; $this->uuid = $uuid;
$this->region = $region; $this->region = $region;
$this->markWorkspaceSafe($form_state);
$form['#attributes']['data-layout-builder-target-highlight-id'] = $this->blockUpdateHighlightId($uuid); $form['#attributes']['data-layout-builder-target-highlight-id'] = $this->blockUpdateHighlightId($uuid);

View File

@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Drupal\layout_builder\OverridesSectionStorageInterface; use Drupal\layout_builder\OverridesSectionStorageInterface;
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage; use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
@ -21,7 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @internal * @internal
* Form classes are internal. * Form classes are internal.
*/ */
class OverridesEntityForm extends ContentEntityForm { class OverridesEntityForm extends ContentEntityForm implements WorkspaceDynamicSafeFormInterface {
use PreviewToggleTrait; use PreviewToggleTrait;
use LayoutBuilderEntityFormTrait; use LayoutBuilderEntityFormTrait;
@ -91,7 +92,6 @@ class OverridesEntityForm extends ContentEntityForm {
*/ */
public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL) { public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL) {
$this->sectionStorage = $section_storage; $this->sectionStorage = $section_storage;
$this->markWorkspaceSafe($form_state);
$form = parent::buildForm($form, $form_state); $form = parent::buildForm($form, $form_state);
$form['#attributes']['class'][] = 'layout-builder-form'; $form['#attributes']['class'][] = 'layout-builder-form';

View File

@ -4,6 +4,7 @@ namespace Drupal\layout_builder\Form;
use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Messenger\MessengerInterface;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Drupal\layout_builder\OverridesSectionStorageInterface; use Drupal\layout_builder\OverridesSectionStorageInterface;
@ -16,7 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @internal * @internal
* Form classes are internal. * Form classes are internal.
*/ */
class RevertOverridesForm extends ConfirmFormBase { class RevertOverridesForm extends ConfirmFormBase implements WorkspaceDynamicSafeFormInterface {
use WorkspaceSafeFormTrait; use WorkspaceSafeFormTrait;
@ -101,7 +102,6 @@ class RevertOverridesForm extends ConfirmFormBase {
} }
$this->sectionStorage = $section_storage; $this->sectionStorage = $section_storage;
$this->markWorkspaceSafe($form_state);
// Mark this as an administrative page for JavaScript ("Back to site" link). // Mark this as an administrative page for JavaScript ("Back to site" link).
$form['#attached']['drupalSettings']['path']['currentPathIsAdmin'] = TRUE; $form['#attached']['drupalSettings']['path']['currentPathIsAdmin'] = TRUE;
return parent::buildForm($form, $form_state); return parent::buildForm($form, $form_state);

View File

@ -19,16 +19,17 @@ trait WorkspaceSafeFormTrait {
protected ?WorkspaceInformationInterface $workspaceInfo = NULL; protected ?WorkspaceInformationInterface $workspaceInfo = NULL;
/** /**
* Marks a form as workspace-safe, if possible. * Determines whether the current form is safe to be submitted in a workspace.
* *
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state * @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state object. * The current state of the form.
*
* @return bool
* TRUE if the form is workspace-safe, FALSE otherwise.
*/ */
protected function markWorkspaceSafe(FormStateInterface $form_state): void { public function isWorkspaceSafeForm(array $form, FormStateInterface $form_state): bool {
if (!\Drupal::hasService('workspaces.information')) {
return;
}
$section_storage = $this->sectionStorage ?: $this->getSectionStorageFromFormState($form_state); $section_storage = $this->sectionStorage ?: $this->getSectionStorageFromFormState($form_state);
if ($section_storage) { if ($section_storage) {
$context_definitions = $section_storage->getContextDefinitions(); $context_definitions = $section_storage->getContextDefinitions();
@ -39,10 +40,12 @@ trait WorkspaceSafeFormTrait {
$ignored = $entity && $this->getWorkspaceInfo()->isEntityIgnored($entity); $ignored = $entity && $this->getWorkspaceInfo()->isEntityIgnored($entity);
if ($supported || $ignored) { if ($supported || $ignored) {
$form_state->set('workspace_safe', TRUE); return TRUE;
} }
} }
} }
return FALSE;
} }
/** /**

View File

@ -5,6 +5,7 @@ namespace Drupal\search\Form;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceSafeFormInterface;
use Drupal\Core\Render\RendererInterface; use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\search\SearchPageRepositoryInterface; use Drupal\search\SearchPageRepositoryInterface;
@ -15,7 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* *
* @internal * @internal
*/ */
class SearchBlockForm extends FormBase { class SearchBlockForm extends FormBase implements WorkspaceSafeFormInterface {
/** /**
* The search page repository. * The search page repository.

View File

@ -4,6 +4,7 @@ namespace Drupal\search\Form;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceSafeFormInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\search\SearchPageInterface; use Drupal\search\SearchPageInterface;
@ -18,7 +19,7 @@ use Drupal\search\SearchPageInterface;
* *
* @internal * @internal
*/ */
class SearchPageForm extends FormBase { class SearchPageForm extends FormBase implements WorkspaceSafeFormInterface {
/** /**
* The search page entity. * The search page entity.

View File

@ -5,6 +5,7 @@ namespace Drupal\views\Form;
use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceSafeFormInterface;
use Drupal\Core\Path\CurrentPathStack; use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Render\Element\Checkboxes; use Drupal\Core\Render\Element\Checkboxes;
use Drupal\Core\Url; use Drupal\Core\Url;
@ -16,7 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* *
* @internal * @internal
*/ */
class ViewsExposedForm extends FormBase { class ViewsExposedForm extends FormBase implements WorkspaceSafeFormInterface {
/** /**
* The exposed form cache. * The exposed form cache.

View File

@ -5,6 +5,7 @@ namespace Drupal\workspaces\Form;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceSafeFormInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\workspaces\WorkspaceManagerInterface; use Drupal\workspaces\WorkspaceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
@ -12,7 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Provides a form that switches to the live version of the site. * Provides a form that switches to the live version of the site.
*/ */
class SwitchToLiveForm extends ConfirmFormBase implements WorkspaceFormInterface, ContainerInjectionInterface { class SwitchToLiveForm extends ConfirmFormBase implements ContainerInjectionInterface, WorkspaceSafeFormInterface {
/** /**
* The workspace manager. * The workspace manager.

View File

@ -5,6 +5,7 @@ namespace Drupal\workspaces\Form;
use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityConfirmFormBase; use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceSafeFormInterface;
use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\workspaces\WorkspaceAccessException; use Drupal\workspaces\WorkspaceAccessException;
@ -14,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Handle activation of a workspace on administrative pages. * Handle activation of a workspace on administrative pages.
*/ */
class WorkspaceActivateForm extends EntityConfirmFormBase implements WorkspaceFormInterface { class WorkspaceActivateForm extends EntityConfirmFormBase implements WorkspaceSafeFormInterface {
/** /**
* The workspace entity. * The workspace entity.

View File

@ -16,7 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* *
* @internal * @internal
*/ */
class WorkspaceDeleteForm extends ContentEntityDeleteForm implements WorkspaceFormInterface { class WorkspaceDeleteForm extends ContentEntityDeleteForm {
/** /**
* The workspace entity. * The workspace entity.

View File

@ -12,7 +12,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Form controller for the workspace edit forms. * Form controller for the workspace edit forms.
*/ */
class WorkspaceForm extends ContentEntityForm implements WorkspaceFormInterface { class WorkspaceForm extends ContentEntityForm {
/** /**
* The workspace entity. * The workspace entity.

View File

@ -8,5 +8,11 @@ use Drupal\Core\Form\FormInterface;
* Defines interface for workspace forms so they can be easily distinguished. * Defines interface for workspace forms so they can be easily distinguished.
* *
* @internal * @internal
*
* @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. Use
* \Drupal\Core\Form\WorkspaceSafeFormInterface or
* \Drupal\Core\Form\WorkspaceDynamicSafeFormInterface instead.
*
* @see https://www.drupal.org/node/3229111
*/ */
interface WorkspaceFormInterface extends FormInterface {} interface WorkspaceFormInterface extends FormInterface {}

View File

@ -6,6 +6,7 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceSafeFormInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\workspaces\WorkspaceInterface; use Drupal\workspaces\WorkspaceInterface;
use Drupal\workspaces\WorkspaceOperationFactory; use Drupal\workspaces\WorkspaceOperationFactory;
@ -14,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Provides a form that merges the contents for a workspace into another one. * Provides a form that merges the contents for a workspace into another one.
*/ */
class WorkspaceMergeForm extends ConfirmFormBase implements WorkspaceFormInterface, ContainerInjectionInterface { class WorkspaceMergeForm extends ConfirmFormBase implements ContainerInjectionInterface, WorkspaceSafeFormInterface {
/** /**
* The source workspace entity. * The source workspace entity.

View File

@ -6,6 +6,7 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceSafeFormInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\workspaces\WorkspaceAccessException; use Drupal\workspaces\WorkspaceAccessException;
use Drupal\workspaces\WorkspaceInterface; use Drupal\workspaces\WorkspaceInterface;
@ -15,7 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Provides the workspace publishing form. * Provides the workspace publishing form.
*/ */
class WorkspacePublishForm extends ConfirmFormBase implements WorkspaceFormInterface, ContainerInjectionInterface { class WorkspacePublishForm extends ConfirmFormBase implements ContainerInjectionInterface, WorkspaceSafeFormInterface {
/** /**
* The workspace that will be published. * The workspace that will be published.

View File

@ -5,6 +5,7 @@ namespace Drupal\workspaces\Form;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceSafeFormInterface;
use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Messenger\MessengerInterface;
use Drupal\workspaces\WorkspaceAccessException; use Drupal\workspaces\WorkspaceAccessException;
use Drupal\workspaces\WorkspaceManagerInterface; use Drupal\workspaces\WorkspaceManagerInterface;
@ -13,7 +14,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Provides a form that activates a different workspace. * Provides a form that activates a different workspace.
*/ */
class WorkspaceSwitcherForm extends FormBase implements WorkspaceFormInterface { class WorkspaceSwitcherForm extends FormBase implements WorkspaceSafeFormInterface {
/** /**
* The workspace manager. * The workspace manager.

View File

@ -4,10 +4,10 @@ namespace Drupal\workspaces;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
use Drupal\Core\Form\WorkspaceSafeFormInterface;
use Drupal\Core\Render\Element; use Drupal\Core\Render\Element;
use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\views\Form\ViewsExposedForm;
use Drupal\workspaces\Form\WorkspaceFormInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
@ -61,28 +61,18 @@ class FormOperations implements ContainerInjectionInterface {
return; return;
} }
// Add an additional validation step for every form if we are in a // Add a validation step for every form if we are in a workspace.
// non-default workspace.
$this->addWorkspaceValidation($form); $this->addWorkspaceValidation($form);
// If a form has already been marked as safe or not to submit in a // If a form has already been marked as safe or not to submit in a
// non-default workspace, we don't have anything else to do. // workspace, we don't have anything else to do.
if ($form_state->has('workspace_safe')) { if ($form_state->has('workspace_safe')) {
return; return;
} }
// No forms are safe to submit in a non-default workspace by default, except
// for the whitelisted ones defined below.
$workspace_safe = FALSE;
// Whitelist a few forms that we know are safe to submit.
$form_object = $form_state->getFormObject(); $form_object = $form_state->getFormObject();
$is_workspace_form = $form_object instanceof WorkspaceFormInterface; $workspace_safe = $form_object instanceof WorkspaceSafeFormInterface
$is_search_form = in_array($form_object->getFormId(), ['search_block_form', 'search_form'], TRUE); || ($form_object instanceof WorkspaceDynamicSafeFormInterface && $form_object->isWorkspaceSafeForm($form, $form_state));
$is_views_exposed_form = $form_object instanceof ViewsExposedForm;
if ($is_workspace_form || $is_search_form || $is_views_exposed_form) {
$workspace_safe = TRUE;
}
$form_state->set('workspace_safe', $workspace_safe); $form_state->set('workspace_safe', $workspace_safe);
} }