Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
W
workflow
Manage
Activity
Members
Labels
Plan
Wiki
Custom issue tracker
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Model registry
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
project
workflow
Commits
ad43249a
Commit
ad43249a
authored
1 month ago
by
John Voskuilen
Browse files
Options
Downloads
Patches
Plain Diff
Issue
#3473989
: Prepare fixing pre hook - factor out code
parent
077f3635
Branches
Branches containing commit
Tags
Tags containing commit
No related merge requests found
Pipeline
#520142
passed with warnings
1 month ago
Stage: build
Stage: validate
Changes
1
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
src/Entity/WorkflowTransition.php
+125
-110
125 additions, 110 deletions
src/Entity/WorkflowTransition.php
with
125 additions
and
110 deletions
src/Entity/WorkflowTransition.php
+
125
−
110
View file @
ad43249a
...
...
@@ -266,54 +266,12 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
* CRUD functions.
*/
/**
/**
* {@inheritdoc}
*/
public
function
execute
(
$force
=
FALSE
)
{
// Load the entity, if not already loaded.
// This also sets the (empty) $revision_id in Scheduled Transitions.
$entity
=
$this
->
getTargetEntity
();
$user
=
$this
->
getOwner
();
$from_sid
=
$this
->
getFromSid
();
$to_sid
=
$this
->
getToSid
();
$field_name
=
$this
->
getFieldName
();
$comment
=
$this
->
getComment
();
static
$static_info
=
NULL
;
$entity_id
=
$entity
->
id
();
// For non-default revisions, there is no way of executing the same transition twice in one call.
// Set a random identifier since we won't be needing to access this variable later.
if
(
$entity
instanceof
RevisionableInterface
)
{
/** @var \Drupal\Core\Entity\RevisionableInterface $entity */
if
(
!
$entity
->
isDefaultRevision
())
{
$entity_id
.
=
$entity
->
getRevisionId
();
}
}
// Create a label to identify this transition,
// even upon insert, when id() is not set, yet.
$label
=
"
{
$from_sid
}
-
{
$to_sid
}
"
;
if
(
isset
(
$static_info
[
$entity_id
][
$field_name
][
$label
])
&&
!
$this
->
isEmpty
())
{
// Error: this Transition is already executed.
// On the development machine, execute() is called twice, when
// on an Edit Page, the entity has a scheduled transition, and
// user changes it to 'immediately'.
// Why does this happen?? ( BTW. This happens with every submit.)
// Remedies:
// - search root cause of second call.
// - try adapting code of transition->save() to avoid second record.
// - avoid executing twice.
$message
=
'Transition is executed twice in a call. The second call for
@entity_type %entity_id is not executed.'
;
$this
->
logError
(
$message
);
// Return the result of the last call.
return
$static_info
[
$entity_id
][
$field_name
][
$label
];
// <-- exit !!!
}
// OK. Prepare for next round. Do not set last_sid!!
$static_info
[
$entity_id
][
$field_name
][
$label
]
=
$from_sid
;
// Make sure $force is set in the transition, too.
if
(
$force
)
{
...
...
@@ -321,67 +279,23 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
}
$force
=
$this
->
isForced
();
// Store the transition(s), so it can be easily fetched later on.
// This is a.o. used in:
// - hook_entity_update to trigger 'transition post',
// - hook workflow_access_node_access_records.
$entity
->
workflow_transitions
[
$field_name
]
=
$this
;
if
(
!
$this
->
isValid
())
{
return
$from_sid
;
// <-- exit !!!
return
$from_sid
;
}
// @todo Move below code to $this->isAllowed().
// If the state has changed, check the permissions.
// No need to check if Comments or attached fields are filled.
if
(
$this
->
hasStateChange
())
{
// Determine if user is owner of the entity. If so, add role.
$is_owner
=
WorkflowRole
::
isOwner
(
$user
,
$entity
);
if
(
$is_owner
)
{
$user
->
addRole
(
WorkflowRole
::
AUTHOR_RID
);
}
if
(
!
$this
->
isAllowed
(
$user
,
$force
))
{
$message
=
'User %user not allowed to go from state %sid1 to %sid2'
;
$this
->
logError
(
$message
);
return
FALSE
;
// <-- exit !!!
}
// Make sure this transition is valid and allowed for the current user.
// Invoke a callback indicating a transition is about to occur.
// Modules may veto the transition by returning FALSE.
// (Even if $force is TRUE, but they shouldn't do that.)
// P.S. The D7 hook_workflow 'transition permitted' is removed,
// in favour of below hook_workflow 'transition pre'.
$permitted
=
\Drupal
::
moduleHandler
()
->
invokeAll
(
'workflow'
,
[
'transition pre'
,
$this
,
$user
]);
// Stop if a module says so.
if
(
in_array
(
FALSE
,
$permitted
,
TRUE
))
{
// @todo There is a watchdog error, but no UI-error. Is this OK?
$message
=
'Transition vetoed by module.'
;
$this
->
logError
(
$message
,
'notice'
);
return
FALSE
;
// <-- exit !!!
}
$isExecutedAlready
=
$this
->
isExecutedAlready
();
if
(
$isExecutedAlready
)
{
return
$to_sid
;
}
/*
* Output: process the transition.
*/
if
(
!
$this
->
isScheduled
())
{
// The transition is allowed and must be executed now.
// Let other modules modify the comment.
// The transition (in $context) contains all relevant data.
$context
=
[
'transition'
=>
$this
];
\Drupal
::
moduleHandler
()
->
alter
(
'workflow_comment'
,
$comment
,
$context
);
$this
->
setComment
(
$comment
);
$this
->
isExecuted
=
TRUE
;
}
$this
->
alterComment
();
// Save the transition in {workflow_transition_history} or
// Save the transition in {workflow_transition_scheduled}.
$this
->
save
();
// Save value in static from top of this function.
$static_info
[
$entity_id
][
$field_name
][
$label
]
=
$to_sid
;
return
$to_sid
;
}
...
...
@@ -415,19 +329,70 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
// We create a new transition, or update an existing one.
// Do not update the entity itself.
// Validate transition, save in history table and delete from schedule table.
=>
$this
->
execute
(
$force
),
=>
$this
->
force
(
$force
)
->
execute
(),
default
// Update targetEntity's itemList with the workflow field in two formats.
=>
$this
->
setEntityWorkflowField
()
->
setEntityChangedTime
()
// Save the TargetEntity.
->
getTargetEntity
()
->
save
()
?
$to_sid
:
$from_sid
,
};
return
$sid
;
}
/**
* {@inheritdoc}
*/
public
function
isExecutedAlready
()
{
if
(
$this
->
isEmpty
())
{
return
FALSE
;
}
$entity
=
$this
->
getTargetEntity
();
$field_name
=
$this
->
getFieldName
();
$from_sid
=
$this
->
getFromSid
();
$to_sid
=
$this
->
getToSid
();
static
$static_info
=
NULL
;
$id
=
$entity
->
id
()
??
0
;
// For non-default revisions, there is no way of executing the same transition twice in one call.
// Set a random identifier since we won't be needing to access this variable later.
$vid
=
0
;
if
(
$entity
instanceof
RevisionableInterface
)
{
/** @var \Drupal\Core\Entity\RevisionableInterface $entity */
if
(
!
$entity
->
isDefaultRevision
())
{
$vid
=
$entity
->
getRevisionId
();
}
}
if
(
isset
(
$static_info
[
$id
][
$vid
][
$field_name
][
$from_sid
][
$to_sid
]))
{
// Error: this Transition is already executed.
// On the development machine, execute() is called twice, when
// on an Edit Page, the entity has a scheduled transition, and
// user changes it to 'immediately'.
// Why does this happen?? ( BTW. This happens with every submit.)
// Remedies:
// - search root cause of second call.
// - try adapting code of transition->save() to avoid second record.
// - avoid executing twice.
$message
=
'Transition is executed twice in a call. The second call for
@entity_type %entity_id is not executed.'
;
$this
->
logError
(
$message
);
// Return the result of the last call.
return
$static_info
[
$id
][
$vid
][
$field_name
][
$from_sid
][
$to_sid
];
// <-- exit !!!
}
// OK. Prepare for next round.
$static_info
[
$id
][
$vid
][
$field_name
][
$from_sid
][
$to_sid
]
=
TRUE
;
return
FALSE
;
}
/**
* {@inheritdoc}
*
...
...
@@ -435,6 +400,9 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
* @todo Also update entity with additional fields.
*/
public
function
setEntityWorkflowField
():
WorkflowTransitionInterface
{
if
(
$this
->
isScheduled
())
{
return
$this
;
}
$entity
=
$this
->
getTargetEntity
();
$field_name
=
$this
->
getFieldName
();
...
...
@@ -537,10 +505,10 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
break
;
case
$this
->
id
()
:
// Update the transition. It already exists.
WorkflowEntityHooks
::
deleteTransitionsOfEntity
(
$entity
,
'workflow_scheduled_transition'
,
$field_name
);
$result
=
parent
::
save
();
break
;
// Update the transition. It already exists.
WorkflowEntityHooks
::
deleteTransitionsOfEntity
(
$entity
,
'workflow_scheduled_transition'
,
$field_name
);
$result
=
parent
::
save
();
break
;
default
:
// Insert the executed transition, unless it has already been inserted.
...
...
@@ -652,35 +620,82 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
*/
/**
* Determines if the Transition is valid and can be executed.
* Invokes 'hook_workflow_comment'.
*/
public
function
alterComment
()
{
if
(
$this
->
isScheduled
())
{
return
$this
;
}
// The transition is allowed and must be executed now.
// Let other modules modify the comment.
$comment
=
$this
->
getComment
();
// The transition (in $context) contains all relevant data.
$context
=
[
'transition'
=>
$this
];
\Drupal
::
moduleHandler
()
->
alter
(
'workflow_comment'
,
$comment
,
$context
);
$this
->
setComment
(
$comment
);
return
$this
;
}
/**
* {@inheritdoc}
*
* @todo Add to isAllowed() ?
* @todo Add checks to WorkflowTransitionElement ?
*
* @return bool
* TRUE is the Transition is OK, else FALSE.
*/
public
function
isValid
()
{
$valid
=
TRUE
;
// Load the entity, if not already loaded.
// This also sets the (empty) $revision_id in Scheduled Transitions.
$entity
=
$this
->
getTargetEntity
();
$user
=
$this
->
getOwner
();
$force
=
$this
->
isForced
();
if
(
!
$entity
)
{
// @todo There is a watchdog error, but no UI-error. Is this OK?
$message
=
'User tried to execute a Transition without an entity.'
;
$this
->
logError
(
$message
);
$valid
=
FALSE
;
return
FALSE
;
}
elseif
(
!
$this
->
getFieldName
())
{
// else
if (!$this->getF
romSid
()) {
// @todo The page is not correctly refreshed after this error.
$message
=
$this
->
t
(
'
You tried to set a Workflow State, but
the entity is not relevant
. Please contact your system administrator.'
);
if
(
!
$this
->
getF
ieldName
())
{
// @todo The page is not correctly refreshed after this error.
$message
=
$this
->
t
(
'
The entity is not relevant for setting
a Workflow State
. Please contact your system administrator.'
);
$this
->
messenger
()
->
addError
(
$message
);
$message
=
'Setting a non-relevant Entity from state %sid1 to %sid2'
;
$this
->
logError
(
$message
);
$valid
=
FALSE
;
return
FALSE
;
}
// @todo Move below code to $this->isAllowed().
// If the state has changed, check the permissions.
// No need to check if Comments or attached fields are filled.
if
(
$this
->
hasStateChange
())
{
if
(
!
$this
->
isAllowed
(
$user
,
$force
))
{
$message
=
'User %user not allowed to go from state %sid1 to %sid2'
;
$this
->
logError
(
$message
);
return
FALSE
;
// <-- exit !!!
}
}
if
(
$this
->
hasStateChange
())
{
// Make sure this transition is valid and allowed for the current user.
// Invoke a callback indicating a transition is about to occur.
// Modules may veto the transition by returning FALSE.
// (Even if $force is TRUE, but they shouldn't do that.)
// P.S. The D7 hook_workflow 'transition permitted' is removed,
// in favour of below hook_workflow 'transition pre'.
$permitted
=
\Drupal
::
moduleHandler
()
->
invokeAll
(
'workflow'
,
[
'transition pre'
,
$this
,
$user
]);
// Stop if a module says so.
if
(
in_array
(
FALSE
,
$permitted
,
TRUE
))
{
// @todo There is a watchdog error, but no UI-error. Is this OK?
$message
=
'Transition vetoed by module.'
;
$this
->
logError
(
$message
,
'notice'
);
return
FALSE
;
// <-- exit !!!
}
}
return
$valid
;
...
...
@@ -1336,8 +1351,8 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition
$output
[]
=
"From/To =
{
$transition
->
getFromState
()
}
>
{
$transition
->
getToState
()
}
"
;
$output
[]
=
"Comment =
{
$user_name
}
says:
{
$transition
->
getComment
()
}
"
;
$output
[]
=
"Forced = "
.
(
$transition
->
isForced
()
?
'yes'
:
'no'
)
.
"; Scheduled = "
.
(
$transition
->
isScheduled
()
?
'yes'
:
'no'
)
.
"; Executed = "
.
(
$transition
->
isExecuted
()
?
'yes'
:
'no'
);
.
"; Scheduled = "
.
(
$transition
->
isScheduled
()
?
'yes'
:
'no'
)
.
"; Executed = "
.
(
$transition
->
isExecuted
()
?
'yes'
:
'no'
);
foreach
(
$this
->
getAttachedFieldDefinitions
()
as
$field_name
=>
$field
)
{
$value
=
(
string
)
(
$this
->
{
$field_name
}
?->
value
??
'value not found (for scheduled transition?)'
);
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment