Problem/Motivation
The Workspaces module needs to take every precaution possible to disallow any kind of information/content from leaking into the live site, so it provides and enforces a form validation handler which doesn't allow most Drupal forms to be submitted when a workspace is active.
However, other modules need an easy way to mark their forms as workspace-safe. In HEAD, this can be accomplished by setting $form_state->set('workspace_safe', TRUE); in a form, but this approach has various drawbacks:
- there are modules which provide many forms and the logic for determining whether they're workspace-safe can be complex and needs to be reused
- it requires module authors to be aware of this custom form state value
Proposed resolution
Standardize the way for modules to mark their forms as workspace-safe by using two helper interfaces:
\Drupal\Core\Form\WorkspaceSafeFormInterface
\Drupal\Core\Form\WorkspaceDynamicSafeFormInterface
Remaining tasks
Review.
User interface changes
Nope.
API changes
Two new interfaces are added in the \Drupal\Core\Form namespace.
Data model changes
Nope.
Release notes snippet
TBD.
| Comment | File | Size | Author |
|---|
Issue fork drupal-3208390
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
amateescu commentedThis should do it.
Comment #3
amateescu commentedHere's a 8.9.x backport as well, to go along with #3000749-16: Layout builder overrides on a single content item not allowed in a workspace.
Comment #5
fabianx commentedOverall looks great, and the event will work really well - I think - Code is really clean.
There are normally 3 cases, but one is not really needed:
- Form is safe to submit while in a workspace and in Live [workspace_safe = TRUE]
(mostly GET request forms -- maybe we could mark those automatically as safe as they must not mutate data per HTTP specification?)
- Form is not safe to submit while in a workspace, but safe to submit in live [workspace_safe == FALSE], e.g. config entities
- Form is safe to submit, while in a workspace, but not in Live => We don't need this case
==> The boolean is fine to use (and pre-existing anyway).
While a form alter is as centralized (because the event piggy-backs on the form alter), I can see that the event is making the logic cleaner.
I defer to core committers to decide if that's a warranted use-case for a new event.
Unrelated, but saw this here:
- We could consider a follow-up to automatically mark all GET request forms as safe. If someone violates HTTP spec, I don't think that is a problem of Core to protect them.
- Overall I think contrib will provide a way to mark all forms as safe -- if you have a special permission, but I am not sure if that's a core thing.
Because I can see the case of a developer that wants to develop views while in a workspace. [e.g. being really frustrated of that safeness feature]
Nice patch!
Comment #6
catchWouldn't search and views module be responsible for implementing this? Since it's core it doesn't really matter and workspaces could do it on their behalf, but more to the point there are no implementation examples outside of workspaces module itself.
Also needs a change record and tests.
I'm not keen on adding events to core, but that's a separate issue to the need for an API for this.
Comment #7
tim.plunkettEven though it just means another two subscribers, I'm +1 to doing things "correctly" and having more examples of how it's done. One for search, one for views.
Comment #9
tim.plunkettMR is empty.
Comment #10
s_leu commentedI will provide patches instead of using an MR as discussed with amateescu because we need a packport to 8.x , so patches seem to be easier
Comment #11
s_leu commentedHere's first patch and interdiff adding tests as requested. I didn't add a dedicated test method to test workspaces internal forms as that's already being done within the added test methods when the workspace gets switched, as the switcher form is one of these module internal forms.
I can still add tests for more internal forms if that's desired. Also, I'll provide a backport of the patch for 8.x sometime soon, will set this to on needs review once I got the backport ready too.
Comment #12
s_leu commentedHere's the patch for 8.9.x .
Comment #13
joachim commented> - it scatters the knowledge about workspace integration across many files (forms), instead of a centralized place
From the point of view of the module that defines the form, that's a good thing though, isn't it? The form class defines the form, and having the form class also declare itself as workspace-safe keeps things about the form in the same place.
The current MR is requiring modules that define forms to add a lot of boilerplate, and an event seems like overkill for something that sounds like it's declarative.
I can see that having to do '$form_state->set()' means that you can't know whether a form is OK until you've actually created the form build array.
But what about adding a method to the form class? The one drawback I can see with that is if a hook_form_alter() changes the workspace-safety of a form. But with the event, the module doing the form alteration has to be pretty careful with event weights to make sure it fires after the form-defining module's event! To cover that case, we could keep the $form_state->set() for dynamic cases?
Comment #14
amateescu commented@joachim, we are keeping the
$form_state->set()option in place. The new code just does$form_state->set('workspace_safe', $event->isSafe());based on the event data.Simple (one-off) forms still have the option to do it in the form class, the new event is more geared towards the cases where multiple forms have to be handled together, for example based on shared logic like checking the form class inheritance or more complex scenarios like we need to use for Layout Builder in #3000749-15: Layout builder overrides on a single content item not allowed in a workspace.
Comment #15
amateescu commentedReviewed the changes from #11 and they look good to me. Here's a quick fix for the cspell errors.
Comment #16
amateescu commentedAdded a change record: https://www.drupal.org/node/3229111
Comment #17
amateescu commentedMissed a spot :)
Comment #19
eclipsegc commentedHaving looked over this patch, I have a couple thoughts about the EventSubscribing portions of this that I hope will bring us more in line with where Symfony is going, but I'm unsure of the current approach within core to this space.
Are we at a point where we can safely stop assigning names to the event and just use the event class convention in Symfony?
If we can stop naming events, we could remove this class. Even if we can't stop doing that yet, we could potentially use WorkspaceSafeFormEvent::class as the name. That would conform to the new standard, and allow us to just drop the second parameter when the time comes.
Comment #20
xjmNot sure #19 is in scope; followup maybe?
I have no meaningful feedback on the API addition itself, but since this has been stuck in the RTBC queue for six months, here is your "committer guilt review". :)
voidreturn typehints in particular.This is 81 chars and so should use multiline array syntax.
string[]?Nit (here and elsewhere): "workspace-safe" should be hyphenated.
Nit: "ID" should be uppercase.
Excellent test data nouns.
Almost gave kneejerk feedback that this needed a deprecation for the new service, but that is not necessary since the class has a factory method to inject the services.
Edit: Fixing bad code snippet. Dreditor and GitLab require opposite cursor-drag directions to highlight lines in the diff and apparently I'm starting to adapt to GitLab despite more than a decade of core patches. :P
NW for points 1-7. Thanks!
Comment #21
amateescu commentedThanks @xjm for reviewing! :)
#7 was addressed in #11, and I fixed the rest of #20 here.
I agree that #19 should be first established in a policy issue or something, then changed everywhere at once.
Comment #24
s_leu commentedThis is working fine and I guess it's time to set it to RTBC once someone else confirms. I guess it's also worth mentioning that there's some other approach to this topic in the workspaces extra module's issue over here: #3304460: Different handling of form submissions inside workspaces
Comment #25
xjm@s_leu, the next step is indeed a code review of @amateescu's fixes.
Comment #26
smustgrave commentedIssue summary as a TBD for a release note, that probably still needs to happen.
Think the change record should include the new service
+ search.workspace_safe_forms:
+ class: Drupal\search\EventSubscriber\SearchWorkspaceSafeFormsSubscriber
+ tags:
+ - { name: event_subscriber }
getSubscribedEventsShould be typehinted
Change record also doesn't mean new event/service for views ViewsWorkspaceSafeFormsSubscriber
Comment #28
amateescu commentedRerolled #21, addressed #19 and #26, and updated to the latest core standards.
@smustgrave those new services are event subscribers, so they're not APIs that need to be announced in the CR :)
Comment #30
amateescu commentedFixed the test failure.
Comment #31
smustgrave commentedChange record reads well but could it or the issue summary be updated that this change is also marking certain core blocks as workspace safe? The change makes sense 100% just feels like something that should be noted.
Comment #32
s_leu commentedAdding a patch that still applies on 10.1.x as the latest patch here doesn't any more.
Comment #36
amateescu commentedSince @catch was not a fan of the event approach, here's an alternative that would allow modules to declare their forms as workspace-safe even when the
workspacesmodule is not installed.I added the trait in the
Drupal\Core\Formas an initial proposal, but it could also be added somewhere in thesystemmodule I guess..Once Workspaces will be marked as stable, more and more modules will have to integrate with it, so I'm raising the priority of this issue.
Comment #37
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue.
While you are making the above changes, we recommend that you convert this patch to a merge request. Merge requests are preferred over patches. Be sure to hide the old patch files as well. (Converting an issue to a merge request without other contributions to the issue will not receive credit.)
Comment #38
amateescu commentedComment #39
fabianx commentedRTBC
My only concern was for existing code that sets workspace_safe = TRUE or FALSE, but the $form_state property always takes precedence, so this is just a way to set the default state of the form.
And it's very nice to use.
Comment #40
amateescu commentedHiding old patch files.
Comment #41
alexpottWhy a trait over an empty interface in the Drupal\Core\Form namespace? There no logic or possible dynamism with an interface (or is that an intended side effect... sometimes a form is workspace safe and sometimes it is not... that feels odd).
I think
is nicer than messing with a trait and possible dynamic safeness. Anything that wants to be dynamic can still manipulate form state and set workspace_safe as they see fit.
Comment #42
amateescu commented@alexpott the idea with a trait came up specifically for the dynamic nature that some modules might need, like Layout Builder in core :)
The difference is that in HEAD, LB has to call a method in each form class for marking its forms as workspace-safe, while with the trait approach it only has to ensure that its method name matches the one from the "top-level" trait, and Workspaces takes care of calling it in its own form alter.
I don't mind going for the interface approach you suggested, Workspaces itself does that currently and it works great when there's no dynamism involved. Should we wait or ask for other opinions or do you feel (strongly or not) that the interface will be far simpler to implement for the majority use cases?
Comment #43
alexpottSorry I completely missed that we already have the dynamic use-case in core - so the trait implementing a method works out nicely there I agree.
What's not so nice is
we're making assumptions about the signature of isWorkspaceSafeForm that we can't really. So... I think we should add an interface WorkspaceSafeInterface that declares one method - isWorkSpaceSafeForm() and have the two trait be implementations of the interface for people to use.
Then at least we are programming to interfaces and people can come up with their own implementations safe in the knowledge that they are implementing an interface.
Yes adding an interface and a trait is not as nice as just adding a trait - and maybe PHP will solve that someday but I think it is a price worth paying in order to avoid calling a method incorrectly and ding type-safe programming.
Comment #44
amateescu commentedChanged the approach to use two interfaces instead of a trait, and updated the issue summary and the change record.
Comment #45
fabianx commentedBack to RTBC!
The new approach is fully BC compatible for layout_builder and makes it much easier for forms to mark themselves as safe by default / definition.
Comment #48
catchThis looks great now, glad I pushed back on the new event, much easier to see what's going on everywhere.
Committed/pushed to 11.x and cherry-picked to 10.3.x, thanks!