Version 1
[yaffs-website] / web / core / modules / content_moderation / src / Plugin / Field / FieldWidget / ModerationStateWidget.php
diff --git a/web/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php b/web/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php
new file mode 100644 (file)
index 0000000..a8abe07
--- /dev/null
@@ -0,0 +1,214 @@
+<?php
+
+namespace Drupal\content_moderation\Plugin\Field\FieldWidget;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\Plugin\Field\FieldWidget\OptionsSelectWidget;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\content_moderation\ModerationInformation;
+use Drupal\content_moderation\StateTransitionValidation;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Plugin implementation of the 'moderation_state_default' widget.
+ *
+ * @FieldWidget(
+ *   id = "moderation_state_default",
+ *   label = @Translation("Moderation state"),
+ *   field_types = {
+ *     "string"
+ *   }
+ * )
+ */
+class ModerationStateWidget extends OptionsSelectWidget implements ContainerFactoryPluginInterface {
+
+  /**
+   * Current user service.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Moderation information service.
+   *
+   * @var \Drupal\content_moderation\ModerationInformation
+   */
+  protected $moderationInformation;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Moderation state transition validation service.
+   *
+   * @var \Drupal\content_moderation\StateTransitionValidation
+   */
+  protected $validator;
+
+  /**
+   * Constructs a new ModerationStateWidget object.
+   *
+   * @param string $plugin_id
+   *   Plugin id.
+   * @param mixed $plugin_definition
+   *   Plugin definition.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   Field definition.
+   * @param array $settings
+   *   Field settings.
+   * @param array $third_party_settings
+   *   Third party settings.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   Current user service.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   Entity type manager.
+   * @param \Drupal\content_moderation\ModerationInformation $moderation_information
+   *   Moderation information service.
+   * @param \Drupal\content_moderation\StateTransitionValidation $validator
+   *   Moderation state transition validation service
+   */
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, ModerationInformation $moderation_information, StateTransitionValidation $validator) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
+    $this->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';
+  }
+
+}