3 namespace Drupal\content_moderation\Plugin\Field\FieldWidget;
5 use Drupal\Core\Entity\ContentEntityInterface;
6 use Drupal\Core\Entity\EntityTypeManagerInterface;
7 use Drupal\Core\Field\FieldDefinitionInterface;
8 use Drupal\Core\Field\FieldItemListInterface;
9 use Drupal\Core\Field\Plugin\Field\FieldWidget\OptionsSelectWidget;
10 use Drupal\Core\Form\FormStateInterface;
11 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
12 use Drupal\Core\Session\AccountInterface;
13 use Drupal\content_moderation\ModerationInformation;
14 use Drupal\content_moderation\StateTransitionValidation;
15 use Symfony\Component\DependencyInjection\ContainerInterface;
18 * Plugin implementation of the 'moderation_state_default' widget.
21 * id = "moderation_state_default",
22 * label = @Translation("Moderation state"),
28 class ModerationStateWidget extends OptionsSelectWidget implements ContainerFactoryPluginInterface {
31 * Current user service.
33 * @var \Drupal\Core\Session\AccountInterface
35 protected $currentUser;
38 * Moderation information service.
40 * @var \Drupal\content_moderation\ModerationInformation
42 protected $moderationInformation;
45 * The entity type manager.
47 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
49 protected $entityTypeManager;
52 * Moderation state transition validation service.
54 * @var \Drupal\content_moderation\StateTransitionValidation
59 * Constructs a new ModerationStateWidget object.
61 * @param string $plugin_id
63 * @param mixed $plugin_definition
65 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
67 * @param array $settings
69 * @param array $third_party_settings
70 * Third party settings.
71 * @param \Drupal\Core\Session\AccountInterface $current_user
72 * Current user service.
73 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
74 * Entity type manager.
75 * @param \Drupal\content_moderation\ModerationInformation $moderation_information
76 * Moderation information service.
77 * @param \Drupal\content_moderation\StateTransitionValidation $validator
78 * Moderation state transition validation service
80 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) {
81 parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
82 $this->entityTypeManager = $entity_type_manager;
83 $this->currentUser = $current_user;
84 $this->moderationInformation = $moderation_information;
85 $this->validator = $validator;
91 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
95 $configuration['field_definition'],
96 $configuration['settings'],
97 $configuration['third_party_settings'],
98 $container->get('current_user'),
99 $container->get('entity_type.manager'),
100 $container->get('content_moderation.moderation_information'),
101 $container->get('content_moderation.state_transition_validation')
108 public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
109 /** @var ContentEntityInterface $entity */
110 $entity = $items->getEntity();
112 if (!$this->moderationInformation->isModeratedEntity($entity)) {
113 // @todo https://www.drupal.org/node/2779933 write a test for this.
114 return $element + ['#access' => FALSE];
117 $workflow = $this->moderationInformation->getWorkflowForEntity($entity);
118 $default = $items->get($delta)->value ? $workflow->getState($items->get($delta)->value) : $workflow->getTypePlugin()->getInitialState($workflow, $entity);
120 /** @var \Drupal\workflows\Transition[] $transitions */
121 $transitions = $this->validator->getValidTransitions($entity, $this->currentUser);
124 foreach ($transitions as $transition) {
125 $target_states[$transition->to()->id()] = $transition->label();
128 // @todo https://www.drupal.org/node/2779933 write a test for this.
132 '#options' => $target_states,
133 '#default_value' => $default->id(),
134 '#published' => $default->isPublishedState(),
135 '#key_column' => $this->column,
137 $element['#element_validate'][] = [get_class($this), 'validateElement'];
139 // Use the dropbutton.
140 $element['#process'][] = [get_called_class(), 'processActions'];
145 * Entity builder updating the node moderation state with the submitted value.
147 * @param string $entity_type_id
148 * The entity type identifier.
149 * @param \Drupal\Core\Entity\ContentEntityInterface $entity
150 * The entity updated with the submitted values.
152 * The complete form array.
153 * @param \Drupal\Core\Form\FormStateInterface $form_state
154 * The current state of the form.
156 public static function updateStatus($entity_type_id, ContentEntityInterface $entity, array $form, FormStateInterface $form_state) {
157 $element = $form_state->getTriggeringElement();
158 if (isset($element['#moderation_state'])) {
159 $entity->moderation_state->value = $element['#moderation_state'];
164 * Process callback to alter action buttons.
166 public static function processActions($element, FormStateInterface $form_state, array &$form) {
168 // We'll steal most of the button configuration from the default submit
169 // button. However, NodeForm also hides that button for admins (as it adds
170 // its own, too), so we have to restore it.
171 $default_button = $form['actions']['submit'];
172 $default_button['#access'] = TRUE;
174 // Add a custom button for each transition we're allowing. The #dropbutton
175 // property tells FAPI to cluster them all together into a single widget.
176 $options = $element['#options'];
178 $entity = $form_state->getFormObject()->getEntity();
179 $translatable = !$entity->isNew() && $entity->isTranslatable();
180 foreach ($options as $id => $label) {
182 '#dropbutton' => 'save',
183 '#moderation_state' => $id,
187 $button['#value'] = $translatable
188 ? t('Save and @transition (this translation)', ['@transition' => $label])
189 : t('Save and @transition', ['@transition' => $label]);
191 $form['actions']['moderation_state_' . $id] = $button + $default_button;
194 // Hide the default buttons, including the specialty ones added by
196 foreach (['publish', 'unpublish', 'submit'] as $key) {
197 $form['actions'][$key]['#access'] = FALSE;
198 unset($form['actions'][$key]['#dropbutton']);
201 // Setup a callback to translate the button selection back into field
202 // widget, so that it will get saved properly.
203 $form['#entity_builders']['update_moderation_state'] = [get_called_class(), 'updateStatus'];
210 public static function isApplicable(FieldDefinitionInterface $field_definition) {
211 return $field_definition->getName() === 'moderation_state';