Version 1
[yaffs-website] / web / core / modules / content_moderation / src / Plugin / Field / FieldWidget / ModerationStateWidget.php
1 <?php
2
3 namespace Drupal\content_moderation\Plugin\Field\FieldWidget;
4
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;
16
17 /**
18  * Plugin implementation of the 'moderation_state_default' widget.
19  *
20  * @FieldWidget(
21  *   id = "moderation_state_default",
22  *   label = @Translation("Moderation state"),
23  *   field_types = {
24  *     "string"
25  *   }
26  * )
27  */
28 class ModerationStateWidget extends OptionsSelectWidget implements ContainerFactoryPluginInterface {
29
30   /**
31    * Current user service.
32    *
33    * @var \Drupal\Core\Session\AccountInterface
34    */
35   protected $currentUser;
36
37   /**
38    * Moderation information service.
39    *
40    * @var \Drupal\content_moderation\ModerationInformation
41    */
42   protected $moderationInformation;
43
44   /**
45    * The entity type manager.
46    *
47    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
48    */
49   protected $entityTypeManager;
50
51   /**
52    * Moderation state transition validation service.
53    *
54    * @var \Drupal\content_moderation\StateTransitionValidation
55    */
56   protected $validator;
57
58   /**
59    * Constructs a new ModerationStateWidget object.
60    *
61    * @param string $plugin_id
62    *   Plugin id.
63    * @param mixed $plugin_definition
64    *   Plugin definition.
65    * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
66    *   Field definition.
67    * @param array $settings
68    *   Field 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
79    */
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;
86   }
87
88   /**
89    * {@inheritdoc}
90    */
91   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
92     return new static(
93       $plugin_id,
94       $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')
102     );
103   }
104
105   /**
106    * {@inheritdoc}
107    */
108   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
109     /** @var ContentEntityInterface $entity */
110     $entity = $items->getEntity();
111
112     if (!$this->moderationInformation->isModeratedEntity($entity)) {
113       // @todo https://www.drupal.org/node/2779933 write a test for this.
114       return $element + ['#access' => FALSE];
115     }
116
117     $workflow = $this->moderationInformation->getWorkflowForEntity($entity);
118     $default = $items->get($delta)->value ? $workflow->getState($items->get($delta)->value) : $workflow->getTypePlugin()->getInitialState($workflow, $entity);
119
120     /** @var \Drupal\workflows\Transition[] $transitions */
121     $transitions = $this->validator->getValidTransitions($entity, $this->currentUser);
122
123     $target_states = [];
124     foreach ($transitions as $transition) {
125       $target_states[$transition->to()->id()] = $transition->label();
126     }
127
128     // @todo https://www.drupal.org/node/2779933 write a test for this.
129     $element += [
130       '#access' => FALSE,
131       '#type' => 'select',
132       '#options' => $target_states,
133       '#default_value' => $default->id(),
134       '#published' => $default->isPublishedState(),
135       '#key_column' => $this->column,
136     ];
137     $element['#element_validate'][] = [get_class($this), 'validateElement'];
138
139     // Use the dropbutton.
140     $element['#process'][] = [get_called_class(), 'processActions'];
141     return $element;
142   }
143
144   /**
145    * Entity builder updating the node moderation state with the submitted value.
146    *
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.
151    * @param array $form
152    *   The complete form array.
153    * @param \Drupal\Core\Form\FormStateInterface $form_state
154    *   The current state of the form.
155    */
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'];
160     }
161   }
162
163   /**
164    * Process callback to alter action buttons.
165    */
166   public static function processActions($element, FormStateInterface $form_state, array &$form) {
167
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;
173
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'];
177
178     $entity = $form_state->getFormObject()->getEntity();
179     $translatable = !$entity->isNew() && $entity->isTranslatable();
180     foreach ($options as $id => $label) {
181       $button = [
182         '#dropbutton' => 'save',
183         '#moderation_state' => $id,
184         '#weight' => -10,
185       ];
186
187       $button['#value'] = $translatable
188         ? t('Save and @transition (this translation)', ['@transition' => $label])
189         : t('Save and @transition', ['@transition' => $label]);
190
191       $form['actions']['moderation_state_' . $id] = $button + $default_button;
192     }
193
194     // Hide the default buttons, including the specialty ones added by
195     // NodeForm.
196     foreach (['publish', 'unpublish', 'submit'] as $key) {
197       $form['actions'][$key]['#access'] = FALSE;
198       unset($form['actions'][$key]['#dropbutton']);
199     }
200
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'];
204     return $element;
205   }
206
207   /**
208    * {@inheritdoc}
209    */
210   public static function isApplicable(FieldDefinitionInterface $field_definition) {
211     return $field_definition->getName() === 'moderation_state';
212   }
213
214 }