entityTypeManager = $entity_type_manager; $this->currentUser = $current_user; $this->moderationInformation = $moderation_information; $this->validator = $validator; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['third_party_settings'], $container->get('current_user'), $container->get('entity_type.manager'), $container->get('content_moderation.moderation_information'), $container->get('content_moderation.state_transition_validation') ); } /** * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { /** @var ContentEntityInterface $entity */ $entity = $items->getEntity(); if (!$this->moderationInformation->isModeratedEntity($entity)) { // @todo https://www.drupal.org/node/2779933 write a test for this. return $element + ['#access' => FALSE]; } $workflow = $this->moderationInformation->getWorkflowForEntity($entity); $default = $items->get($delta)->value ? $workflow->getState($items->get($delta)->value) : $workflow->getTypePlugin()->getInitialState($workflow, $entity); /** @var \Drupal\workflows\Transition[] $transitions */ $transitions = $this->validator->getValidTransitions($entity, $this->currentUser); $target_states = []; foreach ($transitions as $transition) { $target_states[$transition->to()->id()] = $transition->label(); } // @todo https://www.drupal.org/node/2779933 write a test for this. $element += [ '#access' => FALSE, '#type' => 'select', '#options' => $target_states, '#default_value' => $default->id(), '#published' => $default->isPublishedState(), '#key_column' => $this->column, ]; $element['#element_validate'][] = [get_class($this), 'validateElement']; // Use the dropbutton. $element['#process'][] = [get_called_class(), 'processActions']; return $element; } /** * Entity builder updating the node moderation state with the submitted value. * * @param string $entity_type_id * The entity type identifier. * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity updated with the submitted values. * @param array $form * The complete form array. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. */ public static function updateStatus($entity_type_id, ContentEntityInterface $entity, array $form, FormStateInterface $form_state) { $element = $form_state->getTriggeringElement(); if (isset($element['#moderation_state'])) { $entity->moderation_state->value = $element['#moderation_state']; } } /** * Process callback to alter action buttons. */ public static function processActions($element, FormStateInterface $form_state, array &$form) { // We'll steal most of the button configuration from the default submit // button. However, NodeForm also hides that button for admins (as it adds // its own, too), so we have to restore it. $default_button = $form['actions']['submit']; $default_button['#access'] = TRUE; // Add a custom button for each transition we're allowing. The #dropbutton // property tells FAPI to cluster them all together into a single widget. $options = $element['#options']; $entity = $form_state->getFormObject()->getEntity(); $translatable = !$entity->isNew() && $entity->isTranslatable(); foreach ($options as $id => $label) { $button = [ '#dropbutton' => 'save', '#moderation_state' => $id, '#weight' => -10, ]; $button['#value'] = $translatable ? t('Save and @transition (this translation)', ['@transition' => $label]) : t('Save and @transition', ['@transition' => $label]); $form['actions']['moderation_state_' . $id] = $button + $default_button; } // Hide the default buttons, including the specialty ones added by // NodeForm. foreach (['publish', 'unpublish', 'submit'] as $key) { $form['actions'][$key]['#access'] = FALSE; unset($form['actions'][$key]['#dropbutton']); } // Setup a callback to translate the button selection back into field // widget, so that it will get saved properly. $form['#entity_builders']['update_moderation_state'] = [get_called_class(), 'updateStatus']; return $element; } /** * {@inheritdoc} */ public static function isApplicable(FieldDefinitionInterface $field_definition) { return $field_definition->getName() === 'moderation_state'; } }