You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Related, but independent: #455 (Studio applies transitions to the published element instead of the latest draft).
Summary
With the default state_table marking store, a transition configured with changePublishedState: save_version writes the new place to element_workflow_state immediately, while the element's content is only saved as a draft (version). The place is therefore committed although the content is not: the published element already reports the new place, and discarding the draft deletes the version but leaves the state row untouched, so the element stays in the new place.
The attribute based marking stores (single_state, multiple_state, data_object_multiple_state, data_object_splitted_state) keep the place in a field of the element, so for them the place is part of the draft and discarding the draft reverts it. The two store families behave differently for the same transition configuration.
Steps to reproduce
Workflow config (applies to every data object class, default state_table store):
Open a published data object and apply Submit for review. The transition saves a draft version, the element is not published.
Open the object again: the workflow shows the place review. The published object (frontend, element_workflow_state, workflow reports) also reports review already.
Discard the draft.
Actual: the draft's content changes are gone, but the element stays in place review.
Expected: the element returns to place draft, i.e. the place change is discarded together with the draft. This is what happens with single_state, where the place lives in a field of the element.
Compare with marking_store: { type: single_state, arguments: [workflowState] } on a class with a workflowState select field: same steps, the place reverts on discard.
Where it happens
lib/Workflow/MarkingStore/StateTableMarkingStore.php, setMarking(): writes the WorkflowState row right away, inside Workflow::apply(), independent of how the subject is saved afterwards.
lib/Workflow/Manager.php, applyWithAdditionalData(): for save_version transitions calls saveVersion() on the subject after apply(). Nothing connects the state row to that version.
The admin UIs' discard-draft action ends in Version::delete(), which only removes the version and dispatches the version events. Nothing knows the workflow place needs reverting, and the previous place was never recorded anywhere.
Make state_table follow the transition's declared persistence for save_version transitions, so it behaves like the attribute based stores:
New Pimcore\Workflow\MarkingStore\PendingMarkingStoreInterface: a marking store that persists independently of the subject can keep a marking pending on the subject when told that the subject is only going to be saved as a version. StateTableMarkingStore implements it.
Manager::applyWithAdditionalData() passes a CONTEXT_SAVE_VERSION context flag into apply() when the subject is saved by the manager and the transition uses save_version. Every other transition type, global actions, and direct $workflow->apply() calls keep persisting the place immediately.
AbstractElement carries the pending markings (@internal accessors). The property is part of version dumps, so a draft carries it and discarding the draft drops it; it is excluded from the cache.
WorkflowManagementListener persists pending markings on the element's post-update / post-add events, skipping version-only saves. Publishing a draft is a full save, so that is when the place reaches element_workflow_state.
Docs: marking store docs, changePublishedState config comment, upgrade note.
Consumers reading element_workflow_state directly (workflow reports, the search index workflow filter, Studio's "elements in place" listing) then see the state of the published element while such a draft exists, consistent with the attribute based stores.
Notes
No schema change or migration. Drafts saved before the fix have no pending marking and keep behaving as before; only drafts created after the fix carry the place with them.
Affected capability
Core
Feature description
Affected packages
pimcore/pimcore(2026.x, reproduces on the current maintenance branches as well)Originally reported as pimcore/pimcore#18653. Fix proposed in pimcore/pimcore#19434.
Related, but independent: #455 (Studio applies transitions to the published element instead of the latest draft).
Summary
With the default
state_tablemarking store, a transition configured withchangePublishedState: save_versionwrites the new place toelement_workflow_stateimmediately, while the element's content is only saved as a draft (version). The place is therefore committed although the content is not: the published element already reports the new place, and discarding the draft deletes the version but leaves the state row untouched, so the element stays in the new place.The attribute based marking stores (
single_state,multiple_state,data_object_multiple_state,data_object_splitted_state) keep the place in a field of the element, so for them the place is part of the draft and discarding the draft reverts it. The two store families behave differently for the same transition configuration.Steps to reproduce
Workflow config (applies to every data object class, default
state_tablestore):
yaml pimcore: workflows: review: supports: [Pimcore\Model\DataObject\Concrete] places: draft: {} review: {} transitions: submit: from: draft to: review options: label: Submit for review changePublishedState: save_version review. The published object (frontend,element_workflow_state, workflow reports) also reportsreviewalready.Actual: the draft's content changes are gone, but the element stays in place
review.Expected: the element returns to place
draft, i.e. the place change is discarded together with the draft. This is what happens withsingle_state, where the place lives in a field of the element.Compare with
marking_store: { type: single_state, arguments: [workflowState] }on a class with aworkflowStateselect field: same steps, the place reverts on discard.Where it happens
lib/Workflow/MarkingStore/StateTableMarkingStore.php,setMarking(): writes theWorkflowStaterow right away, insideWorkflow::apply(), independent of how the subject is saved afterwards.lib/Workflow/Manager.php,applyWithAdditionalData(): forsave_versiontransitions callssaveVersion()on the subject afterapply(). Nothing connects the state row to that version.Version::delete(), which only removes the version and dispatches the version events. Nothing knows the workflow place needs reverting, and the previous place was never recorded anywhere.Proposed fix (implemented in pimcore/pimcore#19434)
Make
state_tablefollow the transition's declared persistence forsave_versiontransitions, so it behaves like the attribute based stores:Pimcore\Workflow\MarkingStore\PendingMarkingStoreInterface: a marking store that persists independently of the subject can keep a marking pending on the subject when told that the subject is only going to be saved as a version.StateTableMarkingStoreimplements it.Manager::applyWithAdditionalData()passes aCONTEXT_SAVE_VERSIONcontext flag intoapply()when the subject is saved by the manager and the transition usessave_version. Every other transition type, global actions, and direct$workflow->apply()calls keep persisting the place immediately.AbstractElementcarries the pending markings (@internalaccessors). The property is part of version dumps, so a draft carries it and discarding the draft drops it; it is excluded from the cache.WorkflowManagementListenerpersists pending markings on the element's post-update / post-add events, skipping version-only saves. Publishing a draft is a full save, so that is when the place reacheselement_workflow_state.changePublishedStateconfig comment, upgrade note.Consumers reading
element_workflow_statedirectly (workflow reports, the search index workflow filter, Studio's "elements in place" listing) then see the state of the published element while such a draft exists, consistent with the attribute based stores.Notes
unsavedChangesBehaviouris ignored #455 is fixed, because Studio currently applies transitions to the published element instead of the latest draft.