public static function WorkflowTransitionElement::transitionElement in Workflow 8
Generate an element.
This function is an internal function, to be reused in:
- TransitionElement,
- TransitionDefaultWidget.
@usage: @example $element['#default_value'] = $transition; @example $element += WorkflowTransitionElement::transitionElement($element, $form_state, $form);
Parameters
array $element: Reference to the form element.
\Drupal\Core\Form\FormStateInterface $form_state:
array $complete_form:
Return value
array The form element $element.
4 calls to WorkflowTransitionElement::transitionElement()
- WorkflowDefaultWidget::formElement in src/
Plugin/ Field/ FieldWidget/ WorkflowDefaultWidget.php - Be careful: Widget may be shown in very different places. Test carefully!!
- WorkflowStateActionBase::buildConfigurationForm in src/
Plugin/ Action/ WorkflowStateActionBase.php - Form constructor.
- WorkflowTransitionElement::processTransition in src/
Element/ WorkflowTransitionElement.php - Generate an element.
- WorkflowTransitionForm::form in src/
Form/ WorkflowTransitionForm.php - This function is called by buildForm().
File
- src/
Element/ WorkflowTransitionElement.php, line 62
Class
- WorkflowTransitionElement
- Provides a form element for the WorkflowTransitionForm and ~Widget.
Namespace
Drupal\workflow\ElementCode
public static function transitionElement(array &$element, FormStateInterface $form_state, array &$complete_form) {
/*
* Input.
*/
// A Transition object must have been set explicitly.
/** @var \Drupal\workflow\Entity\WorkflowTransitionInterface $transition */
$transition = $element['#default_value'];
/** @var \Drupal\Core\Session\AccountInterface $user */
$user = \Drupal::currentUser();
/*
* Derived input.
*/
$field_name = $transition
->getFieldName();
$workflow = $transition
->getWorkflow();
$wid = $transition
->getWorkflowId();
$force = $transition
->isForced();
$entity = $transition
->getTargetEntity();
$entity_type = $transition
->getTargetEntityTypeId();
$entity_id = $transition
->getTargetEntityId();
if ($transition
->isExecuted()) {
// We are editing an existing/executed/not-scheduled transition.
// Only the comments may be changed!
$current_sid = $from_sid = $transition
->getFromSid();
// The states may not be changed anymore.
$to_state = $transition
->getToState();
$options = [
$to_state
->id() => $to_state
->label(),
];
// We need the widget to edit the comment.
$show_widget = TRUE;
$default_value = $transition
->getToSid();
}
elseif ($entity) {
// Normal situation: adding a new transition on an new/existing entity.
//
// Get the scheduling info, only when updating an existing entity.
// This may change the $default_value on the Form.
// Technically you could have more than one scheduled transition, but
// this will only add the soonest one.
// @todo Read the history with an explicit langcode?
$langcode = '';
// $entity->language()->getId();
if ($entity_id && ($scheduled_transition = WorkflowScheduledTransition::loadByProperties($entity_type, $entity_id, [], $field_name, $langcode))) {
$transition = $scheduled_transition;
}
$current_sid = $from_sid = $transition
->getFromSid();
$current_state = $from_state = $transition
->getFromState();
$options = $current_state ? $current_state
->getOptions($entity, $field_name, $user, FALSE) : [];
$show_widget = $from_state ? $from_state
->showWidget($entity, $field_name, $user, FALSE) : [];
$default_value = $from_sid;
$default_value = $from_state && $from_state
->isCreationState() ? $workflow
->getFirstSid($entity, $field_name, $user, FALSE) : $default_value;
$default_value = $transition
->isScheduled() ? $transition
->getToSid() : $default_value;
}
elseif (!$entity) {
// Sometimes, no entity is given. We encountered the following cases:
// - D7: the Field settings page,
// - D7: the VBO action form;
// - D7/D8: the Advance Action form on admin/config/system/actions;
// If so, show all options for the given workflow(s).
if (!($temp_state = $transition
->getFromState())) {
$temp_state = $transition
->getToState();
}
$options = $temp_state ? $temp_state
->getOptions($entity, $field_name, $user, FALSE) : workflow_get_workflow_state_names($wid, $grouped = TRUE);
$show_widget = TRUE;
$current_sid = $transition
->getToSid();
$default_value = $from_sid = $transition
->getToSid();
}
else {
// We are in trouble! A message is already set in workflow_node_current_state().
$options = [];
$current_sid = 0;
$show_widget = FALSE;
$default_value = FALSE;
}
/*
* Output: generate the element.
*/
// Get settings from workflow.
if (!$workflow) {
// Might be empty on Action configuration.
$workflow_settings = Workflow::defaultSettings();
$wid = '';
}
else {
$workflow_settings = $workflow
->getSettings();
$wid = $workflow
->id();
// Might be empty on Action/VBO configuration.
}
$settings_options_type = $workflow_settings['options'];
// Display scheduling form if user has permission.
// Not shown on new entity (not supported by workflow module, because that
// leaves the entity in the (creation) state until scheduling time.)
// Not shown when editing existing transition.
$settings_schedule = !$transition
->isExecuted() && $user
->hasPermission("schedule {$wid} workflow_transition") && $workflow_settings['schedule_enable'];
if ($settings_schedule) {
// @todo D8: check below code: form on VBO.
// workflow_debug(__FILE__, __FUNCTION__, __LINE__); // @todo D8: test this snippet.
$step = $form_state
->getValue('step');
if (isset($step) && $form_state
->getValue('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);
}
}
// Save the current value of the entity in the form, for later Workflow-module specific references.
// We add prefix, since #tree == FALSE.
$element['workflow_transition'] = [
'#type' => 'value',
'#value' => $transition,
];
// Decide if we show either a widget or a formatter.
// Add a state formatter before the rest of the form,
// when transition is scheduled or widget is hidden.
// Also no widget if the only option is the current sid.
if (!$show_widget || $transition
->isScheduled() || $transition
->isExecuted()) {
if ($entity) {
// may be empty in VBO.
$element['workflow_current_state'] = workflow_state_formatter($entity, $field_name, $current_sid);
// Set a proper weight, which works for Workflow Options in select list AND action buttons.
$element['workflow_current_state']['#weight'] = -0.005;
}
}
$element['#tree'] = TRUE;
// Add class following node-form pattern (both on form and container).
$workflow_type_id = $workflow ? $workflow
->id() : '';
$element['#attributes']['class'][] = 'workflow-transition-' . $workflow_type_id . '-container';
$element['#attributes']['class'][] = 'workflow-transition-container';
if (!$show_widget) {
// Show no widget.
$element['to_sid']['#type'] = 'value';
$element['to_sid']['#value'] = $default_value;
$element['to_sid']['#options'] = $options;
// In case action buttons need them.
$element['comment']['#type'] = 'value';
$element['comment']['#value'] = '';
return $element;
// <-- exit.
}
// Prepare a UI wrapper. This might be a fieldset.
$element['#type'] = 'container';
if ($workflow_settings['fieldset']) {
unset($element['#type']);
$element += [
'#type' => 'details',
'#collapsible' => TRUE,
'#open' => $workflow_settings['fieldset'] != 2,
];
}
$element['field_name'] = [
'#type' => 'select',
'#title' => t('Field name'),
'#description' => t('Choose the field name.'),
'#access' => FALSE,
// Only show on VBO/Actions screen.
'#options' => workflow_get_workflow_field_names($entity),
'#default_value' => $field_name,
'#required' => TRUE,
'#weight' => -20,
];
// Add the 'options' widget.
// It may be replaced later if 'Action buttons' are chosen.
// The help text is not available for container. Let's add it to the
// State box. N.B. it is empty on Workflow Tab, Node View page.
$help_text = isset($element['#description']) ? $element['#description'] : '';
unset($element['#description']);
// This overrides BaseFieldDefinition. @todo Apply for form and widget.
// @todo Repair $workflow->'name_as_title': no container if no details (schedule/comment).
$element['to_sid'] = [
'#type' => $wid ? $settings_options_type : 'select',
// Avoid error with grouped options.
'#title' => !$workflow_settings['name_as_title'] && !$transition
->isExecuted() ? t('Change @name state', [
'@name' => $workflow
->label(),
]) : t('Change state'),
'#access' => TRUE,
'#options' => $options,
'#default_value' => $default_value,
'#description' => $help_text,
];
$element['force'] = [
'#type' => 'checkbox',
'#title' => t('Force transition'),
'#description' => t('If this box is checked, the new state will be
assigned even if workflow permissions disallow it.'),
'#access' => FALSE,
// Only show on VBO/Actions screen.
'#default_value' => $force,
'#weight' => -19,
];
// Display scheduling form under certain conditions.
if ($settings_schedule) {
$timezone = $user
->getTimeZone();
if (empty($timezone)) {
$timezone = \Drupal::config('system.date')
->get('timezone.default');
}
$timezone_options = array_combine(timezone_identifiers_list(), timezone_identifiers_list());
$timestamp = $transition
->getTimestamp();
$hours = $transition
->isScheduled() ? \Drupal::service('date.formatter')
->format($timestamp, 'custom', 'H:i', $timezone) : '00:00';
// Add a container, so checkbox and time stay together in extra fields.
$element['workflow_scheduling'] = [
'#type' => 'container',
'#tree' => TRUE,
];
// Define class for '#states' behaviour.
// 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 indeed on a Transition form (so, not a Node Form with widget)
// then change the form id, too.
$form_id = $form_state
->getBuildInfo()['form_id'];
// @todo Align with WorkflowTransitionForm->getFormId().
$class_identifier = Html::getClass('scheduled_' . Html::getUniqueId($form_id) . '-' . $field_name);
$element['workflow_scheduling']['scheduled'] = [
'#type' => 'radios',
'#title' => t('Schedule'),
'#options' => [
'0' => t('Immediately'),
'1' => t('Schedule for state change'),
],
'#default_value' => (string) $transition
->isScheduled(),
'#attributes' => [
// 'id' => 'scheduled_' . $form_id,
'class' => [
$class_identifier,
],
],
];
$element['workflow_scheduling']['date_time'] = [
'#type' => 'details',
// 'container',
'#open' => TRUE,
// Controls the HTML5 'open' attribute. Defaults to FALSE.
'#attributes' => [
'class' => [
'container-inline',
],
],
'#prefix' => '<div style="margin-left: 1em;">',
'#suffix' => '</div>',
'#states' => [
'visible' => [
'input.' . $class_identifier => [
'value' => '1',
],
],
],
];
$element['workflow_scheduling']['date_time']['workflow_scheduled_date'] = [
'#type' => 'date',
'#prefix' => t('At'),
'#default_value' => implode('-', [
'year' => date('Y', $timestamp),
'month' => date('m', $timestamp),
'day' => date('d', $timestamp),
]),
];
$element['workflow_scheduling']['date_time']['workflow_scheduled_hour'] = [
'#type' => 'textfield',
'#title' => t('Time'),
'#maxlength' => 7,
'#size' => 6,
'#default_value' => $hours,
'#element_validate' => [
'_workflow_transition_form_element_validate_time',
],
];
$element['workflow_scheduling']['date_time']['workflow_scheduled_timezone'] = [
'#type' => $workflow_settings['schedule_timezone'] ? 'select' : 'hidden',
'#title' => t('Time zone'),
'#options' => $timezone_options,
'#default_value' => [
$timezone => $timezone,
],
];
$element['workflow_scheduling']['date_time']['workflow_scheduled_help'] = [
'#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.', [
'@time' => \Drupal::service('date.formatter')
->format(\Drupal::time()
->getRequestTime(), 'custom', 'H:i', $timezone),
]),
];
}
// Show comment, when both Field and Instance allow this.
// This overrides BaseFieldDefinition. @todo Apply for form and widget.
$element['comment'] = [
'#type' => 'textarea',
'#required' => $workflow_settings['comment_log_node'] == '2',
'#access' => $workflow_settings['comment_log_node'] != '0',
// Align with action buttons.
'#title' => t('Comment'),
'#description' => t('Briefly describe the changes you have made.'),
'#default_value' => $transition
->getComment(),
'#rows' => 2,
];
if ($settings_options_type == 'buttons' || $settings_options_type == 'dropbutton') {
// In WorkflowTransitionForm, a default 'Submit' button is added there.
// In Entity Form, workflow_form_alter() adds button per permitted state.
//
// D7: How do action buttons work? See also d.o. issue #2187151.
// D7: 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 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.
//
// Performance: inform workflow_form_alter() to do its job.
_workflow_use_action_buttons($settings_options_type);
// Make sure the '#type' is not set to the invalid 'buttons' value.
// It will be replaced by action buttons, but sometimes, the select box
// is still shown.
// @see workflow_form_alter().
$element['to_sid']['#type'] = 'select';
$element['to_sid']['#access'] = FALSE;
}
return $element;
}