lightning_scheduler-8.x-1.x-dev/src/Plugin/Field/FieldWidget/ModerationStateWidget.php
src/Plugin/Field/FieldWidget/ModerationStateWidget.php
<?php
namespace Drupal\lightning_scheduler\Plugin\Field\FieldWidget;
use Drupal\Component\Serialization\Json;
use Drupal\content_moderation\Plugin\Field\FieldWidget\ModerationStateWidget as BaseModerationStateWidget;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\lightning_scheduler\TransitionManager;
use Drupal\lightning_scheduler\TransitionSet;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Scheduler extension of Content Moderation's widget.
*
* @internal
* This is an internal part of Lightning Scheduler and may be changed or
* removed at any time without warning. It should not be used by external
* code in any way.
*/
final class ModerationStateWidget extends BaseModerationStateWidget {
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$configuration['third_party_settings'] += [
'lightning_scheduler' => [
'time_step' => $container->get('config.factory')->get('lightning_scheduler.settings')->get('time_step'),
],
];
return parent::create($container, $configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
$entity = $items->getEntity();
assert($entity instanceof ContentEntityInterface);
// The entity must have the proper fields.
$has_fields = $entity->hasField('scheduled_transition_date') && $entity->hasField('scheduled_transition_state');
if (! $has_fields) {
return $element;
}
$states = $this->getStates($entity);
// The latest revision, if there is one, is the canonical source of truth
// regarding scheduled transitions.
/** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
$storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId());
if (!$entity->isNew() && $storage->getEntityType()->isRevisionable() && $latest_revision_id = $storage->getLatestRevisionId($entity->id())) {
$latest_revision = $storage->loadRevision($latest_revision_id) ?: $entity;
}
else {
$latest_revision = $entity;
}
$transition_set = new TransitionSet(
$latest_revision->get('scheduled_transition_date'),
$latest_revision->get('scheduled_transition_state')
);
// Get the allow_past_dates configuration setting.
$allow_past_dates = \Drupal::configFactory()
->get('lightning_scheduler.settings')
->get('allow_past_dates');
$element['scheduled_transitions'] = [
'#type' => 'html_tag',
'#tag' => 'TransitionSet',
'#attributes' => [
'states' => Json::encode($states),
'step' => $this->getThirdPartySetting('lightning_scheduler', 'time_step', 60),
],
'#attached' => [
'library' => ['lightning_scheduler/widget'],
'drupalSettings' => [
'lightning_scheduler' => [
'allow_past_dates' => $allow_past_dates,
],
],
],
'data' => [
'#type' => 'hidden',
'#entity_uuid' => $entity->uuid(),
'#element_validate' => [
[
TransitionManager::class,
'validate',
],
[$this, 'storeValue'],
],
'#default_value' => $transition_set->toJSON(),
'#process' => [
[$this, 'processComponentInput'],
],
],
];
return $element;
}
/**
* #process callback for the scheduler component's input element.
*
* @param array $element
* The unprocessed element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current form state.
*
* @return array
* The processed element.
*/
public function processComponentInput(array $element, FormStateInterface $form_state) {
$key = $element['#parents'];
if ($form_state->hasValue($key)) {
$element['#default_value'] = $form_state->getValue($key);
}
return $element;
}
/**
* Validation method that accesses the hidden input element, and stores its
* value in the form state.
*
* @param array $element
* The hidden input.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state to update.
*/
public function storeValue(array $element, FormStateInterface $form_state) {
if ($form_state->getErrors()) {
return;
}
assert(! empty($element['#entity_uuid']));
$decoded = Json::decode($element['#value']);
assert(is_array($decoded));
$transition_storage = $form_state->getValue('transition_storage') ?: [];
// Support multiple widgets on one form (e.g. Inline Entity Form).
$uuid = $element['#entity_uuid'];
$transition_storage[$uuid] = $decoded;
$form_state->setValue('transition_storage', $transition_storage);
}
/**
* {@inheritdoc}
*/
public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
parent::extractFormValues($items, $form, $form_state);
$transitions = $form_state->getValue('transition_storage');
$entity = $items->getEntity();
$uuid = $entity->uuid();
// Do not use empty() here, because it's possible that the user is trying to
// clear all scheduled transitions, which means $transitions[$uuid] will
// be an empty array.
if (! isset($transitions[$uuid])) {
return;
}
$states = array_map(function (array $transition) {
assert(!empty($transition['state']) && is_string($transition['state']));
return [
'value' => $transition['state'],
];
}, $transitions[$uuid]);
$dates = array_map(function (array $transition) {
return [
'value' => gmdate(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $transition['when']),
];
}, $transitions[$uuid]);
assert(count($states) === count($dates));
$entity
->set('scheduled_transition_state', $states)
->set('scheduled_transition_date', $dates);
}
/**
* Returns an array of available workflow states for an entity.
*
* A workflow state is considered "available" if the current user has
* permission to use or schedule it.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity which has the workflow.
*
* @return array
* An associative array where the keys are the workflow state IDs, and the
* values are the states' human-readable labels.
*/
private function getStates(ContentEntityInterface $entity) {
$states = [];
$workflow = $this->moderationInformation->getWorkflowForEntity($entity);
foreach ($workflow->getTypePlugin()->getTransitions() as $transition) {
$base_permission = $workflow->id() . ' transition ' . $transition->id();
if ($this->currentUser->hasPermission("schedule $base_permission") || $this->currentUser->hasPermission("use $base_permission")) {
$to_state = $transition->to();
$states[ $to_state->id() ] = $to_state->label();
}
}
return $states;
}
}
