/** * {@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')); } }
/** * {@inheritdoc} */ public function build() { $form = []; // Get the entity for this form. /* @var $entity EntityInterface */ if (!($entity = workflow_url_get_entity())) { return $form; } // Get the field name. Avoid error on Node Add page. if (!($field_name = workflow_get_field_name($entity))) { return $form; } /* * Output: generate the Transition Form. */ // Create a transition, to pass to the form. No need to use setValues(). $current_sid = workflow_node_current_state($entity, $field_name); $transition = WorkflowTransition::create([$current_sid, 'field_name' => $field_name]); $transition->setTargetEntity($entity); // Add the WorkflowTransitionForm to the page. $form = $this->entityFormBuilder()->getForm($transition, 'add'); return $form; }
/** * Implements workflow_transition() -> WorkflowDefaultWidget::submit(). * * Overrides submit(array $form, array &$form_state). * Contains 2 extra parameters for D7 * * @param array $form * @param array $form_state * @param array $items * The value of the field. * @param bool $force * TRUE if all access must be overridden, e.g., for Rules. * * @return int * If update succeeded, the new State Id. Else, the old Id is returned. * * This is called from function _workflowfield_form_submit($form, &$form_state) * It is a replacement of function workflow_transition($node, $new_sid, $force, $field) * It performs the following actions; * - save a scheduled action * - update history * - restore the normal $items for the field. * @todo: remove update of {node_form} table. (separate task, because it has features, too) */ public function submit(array $form, array &$form_state, array &$items, $force = FALSE) { $entity_type = $this->entity_type; $entity = $this->entity; // $entity_id = entity_id($entity_type, $entity); $field_name = isset($this->field['field_name']) ? $this->field['field_name'] : ''; // Extract the data from $items, depending on the type of widget. // @todo D8: use MassageFormValues($values, $form, $form_state). $old_sid = workflow_node_current_state($entity, $entity_type, $field_name); $transition = $this->getTransition($old_sid, $items); $force = $force || $transition->isForced(); $new_sid = $transition->new_sid; if (!$transition) { drupal_set_message(t('Error when try to find a WorkflowTransition.'), 'status'); // The current value is still the previous state. $new_sid = $old_sid; } elseif ($error = $transition->isAllowed($force)) { drupal_set_message($error, 'error'); } elseif (!$transition->isScheduled()) { // Now the data is captured in the Transition, and before calling the Execution, // restore the default values for Workflow Field. // For instance, workflow_rules evaluates this. if ($field_name) { $items = array(); $items[0]['value'] = $old_sid; $entity->{$field_name}['und'] = $items; } // It's an immediate change. Do the transition. // - validate option; add hook to let other modules change comment. // - add to history; add to watchdog // return the new value of the sid. (Execution may fail and return the old Sid.) $new_sid = $transition->execute($force); } else { // A scheduled transition must only be saved to the database. The entity is not changed. $transition->save(); // The current value is still the previous state. $new_sid = $old_sid; } // The entity is still to be saved, so set to a 'normal' value. if ($field_name) { $items = array(); $items[0]['value'] = $new_sid; $entity->{$field_name}['und'] = $items; } return $new_sid; }
/** * Implements hook_form_BASE_FORM_ID_alter(). * * Use this hook to alter the form. * It is only suited if you only use View Page or Workflow Tab. * If you change the state on the Node Form (Edit modus), you need the hook * hook_form_alter(). See below for more info. */ function hook_form_workflow_transition_form_alter(&$form, &$form_state, $form_id) { // Get the Entity. $entity = $form['workflow']['workflow_entity']['#value']; $entity_type = $form['workflow']['workflow_entity_type']['#value']; // Use the complicated form, which is suited for all Entity types. // For nodes only: $entity_type = 'node'; $entity_bundle = $entity->type; list(, , $entity_bundle) = entity_extract_ids($entity_type, $entity); // Get the current State ID. $sid = workflow_node_current_state($entity, $entity_type, $field_name = NULL); // Get the State object, if needed. $state = workflow_state_load($sid); // Change the form, depending on the state ID. // In the upcoming version 7.x-2.4, States should have a machine_name, too. if ($entity_type == 'node' && $entity_bundle == 'MY_NODE_TYPE') { switch ($state->sid) { case '2': // Change form element, form validate and form submit for state '2'. break; case '3': // Change form element, form validate and form submit for state '3'. break; } } }
/** * {@inheritdoc} * * @todo D8-port: add D7-theming to TransitionListBuilder. */ public function buildRow(EntityInterface $transition) { // Show the history table. $current_themed = FALSE; /* @var $transition WorkflowTransitionInterface */ $entity = $transition->getTargetEntity(); $field_name = $transition->getFieldName(); $current_sid = workflow_node_current_state($entity, $field_name); $to_state = $transition->getToState(); if (!$to_state) { // This is an invalid/deleted state. $to_label = WORKFLOW_MARK_STATE_IS_DELETED; // Add a footer to explain the addition. $this->footer_needed = TRUE; } else { $label = Html::escape($this->t($to_state->label())); if ($transition->getToSid() == $current_sid && $to_state->isActive() && !$current_themed) { $to_label = $label; if (!$current_themed) { // Make a note that we have themed the current state; other times in the history // of this entity where the entity was in this state do not need to be specially themed. $current_themed = TRUE; } } elseif (!$to_state->isActive()) { $to_label = $label . WORKFLOW_MARK_STATE_IS_DELETED; // Add a footer to explain the addition. $this->footer_needed = TRUE; } else { // Regular state. $to_label = $label; } } unset($to_state); // Not needed anymore. $from_state = $transition->getFromState(); if (!$from_state) { // This is an invalid/deleted state. $from_label = WORKFLOW_MARK_STATE_IS_DELETED; // Add a footer to explain the addition. $this->footer_needed = TRUE; } else { $label = Html::escape($this->t($from_state->label())); if (!$from_state->isActive()) { $from_label = $label . WORKFLOW_MARK_STATE_IS_DELETED; // Add a footer to explain the addition. $this->footer_needed = TRUE; } else { // Regular state. $from_label = $label; } } unset($from_state); // Not needed anymore. $owner = $transition->getOwner(); $field_name = $transition->getFieldName(); $field_label = $transition->getFieldName(); $variables = array('transition' => $transition, 'extra' => '', 'from_label' => $from_label, 'to_label' => $to_label, 'user' => $owner); // Allow other modules to modify the row. \Drupal::moduleHandler()->alter('workflow_history', $variables); // 'class' => array('workflow_history_row'), // TODO D8-port $row['timestamp']['data'] = $transition->getTimestampFormatted(); // 'class' => array('timestamp') // html_entity_decode() transforms chars like '&' correctly. if ($this->showColumnFieldname($entity)) { $row['field_name']['data'] = html_entity_decode($field_label); } $row['from_state']['data'] = html_entity_decode($from_label); // 'class' => array('previous-state-name')) $row['to_state']['data'] = html_entity_decode($to_label); // 'class' => array('state-name')) $row['user_name']['data'] = $owner->getUsername(); // 'class' => array('user-name') $row['comment']['data'] = html_entity_decode($transition->getComment()); // 'class' => array('log-comment') // $row['comment'] = array( // '#type' => 'textarea', // '#default_value' => $transition->getComment(), // ); // Column 'Operations' is now added by core. // D7: $row['operations']['data'] = $this->buildOperations($entity); $row += parent::buildRow($transition); return $row; }
/** * Implements hook_field_widget_form --> WidgetInterface::formElement(). * * {@inheritdoc} * * Be careful: Widget may be shown in very different places. Test carefully!! * - On a entity add/edit page * - On a entity preview page * - On a entity view page * - On a entity 'workflow history' tab * - On a comment display, in the comment history * - On a comment form, below the comment history * * @todo D8: change "array $items" to "FieldInterface $items" */ public function formElement(array $items, $delta, array $element, array &$form, array &$form_state) { global $user; // @todo #2287057: verify if formElement() really is only used for UI. If not, $user must be passed. $field = $this->field; $instance = $this->instance; $entity = $this->entity; $entity_type = $this->entity_type; $entity_id = $entity ? entity_id($entity_type, $entity) : 0; $field_name = $field['field_name']; $current_sid = FALSE; // $field['settings']['wid'] can be numeric or named. // $wid may not be specified. $wid = $field['settings']['wid']; $workflow = workflow_load_single($wid); $workflow_label = $workflow ? check_plain(t($workflow->label())) : ''; // Capture settings to format the form/widget. $settings_title_as_name = !empty($field['settings']['widget']['name_as_title']); $settings_options_type = $field['settings']['widget']['options']; // The schedule can be hidden via field settings, ... $settings_schedule = !empty($field['settings']['widget']['schedule']); if ($settings_schedule) { if (isset($form_state['step']) && $form_state['step'] == 'views_bulk_operations_config_form') { // On VBO 'modify entity values' form, leave field settings. $settings_schedule = TRUE; } else { // ... and cannot be shown on a Content add page (no $entity_id), // ...but can be shown on a VBO 'set workflow state to..'page (no entity). $settings_schedule = !($entity && !$entity_id); } } $settings_schedule_timezone = !empty($field['settings']['widget']['schedule_timezone']); // Show comment, when both Field and Instance allow this. $settings_comment = $field['settings']['widget']['comment']; $options = array(); if (!$entity) { // Sometimes, no entity is given. We encountered the following cases: // - the Field settings page, // - the VBO action form; // - the Advance Action form on admin/config/system/actions; // If so, show all options for the given workflow(s). // Set 'grouped' option. This is only valid for select list. $grouped = $settings_options_type == 'select'; $options = workflow_get_workflow_state_names($wid, $grouped, $all = FALSE); $show_widget = TRUE; $default_value = isset($items[0]['value']) ? $items[0]['value'] : '0'; } else { $force = FALSE; $current_sid = workflow_node_current_state($entity, $entity_type, $field_name); if ($current_state = workflow_state_load_single($current_sid)) { // $grouped = TRUE; // Grouped options only makes sense for multiple workflows. $options = $current_state->getOptions($entity_type, $entity, $field_name, $user, $force); $show_widget = $current_state->showWidget($entity_type, $entity, $field_name, $user, $force); // Determine the default value. If we are in CreationState, use a fast alternative for $workflow->getFirstSid(). $default_value = $current_state->isCreationState() ? key($options) : $current_sid; } else { // We are in trouble! A message is already set in workflow_node_current_state(). $show_widget = FALSE; $default_value = $current_sid; } } // Get the scheduling info. This may change the $current_sid on the Form. $scheduled = '0'; $timestamp = REQUEST_TIME; $comment = NULL; if ($settings_schedule) { // Read scheduled information. // Technically you could have more than one scheduled, but this will only add the soonest one. foreach (WorkflowScheduledTransition::load($entity_type, $entity_id, $field_name) as $scheduled_transition) { $scheduled = '1'; $default_value = $scheduled_transition->new_sid; $timestamp = $scheduled_transition->scheduled; $comment = $scheduled_transition->comment; break; } } // Fetch the form ID. This is unique for each entity, to allow multiple form per page (Views, etc.). // Make it uniquer by adding the field name, or else the scheduling of // multiple workflow_fields is not indendent. $form_id = $form_state['build_info']['form_id'] . '_' . $field_name; // Prepare a UI wrapper. This might be a fieldset. $element['workflow']['#type'] = 'container'; // 'fieldset'; $element['workflow']['#attributes'] = array('class' => array('workflow-form-container')); // Save the current value of the node in the form, for later Workflow-module specific references. // We add prefix, since #tree == FALSE. $element['workflow']['workflow_entity'] = array('#type' => 'value', '#value' => $this->entity); $element['workflow']['workflow_entity_type'] = array('#type' => 'value', '#value' => $this->entity_type); $element['workflow']['workflow_field'] = array('#type' => 'value', '#value' => $field); $element['workflow']['workflow_instance'] = array('#type' => 'value', '#value' => $instance); // Save the form_id, so the form values can be retrieved in submit function. $element['workflow']['form_id'] = array('#type' => 'value', '#value' => $form_id); // First of all, we add the default value in the place were normal fields // have it. This is to cater for 'preview' of the entity. $element['#default_value'] = $default_value; // Decide if we show a widget or a formatter. // There is no need to a widget when the only choice is the current sid. if (!$show_widget) { $element['workflow']['workflow_sid'] = workflow_state_formatter($entity_type, $entity, $field, $instance, $default_value); return $element; // <---- exit. } // The 'options' widget. May be removed later if 'Action buttons' are chosen. $element['workflow']['workflow_sid'] = array('#type' => $settings_options_type, '#title' => $settings_title_as_name ? t('Change !name state', array('!name' => $workflow_label)) : t('Target state'), '#options' => $options, '#default_value' => $default_value); // Display scheduling form, but only if entity is being edited and user has // permission. State change cannot be scheduled at entity creation because // that leaves the entity in the (creation) state. if ($settings_schedule == TRUE && user_access('schedule workflow transitions')) { if (variable_get('configurable_timezones', 1) && $user->uid && drupal_strlen($user->timezone)) { $timezone = $user->timezone; } else { $timezone = variable_get('date_default_timezone', 0); } $timezones = drupal_map_assoc(timezone_identifiers_list()); $hours = format_date($timestamp, 'custom', 'H:i', $timezone); $element['workflow']['workflow_scheduled'] = array('#type' => 'radios', '#title' => t('Schedule'), '#options' => array('0' => t('Immediately'), '1' => t('Schedule for state change')), '#default_value' => $scheduled, '#attributes' => array('id' => 'scheduled_' . $form_id)); $element['workflow']['workflow_scheduled_date_time'] = array('#type' => 'fieldset', '#title' => t('At'), '#attributes' => array('class' => array('container-inline')), '#prefix' => '<div style="margin-left: 1em;">', '#suffix' => '</div>', '#states' => array('visible' => array(':input[id="' . 'scheduled_' . $form_id . '"]' => array('value' => '1')))); $element['workflow']['workflow_scheduled_date_time']['workflow_scheduled_date'] = array('#type' => 'date', '#default_value' => array('day' => date('j', $timestamp), 'month' => date('n', $timestamp), 'year' => date('Y', $timestamp))); $element['workflow']['workflow_scheduled_date_time']['workflow_scheduled_hour'] = array('#type' => 'textfield', '#title' => t('Time'), '#maxlength' => 7, '#size' => 6, '#default_value' => $scheduled ? $hours : '00:00', '#element_validate' => array('_workflow_transition_form_element_validate_time')); $element['workflow']['workflow_scheduled_date_time']['workflow_scheduled_timezone'] = array('#type' => $settings_schedule_timezone ? 'select' : 'hidden', '#title' => t('Time zone'), '#options' => $timezones, '#default_value' => array($timezone => $timezone)); $element['workflow']['workflow_scheduled_date_time']['workflow_scheduled_help'] = array('#type' => 'item', '#prefix' => '<br />', '#description' => t('Please enter a time. If no time is included, the default will be midnight on the specified date. The current time is: @time.', array('@time' => format_date(REQUEST_TIME, 'custom', 'H:i', $timezone)))); } $element['workflow']['workflow_comment'] = array('#type' => $settings_comment == '0' ? 'hidden' : 'textarea', '#required' => $settings_comment == '2', '#title' => t('Workflow comment'), '#description' => t('A comment to put in the workflow log.'), '#default_value' => $comment, '#rows' => 2); // Finally, add Submit buttons/Action buttons. // Either a default 'Submit' button is added, or a button per permitted state. if ($settings_options_type == 'buttons') { // How do action buttons work? See also d.o. issue #2187151. // Create 'action buttons' per state option. Set $sid property on each button. // 1. Admin sets ['widget']['options']['#type'] = 'buttons'. // 2. This function formElelent() creates 'action buttons' per state option; // sets $sid property on each button. // 3. User clicks button. // 4. Callback _workflow_transition_form_validate_buttons() sets proper State. // 5. Callback _workflow_transition_form_validate_buttons() sets Submit function. // @todo: this does not work yet for the Add Comment form. // Performance: inform workflow_form_alter() to do its job. _workflow_use_action_buttons(TRUE); } $submit_functions = empty($instance['widget']['settings']['submit_function']) ? array() : array($instance['widget']['settings']['submit_function']); if ($settings_options_type == 'buttons' || $submit_functions) { $element['workflow']['actions']['#type'] = 'actions'; $element['workflow']['actions']['submit'] = array('#type' => 'submit', '#value' => t('Update workflow'), '#weight' => -5, '#attributes' => array('class' => array('form-save-default-button'))); // The 'add submit' can explicitely set by workflowfield_field_formatter_view(), // to add the submit button on the Content view page and the Workflow history tab. // Add a submit button, but only on Entity View and History page. // Add the submit function only if one provided. Set the submit_callback accordingly. if ($submit_functions) { $element['workflow']['actions']['submit']['#submit'] = $submit_functions; } else { // '#submit' Must be empty, or else the submit function is not called. // $element['workflow']['actions']['submit']['#submit'] = array(); } } else { // In some cases, no submit callback function is specified. This is // explicitly done on e.g., the node edit form, because the workflow form // is 'just a field'. // So, no Submit button is to be shown. } return $element; }
/** * Implements hook_form_BASE_FORM_ID_alter(). * * Use this hook to alter the form. * It is only suited if you only use View Page or Workflow Tab. * If you change the state on the Entity Edit page (form), you need the hook * hook_form_alter(). See below for more info. */ function hook_form_workflow_transition_form_alter(&$form, FormStateInterface $form_state, $form_id) { // workflow_debug(__FILE__, __FUNCTION__, __LINE__, $form_id, ''); // The WorkflowTransitionForm (E.g., Workflow History tab, Block). // It has its own handling. // Todo, populate the WorkflowTranstiionForm with a Widget, so we have 1 way-of-working. // Let's take a (changeable) reference to the element. $workflow_element =& $form; // This object contains all you need. You may find it in one of two locations. /* @var $transition WorkflowTransitionInterface */ $transition = $form['workflow_transition']['#value']; // dpm($transition); // An example of customizing/overriding the workflow widget. // Beware, until now, you must do this twice: on the widget and on the form. if ($transition->getOwnerId() == 1) { drupal_set_message('I got you, user 1, you will never schedule again, and you WILL document each state change!', 'warning'); // Let's prohibit scheduling for user 1. $workflow_element['workflow_scheduling']['#access'] = FALSE; // Let's prohibit scheduling for user 1. if ($workflow_element['comment']['#access'] == TRUE) { $workflow_element['comment']['#required'] = TRUE; } } // Get the Entity. /* @var $entity \Drupal\Core\Entity\EntityInterface */ $entity = NULL; //$entity = $form['workflow_entity']['#value']; $entity_type = 'node'; // $form['workflow_entity_type']['#value']; $entity_bundle = ''; // $entity->bundle(); $sid = ''; if ($entity) { $entity_type = $entity->getEntityTypeId(); $entity_bundle = $entity->bundle(); // Get the current State ID. $sid = workflow_node_current_state($entity, $field_name = NULL); // Get the State object, if needed. $state = WorkflowState::load($sid); } // Change the form, depending on the state ID. // In the upcoming version 7.x-2.4, States should have a machine_name, too. if ($entity_type == 'node' && $entity_bundle == 'MY_NODE_TYPE') { switch ($sid) { case '2': // Change form element, form validate and form submit for state '2'. break; case '3': // Change form element, form validate and form submit for state '3'. break; } } }
include_once './includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); // definiton $magic_str = "trash"; $sid_creation = 1; $sid_open = 2; $sid_inaccessible = 3; $sid_corrected = 4; global $user; $tickets_result = db_query("SELECT nid, 'ticket' as type, field_url_value FROM content_type_ticket"); while ($ticket = db_fetch_object($tickets_result)) { $nid = $ticket->nid; $n = node_load($ticket->nid); $url = $ticket->field_url_value; $sid = workflow_node_current_state($ticket); print $sid; // we only give chance to tickets that are newly created or previously inaccessible (perhaps due to network failure) if ($sid == $sid_creation || $sid == $sid_inaccessible || $sid == $sid_corrected) { $content = file_get_contents($url); if ($content) { // if the URL is readable if (strpos($content, $magic_str)) { // if we find the magic string print "we found the string"; $user->uid = 1; workflow_execute_transition(&$n, $sid_corrected, $comment = NULL, TRUE); $user->uid = 0; } else { print "No string, reopen"; $user->uid = 1;
/** * {@inheritdoc} * * Be careful: Widget may be shown in very different places. Test carefully!! * - On a entity add/edit page * - On a entity preview page * - On a entity view page * - On a entity 'workflow history' tab * - On a comment display, in the comment history * - On a comment form, below the comment history * * @todo D8: change "array $items" to "FieldInterface $items" */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $wid = $this->getFieldSetting('workflow_type'); if (!($workflow = Workflow::load($wid))) { // @todo: add error message. return $element; } /* @var $item \Drupal\workflowfield\Plugin\Field\FieldType\WorkflowItem */ $item = $items[$delta]; /* @var $field_config \Drupal\field\Entity\FieldConfig */ $field_config = $item->getFieldDefinition(); /* @var $field_storage \Drupal\field\Entity\FieldStorageConfig */ $field_storage = $field_config->getFieldStorageDefinition(); $field_name = $field_storage->get('field_name'); $entity = $item->getEntity(); $from_sid = workflow_node_current_state($entity, $field_name); // Create a transition, to pass to the form. No need to use setValues(). /* @var $transition WorkflowTransition */ $transition = WorkflowTransition::create([$from_sid, 'field_name' => $field_name]); $transition->setTargetEntity($entity); if (!$this->isDefaultValueWidget($form_state)) { // Here, not the $element is added, but the entity form. // Option 1: use the Element. $element['#default_value'] = $transition; $element += WorkflowTransitionElement::transitionElement($element, $form_state, $form); // Option 2: use the Form, in order to get extra fields. //$entity_form_builder = \Drupal::getContainer()->get('entity.form_builder'); //$element += $entity_form_builder->getForm($transition, 'add'); //// Remove the action button. The Entity itself has one. //unset($element['actions']); // Option 3: use the true Element. // $form = $this->element($form, $form_state, $transition); //$element['workflow_transition'] = array( // '#type' => 'workflow_transition', // '#title' => t('Workflow transition'), // '#default_value' => $transition, // ); } else { // @todo D8: add a default value, so people can set a default comment. // On the Field settings page, User may not set a default value // (this is done by the Workflow module). // @see WorkflowState::getOptions(); // @see WorkflowDefaultWidget::formElement(); $element = array(); return $element; workflow_debug(__FILE__, __FUNCTION__, __LINE__, '', ''); // @todo D8-port: still test this snippet. // @see workflowfield_form_field_config_edit_form_alter for other settings // The Workflow field must have a value, so set to required. // Unfortunately, we need hook_form_alter for this. //$form['required']['#default_value'] = 1; //$form['required']['#disabled'] = TRUE; // Explicitly set default value to 'creation' and show element, // so people can set a default comment. $transition->to_sid = $workflow->getCreationSid(); $transition->setExecuted(TRUE); // @TODO D8-port: use a proper WorkflowTransitionElement call. $element['#default_value'] = $transition; $element += WorkflowTransitionElement::transitionElement($element, $form_state, $form); // No action buttons on default field settings page. // This is evaluated in hook_form_alter. _workflow_use_action_buttons(FALSE); // Make sure the options box is not hidden (when action buttons active). //$element['to_sid']['#type'] = 'select'; $element['to_sid']['#title'] = 'Initial state'; $element['to_sid']['#access'] = TRUE; unset($element['workflow_current_state']); return $element; } return $element; }
/** * Prepares a transition to be reverted. * * @param \Drupal\workflow\Entity\WorkflowTransitionInterface $transition * The transition to be reverted. * * @return \Drupal\workflow\Entity\WorkflowTransitionInterface * The prepared transition ready to be stored. */ protected function prepareRevertedTransition(WorkflowTransitionInterface $transition) { $user = \Drupal::currentUser(); $entity = $transition->getTargetEntity(); $field_name = $transition->getFieldName(); $current_sid = workflow_node_current_state($entity, $field_name); $previous_sid = $transition->getFromSid(); $comment = t('State reverted.'); $transition = WorkflowTransition::create([$current_sid, 'field_name' => $field_name]); $transition->setTargetEntity($entity); $transition->setValues($previous_sid, $user->id(), REQUEST_TIME, $comment); return $transition; }
/** * @return WorkflowTransitionInterface */ protected function getTransitionForExecution(EntityInterface $entity) { $user = workflow_current_user(); if (!$entity) { \Drupal::logger('workflow_action')->notice('Unable to get current entity - entity is not defined.', []); return NULL; } // Get the entity type and numeric ID. $entity_id = $entity->id(); if (!$entity_id) { \Drupal::logger('workflow_action')->notice('Unable to get current entity ID - entity is not yet saved.', []); return NULL; } // In 'after saving new content', the node is already saved. Avoid second insert. // Todo: clone? $entity->enforceIsNew(FALSE); $config = $this->configuration; $field_name = workflow_get_field_name($entity, $config['field_name']); $current_sid = workflow_node_current_state($entity, $field_name); if (!$current_sid) { \Drupal::logger('workflow_action')->notice('Unable to get current workflow state of entity %id.', array('%id' => $entity_id)); return NULL; } $to_sid = isset($config['to_sid']) ? $config['to_sid'] : ''; // Get the Comment. Parse the $comment variables. $comment_string = $this->configuration['comment']; $comment = t($comment_string, array('%title' => $entity->label(), '%state' => workflow_get_sid_name($to_sid), '%user' => $user->getUsername())); $force = $this->configuration['force']; $transition = WorkflowTransition::create([$current_sid, 'field_name' => $field_name]); $transition->setTargetEntity($entity); $transition->setValues($to_sid, $user->id(), REQUEST_TIME, $comment); $transition->force($force); return $transition; }
/** * Returns the next state for the current state. * * @param string $entity_type * The type of the entity at hand. * @param object $entity * The entity at hand. May be NULL (E.g., on a Field settings page). * @param $field_name * @param $user * @param bool $force * * @return int $sid * A state ID. */ public function getNextSid($entity_type, $entity, $field_name, $user, $force = FALSE) { $new_sid = workflow_node_current_state($entity, $entity_type, $field_name); if ($new_sid && ($new_state = workflow_state_load_single($new_sid))) { /* @var $current_state WorkflowState */ $options = $new_state->getOptions($entity_type, $entity, $field_name, $user, $force); // Loop over every option. To find the next one. $flag = $new_state->isCreationState(); foreach ($options as $sid => $name) { if ($flag) { $new_sid = $sid; break; } if ($sid == $new_state->sid) { $flag = TRUE; } } } return $new_sid; }
if ($node->_workflow != 12) { print l('Cancel Certificate', 'crlt/certificate/cancel/' . $node->nid, array('query' => array('destination' => implode('/', arg())))) . '<br />'; } $wid = workflow_get_workflow_for_type($node->type); $states_per_page = 250; $states = array(); $result = db_query("SELECT sid, state FROM {workflow_states} WHERE status = 1 ORDER BY sid"); while ($data = db_fetch_object($result)) { $states[$data->sid] = check_plain(t($data->state)); } $deleted_states = array(); $result = db_query("SELECT sid, state FROM {workflow_states} WHERE status = 0 ORDER BY sid"); while ($data = db_fetch_object($result)) { $deleted_states[$data->sid] = check_plain(t($data->state)); } $current = workflow_node_current_state($node); // theme_workflow_current_state() must run state through check_plain(). $output = '<p>' . t('Current state: <strong>!state</strong>', array('!state' => $states[$current])) . "</p>\n"; $result = pager_query("SELECT h.*, u.name FROM {workflow_node_history} h LEFT JOIN {users} u ON h.uid = u.uid WHERE nid = %d ORDER BY hid DESC", $states_per_page, 0, NULL, $node->nid); $rows = array(); while ($history = db_fetch_object($result)) { if ($history->sid == $current && !isset($deleted_states[$history->sid]) && !isset($current_themed)) { // Theme the current state differently so it stands out. $state_name = $states[$history->sid]; // Make a note that we have themed the current state; other times in the history // of this node where the node was in this state do not need to be specially themed. $current_themed = TRUE; } elseif (isset($deleted_states[$history->sid])) { // The state has been deleted, but we include it in the history. $state_name = $deleted_states[$history->sid]; $footer_needed = TRUE;
/** * Generates an overview table of older revisions of a node, * but only if this::historyAccess() allows it. * * @param \Drupal\node\NodeInterface $node * A node object. * * @return array * An array as expected by drupal_render(). */ public function historyOverview(EntityInterface $node = NULL) { $form = array(); /* * Get data from parameters. */ // TODO D8-port: make Workflow History tab happen for every entity_type. // For workflow_tab_page with multiple workflows, use a separate view. See [#2217291]. // @see workflow.routing.yml, workflow.links.task.yml, WorkflowTransitionListController. // workflow_debug(__FILE__, __FUNCTION__, __LINE__); // @todo D8-port: still test this snippet. // ATM it only works for Nodes and Terms. // This is a hack. The Route should always pass an object. // On view tab, $entity is object, // On workflow tab, $entity is id(). // Get the entity for this form. if (!($entity = workflow_url_get_entity($node))) { return $form; } /* * Get derived data from parameters. */ if (!($field_name = workflow_get_field_name($entity, workflow_url_get_field_name()))) { return $form; } /* * Step 1: generate the Transition Form. */ // Create a transition, to pass to the form. No need to use setValues(). $current_sid = workflow_node_current_state($entity, $field_name); $transition = WorkflowTransition::create([$current_sid, 'field_name' => $field_name]); $transition->setTargetEntity($entity); // Add the WorkflowTransitionForm to the page. $form = $this->entityFormBuilder()->getForm($transition, 'add'); /* * Step 2: generate the Transition History List. */ $entity_type = 'workflow_transition'; // $form = $this->listing('workflow_transition'); $list_builder = $this->entityManager()->getListBuilder($entity_type); // Add the Node explicitly, since $list_builder expects a Transition. $list_builder->workflow_entity = $entity; $form += $list_builder->render(); /* * Finally: sort the elements (overriding their weight). */ // $form['#weight'] = 10; $form['actions']['#weight'] = 100; $form['workflow_list_title']['#weight'] = 200; $form['table']['#weight'] = 201; return $form; }
function ecogig_neat_preprocess_node(&$variables) { $node = $variables['node']; if ($node->type == 'dataset') { $workflow = !empty($node->field_dataset_workflow['und'][0]['value']) ? $node->field_dataset_workflow['und'][0]['value'] : ''; $workflow = workflow_node_current_state($node); if (!empty($workflow)) { $state = ecogig_workflow_get_state_name($workflow); $comment = ecogig_workflow_get_latest_comment($node->nid); $variables['workflow_state'] = !empty($state) ? $state : ''; $variables['workflow_comment'] = !empty($comment) ? $comment : ''; } $record_type = !empty($node->field_record_type[LANGUAGE_NONE][0]['taxonomy_term']) ? $node->field_record_type[LANGUAGE_NONE][0]['taxonomy_term'] : ''; if (!empty($record_type)) { $icon_class = !empty($record_type->field_icon_class[LANGUAGE_NONE][0]['value']) ? $record_type->field_icon_class[LANGUAGE_NONE][0]['value'] : ''; $type_name = $record_type->name; $variables['record_type_icon'] = $icon_class; $variables['record_type_name'] = $type_name; $variables['record_type_body'] = $record_type->description; $variables['record_type_tid'] = $record_type->tid; } if (node_access('update', $node)) { drupal_add_js(drupal_get_path('module', 'dataset_record_type') . '/js/dataset_record_type.js', 'file'); $vocab = taxonomy_vocabulary_machine_name_load('dataset_record_types'); $terms = taxonomy_get_tree($vocab->vid); $variables['record_type_options'] = $terms; } } }
/** * {@inheritdoc} * * @param array $form * @param array $form_state * @param WorkflowTransition * The Transition to be edited, created. * * @return * The enhanced form structure. */ public function buildForm(array $form, array &$form_state) { global $user; /* @var $transition WorkflowTransition */ $transition = NULL; if (isset($form_state['WorkflowTransition'])) { // If provided, get data from WorkflowTransition. // This happens when calling entity_ui_get_form(), like in the // WorkflowTransition Comment Edit form. $transition = $form_state['WorkflowTransition']; $field_name = $transition->field_name; $workflow = $transition->getWorkflow(); $wid = $transition->wid; $entity = $this->entity = $transition->getEntity(); $entity_type = $this->entity_type = $transition->entity_type; // Figure out the $entity's bundle and id. list(, , $entity_bundle) = entity_extract_ids($entity_type, $entity); $entity_id = entity_id($entity_type, $entity); // Show the current state and the Workflow form to allow state changing. // N.B. This part is replicated in hook_node_view, workflow_tab_page, workflow_vbo, transition_edit. // @todo: support multiple workflows per entity. // For workflow_tab_page with multiple workflows, use a separate view. See [#2217291]. $field = _workflow_info_field($field_name, $workflow); $instance = $this->instance + field_info_instance($entity_type, $field_name, $entity_bundle); } else { // Get data from normal parameters. $entity = $this->entity; $entity_type = $this->entity_type; $entity_id = $entity ? entity_id($entity_type, $entity) : 0; $field = $this->field; $field_name = $field['field_name']; $instance = $this->instance; // $field['settings']['wid'] can be numeric or named. // $wid may not be specified. $wid = $field['settings']['wid']; $workflow = workflow_load_single($wid); } $force = FALSE; // Get values. // Current sid and default value may differ in a scheduled transition. // Set 'grouped' option. Only valid for select list and undefined/multiple workflows. $settings_options_type = $field['settings']['widget']['options']; $grouped = $settings_options_type == 'select'; if ($transition) { // If a Transition is passed as parameter, use this. if ($transition->isExecuted()) { // We are editing an existing/executed/not-scheduled transition. // Only the comments may be changed! // Fetch the old state for the formatter on top of form. $current_state = $transition->getOldState(); $current_sid = $current_state->sid; // The states may not be changed anymore. $new_state = $transition->getNewState(); $options = array($new_state->sid => $new_state->label()); // We need the widget to edit the comment. $show_widget = TRUE; } else { $current_state = $transition->getOldState(); $current_sid = $current_state->sid; $options = $current_state->getOptions($entity_type, $entity, $field_name, $user, $force); $show_widget = $current_state->showWidget($entity_type, $entity, $field_name, $user, $force); } $default_value = $transition->new_sid; } elseif (!$entity) { // Sometimes, no entity is given. We encountered the following cases: // - the Field settings page, // - the VBO action form; // - the Advance Action form on admin/config/system/actions; // If so, show all options for the given workflow(s). $options = workflow_get_workflow_state_names($wid, $grouped, $all = FALSE); $show_widget = TRUE; $default_value = $current_sid = isset($items[0]['value']) ? $items[0]['value'] : '0'; } else { $current_sid = workflow_node_current_state($entity, $entity_type, $field_name); if ($current_state = workflow_state_load_single($current_sid)) { /* @var $current_state WorkflowTransition */ $options = $current_state->getOptions($entity_type, $entity, $field_name, $user, $force); $show_widget = $current_state->showWidget($entity_type, $entity, $field_name, $user, $force); $default_value = !$current_state->isCreationState() ? $current_sid : $workflow->getFirstSid($entity_type, $entity, $field_name, $user, FALSE); } else { // We are in trouble! A message is already set in workflow_node_current_state(). $options = array(); $show_widget = FALSE; $default_value = $current_sid; } // Get the scheduling info. This may change the $default_value on the Form. // Read scheduled information, only if an entity exists. // Technically you could have more than one scheduled, but this will only add the soonest one. foreach (WorkflowScheduledTransition::load($entity_type, $entity_id, $field_name, 1) as $transition) { $default_value = $transition->new_sid; break; } } // Prepare a new transition, if still not provided. if (!$transition) { $transition = new WorkflowTransition(array('old_sid' => $default_value, 'stamp' => REQUEST_TIME)); } // Fetch the form ID. This is unique for each entity, to allow multiple form per page (Views, etc.). // Make it uniquer by adding the field name, or else the scheduling of // multiple workflow_fields is not independent of each other. // IF we are truly on a Transition form (so, not a Node Form with widget) // then change the form id, too. $form_id = $this->getFormId(); if (!isset($form_state['build_info']['base_form_id'])) { // Strange: on node form, the base_form_id is node_form, // but on term form, it is not set. // In both cases, it is OK. } else { if ($form_state['build_info']['base_form_id'] == 'workflow_transition_wrapper_form') { $form_state['build_info']['base_form_id'] = 'workflow_transition_form'; } if ($form_state['build_info']['base_form_id'] == 'workflow_transition_form') { $form_state['build_info']['form_id'] = $form_id; } } $workflow_label = $workflow ? check_plain(t($workflow->label())) : ''; // Change settings locally. if (!$field_name) { // This is a Workflow Node workflow. Set widget options as in v7.x-1.2 if ($form_state['build_info']['base_form_id'] == 'node_form') { $field['settings']['widget']['comment'] = isset($workflow->options['comment_log_node']) ? $workflow->options['comment_log_node'] : 1; // vs. ['comment_log_tab']; $field['settings']['widget']['current_status'] = TRUE; } else { $field['settings']['widget']['comment'] = isset($workflow->options['comment_log_tab']) ? $workflow->options['comment_log_tab'] : 1; // vs. ['comment_log_node']; $field['settings']['widget']['current_status'] = TRUE; } } // Capture settings to format the form/widget. $settings_title_as_name = !empty($field['settings']['widget']['name_as_title']); $settings_fieldset = isset($field['settings']['widget']['fieldset']) ? $field['settings']['widget']['fieldset'] : 0; $settings_options_type = $field['settings']['widget']['options']; // The scheduling info can be hidden via field settings, ... // You may not schedule an existing Transition. // You must have the correct permission. $settings_schedule = !empty($field['settings']['widget']['schedule']) && !$transition->isExecuted() && user_access('schedule workflow transitions'); if ($settings_schedule) { if (isset($form_state['step']) && $form_state['step'] == 'views_bulk_operations_config_form') { // On VBO 'modify entity values' form, leave field settings. $settings_schedule = TRUE; } else { // ... and cannot be shown on a Content add page (no $entity_id), // ...but can be shown on a VBO 'set workflow state to..'page (no entity). $settings_schedule = !($entity && !$entity_id); } } $settings_schedule_timezone = !empty($field['settings']['widget']['schedule_timezone']); // Show comment, when both Field and Instance allow this. $settings_comment = $field['settings']['widget']['comment']; // Save the current value of the node in the form, for later Workflow-module specific references. // We add prefix, since #tree == FALSE. $element['workflow']['workflow_entity'] = array('#type' => 'value', '#value' => $this->entity); $element['workflow']['workflow_entity_type'] = array('#type' => 'value', '#value' => $this->entity_type); $element['workflow']['workflow_field'] = array('#type' => 'value', '#value' => $field); $element['workflow']['workflow_instance'] = array('#type' => 'value', '#value' => $instance); // Save the form_id, so the form values can be retrieved in submit function. $element['workflow']['form_id'] = array('#type' => 'value', '#value' => $form_id); // Save the hid, when editing an existing transition. $element['workflow']['workflow_hid'] = array('#type' => 'hidden', '#value' => $transition->hid); // Add the default value in the place where normal fields // have it. This is to cater for 'preview' of the entity. $element['#default_value'] = $default_value; // Decide if we show a widget or a formatter. // There is no need for a widget when the only option is the current sid. // Show state formatter before the rest of the form, // when transition is scheduled or widget is hidden. if (!$show_widget || $transition->isScheduled() || $transition->isExecuted()) { $form['workflow_current_state'] = workflow_state_formatter($entity_type, $entity, $field, $instance, $current_sid); // Set a proper weight, which works for Workflow Options in select list AND action buttons. $form['workflow_current_state']['#weight'] = -0.005; } // Add class following node-form pattern (both on form and container). $workflow_type_id = $workflow ? $workflow->getName() : 'none'; // No workflow on New Action form. $element['workflow']['#attributes']['class'][] = 'workflow-transition-container'; $element['workflow']['#attributes']['class'][] = 'workflow-transition-' . $workflow_type_id . '-container'; // Add class for D7-backwards compatibility (only on container). $element['workflow']['#attributes']['class'][] = 'workflow-form-container'; if (!$show_widget) { // Show no widget. $element['workflow']['workflow_sid']['#type'] = 'value'; $element['workflow']['workflow_sid']['#value'] = $default_value; $element['workflow']['workflow_sid']['#options'] = $options; // In case action buttons need them. $form += $element; return $form; // <---- exit. } else { // Prepare a UI wrapper. This might be a fieldset or a container. if ($settings_fieldset == 0) { // Use 'container'. $element['workflow'] += array('#type' => 'container'); } else { $element['workflow'] += array('#type' => 'fieldset', '#title' => t($workflow_label), '#collapsible' => TRUE, '#collapsed' => $settings_fieldset == 1 ? FALSE : TRUE); } // The 'options' widget. May be removed later if 'Action buttons' are chosen. // The help text is not available for container. Let's add it to the // State box. $help_text = isset($instance['description']) ? $instance['description'] : ''; $element['workflow']['workflow_sid'] = array('#type' => $settings_options_type, '#title' => $settings_title_as_name ? t('Change !name state', array('!name' => $workflow_label)) : t('Target state'), '#access' => TRUE, '#options' => $options, '#default_value' => $default_value, '#description' => $help_text); } // Display scheduling form, but only if entity is being edited and user has // permission. State change cannot be scheduled at entity creation because // that leaves the entity in the (creation) state. if ($settings_schedule == TRUE) { if (variable_get('configurable_timezones', 1) && $user->uid && drupal_strlen($user->timezone)) { $timezone = $user->timezone; } else { $timezone = variable_get('date_default_timezone', 0); } $timezones = drupal_map_assoc(timezone_identifiers_list()); $timestamp = $transition->getTimestamp(); $hours = !$transition->isScheduled() ? '00:00' : format_date($timestamp, 'custom', 'H:i', $timezone); // Add a container, so checkbox and time stay together in extra fields. $element['workflow']['workflow_scheduling'] = array('#type' => 'container', '#tree' => TRUE); $element['workflow']['workflow_scheduling']['scheduled'] = array('#type' => 'radios', '#title' => t('Schedule'), '#options' => array('0' => t('Immediately'), '1' => t('Schedule for state change')), '#default_value' => $transition->isScheduled() ? '1' : '0', '#attributes' => array('class' => array(drupal_html_class('scheduled_' . $form_id)))); $element['workflow']['workflow_scheduling']['date_time'] = array('#type' => 'fieldset', '#title' => t('At'), '#attributes' => array('class' => array('container-inline')), '#prefix' => '<div style="margin-left: 1em;">', '#suffix' => '</div>', '#states' => array('visible' => array('input.' . drupal_html_class('scheduled_' . $form_id) => array('value' => '1')))); $element['workflow']['workflow_scheduling']['date_time']['workflow_scheduled_date'] = array('#type' => 'date', '#default_value' => array('day' => date('j', $timestamp), 'month' => date('n', $timestamp), 'year' => date('Y', $timestamp))); $element['workflow']['workflow_scheduling']['date_time']['workflow_scheduled_hour'] = array('#type' => 'textfield', '#title' => t('Time'), '#maxlength' => 7, '#size' => 6, '#default_value' => $hours, '#element_validate' => array('_workflow_transition_form_element_validate_time')); $element['workflow']['workflow_scheduling']['date_time']['workflow_scheduled_timezone'] = array('#type' => $settings_schedule_timezone ? 'select' : 'hidden', '#title' => t('Time zone'), '#options' => $timezones, '#default_value' => array($timezone => $timezone)); $element['workflow']['workflow_scheduling']['date_time']['workflow_scheduled_help'] = array('#type' => 'item', '#prefix' => '<br />', '#description' => t('Please enter a time. If no time is included, the default will be midnight on the specified date. The current time is: @time.', array('@time' => format_date(REQUEST_TIME, 'custom', 'H:i', $timezone)))); } $element['workflow']['workflow_comment'] = array('#type' => 'textarea', '#required' => $settings_comment == '2', '#access' => $settings_comment != '0', '#title' => t('Workflow comment'), '#description' => t('A comment to put in the workflow log.'), '#default_value' => $transition->comment, '#rows' => 2); // Add the fields and extra_fields from the WorkflowTransition. // Because we have a 'workflow' wrapper, it doesn't work flawlessly. field_attach_form('WorkflowTransition', $transition, $element['workflow'], $form_state); // Undo the following elements from field_attach_from. They mess up $this->getTransition(). // - '#parents' corrupts the Defaultwidget. unset($element['workflow']['#parents']); // - '#pre_render' adds the exra_fields from workflow_field_extra_fields(). // That doesn't work, since 'workflow' is not of #type 'form', but // 'container' or 'fieldset', and must be executed separately,. $element['workflow']['#pre_render'] = array_diff($element['workflow']['#pre_render'], array('_field_extra_fields_pre_render')); // Add extra fields. $rescue_value = $element['workflow']['#type']; $element['workflow']['#type'] = 'form'; $element['workflow'] = _field_extra_fields_pre_render($element['workflow']); $element['workflow']['#type'] = $rescue_value; // Finally, add Submit buttons/Action buttons. // Either a default 'Submit' button is added, or a button per permitted state. if ($settings_options_type == 'buttons') { // How do action buttons work? See also d.o. issue #2187151. // Create 'action buttons' per state option. Set $sid property on each button. // 1. Admin sets ['widget']['options']['#type'] = 'buttons'. // 2. This function formElement() creates 'action buttons' per state option; // sets $sid property on each button. // 3. User clicks button. // 4. Callback _workflow_transition_form_validate_buttons() sets proper State. // 5. Callback _workflow_transition_form_validate_buttons() sets Submit function. // @todo: this does not work yet for the Add Comment form. // Performance: inform workflow_form_alter() to do its job. _workflow_use_action_buttons(TRUE); // Hide the options box. It will be replaced by action buttons. $element['workflow']['workflow_sid']['#type'] = 'select'; $element['workflow']['workflow_sid']['#access'] = FALSE; } if ($form_state['build_info']['base_form_id'] == 'workflow_transition_form') { // Add action buttons on WorkflowTransitionForm (history tab, formatter) // but not on Entity form, and not if action_buttons is selected. // you can explicitly NOT add a submit button, e.g., on VBO page. if ($instance['widget']['settings']['submit_function'] !== '') { // @todo D8: put buttons outside of 'workflow' element, in the standard location. $element['workflow']['actions']['#type'] = 'actions'; $element['workflow']['actions']['submit'] = array('#type' => 'submit', '#value' => t('Update workflow'), '#weight' => -5, '#attributes' => array('class' => array('form-save-default-button'))); // The 'add submit' can explicitly set by workflowfield_field_formatter_view(), // to add the submit button on the Content view page and the Workflow history tab. // Add a submit button, but only on Entity View and History page. // Add the submit function only if one provided. Set the submit_callback accordingly. if (!empty($instance['widget']['settings']['submit_function'])) { $element['workflow']['actions']['submit']['#submit'] = array($instance['widget']['settings']['submit_function']); } else { // '#submit' Must be empty, or else the submit function is not called. // $element['workflow']['actions']['submit']['#submit'] = array(); } } } /* $submit_functions = empty($instance['widget']['settings']['submit_function']) ? array() : array($instance['widget']['settings']['submit_function']); if ($settings_options_type == 'buttons' || $submit_functions) { } else { // In some cases, no submit callback function is specified. This is // explicitly done on e.g., the node edit form, because the workflow form // is 'just a field'. // So, no Submit button is to be shown. } */ $form += $element; // Add class following node-form pattern (both on form and container). $workflow_type_id = $workflow ? $workflow->getName() : 'none'; // No workflow on New Action form. $form['#attributes']['class'][] = 'workflow-transition-form'; $form['#attributes']['class'][] = 'workflow-transition-' . $workflow_type_id . '-form'; return $form; }