diff --git a/src/Element/WorkflowTransitionElement.php b/src/Element/WorkflowTransitionElement.php index b3fc2207c5252807201ad3d080b51e0567398367..ba4a73129357f01ed361d4bf122334e09d165c4b 100644 --- a/src/Element/WorkflowTransitionElement.php +++ b/src/Element/WorkflowTransitionElement.php @@ -6,7 +6,6 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element\FormElement; use Drupal\workflow\Entity\Workflow; -use Drupal\workflow\Entity\WorkflowTransitionInterface; /** * Provides a form element for the WorkflowTransitionForm and ~Widget. @@ -346,28 +345,23 @@ class WorkflowTransitionElement extends FormElement { // @todo #2287057: verify if submit() really is only used for UI. If not, $user must be passed. $user = workflow_current_user(); + // Read value from form input, else widget values. + // May return NULL's upon hitting a 3rd party button (e.g., file upload) + $action_values = _workflow_transition_form_get_triggering_button($transition, $form_state, $values); + $field_name = $action_values['field_name']; + $to_sid = $action_values['to_sid']; + // Get user input from element. - $field_name = $transition->getFieldName(); $uid = $user->id(); $force = FALSE; - // Read value from form input, else widget values. - $action_values = _workflow_transition_form_get_triggering_button($form_state); - $to_sid = $action_values['to_sid'] ?? $values['to_sid'][0]['target_id']; // Note: when editing existing Transition, user may still change comments. $comment = $values['comment'][0]['value'] ?? ''; // @todo Why is 'timestamp' empty at create Node - when is it unset? - $timestamp_values = $values['timestamp'][0]['value'] ?? ['scheduled' => FALSE]; + $timestamp_values = $values['timestamp'][0]['value'] ?? ['scheduled' => false]; $is_scheduled = (bool) $timestamp_values['scheduled']; $timestamp = WorkflowTransitionTimestamp::valueCallback($timestamp_values, $timestamp_values, $form_state); - if (!isset($to_sid)) { - $entity_id = $transition->getTargetEntityId(); - \Drupal::messenger()->addError(t('Error: content @id has no workflow attached. The data is not saved.', ['@id' => $entity_id])); - // The new state is still the previous state. - return $transition; - } - // @todo D8: add below exception. // Extract the data from $values, depending on the type of widget. // @todo D8: use massageFormValues($values, $form, $form_state). @@ -400,7 +394,7 @@ class WorkflowTransitionElement extends FormElement { $transition = self::copyAttachedFields($transition, $form, $form_state, $values); // Update targetEntity's itemList with the workflow field in two formats. - $transition->updateEntity(); // jvo + $transition->updateEntityWorkflowField(); // Update form_state, so core can update entity as well. $to_sid = $transition->getToSid(); diff --git a/src/Entity/WorkflowManager.php b/src/Entity/WorkflowManager.php index 06e597a9f945eb6b5584017339452dc964c92de4..f487a44d9c29911074c500b4cca01aaa432ddcb3 100644 --- a/src/Entity/WorkflowManager.php +++ b/src/Entity/WorkflowManager.php @@ -155,7 +155,7 @@ class WorkflowManager implements WorkflowManagerInterface { foreach ($field_names as $field_name) { /** @var \Drupal\workflow\Entity\WorkflowTransitionInterface $transition */ // @todo Transition is created in widget or WorkflowTransitionForm. - $transition = $entity->{$field_name}->__get('_workflow_transition') ?? NULL; + $transition = $entity->{$field_name}[0]->getTransition(); if (!$transition) { // We come from creating/editing an entity via entity_form, with core widget or hidden Workflow widget. // @todo D8: From an Edit form with hidden widget. @@ -203,6 +203,7 @@ class WorkflowManager implements WorkflowManagerInterface { } else { // Execute and check the result. + $transition->updateEntityChangedTime(); $new_sid = $transition->execute(); $executed = ($new_sid == $transition->getToSid()) ? TRUE : FALSE; } @@ -302,9 +303,9 @@ class WorkflowManager implements WorkflowManagerInterface { return $sid; } - // Entity is new or in preview or there is no current state. Use previous state. + // Use previous state if Entity is new/in preview/without current state. // (E.g., content was created before adding workflow.) - if (!$sid || !empty($entity->isNew()) || !empty($entity->in_preview)) { + if ((!$sid) || $entity->isNew() || (!empty($entity->in_preview))) { $sid = self::getPreviousStateId($entity, $field_name); } diff --git a/src/Entity/WorkflowTransition.php b/src/Entity/WorkflowTransition.php index 2f7ff705c12fb83a524c0ca5a926afc455c0fd84..642d530798562999ce5fa6b0eecc1361e2409afe 100644 --- a/src/Entity/WorkflowTransition.php +++ b/src/Entity/WorkflowTransition.php @@ -647,7 +647,8 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition $do_update_entity = (!$this->isScheduled() && !$this->isExecuted()); if ($do_update_entity) { // Update targetEntity's itemList with the workflow field in two formats. - $this->updateEntity(); + $this->updateEntityWorkflowField(); + $this->updateEntityChangedTime(); $entity->save(); } elseif ($this->isScheduled()) { @@ -667,18 +668,11 @@ class WorkflowTransition extends ContentEntityBase implements WorkflowTransition /** * Update the workflow field of the entity in two formats. */ - public function updateEntity() { + public function updateEntityWorkflowField() { $entity = $this->getTargetEntity(); $field_name = $this->getFieldName(); - $to_sid = $this->getToSid(); - // N.B. Align the following functions: - // - WorkflowDefaultWidget::massageFormValues(); - // - WorkflowManager::executeTransition(). - $items = $entity->{$field_name}; - $items->setValue($to_sid); - $items->__set('_workflow_transition', $this); - - $this->updateEntityChangedTime(); + // Set the Transition to the field. This also sets the value to State ID. + $entity->{$field_name}->setValue($this); } /** diff --git a/src/Entity/WorkflowTransitionInterface.php b/src/Entity/WorkflowTransitionInterface.php index 7a2c08237f0c38b5d6db96123d8ce5c819e6a266..70d3dfc95f561e82e85399df464ff179a10ea457 100644 --- a/src/Entity/WorkflowTransitionInterface.php +++ b/src/Entity/WorkflowTransitionInterface.php @@ -285,7 +285,7 @@ interface WorkflowTransitionInterface extends WorkflowConfigTransitionInterface, /** * Updates the entity's workflow field with value and transition. */ - public function updateEntity(); + public function updateEntityWorkflowField(); /** * Returns if this is an Executed Transition. diff --git a/src/Form/WorkflowTransitionForm.php b/src/Form/WorkflowTransitionForm.php index 7b76b2cfb3bafa03e4be674c1c68a828eb47d849..6d417da36fbd4632bfac1f9a00f41829feb3c2a1 100644 --- a/src/Form/WorkflowTransitionForm.php +++ b/src/Form/WorkflowTransitionForm.php @@ -90,7 +90,7 @@ class WorkflowTransitionForm extends ContentEntityForm { $entity_id = $entity->id(); // Only 1 scheduled transition can be found, but multiple executed ones. - $transition ??= ($entity->{$field_name}->__get('_workflow_transition') ); + $transition ??= $entity->{$field_name}[0]->getTransition(); $transition ??= WorkflowScheduledTransition::loadByProperties($entity_type_id, $entity_id, [], $field_name); if (!$transition) { // Create a transition, to pass to the form. No need to use setValues(). diff --git a/src/Plugin/Action/WorkflowStateActionBase.php b/src/Plugin/Action/WorkflowStateActionBase.php index 6dd5c64bde89d49fa3dc09c53bcf0f3eb050aad9..aea7cf233e1906cb0fd985547e667424d75c0b5a 100644 --- a/src/Plugin/Action/WorkflowStateActionBase.php +++ b/src/Plugin/Action/WorkflowStateActionBase.php @@ -172,7 +172,7 @@ abstract class WorkflowStateActionBase extends ConfigurableActionBase implements $transition->force($config['force']); $transition->setTargetEntity($entity); // Update targetEntity's itemList with the workflow field in two formats. - $transition->updateEntity(); + $transition->updateEntityWorkflowField(); $entity->setOwnerId($transition->getOwnerId()); // Add the WorkflowTransitionForm element to the page. diff --git a/src/Plugin/Field/FieldType/WorkflowItem.php b/src/Plugin/Field/FieldType/WorkflowItem.php index c3b5304279cb4567513c7855f7994a2b6274d181..f4cd03f4c25903aa7724823a2ba61b183b76011c 100644 --- a/src/Plugin/Field/FieldType/WorkflowItem.php +++ b/src/Plugin/Field/FieldType/WorkflowItem.php @@ -12,6 +12,7 @@ use Drupal\Core\Url; use Drupal\options\Plugin\Field\FieldType\ListItemBase; use Drupal\workflow\Entity\Workflow; use Drupal\workflow\Entity\WorkflowState; +use Drupal\workflow\Entity\WorkflowTransitionInterface; /** * Plugin implementation of the 'workflow' field type. @@ -56,15 +57,15 @@ class WorkflowItem extends ListItemBase { ->setLabel(t('Workflow state')) ->addConstraint('Length', ['max' => 128]) ->setRequired(TRUE); - /* - $propertyDefinitions[$key]['workflow_transition'] = DataDefinition::create('any') - // $properties['workflow_transition'] = DataDefinition::create('WorkflowTransition') - ->setLabel(t('Transition')) - ->setDescription(t('The computed WorkflowItem object.')) - ->setComputed(TRUE) - ->setClass('\Drupal\workflow\Entity\WorkflowTransition'); - // ->setSetting('date source', 'value'); - */ + + $propertyDefinitions[$key]['_workflow_transition'] = DataDefinition::create('any') + // = DataDefinition::create('WorkflowTransition') + ->setLabel(t('Transition')) + ->setDescription(t('The WorkflowTransition setting the Workflow state.')) + ->setComputed(TRUE) + // ->setClass('\Drupal\workflow\Entity\WorkflowTransition') + // ->setSetting('date source', 'value') + ; } return $propertyDefinitions[$key]; } @@ -223,6 +224,37 @@ class WorkflowItem extends ListItemBase { return $is_empty; } + /** + * {@inheritdoc} + * + * Set both the Transition property AND the to_sid value. + */ + public function setValue($values, $notify = TRUE) { + if ($values instanceof WorkflowTransitionInterface) { + /** @var \Drupal\workflow\Entity\WorkflowTransitionInterface $values */ + $to_sid = $values->getToSid(); + $keys = array_keys($this->definition->getPropertyDefinitions()); + $values = [ + $keys[0] => $to_sid, // 'value'. + $keys[1] => $values, // '_workflow_transition'. + ]; + } + parent::setValue($values, $notify); + } + + /** + * Gets the '_workflow_transition' property. + * + * @return \Drupal\workflow\Entity\WorkflowTransitionInterface + * The Transition object, or NULL of not set. + */ + public function getTransition() { + // Implements $transition = $entity->{$field_name}->__get('_workflow_transition') ?? NULL; + /** @var \Drupal\workflow\Entity\WorkflowTransitionInterface $transition */ + $property = $this->get('_workflow_transition'); + return $property->getValue(); + } + /** * {@inheritdoc} */ diff --git a/src/Plugin/Field/FieldWidget/WorkflowDefaultWidget.php b/src/Plugin/Field/FieldWidget/WorkflowDefaultWidget.php index 022cc50e97844febe0af1351b6d1cadfaf0b8c0a..780f3012d85583862b20458124999c7ae23c8ce7 100644 --- a/src/Plugin/Field/FieldWidget/WorkflowDefaultWidget.php +++ b/src/Plugin/Field/FieldWidget/WorkflowDefaultWidget.php @@ -130,17 +130,9 @@ class WorkflowDefaultWidget extends WidgetBase { // Let the widget massage the submitted values. $values = $this->massageFormValues($values, $form, $form_state); - // Make sure the targetEntity is set correctly. - $is_new = $transition->getTargetEntity()->isNew(); - if ($is_new){ - // For some reason this is not OK for inserting Nodes, so update $items. - $to_sid = $transition->getToSid(); - $items->setValue($to_sid); - $items->__set('_workflow_transition', $transition); - } - // Update the entity in a 'normal' situation. // Update targetEntity's itemList with the workflow field in two formats. - $transition->updateEntity(); + // In other locations, $transition->updateEntityWorkflowField() is used. + $items->setValue($transition); } /** diff --git a/workflow.form.inc b/workflow.form.inc index 58a52ff5318b7e419ed34374d5b8c0b78764ad96..5e60f4fdce8b05a7d1551593efe8ee3254ad77c3 100644 --- a/workflow.form.inc +++ b/workflow.form.inc @@ -6,6 +6,7 @@ */ use Drupal\Component\Utility\Html; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\workflow\Entity\WorkflowTransitionInterface; @@ -219,19 +220,30 @@ function _workflow_transition_form_get_action_buttons(array &$form, FormStateInt * @return array * A $field_name => $to_sid array. */ -function _workflow_transition_form_get_triggering_button(FormStateInterface $form_state) { +function _workflow_transition_form_get_triggering_button(WorkflowTransitionInterface $transition, FormStateInterface $form_state, array $values) { $result = ['field_name' => NULL, 'to_sid' => NULL]; $triggering_element = $form_state->getTriggeringElement(); if (isset($triggering_element['#workflow'])) { + // This is a Workflow action button/dropbutton. $result['field_name'] = $triggering_element['#workflow']['field_name']; $result['to_sid'] = $triggering_element['#workflow']['to_sid']; } else { + // This is a normal Save button, + // or another button like 'File upload'. $input = $form_state->getUserInput(); + // Field_name may not exist due to '#access' = false. $result['field_name'] = $input['field_name'] ?? NULL; + // To_sid is taken from the Workflow widget, not from the button. $result['to_sid'] = $input['to_sid'][0]['target_id'] ?? NULL; } + // Try to get new Sid from a value; + // $result['to_sid'] ??= $values['to_sid'][0]['target_id'] ?? NULL; + + // A 3rd party button is hit (e.g., File upload field), get default value. + $result['field_name'] ??= $transition->getFieldName(); + $result['to_sid'] ??= $transition->getFromSid(); return $result; }