diff --git a/core/modules/workspace/src/EntityOperations.php b/core/modules/workspace/src/EntityOperations.php index af090f3..86ed6ef 100644 --- a/core/modules/workspace/src/EntityOperations.php +++ b/core/modules/workspace/src/EntityOperations.php @@ -3,12 +3,10 @@ namespace Drupal\workspace; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; -use Drupal\Core\Entity\EntityFormInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\RevisionableInterface; -use Drupal\Core\Form\BaseFormIdInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -301,33 +299,31 @@ protected function trackEntity(EntityInterface $entity) { * * @see hook_form_alter() */ - public function formAlter(array &$form, FormStateInterface $form_state, $form_id) { - $form_object = $form_state->getFormObject(); - $active_workspace = $this->workspaceManager->getActiveWorkspace(); - - $is_entity_supported = $form_object instanceof EntityFormInterface - && ($entity = $form_object->getEntity()) - && $this->workspaceManager->isEntityTypeSupported($entity->getEntityType()); - - // Add an additional validation step for every form that does not handle an - // revisionable and publishable entity. - if (!$is_entity_supported && !$active_workspace->isDefaultWorkspace() && !static::isFormSafeToEdit($form_state)) { - $form['#validate'][] = [$this, 'validateForm']; + public function entityFormAlter(array &$form, FormStateInterface $form_state, $form_id) { + /** @var \Drupal\Core\Entity\EntityInterface $entity */ + $entity = $form_state->getFormObject()->getEntity(); + if (!$this->workspaceManager->isEntityTypeSupported($entity->getEntityType())) { return; } + // For supported entity types, signal the fact that this form is safe to use + // in a non-default workspace. + // @see \Drupal\workspace\FormOperations::validateForm() + $form_state->set('workspace_safe', TRUE); + // Add an entity builder to the form which marks the edited entity object as // a pending revision. This is needed so validation constraints like // \Drupal\path\Plugin\Validation\Constraint\PathAliasConstraintValidator // know in advance (before hook_entity_presave()) that the new revision will // be a pending one. + $active_workspace = $this->workspaceManager->getActiveWorkspace(); if (!$active_workspace->isDefaultWorkspace()) { $form['#entity_builders'][] = [$this, 'entityFormEntityBuild']; } /** @var \Drupal\workspace\WorkspaceAssociationStorageInterface $workspace_association_storage */ $workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association'); - if ($is_entity_supported && $workspace_ids = $workspace_association_storage->getEntityTrackingWorkspaceIds($entity)) { + if ($workspace_ids = $workspace_association_storage->getEntityTrackingWorkspaceIds($entity)) { // An entity can only be edited in one workspace. $workspace_id = reset($workspace_ids); @@ -341,15 +337,6 @@ public function formAlter(array &$form, FormStateInterface $form_state, $form_id } /** - * Validation handler for forms that do not handle a supported entity type. - */ - public function validateForm(array &$form, FormStateInterface $form_state) { - // The exception in ::entityPresave() is thrown upon submission of the form, - // at which point we can longer transform it into a valiation error. - $form_state->setError($form, $this->t('This form can only be submitted in the default workspace.')); - } - - /** * Entity builder that marks all supported entities as pending revisions. */ public function entityFormEntityBuild($entity_type_id, RevisionableInterface $entity, &$form, FormStateInterface &$form_state) { @@ -382,51 +369,4 @@ public static function isEntityTypeSafeToEdit(EntityTypeInterface $entity_type) return in_array($entity_type->id(), $whitelist, TRUE); } - /** - * Determines whether a form is safe to use in non-default workspaces. - * - * @param \Drupal\Core\Form\FormStateInterface $form_state - * A form state object. - * - * @return bool - * Returns TRUE if the given form state object represents a form that is - * safe to use in a non-default workspace, FALSE otherwise. - * - * @internal - */ - public static function isFormSafeToEdit(FormStateInterface $form_state) { - $form_object = $form_state->getFormObject(); - - $whitelist = [ - 'workspace_confirm_form', - 'workspace_form', - 'workspace_switcher_form', - // Forms that are not submitting data meant to be persisted on the backend - // are allowed by default. - 'search_block_form', - 'search_form', - 'views_exposed_form', - 'views_form_', - ]; - - // Allow modules to add their own entity type IDs to the whitelist. - \Drupal::moduleHandler()->alter('workspace_form_id_whitelist', $whitelist); - - $allowed = FALSE; - if ($form_object instanceof BaseFormIdInterface) { - foreach ($whitelist as $whitelist_id) { - if (strpos($form_object->getBaseFormId(), $whitelist_id) !== FALSE) { - $allowed = TRUE; - } - } - } - foreach ($whitelist as $whitelist_id) { - if (strpos($form_object->getFormId(), $whitelist_id) !== FALSE) { - $allowed = TRUE; - } - } - - return $allowed; - } - } diff --git a/core/modules/workspace/src/FormOperations.php b/core/modules/workspace/src/FormOperations.php new file mode 100644 index 0000000..ee9efca --- /dev/null +++ b/core/modules/workspace/src/FormOperations.php @@ -0,0 +1,97 @@ +workspaceManager = $workspace_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('workspace.manager') + ); + } + + /** + * Alters forms to disallow editing in non-default workspaces. + * + * @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. + * @param string $form_id + * The form ID. + * + * @see hook_form_alter() + */ + public function formAlter(array &$form, FormStateInterface $form_state, $form_id) { + // No alterations are needed in the default workspace. + if ($this->workspaceManager->getActiveWorkspace()->isDefaultWorkspace()) { + return; + } + + // Add an additional validation step for every form if we are in a + // non-default workspace. + $form['#validate'][] = [$this, 'validateForm']; + + // No forms are safe to submit in a non-default workspace by fault, except + // for our own whitelist defined above. + $workspace_safe = FALSE; + + // Whitelist a few forms that we know are safe to submit. + $form_object = $form_state->getFormObject(); + $is_workspace_form = in_array($form_object->getFormId(), ['workspace_confirm_form', 'workspace_form', 'workspace_switcher_form'], TRUE); + $is_search_form = in_array($form_object->getFormId(), ['search_block_form', 'search_form'], TRUE); + $is_views_exposed_form = $form_object instanceof ViewsExposedForm; + $is_views_form = $form_object instanceof ViewsForm; + if ($is_workspace_form || $is_search_form || $is_views_exposed_form || $is_views_form) { + $workspace_safe = TRUE; + } + + if (!$form_state->has('workspace_safe')) { + $form_state->set('workspace_safe', $workspace_safe); + } + } + + /** + * Validation handler which sets a validation error for all unsupported forms. + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + if ($form_state->get('workspace_safe') !== TRUE) { + $form_state->setError($form, $this->t('This form can only be submitted in the default workspace.')); + } + } + +} diff --git a/core/modules/workspace/workspace.api.php b/core/modules/workspace/workspace.api.php index 837bd3b..598a733 100644 --- a/core/modules/workspace/workspace.api.php +++ b/core/modules/workspace/workspace.api.php @@ -28,22 +28,5 @@ function hook_workspace_entity_type_whitelist_alter(array &$whitelist) { } /** - * Alter the form IDs which are safe to submit in a non-default workspace. - * - * If a module decides that the submission of a specific form does not impact - * the default (Live) workspace, they can use this hook and add the form ID or - * base form ID to the list. - * - * @param array &$whitelist - * An array containing form and base form IDs. - * - * @ingroup workspace_api - */ -function hook_workspace_form_id_whitelist_alter(array &$whitelist) { - // Add the ID of a custom form to the whitelist. - $whitelist[] = 'custom_form'; -} - -/** * @} End of "addtogroup hooks". */ diff --git a/core/modules/workspace/workspace.module b/core/modules/workspace/workspace.module index d37e773..ced2be4 100644 --- a/core/modules/workspace/workspace.module +++ b/core/modules/workspace/workspace.module @@ -8,6 +8,7 @@ use Drupal\Component\Serialization\Json; use Drupal\Core\Cache\Cache; use Drupal\Core\Url; +use Drupal\Core\Entity\EntityFormInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; @@ -17,6 +18,7 @@ use Drupal\workspace\EntityAccess; use Drupal\workspace\EntityOperations; use Drupal\workspace\EntityTypeInfo; +use Drupal\workspace\FormOperations; use Drupal\workspace\ViewsQueryAlter; /** @@ -46,8 +48,13 @@ function workspace_entity_type_build(array &$entity_types) { * Implements hook_form_alter(). */ function workspace_form_alter(&$form, FormStateInterface $form_state, $form_id) { - return \Drupal::service('class_resolver') - ->getInstanceFromDefinition(EntityOperations::class) + if ($form_state->getFormObject() instanceof EntityFormInterface) { + \Drupal::service('class_resolver') + ->getInstanceFromDefinition(EntityOperations::class) + ->entityFormAlter($form, $form_state, $form_id); + } + \Drupal::service('class_resolver') + ->getInstanceFromDefinition(FormOperations::class) ->formAlter($form, $form_state, $form_id); }