Closed (fixed)
Project:
Drupal core
Version:
8.3.x-dev
Component:
content_moderation.module
Priority:
Normal
Category:
Task
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
18 Jan 2017 at 14:08 UTC
Updated:
30 Dec 2018 at 17:08 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
timmillwoodInitial patch
Comment #4
alexpottThe problem is to do with ordering. Yay for the default config test :)
Comment #5
alexpottI think we need to translate the default config...
Comment #6
sam152 commentedQuestions and comments.
Are you sure this needs translating. The config schema defines the key as "type: label" which is already translatable once it's stored in config.
funky indentation.
nit, trailing comma
Should we stick some asserts in here to make sure the default states show up in the UI?
I don't think it's a bad thing to decouple tests like this from module defaults, if that's possible.
missing docblock
Would it be more consistent with the behaviour of defaults in general to not add any states or transitions if a Workflow is created with any of this information already. Feels like it would be strange getting two extra states if you already intended to create some.
Edit: also, I think this might have some implicit coverage based on the permissions test, but might be good to spin off a test to explicitly cover this.
What about a success message here as well?
Comment #7
sam152 commentedComment #8
timmillwood#6.1 - I like translating, just so when a user creates their new workflow they get the default states and transitions in their language.
#6.2 - fixed
#6.3 - fixed
#6.4 - done
#6.5 - I think we need a permissions test with the defaults in Content Moderation, then a permissions test in Workflows without the defaults. Maybe as a follow up though.
#6.6 - fixed
#6.7 - Not sure I understand?
#6.8 - done
Comment #9
sam152 commentedRe: #6.1, once the config is saved, isn't it already going to be translated back out in the right language by the config system? Wouldn't storing it in a non english language actually break translations? I might see if I can replicate this in a test, could just be thinking about the wrong.
Re: #6.7, if I go Workflow::create(['states' => [...]]), do I still get the two defaults? I would expect not to see them.
Comment #10
timmillwoodOk, I'm not really sure how translations and config works. Maybe a test would be good.
Workflow::create()will give no states, thensave()will add the default states (unless you added ones this the same machine name).Comment #11
sam152 commentedRight, my point is if you're passing in some states to be created, you probably don't want two extra ones added at the end.
Comment #12
timmillwoodBut isn't that the point of default states? We're trying to add two states in Content Moderation that we can guarantee exist and depend on.
Comment #13
sam152 commentedOkay, my misunderstanding, I assumed users would be free to remove these if they chose and it would just be an initial seed. Reread the issue summary more closely.
Comment #15
timmillwoodTrying to enforce the defaults.
I think the enforcing should be done in the implementation. So Workflows module shouldn't care if the defaults are enforced or not, but for content_moderation we need to enforce them.
This is still a work in progress, but so far:
Comment #16
sam152 commentedI did get around to testing the language stuff and the test confirmed wrapping in t() is indeed correct, my bad! Attached interdiff will be green on top of #15 if you're curious, but I think given it confirms something that we can probably take for granted based on the config system as an abstraction, it should probably be left out.
Side note: figuring out how to test this was quite tricky, but also TIL a lot about translation.
Comment #18
scott_euser commentedReviewing this now.
Comment #19
scott_euser commentedThe functionality works great and solves the issue!
I have also reviewed the code and it looks solid. Two small changes:
Comment #21
alexpottThis shouldn't be necessary. If we make the changes below.
I think we should use
hook_ENTITY_TYPE_access()so that this limited to workflows. But actually we already have the opportunity to do this in\Drupal\content_moderation\Plugin\WorkflowType\ContentModeration::checkWorkflowAccess().One of the problems here is that we can't pre-work out what state is being deleted. Maybe we could make the operation
delete-state:STATE_ID. That way we could parse the state from the operation. It might be good to get an opinion from another entity API maintainer here.This just ensures that the states and transitions exist - not that they have the expected values... think if someone makes the published state be unpublished? We have protection against this in the form but not in the API.
I'm still not sure that this is the right place to do this.
Comment #22
timmillwood@alexpott if state was a Datatype I was hoping we could use context in the access control to know which state is being deleted.
Comment #23
scott_euser commentedCould we simplify the access issues and avoid the dynamic operations like
delete-state:STATE_IDby allowing deletion of any state and simply either:Comment #24
Saphyel commentedcould you avoid to use the continue?
@alexpott I think is better fix this /issue/ first and then if someone expected or want the behavior you describe he can create another issue to fix that? I'm not saying ignore it.. just what @scott_euser said make sense to me.
Comment #25
sam152 commentedOne thing I think I mentioned on IRC but failed to add to the issue was the use of "default". All other contexts that have an interface like this imply that the config can be changed after it's installed, but that isn't the case here. These fit more inline with terminology like "enforced".
I think there is a variation on the way to do this which would make things more flexible and solve two other use cases at the same time:
The proposal would be to keep the same methods and interface but introduce a new key called "enforced" on both states and transitions. Once that key exists, all of the existing constraints in the patch are there, just for those states. It might looks something like:
That way, if I'm an install profile I can ship my config for the content moderation workflow with as many states as I like, enforce them all and then be assured they will exist in code. Similarly, a new CM plugin can come along, implement this method, omit the enforced key and then these act like other default* methods in Drupal with config that can be overriden and deleted.
Comment #26
scott_euser commentedI like that idea @Sam152, I guess the challenge we need to first overcome is preventing deletion in reaction to the operation in a robust way. I can't see how to implement
delete-state:STATE_IDthat alexpott suggests, but perhaps someone can point in the right direction as to how to make dynamic operations.Alternatively, to avoid getting too stuck on the delete aspect of this issue, we can go down the warn / deactivate route I suggested and avoid the deletion protection issue altogether?
Comment #27
scott_euser commentedAttached is what I am proposing, but if we would go down this route, it still needs work to stop the workflow from being attached to the entity if
!$workflow->status(). This uses the core ConfigEntityBase enable / disable / status methods. I imagine there is an easy way to stop the workflow from being attached, perhaps incore/modules/content_moderation/src/EntityTypeInfo.phpComment #28
sam152 commentedComment #29
sam152 commentedThe approach suggested in #27does make sense, but I don't think it meets the needs of the three scenarios mentioned in the issue summary, that is, actually having something concrete to rely on. Disabling or unpublishing a workflow doesn't mean the content that was using that workflow previously doesn't still need to be accounted for in a reasonable way.
Comment #31
alexpottHere's a approach that allows us to prevent deleting states in default states via the UI using the access system. I'm not convinced about the disabling solution because we still have to decide what happens to existing content entities that become under a workflow once it is enabled or disabled. That feels complex. I think that mandating the default states has a lot of benefits. Basically we need to ensure the content moderation can handle what the most important content entity (node) supports by default ie. unpublished and published. And there is a thought that content moderation should only work on entity types that are for entities which implement EntityPublishedInterface which makes sense to me.
Interdiff to #19.
Comment #32
alexpottI'm not sure about the use case for default transitions. It's a bit more complex and not required for many of the problems that doing this allows us to fix - ie. what to do with existing content entities when we put them under moderation.
Comment #33
timmillwood@alexpott - I quite like the solution in #31, it seems a bit odd to have an access operation per state, but it works well.
I think we need default transitions just so the default states work out of the box, but I don't think we should prevent them from being deleted like we do with states, although I guess the type plugin can do that if it wants.
Comment #34
scott_euser commentedNice one with the WorkflowDeleteAccessCheck, I really couldn't figure out how to do that myself!
If there a case where someone might want to use workflows for some purpose that might not be typical (ie, might not have a non-published state), I suppose they can implement WorkflowTypeInterface in their own way (ie, not use content_moderation).
Comment #35
alexpott@scott_euser or set the published state as the initial state and prevent transitions to the unpublished state. This is kind of how core is configured out of the box when content_moderation is not installed.
Comment #36
scott_euser commentedI've reviewed the patch. I ran into an error where after adding a 3rd state, the Published default state would also get a delete button. Adding $links = []; at the start of the for loop where the operations are added to the rows in states container solves that.
I cleaned up a couple minor phpcs complaints my editor was throwing.
Finally I added a Save and edit button; I think it is important for the UX when you reorder states or transitions that you can save and remain on the same page (similar to when editing a view and there are unsaved changes, you can remain on the same page).
Otherwise all worked well for me in testing.
Comment #37
alexpott@scott_euser nice find on links! Can you add an automated test for that? Plus the other changes might be good but they are out-of-scope here - please don't mix unrelated UI and coding standard changes in this change - just create new issues to discuss these separate things.
Comment #38
scott_euser commentedOkay sounds good, thanks for the feedback; still new to how things work, especially with core. Will try to send an updated patch tomorrow morning.
Comment #39
alexpottDiscussed with @Sam152 and @timmillwood in IRC. The mix of defaultConfiguration(), defaultStates(), defaultTransitions() in latest patch is not very nice. Going to explore using just defaultConfiguration and making the plugin responsible for all the state and transition configuration. This will also impact transition/state decoration - it might end up making everything a bit simpler :)
Comment #40
scott_euser commentedMakes sense and simplicity sounds like a good thing here.
Comment #41
alexpottHere's step 1 - moves all the state and transition configuration to be part of the workflow type config. This results in use not having to duplicate state IDs in core/modules/content_moderation/config/install/workflows.workflow.editorial.yml which is really nice. The next step of this will be to move how the classes used for decorating states and transitions to the annotation as well - and generalise the ability to get states and transitions to WorkflowTypeBase. And after that instead of having all the setStateLabel etc on Workflow just have addState, deleteState, updateState which should all just accept a state object (same for transitions). We'll end up with way less methods. These steps are necessary to solve this issue but they are implied by the change made to solve this issue because now the plugin has the complete state and transition configuration. My preference would be to do the necessary to implement required states and have followups for the next steps since they should not have data structure change at all. Just code and annotation change.
So @scott_euser there's some way to go to get things to be as simple as they can be :)
I need to add further test coverage before we're done here.
There's no interdiff because whilst some of the code is the same it is just distracting from the bulk of the change cause this is new approach.
The patch contains test coverage for the bug noticed by @scott_euser in #36.
Comment #42
alexpottHere's some kernel test coverage of the new
required_statesannotation and the population of the default configuration - separate from the implied coverage of having the content_moderation module.Comment #45
alexpottThis fixes the sorting and merging issues due to default configuration and adds more test coverage.
Comment #46
sam152 commentedWhat happens if the plugin makes changes to configuration, do they go out of sync until the entity is saved? Should the plugin at least have access to the entity, to use the setters there for making config changes? Juggling these two properties seems like it could be problematic, but I could be missing something. As an alternative could we:
The way these two things pass config between each other is the only thing that really has me scratching my head, rest looks great.
Comment #47
alexpottThe plugin should never have access to the configuration entity - that'd create a circular dependency. The config entity should configure the plugin and that's it. This code exists because we do this in ConfigEntityBase...
Which ensures that the latest config is got from the plugin. What we probably want to do is to move all the workflow methods to the plugin. That way the config entity just becomes about storage and the Workflow plugin is the state machine. Basically we should do what blocks do.
Comment #48
sam152 commentedHaving had this spelled out for me on IRC, this totally makes sense. There is a bit of follow up to ensure that plugins can define and manage the structure of the "settings" key on the workflow object and so long as WorkflowTypeInterface is satisfied, each plugin can essentially do what it wants.
Follow-ups ahoy, but this seems like a good stepping stone to get in now.
Comment #49
alexpottThanks for the rtbc @Sam152 - just need to clean up this.
We need to fix the docs here and point to the follow up that should get rid of these.
Comment #50
sam152 commentedOpened the followup as I understand it here #2849827: Move workflow "settings" setters and getters to WorkflowTypeInterface, updated docs.
Comment #51
scott_euser commentedI've gone through this and tested as a user as thoroughly as I could creating various states and transitions and generating contents in various orders to try to get stuck.
At first, with own user error, I got stuck in a Needs Review state I had created and was unable to move from there into another state until I realised I needed to update the default Create New Draft and Publish states to allow transition from my new Needs Review state. That helped me realise the power of this and how customised a workflow you could get; nice!
I further tested the permissions as an editor / author / reviewer and that seems solid as well, no issues.
Comment #52
alexpottIf we're going to put this in we should obey docs standards...
Comment #53
alexpottI'm not sure about this behaviour. It makes sense to not delete required states but the transitions aren't required. The thing is
\Drupal\Component\Plugin\ConfigurablePluginInterface::defaultConfiguration()is not the same initial configuration. And the behaviour of allowing the meanings of the transitions to be changed but not deleted is super weird. Also this fix leads to the very strange behaviour that you can try to delete the "Create New Draft" transition via UI - it looks like it works but then it does not.In the new patch it makes lots of sense that the required states are part of the annotation. But I guess using ::defaultConfiguration() is a wrong move - oops. This is such a tricky issue.
I'm going to merge #36 and with the
require_statesannotation and improved test coverage.Comment #54
alexpottHere's an attempt to add the necessary to do the required states. It does not suffer from the problems of deleting the transition described in #53.
I still think that in an ideal world we'd do #2849827: Move workflow "settings" setters and getters to WorkflowTypeInterface so that WorkflowType's don't have any knowledge of the Workflow configuration entity. This would make the system more like Blocks and allow full programmatic creation of workflows without config entities that might be very useful for things like commerce. But this is a step in the right direction. Being able to guarantee the existence on draft and published for content moderation workflows means that we can start to solve the problem of what happens when installing content_moderation on existing sites. Or adding a workflow to a bundle with existing content.
Not entirely what sure to interdiff too since the patch is a chimera of #52 and #36 - so no interdiff proved.
Comment #55
sam152 commentedCouldn't fault this, mostly nits and adding links to follow ups.
I wonder if 2849827 should be added as a @todo to indicate $workflow should go away.
There is a functional test for the UI this plugin provides, would be good to add these.
Note to create a follow up.
Nit, full stop.
trailing comma
Tailing comma.
trailing comma
trailing comma
Does the comment here mismatch the behavior?
missing newline
Comment #56
alexpottThanks @Sam152
Comment #58
alexpott\Drupal\Tests\content_moderation\Kernel\ContentModerationPermissionsTest() needs to use real config - missing weight info on the states.
Comment #59
sam152 commentedLooks good +1 RTBC
Comment #60
timmillwoodOn my first read through I kinda expected this to be a state string not a boolean. Maybe it'd be useful to have a isStateRequired($state); method on the ContentModeration plugin?
Can they be changed programatically?
Wondering if this should be $default_states rather than required? Are we enforcing at a Workflows level they are required?
ah yes! looks like we are. I'm not sure we should, I think we should allow the flexibility of default states, but it's up to the implementation to decide if they are required or not.
Comment #61
alexpott1. We could add this I guess - although it just adds complexity to State construction.
2. Yes they could - we could validate this in setConfiguration I guess - but this is always a question of how far should we go in preventing odd configuration. Through the UI this makes total sense. Through the API we could argue that whatever has allowed that is the place that should be fixed. Not sure.
3 & 4. required states are exactly that - states the workflow requires. Hence this should be enforced at the workflow level. They are not default states. If you want to add default states then you need to use the initializeWorkflow() method - the states and transitions added are not required.
Comment #62
alexpottRe #60.1 we can improve the variable name of course.
Comment #63
timmillwoodok, then, looks good to me.
Comment #64
scott_euser commentedJust a couple things I noticed:
1) On the states edit form, when there are no transitions yet, it says there are no states yet. Perhaps like the attached so it's clear that the list of transitions are the ones that involve the selected state (as the table headings don't indicate that).
2) Is it necessary to always disable the 'to' on the transition edit form given that they are deletable? Is there some undesired behaviour if it were to be changed that needs the user to delete first and recreate it?
Comment #65
alexpottHey @scott_euser you've found yet another bug! Thanks. But it is out of scope for this issue. We need a new issue to fix that. See https://www.drupal.org/core/scope for guidelines and examples for Drupal core issue scope. I'm going to upload the most recent in-scope patch again so the most recent patch on the issue is the correct one. @scott_euser if you file another issue I'm sure it'll get reviewed and committed quickly! Also feel free to reach out to me on IRC or Drupal slack if you've got questions about this.
Comment #66
scott_euser commentedAh okay, I thought that was directly related to what you were working on, sorry! I'll learn soon enough :) (and gave that a re-read).
https://www.drupal.org/node/2850546 and https://www.drupal.org/node/2850549 opened.
Switching back to RTBC.
Comment #67
sam152 commentedComment #68
catchGenerally this looks good, couple of questions though, first might be off topic here but feels conceptually important.
Setting published false when it's a forward revision of a published node is a bit tricky. For example with CPE when you preview a changeset/workspace, you'll see all the 'published' revisions connected to that workspace (and anything published that's not in the workspace).
This includes content that's never been published yet too - if you want to preview new content in a changeset/workspace, the workspace revision would need to be published to (the default revision stays unpublished).
Is this the only place we do this?
First sentence doesn't scan for me, should it just drop the 'the'?
Comment #69
timmillwood#68.1 - I think that is a seperate issue, and something I'm kinda already hitting when building Workspace 2.x in a similar way to CPS. My current implementation actually prevents Workspace and Content Moderation to both try and manage a Node (or similar supported entity).
#68.2 - Currently, yes.
#68.3 - hrm... yes, maybe it should be
The value of '_workflow_state_delete_access' is ignored. The route must?Leaving as RTBC because if 1 is a follow up, and 2 is a non-issue, then maybe 3 can be fixed at commit?
Comment #70
catchCommitted/pushed to 8.4.x and cherry-picked to 8.3.x. Thanks!
Comment #73
cilefen commented