/** * {@inheritdoc} */ public static function executeScheduledTransitionsBetween($start = 0, $end = 0) { $clear_cache = FALSE; // If the time now is greater than the time to execute a transition, do it. foreach (WorkflowScheduledTransition::loadBetween($start, $end) as $scheduled_transition) { $field_name = $scheduled_transition->getFieldName(); $entity = $scheduled_transition->getTargetEntity(); // Make sure transition is still valid: the entity must still be in // the state it was in, when the transition was scheduled. // Scheduling on comments is a testing error, and leads to 'recoverable error'. $current_sid = ''; if ($entity && $entity->getEntityTypeId() !== 'comment') { $current_sid = workflow_node_current_state($entity, $field_name); } if ($current_sid && $current_sid == $scheduled_transition->getFromSid()) { // If user didn't give a comment, create one. $comment = $scheduled_transition->getComment(); if (empty($comment)) { $scheduled_transition->addDefaultComment(); } // Do transition. Force it because user who scheduled was checked. // The scheduled transition is not scheduled anymore, and is also deleted from DB. // A watchdog message is created with the result. $scheduled_transition->schedule(FALSE); $scheduled_transition->force(TRUE); workflow_execute_transition($scheduled_transition, TRUE); if (!$field_name) { $clear_cache = TRUE; } } else { // Entity is not in the same state it was when the transition // was scheduled. Defer to the entity's current state and // abandon the scheduled transition. $scheduled_transition->delete(); } } if ($clear_cache) { // Clear the cache so that if the transition resulted in a entity // being published, the anonymous user can see it. Cache::invalidateTags(array('rendered')); } }
/** * Saves the entity. * Mostly, you'd better use WorkflowTransitionInterface::execute(); * * {@inheritdoc} */ public function save() { // return parent::save(); // Avoid custom actions for subclass WorkflowScheduledTransition. if ($this->isScheduled()) { return parent::save(); } if ($this->getEntityTypeId() != 'workflow_transition') { return parent::save(); } $transition = $this; $entity_type = $transition->getTargetEntityTypeId(); $entity_id = $transition->getTargetEntityId(); $field_name = $transition->getFieldName(); // Remove any scheduled state transitions. foreach (WorkflowScheduledTransition::loadMultipleByProperties($entity_type, [$entity_id], [], $field_name) as $scheduled_transition) { /* @var WorkflowTransitionInterface $scheduled_transition */ $scheduled_transition->delete(); } // Check for no transition. if ($this->getFromSid() == $this->getToSid()) { if (!$this->getComment()) { // Write comment into history though. return SAVED_UPDATED; } } $hid = $this->id(); if (!$hid) { // Insert the transition. Make sure it hasn't already been inserted. // @todo: Allow a scheduled transition per revision. // @todo: Allow a state per language version (langcode). $found_transition = self::loadByProperties($entity_type, $entity_id, [], $field_name); if ($found_transition && $found_transition->getTimestamp() == REQUEST_TIME && $found_transition->getToSid() == $this->getToSid()) { return SAVED_UPDATED; } else { return parent::save(); } } else { // Update the transition. return parent::save(); } }
/** * Implements ContentEntityForm::copyFormValuesToEntity(), and is called from: * - WorkflowTransitionForm::buildEntity() * - WorkflowDefaultWidget * * N.B. in contrary to ContentEntityForm::copyFormValuesToEntity(), * - parameter 1 is returned as result, to be able to create a new Transition object. * - parameter 3 is not $form_state (from Form), but an $item array (from Widget). * * @param \Drupal\Core\Entity\EntityInterface $entity * @param array $form * @param array $item * * @return \Drupal\workflow\Entity\WorkflowTransitionInterface */ public static function copyFormItemValuesToEntity(EntityInterface $entity, array $form, array $item) { /** * Input */ $user = workflow_current_user(); // @todo #2287057: verify if submit() really is only used for UI. If not, $user must be passed. /* @var $transition WorkflowTransitionInterface */ $transition = $entity; /** * Derived input */ // Make sure we have subset ['workflow_scheduled_date_time'] if (isset($item['to_sid'])) { // In WorkflowTransitionForm, we receive the complete $form_state. // Remember, the workflow_scheduled element is not set on 'add' page. $scheduled = !empty($item['workflow_scheduling']['scheduled']); $schedule_values = $scheduled ? $item['workflow_scheduling']['date_time'] : []; } else { $entity_id = $transition->getTargetEntityId(); drupal_set_message(t('Error: content !id has no workflow attached. The data is not saved.', array('!id' => $entity_id)), 'error'); // The new state is still the previous state. return $transition; } // Get user input from element. $to_sid = $item['to_sid']; $comment = $item['comment']; $force = FALSE; // @todo D8: add the VBO use case. // // Determine if the transition is forced. // // This can be set by a 'workflow_vbo action' in an additional form element. // $force = isset($form_state['input']['workflow_force']) ? $form_state['input']['workflow_force'] : FALSE; // if (!$entity) { // // E.g., on VBO form. // } // @todo D8-port: add below exception. /* // Extract the data from $items, depending on the type of widget. // @todo D8: use MassageFormValues($item, $form, $form_state). $old_sid = workflow_node_previous_state($entity, $entity_type, $field_name); if (!$old_sid) { // At this moment, $old_sid should have a value. If the content does not // have a state yet, old_sid contains '(creation)' state. But if the // content is not associated to a workflow, old_sid is now 0. This may // happen in workflow_vbo, if you assign a state to non-relevant nodes. $entity_id = entity_id($entity_type, $entity); drupal_set_message(t('Error: content !id has no workflow attached. The data is not saved.', array('!id' => $entity_id)), 'error'); // The new state is still the previous state. $new_sid = $old_sid; return $new_sid; } */ $timestamp = REQUEST_TIME; if ($scheduled) { // Fetch the (scheduled) timestamp to change the state. // Override $timestamp. $scheduled_date_time = implode(' ', array($schedule_values['workflow_scheduled_date'], $schedule_values['workflow_scheduled_hour'])); $timezone = $schedule_values['workflow_scheduled_timezone']; $old_timezone = date_default_timezone_get(); date_default_timezone_set($timezone); $timestamp = strtotime($scheduled_date_time); date_default_timezone_set($old_timezone); if (!$timestamp) { // Time should have been validated in form/widget. $timestamp = REQUEST_TIME; } } /** * Process */ /* * Create a new ScheduledTransition. */ if ($scheduled) { $transition_entity = $transition->getTargetEntity(); $field_name = $transition->getFieldName(); $from_sid = $transition->getFromSid(); /* @var $transition WorkflowTransitionInterface */ $transition = WorkflowScheduledTransition::create([$from_sid, 'field_name' => $field_name]); $transition->setTargetEntity($transition_entity); $transition->setValues($to_sid, $user->id(), $timestamp, $comment); } if (!$transition->isExecuted()) { // Set new values. // When editing an existing Transition, only comments may change. $transition->set('to_sid', $to_sid); $transition->setOwner($user); $transition->setTimestamp($timestamp); $transition->schedule($scheduled); $transition->force($force); } $transition->setComment($comment); // Explicitely set $entity in case of ScheduleTransition. It is now returned as parameter, not result. $entity = $transition; return $transition; }