Update core 8.3.0
This commit is contained in:
parent
da7a7918f8
commit
cd7a898e66
6144 changed files with 132297 additions and 87747 deletions
53
web/core/modules/workflows/src/Annotation/WorkflowType.php
Normal file
53
web/core/modules/workflows/src/Annotation/WorkflowType.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines an Workflow type annotation object.
|
||||
*
|
||||
* Plugin Namespace: Plugin\WorkflowType
|
||||
*
|
||||
* For a working example, see \Drupal\content_moderation\Plugin\Workflow\ContentModerate
|
||||
*
|
||||
* @see \Drupal\workflows\WorkflowTypeInterface
|
||||
* @see \Drupal\workflows\WorkflowManager
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
class WorkflowType extends Plugin {
|
||||
|
||||
/**
|
||||
* The plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The label of the workflow.
|
||||
*
|
||||
* Describes how the plugin is used to apply a workflow to something.
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*/
|
||||
public $label = '';
|
||||
|
||||
/**
|
||||
* States required to exist.
|
||||
*
|
||||
* Normally supplied by WorkflowType::defaultConfiguration().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $required_states = [];
|
||||
|
||||
}
|
||||
533
web/core/modules/workflows/src/Entity/Workflow.php
Normal file
533
web/core/modules/workflows/src/Entity/Workflow.php
Normal file
|
|
@ -0,0 +1,533 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
|
||||
use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
|
||||
use Drupal\workflows\Exception\RequiredStateMissingException;
|
||||
use Drupal\workflows\State;
|
||||
use Drupal\workflows\Transition;
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
|
||||
/**
|
||||
* Defines the workflow entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "workflow",
|
||||
* label = @Translation("Workflow"),
|
||||
* label_collection = @Translation("Workflows"),
|
||||
* handlers = {
|
||||
* "access" = "Drupal\workflows\WorkflowAccessControlHandler",
|
||||
* "route_provider" = {
|
||||
* "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
|
||||
* },
|
||||
* },
|
||||
* config_prefix = "workflow",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label",
|
||||
* "uuid" = "uuid",
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "label",
|
||||
* "states",
|
||||
* "transitions",
|
||||
* "type",
|
||||
* "type_settings"
|
||||
* },
|
||||
* )
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
class Workflow extends ConfigEntityBase implements WorkflowInterface, EntityWithPluginCollectionInterface {
|
||||
|
||||
/**
|
||||
* The Workflow ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The Moderation state label.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The states of the workflow.
|
||||
*
|
||||
* The array key is the machine name for the state. The structure of each
|
||||
* array item is:
|
||||
* @code
|
||||
* label: {translatable label}
|
||||
* weight: {integer value}
|
||||
* @endcode
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $states = [];
|
||||
|
||||
/**
|
||||
* The permitted transitions of the workflow.
|
||||
*
|
||||
* The array key is the machine name for the transition. The machine name is
|
||||
* generated from the machine names of the states. The structure of each array
|
||||
* item is:
|
||||
* @code
|
||||
* from:
|
||||
* - {state machine name}
|
||||
* - {state machine name}
|
||||
* to: {state machine name}
|
||||
* label: {translatable label}
|
||||
* @endcode
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $transitions = [];
|
||||
|
||||
/**
|
||||
* The workflow type plugin ID.
|
||||
*
|
||||
* @see \Drupal\workflows\WorkflowTypeManager
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The configuration for the workflow type plugin.
|
||||
* @var array
|
||||
*/
|
||||
protected $type_settings = [];
|
||||
|
||||
/**
|
||||
* The workflow type plugin collection.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\LazyPluginCollection
|
||||
*/
|
||||
protected $pluginCollection;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
$workflow_type = $this->getTypePlugin();
|
||||
$missing_states = array_diff($workflow_type->getRequiredStates(), array_keys($this->getStates()));
|
||||
if (!empty($missing_states)) {
|
||||
throw new RequiredStateMissingException(sprintf("Workflow type '{$workflow_type->label()}' requires states with the ID '%s' in workflow '{$this->id()}'", implode("', '", $missing_states)));
|
||||
}
|
||||
parent::preSave($storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addState($state_id, $label) {
|
||||
if (isset($this->states[$state_id])) {
|
||||
throw new \InvalidArgumentException("The state '$state_id' already exists in workflow '{$this->id()}'");
|
||||
}
|
||||
if (preg_match('/[^a-z0-9_]+/', $state_id)) {
|
||||
throw new \InvalidArgumentException("The state ID '$state_id' must contain only lowercase letters, numbers, and underscores");
|
||||
}
|
||||
$this->states[$state_id] = [
|
||||
'label' => $label,
|
||||
'weight' => $this->getNextWeight($this->states),
|
||||
];
|
||||
ksort($this->states);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasState($state_id) {
|
||||
return isset($this->states[$state_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStates($state_ids = NULL) {
|
||||
if ($state_ids === NULL) {
|
||||
$state_ids = array_keys($this->states);
|
||||
}
|
||||
/** @var \Drupal\workflows\StateInterface[] $states */
|
||||
$states = array_combine($state_ids, array_map([$this, 'getState'], $state_ids));
|
||||
if (count($states) > 1) {
|
||||
// Sort states by weight and then label.
|
||||
$weights = $labels = [];
|
||||
foreach ($states as $id => $state) {
|
||||
$weights[$id] = $state->weight();
|
||||
$labels[$id] = $state->label();
|
||||
}
|
||||
array_multisort(
|
||||
$weights, SORT_NUMERIC, SORT_ASC,
|
||||
$labels, SORT_NATURAL, SORT_ASC
|
||||
);
|
||||
$states = array_replace($weights, $states);
|
||||
}
|
||||
return $states;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getState($state_id) {
|
||||
if (!isset($this->states[$state_id])) {
|
||||
throw new \InvalidArgumentException("The state '$state_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
$state = new State(
|
||||
$this,
|
||||
$state_id,
|
||||
$this->states[$state_id]['label'],
|
||||
$this->states[$state_id]['weight']
|
||||
);
|
||||
return $this->getTypePlugin()->decorateState($state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setStateLabel($state_id, $label) {
|
||||
if (!isset($this->states[$state_id])) {
|
||||
throw new \InvalidArgumentException("The state '$state_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
$this->states[$state_id]['label'] = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setStateWeight($state_id, $weight) {
|
||||
if (!isset($this->states[$state_id])) {
|
||||
throw new \InvalidArgumentException("The state '$state_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
$this->states[$state_id]['weight'] = $weight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteState($state_id) {
|
||||
if (!isset($this->states[$state_id])) {
|
||||
throw new \InvalidArgumentException("The state '$state_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
if (count($this->states) === 1) {
|
||||
throw new \InvalidArgumentException("The state '$state_id' can not be deleted from workflow '{$this->id()}' as it is the only state");
|
||||
}
|
||||
|
||||
foreach ($this->transitions as $transition_id => $transition) {
|
||||
$from_key = array_search($state_id, $transition['from'], TRUE);
|
||||
if ($from_key !== FALSE) {
|
||||
// Remove state from the from array.
|
||||
unset($transition['from'][$from_key]);
|
||||
}
|
||||
if (empty($transition['from']) || $transition['to'] === $state_id) {
|
||||
$this->deleteTransition($transition_id);
|
||||
}
|
||||
elseif ($from_key !== FALSE) {
|
||||
$this->setTransitionFromStates($transition_id, $transition['from']);
|
||||
}
|
||||
}
|
||||
unset($this->states[$state_id]);
|
||||
$this->getTypePlugin()->deleteState($state_id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInitialState() {
|
||||
$ordered_states = $this->getStates();
|
||||
return reset($ordered_states);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTransition($transition_id, $label, array $from_state_ids, $to_state_id) {
|
||||
if (isset($this->transitions[$transition_id])) {
|
||||
throw new \InvalidArgumentException("The transition '$transition_id' already exists in workflow '{$this->id()}'");
|
||||
}
|
||||
if (preg_match('/[^a-z0-9_]+/', $transition_id)) {
|
||||
throw new \InvalidArgumentException("The transition ID '$transition_id' must contain only lowercase letters, numbers, and underscores");
|
||||
}
|
||||
|
||||
if (!$this->hasState($to_state_id)) {
|
||||
throw new \InvalidArgumentException("The state '$to_state_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
$this->transitions[$transition_id] = [
|
||||
'label' => $label,
|
||||
'from' => [],
|
||||
'to' => $to_state_id,
|
||||
// Always add to the end.
|
||||
'weight' => $this->getNextWeight($this->transitions),
|
||||
];
|
||||
|
||||
try {
|
||||
$this->setTransitionFromStates($transition_id, $from_state_ids);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
unset($this->transitions[$transition_id]);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTransitions(array $transition_ids = NULL) {
|
||||
if ($transition_ids === NULL) {
|
||||
$transition_ids = array_keys($this->transitions);
|
||||
}
|
||||
/** @var \Drupal\workflows\TransitionInterface[] $transitions */
|
||||
$transitions = array_combine($transition_ids, array_map([$this, 'getTransition'], $transition_ids));
|
||||
if (count($transitions) > 1) {
|
||||
// Sort transitions by weights and then labels.
|
||||
$weights = $labels = [];
|
||||
foreach ($transitions as $id => $transition) {
|
||||
$weights[$id] = $transition->weight();
|
||||
$labels[$id] = $transition->label();
|
||||
}
|
||||
array_multisort(
|
||||
$weights, SORT_NUMERIC, SORT_ASC,
|
||||
$labels, SORT_NATURAL, SORT_ASC
|
||||
);
|
||||
$transitions = array_replace($weights, $transitions);
|
||||
}
|
||||
return $transitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTransition($transition_id) {
|
||||
if (!isset($this->transitions[$transition_id])) {
|
||||
throw new \InvalidArgumentException("The transition '$transition_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
$transition = new Transition(
|
||||
$this,
|
||||
$transition_id,
|
||||
$this->transitions[$transition_id]['label'],
|
||||
$this->transitions[$transition_id]['from'],
|
||||
$this->transitions[$transition_id]['to'],
|
||||
$this->transitions[$transition_id]['weight']
|
||||
);
|
||||
return $this->getTypePlugin()->decorateTransition($transition);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasTransition($transition_id) {
|
||||
return isset($this->transitions[$transition_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTransitionsForState($state_id, $direction = 'from') {
|
||||
$transition_ids = array_keys(array_filter($this->transitions, function ($transition) use ($state_id, $direction) {
|
||||
return in_array($state_id, (array) $transition[$direction], TRUE);
|
||||
}));
|
||||
return $this->getTransitions($transition_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTransitionFromStateToState($from_state_id, $to_state_id) {
|
||||
$transition_id = $this->getTransitionIdFromStateToState($from_state_id, $to_state_id);
|
||||
if (empty($transition_id)) {
|
||||
throw new \InvalidArgumentException("The transition from '$from_state_id' to '$to_state_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
return $this->getTransition($transition_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasTransitionFromStateToState($from_state_id, $to_state_id) {
|
||||
return !empty($this->getTransitionIdFromStateToState($from_state_id, $to_state_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the transition ID from state to state.
|
||||
*
|
||||
* @param string $from_state_id
|
||||
* The state ID to transition from.
|
||||
* @param string $to_state_id
|
||||
* The state ID to transition to.
|
||||
*
|
||||
* @return string|null
|
||||
* The transition ID, or NULL if no transition exists.
|
||||
*/
|
||||
protected function getTransitionIdFromStateToState($from_state_id, $to_state_id) {
|
||||
foreach ($this->transitions as $transition_id => $transition) {
|
||||
if (in_array($from_state_id, $transition['from'], TRUE) && $transition['to'] === $to_state_id) {
|
||||
return $transition_id;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTransitionLabel($transition_id, $label) {
|
||||
if (isset($this->transitions[$transition_id])) {
|
||||
$this->transitions[$transition_id]['label'] = $label;
|
||||
}
|
||||
else {
|
||||
throw new \InvalidArgumentException("The transition '$transition_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTransitionWeight($transition_id, $weight) {
|
||||
if (isset($this->transitions[$transition_id])) {
|
||||
$this->transitions[$transition_id]['weight'] = $weight;
|
||||
}
|
||||
else {
|
||||
throw new \InvalidArgumentException("The transition '$transition_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTransitionFromStates($transition_id, array $from_state_ids) {
|
||||
if (!isset($this->transitions[$transition_id])) {
|
||||
throw new \InvalidArgumentException("The transition '$transition_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
|
||||
// Ensure that the states exist.
|
||||
foreach ($from_state_ids as $from_state_id) {
|
||||
if (!$this->hasState($from_state_id)) {
|
||||
throw new \InvalidArgumentException("The state '$from_state_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
if ($this->hasTransitionFromStateToState($from_state_id, $this->transitions[$transition_id]['to'])) {
|
||||
$transition = $this->getTransitionFromStateToState($from_state_id, $this->transitions[$transition_id]['to']);
|
||||
if ($transition_id !== $transition->id()) {
|
||||
throw new \InvalidArgumentException("The '{$transition->id()}' transition already allows '$from_state_id' to '{$this->transitions[$transition_id]['to']}' transitions in workflow '{$this->id()}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve the order of the state IDs in the from value and don't save any
|
||||
// keys.
|
||||
$from_state_ids = array_values($from_state_ids);
|
||||
sort($from_state_ids);
|
||||
$this->transitions[$transition_id]['from'] = $from_state_ids;
|
||||
ksort($this->transitions);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteTransition($transition_id) {
|
||||
if (isset($this->transitions[$transition_id])) {
|
||||
unset($this->transitions[$transition_id]);
|
||||
$this->getTypePlugin()->deleteTransition($transition_id);
|
||||
}
|
||||
else {
|
||||
throw new \InvalidArgumentException("The transition '$transition_id' does not exist in workflow '{$this->id()}'");
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getTypePlugin() {
|
||||
return $this->getPluginCollection()->get($this->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPluginCollections() {
|
||||
return ['type_settings' => $this->getPluginCollection()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the creation of the workflow's plugin collection.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection
|
||||
* The workflow's plugin collection.
|
||||
*/
|
||||
protected function getPluginCollection() {
|
||||
if (!$this->pluginCollection && $this->type) {
|
||||
$this->pluginCollection = new DefaultSingleLazyPluginCollection(\Drupal::service('plugin.manager.workflows.type'), $this->type, $this->type_settings);
|
||||
}
|
||||
return $this->pluginCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all workflows of the provided type.
|
||||
*
|
||||
* @param string $type
|
||||
* The workflow type to load all workflows for.
|
||||
*
|
||||
* @return static[]
|
||||
* An array of workflow objects of the provided workflow type, indexed by
|
||||
* their IDs.
|
||||
*
|
||||
* @see \Drupal\workflows\Annotation\WorkflowType
|
||||
*/
|
||||
public static function loadMultipleByType($type) {
|
||||
return self::loadMultiple(\Drupal::entityQuery('workflow')->condition('type', $type)->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the weight for a new state or transition.
|
||||
*
|
||||
* @param array $items
|
||||
* An array of states or transitions information where each item has a
|
||||
* 'weight' key with a numeric value.
|
||||
*
|
||||
* @return int
|
||||
* The weight for a new item in the array so that it has the highest weight.
|
||||
*/
|
||||
protected function getNextWeight(array $items) {
|
||||
return array_reduce($items, function ($carry, $item) {
|
||||
return max($carry, $item['weight'] + 1);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function status() {
|
||||
// In order for a workflow to be usable it must have at least one state.
|
||||
return !empty($this->status) && !empty($this->states);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onDependencyRemoval(array $dependencies) {
|
||||
$changed = $this->getTypePlugin()->onDependencyRemoval($dependencies);
|
||||
// Ensure the parent method is called in order to process dependencies that
|
||||
// affect third party settings.
|
||||
return parent::onDependencyRemoval($dependencies) || $changed;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Exception;
|
||||
|
||||
use Drupal\Core\Config\ConfigException;
|
||||
|
||||
/**
|
||||
* Indicates that a workflow does not contain a required state.
|
||||
*/
|
||||
class RequiredStateMissingException extends ConfigException {
|
||||
}
|
||||
118
web/core/modules/workflows/src/Form/WorkflowAddForm.php
Normal file
118
web/core/modules/workflows/src/Form/WorkflowAddForm.php
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Form;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Form for adding workflows.
|
||||
*/
|
||||
class WorkflowAddForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The workflow type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $workflowTypePluginManager;
|
||||
|
||||
/**
|
||||
* WorkflowAddForm constructor.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $workflow_type_plugin_manager
|
||||
* The workflow type plugin manager.
|
||||
*/
|
||||
public function __construct(PluginManagerInterface $workflow_type_plugin_manager) {
|
||||
$this->workflowTypePluginManager = $workflow_type_plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.workflows.type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entity;
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $workflow->label(),
|
||||
'#description' => $this->t('Label for the Workflow.'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'machine_name',
|
||||
'#default_value' => $workflow->id(),
|
||||
'#machine_name' => [
|
||||
'exists' => [Workflow::class, 'load'],
|
||||
],
|
||||
];
|
||||
|
||||
$workflow_types = array_map(function ($plugin_definition) {
|
||||
return $plugin_definition['label'];
|
||||
}, $this->workflowTypePluginManager->getDefinitions());
|
||||
$form['workflow_type'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Workflow type'),
|
||||
'#required' => TRUE,
|
||||
'#options' => $workflow_types,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entity;
|
||||
// Initialize the workflow using the selected type plugin.
|
||||
$workflow = $workflow->getTypePlugin()->initializeWorkflow($workflow);
|
||||
$return = $workflow->save();
|
||||
if (empty($workflow->getStates())) {
|
||||
drupal_set_message($this->t('Created the %label Workflow. In order for the workflow to be enabled there needs to be at least one state.', [
|
||||
'%label' => $workflow->label(),
|
||||
]));
|
||||
$form_state->setRedirectUrl($workflow->toUrl('add-state-form'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('Created the %label Workflow.', [
|
||||
'%label' => $workflow->label(),
|
||||
]));
|
||||
$form_state->setRedirectUrl($workflow->toUrl('edit-form'));
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
// This form can only set the workflow's ID, label and the weights for each
|
||||
// state.
|
||||
/** @var \Drupal\workflows\WorkflowInterface $entity */
|
||||
$values = $form_state->getValues();
|
||||
$entity->set('label', $values['label']);
|
||||
$entity->set('id', $values['id']);
|
||||
$entity->set('type', $values['workflow_type']);
|
||||
}
|
||||
|
||||
}
|
||||
49
web/core/modules/workflows/src/Form/WorkflowDeleteForm.php
Normal file
49
web/core/modules/workflows/src/Form/WorkflowDeleteForm.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Builds the form to delete Workflow entities.
|
||||
*/
|
||||
class WorkflowDeleteForm extends EntityConfirmFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete %name?', ['%name' => $this->entity->label()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.workflow.collection');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->entity->delete();
|
||||
|
||||
drupal_set_message($this->t(
|
||||
'Workflow %label deleted.',
|
||||
['%label' => $this->entity->label()]
|
||||
));
|
||||
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
||||
215
web/core/modules/workflows/src/Form/WorkflowEditForm.php
Normal file
215
web/core/modules/workflows/src/Form/WorkflowEditForm.php
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Form;
|
||||
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
use Drupal\workflows\State;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* The form for editing workflows.
|
||||
*/
|
||||
class WorkflowEditForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entity;
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $workflow->label(),
|
||||
'#description' => $this->t('Label for the Workflow.'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'machine_name',
|
||||
'#default_value' => $workflow->id(),
|
||||
'#machine_name' => [
|
||||
'exists' => [Workflow::class, 'load'],
|
||||
],
|
||||
'#disabled' => TRUE,
|
||||
];
|
||||
|
||||
$header = [
|
||||
'state' => $this->t('State'),
|
||||
'weight' => $this->t('Weight'),
|
||||
'operations' => $this->t('Operations')
|
||||
];
|
||||
$form['states_container'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('States'),
|
||||
'#open' => TRUE,
|
||||
'#collapsible' => 'FALSE',
|
||||
];
|
||||
$form['states_container']['states'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#title' => $this->t('States'),
|
||||
'#empty' => $this->t('There are no states yet.'),
|
||||
'#tabledrag' => [
|
||||
[
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'state-weight',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$states = $workflow->getStates();
|
||||
|
||||
// Warn the user if there are no states.
|
||||
if (empty($states)) {
|
||||
drupal_set_message(
|
||||
$this->t(
|
||||
'This workflow has no states and will be disabled until there is at least one, <a href=":add-state">add a new state.</a>',
|
||||
[':add-state' => $workflow->toUrl('add-state-form')->toString()]
|
||||
),
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($states as $state) {
|
||||
$links = [
|
||||
'edit' => [
|
||||
'title' => $this->t('Edit'),
|
||||
'url' => Url::fromRoute('entity.workflow.edit_state_form', ['workflow' => $workflow->id(), 'workflow_state' => $state->id()]),
|
||||
'attributes' => ['aria-label' => $this->t('Edit @state state', ['@state' => $state->label()])],
|
||||
]
|
||||
];
|
||||
if ($this->entity->access('delete-state:' . $state->id())) {
|
||||
$links['delete'] = [
|
||||
'title' => t('Delete'),
|
||||
'url' => Url::fromRoute('entity.workflow.delete_state_form', [
|
||||
'workflow' => $workflow->id(),
|
||||
'workflow_state' => $state->id()
|
||||
]),
|
||||
'attributes' => ['aria-label' => $this->t('Delete @state state', ['@state' => $state->label()])],
|
||||
];
|
||||
}
|
||||
$form['states_container']['states'][$state->id()] = [
|
||||
'#attributes' => ['class' => ['draggable']],
|
||||
'state' => ['#markup' => $state->label()],
|
||||
'#weight' => $state->weight(),
|
||||
'weight' => [
|
||||
'#type' => 'weight',
|
||||
'#title' => t('Weight for @title', ['@title' => $state->label()]),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $state->weight(),
|
||||
'#attributes' => ['class' => ['state-weight']],
|
||||
],
|
||||
'operations' => [
|
||||
'#type' => 'operations',
|
||||
'#links' => $links,
|
||||
],
|
||||
];
|
||||
}
|
||||
$form['states_container']['state_add'] = [
|
||||
'#markup' => $workflow->toLink($this->t('Add a new state'), 'add-state-form')->toString(),
|
||||
];
|
||||
|
||||
$header = [
|
||||
'label' => $this->t('Label'),
|
||||
'weight' => $this->t('Weight'),
|
||||
'from' => $this->t('From'),
|
||||
'to' => $this->t('To'),
|
||||
'operations' => $this->t('Operations')
|
||||
];
|
||||
$form['transitions_container'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Transitions'),
|
||||
'#open' => TRUE,
|
||||
];
|
||||
$form['transitions_container']['transitions'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#title' => $this->t('Transitions'),
|
||||
'#empty' => $this->t('There are no transitions yet.'),
|
||||
'#tabledrag' => [
|
||||
[
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'transition-weight',
|
||||
],
|
||||
],
|
||||
];
|
||||
foreach ($workflow->getTransitions() as $transition) {
|
||||
$links['edit'] = [
|
||||
'title' => $this->t('Edit'),
|
||||
'url' => Url::fromRoute('entity.workflow.edit_transition_form', ['workflow' => $workflow->id(), 'workflow_transition' => $transition->id()]),
|
||||
'attributes' => ['aria-label' => $this->t('Edit \'@transition\' transition', ['@transition' => $transition->label()])],
|
||||
];
|
||||
$links['delete'] = [
|
||||
'title' => t('Delete'),
|
||||
'url' => Url::fromRoute('entity.workflow.delete_transition_form', ['workflow' => $workflow->id(), 'workflow_transition' => $transition->id()]),
|
||||
'attributes' => ['aria-label' => $this->t('Delete \'@transition\' transition', ['@transition' => $transition->label()])],
|
||||
];
|
||||
$form['transitions_container']['transitions'][$transition->id()] = [
|
||||
'#attributes' => ['class' => ['draggable']],
|
||||
'label' => ['#markup' => $transition->label()],
|
||||
'#weight' => $transition->weight(),
|
||||
'weight' => [
|
||||
'#type' => 'weight',
|
||||
'#title' => t('Weight for @title', ['@title' => $transition->label()]),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $transition->weight(),
|
||||
'#attributes' => ['class' => ['transition-weight']],
|
||||
],
|
||||
'from' => [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => array_map([State::class, 'labelCallback'], $transition->from()),
|
||||
'#context' => ['list_style' => 'comma-list'],
|
||||
],
|
||||
'to' => ['#markup' => $transition->to()->label()],
|
||||
'operations' => [
|
||||
'#type' => 'operations',
|
||||
'#links' => $links,
|
||||
],
|
||||
];
|
||||
}
|
||||
$form['transitions_container']['transition_add'] = [
|
||||
'#markup' => $workflow->toLink($this->t('Add a new transition'), 'add-transition-form')->toString(),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entity;
|
||||
$workflow->save();
|
||||
drupal_set_message($this->t('Saved the %label Workflow.', ['%label' => $workflow->label()]));
|
||||
$form_state->setRedirectUrl($workflow->toUrl('collection'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
// This form can only set the workflow's ID, label and the weights for each
|
||||
// state.
|
||||
/** @var \Drupal\workflows\WorkflowInterface $entity */
|
||||
$values = $form_state->getValues();
|
||||
$entity->set('label', $values['label']);
|
||||
$entity->set('id', $values['id']);
|
||||
foreach ($values['states'] as $state_id => $state_values) {
|
||||
$entity->setStateWeight($state_id, $state_values['weight']);
|
||||
}
|
||||
foreach ($values['transitions'] as $transition_id => $transition_values) {
|
||||
$entity->setTransitionWeight($transition_id, $transition_values['weight']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
122
web/core/modules/workflows/src/Form/WorkflowStateAddForm.php
Normal file
122
web/core/modules/workflows/src/Form/WorkflowStateAddForm.php
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Class WorkflowStateAddForm.
|
||||
*/
|
||||
class WorkflowStateAddForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'workflow_state_add_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->getEntity();
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => '',
|
||||
'#description' => $this->t('Label for the state.'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'machine_name',
|
||||
'#machine_name' => [
|
||||
'exists' => [$this, 'exists'],
|
||||
],
|
||||
];
|
||||
|
||||
// Add additional form fields from the workflow type plugin.
|
||||
$form['type_settings'] = [
|
||||
$workflow->get('type') => $workflow->getTypePlugin()->buildStateConfigurationForm($form_state, $workflow),
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the workflow state already exists.
|
||||
*
|
||||
* @param string $state_id
|
||||
* The workflow state ID.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the workflow state exists, FALSE otherwise.
|
||||
*/
|
||||
public function exists($state_id) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $original_workflow */
|
||||
$original_workflow = \Drupal::entityTypeManager()->getStorage('workflow')->loadUnchanged($this->getEntity()->id());
|
||||
return $original_workflow->hasState($state_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies top-level form values to entity properties
|
||||
*
|
||||
* This form can only change values for a state, which is part of workflow.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity the current form should operate upon.
|
||||
* @param array $form
|
||||
* A nested array of form elements comprising the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $entity */
|
||||
$values = $form_state->getValues();
|
||||
|
||||
// This is fired twice so we have to check that the entity does not already
|
||||
// have the state.
|
||||
if (!$entity->hasState($values['id'])) {
|
||||
$entity->addState($values['id'], $values['label']);
|
||||
if (isset($values['type_settings'])) {
|
||||
$configuration = $entity->getTypePlugin()->getConfiguration();
|
||||
$configuration['states'][$values['id']] = $values['type_settings'][$entity->getTypePlugin()->getPluginId()];
|
||||
$entity->set('type_settings', $configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entity;
|
||||
$workflow->save();
|
||||
drupal_set_message($this->t('Created %label state.', [
|
||||
'%label' => $workflow->getState($form_state->getValue('id'))->label(),
|
||||
]));
|
||||
$form_state->setRedirectUrl($workflow->toUrl('edit-form'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#submit' => ['::submitForm', '::save'],
|
||||
];
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Form;
|
||||
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Builds the form to delete states from Workflow entities.
|
||||
*/
|
||||
class WorkflowStateDeleteForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The workflow entity the state being deleted belongs to.
|
||||
*
|
||||
* @var \Drupal\workflows\WorkflowInterface
|
||||
*/
|
||||
protected $workflow;
|
||||
|
||||
/**
|
||||
* The state being deleted.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $stateId;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'workflow_state_delete_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete %state from %workflow?', ['%state' => $this->workflow->getState($this->stateId)->label(), '%workflow' => $this->workflow->label()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return $this->workflow->toUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param \Drupal\workflows\WorkflowInterface $workflow
|
||||
* The workflow entity being edited.
|
||||
* @param string|null $workflow_state
|
||||
* The workflow state being deleted.
|
||||
*
|
||||
* @return array
|
||||
* The form structure.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, WorkflowInterface $workflow = NULL, $workflow_state = NULL) {
|
||||
if (!$workflow->hasState($workflow_state)) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$this->workflow = $workflow;
|
||||
$this->stateId = $workflow_state;
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
|
||||
$workflow_label = $this->workflow->getState($this->stateId)->label();
|
||||
$this->workflow
|
||||
->deleteState($this->stateId)
|
||||
->save();
|
||||
|
||||
drupal_set_message($this->t(
|
||||
'State %label deleted.',
|
||||
['%label' => $workflow_label]
|
||||
));
|
||||
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
||||
174
web/core/modules/workflows/src/Form/WorkflowStateEditForm.php
Normal file
174
web/core/modules/workflows/src/Form/WorkflowStateEditForm.php
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Class WorkflowStateEditForm.
|
||||
*/
|
||||
class WorkflowStateEditForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The ID of the state that is being edited.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $stateId;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'workflow_state_edit_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $workflow_state = NULL) {
|
||||
$this->stateId = $workflow_state;
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->getEntity();
|
||||
$state = $workflow->getState($this->stateId);
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $state->label(),
|
||||
'#description' => $this->t('Label for the state.'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'machine_name',
|
||||
'#default_value' => $this->stateId,
|
||||
'#machine_name' => [
|
||||
'exists' => [$this, 'exists'],
|
||||
],
|
||||
'#disabled' => TRUE,
|
||||
];
|
||||
|
||||
// Add additional form fields from the workflow type plugin.
|
||||
$form['type_settings'] = [
|
||||
$workflow->get('type') => $workflow->getTypePlugin()->buildStateConfigurationForm($form_state, $workflow, $state),
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
|
||||
$header = [
|
||||
'label' => $this->t('Transition'),
|
||||
'state' => $this->t('To'),
|
||||
'operations' => $this->t('Operations'),
|
||||
];
|
||||
$form['transitions'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#empty' => $this->t('There are no transitions to or from this state yet.'),
|
||||
];
|
||||
foreach ($state->getTransitions() as $transition) {
|
||||
$links['edit'] = [
|
||||
'title' => $this->t('Edit'),
|
||||
'url' => Url::fromRoute('entity.workflow.edit_transition_form', [
|
||||
'workflow' => $workflow->id(),
|
||||
'workflow_transition' => $transition->id()
|
||||
]),
|
||||
];
|
||||
$links['delete'] = [
|
||||
'title' => t('Delete'),
|
||||
'url' => Url::fromRoute('entity.workflow.delete_transition_form', [
|
||||
'workflow' => $workflow->id(),
|
||||
'workflow_transition' => $transition->id()
|
||||
]),
|
||||
];
|
||||
$form['transitions'][$transition->id()] = [
|
||||
'label' => [
|
||||
'#markup' => $transition->label(),
|
||||
],
|
||||
'state' => [
|
||||
'#markup' => $transition->to()->label(),
|
||||
],
|
||||
'operations' => [
|
||||
'#type' => 'operations',
|
||||
'#links' => $links,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies top-level form values to entity properties
|
||||
*
|
||||
* This form can only change values for a state, which is part of workflow.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity the current form should operate upon.
|
||||
* @param array $form
|
||||
* A nested array of form elements comprising the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $entity */
|
||||
$values = $form_state->getValues();
|
||||
$entity->setStateLabel($values['id'], $values['label']);
|
||||
if (isset($values['type_settings'])) {
|
||||
$configuration = $entity->getTypePlugin()->getConfiguration();
|
||||
$configuration['states'][$values['id']] = $values['type_settings'][$entity->getTypePlugin()->getPluginId()];
|
||||
$entity->set('type_settings', $configuration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entity;
|
||||
$workflow->save();
|
||||
drupal_set_message($this->t('Saved %label state.', [
|
||||
'%label' => $workflow->getState($this->stateId)->label(),
|
||||
]));
|
||||
$form_state->setRedirectUrl($workflow->toUrl('edit-form'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#submit' => ['::submitForm', '::save'],
|
||||
];
|
||||
|
||||
$actions['delete'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Delete'),
|
||||
'#access' => $this->entity->access('delete-state:' . $this->stateId),
|
||||
'#attributes' => [
|
||||
'class' => ['button', 'button--danger'],
|
||||
],
|
||||
'#url' => Url::fromRoute('entity.workflow.delete_state_form', [
|
||||
'workflow' => $this->entity->id(),
|
||||
'workflow_state' => $this->stateId
|
||||
])
|
||||
];
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\workflows\State;
|
||||
|
||||
/**
|
||||
* Class WorkflowTransitionAddForm.
|
||||
*/
|
||||
class WorkflowTransitionAddForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'workflow_transition_add_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->getEntity();
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => '',
|
||||
'#description' => $this->t('Label for the transition.'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'machine_name',
|
||||
'#machine_name' => [
|
||||
'exists' => [$this, 'exists'],
|
||||
],
|
||||
];
|
||||
|
||||
// @todo https://www.drupal.org/node/2830584 Add some ajax to ensure that
|
||||
// only valid transitions are selectable.
|
||||
$states = array_map([State::class, 'labelCallback'], $workflow->getStates());
|
||||
$form['from'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('From'),
|
||||
'#required' => TRUE,
|
||||
'#default_value' => [],
|
||||
'#options' => $states,
|
||||
];
|
||||
$form['to'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('To'),
|
||||
'#required' => TRUE,
|
||||
'#default_value' => [],
|
||||
'#options' => $states,
|
||||
];
|
||||
|
||||
// Add additional form fields from the workflow type plugin.
|
||||
$form['type_settings'] = [
|
||||
$workflow->get('type') => $workflow->getTypePlugin()->buildTransitionConfigurationForm($form_state, $workflow),
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the workflow transition already exists.
|
||||
*
|
||||
* @param string $transition_id
|
||||
* The workflow transition ID.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the workflow transition exists, FALSE otherwise.
|
||||
*/
|
||||
public function exists($transition_id) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $original_workflow */
|
||||
$original_workflow = \Drupal::entityTypeManager()->getStorage('workflow')->loadUnchanged($this->getEntity()->id());
|
||||
return $original_workflow->hasTransition($transition_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies top-level form values to entity properties
|
||||
*
|
||||
* This form can only change values for a state, which is part of workflow.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity the current form should operate upon.
|
||||
* @param array $form
|
||||
* A nested array of form elements comprising the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
if (!$form_state->isValidationComplete()) {
|
||||
// Only do something once form validation is complete.
|
||||
return;
|
||||
}
|
||||
/** @var \Drupal\workflows\WorkflowInterface $entity */
|
||||
$values = $form_state->getValues();
|
||||
$entity->addTransition($values['id'], $values['label'], array_filter($values['from']), $values['to']);
|
||||
if (isset($values['type_settings'])) {
|
||||
$configuration = $entity->getTypePlugin()->getConfiguration();
|
||||
$configuration['transitions'][$values['id']] = $values['type_settings'][$entity->getTypePlugin()->getPluginId()];
|
||||
$entity->set('type_settings', $configuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->getEntity();
|
||||
$values = $form_state->getValues();
|
||||
foreach (array_filter($values['from']) as $from_state_id) {
|
||||
if ($workflow->hasTransitionFromStateToState($from_state_id, $values['to'])) {
|
||||
$form_state->setErrorByName('from][' . $from_state_id, $this->t('The transition from %from to %to already exists.', [
|
||||
'%from' => $workflow->getState($from_state_id)->label(),
|
||||
'%to' => $workflow->getState($values['to'])->label(),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entity;
|
||||
$workflow->save();
|
||||
drupal_set_message($this->t('Created %label transition.', [
|
||||
'%label' => $form_state->getValue('label'),
|
||||
]));
|
||||
$form_state->setRedirectUrl($workflow->toUrl('edit-form'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#submit' => ['::submitForm', '::save'],
|
||||
];
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Form;
|
||||
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Builds the form to delete transitions from Workflow entities.
|
||||
*/
|
||||
class WorkflowTransitionDeleteForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The workflow entity the transition being deleted belongs to.
|
||||
*
|
||||
* @var \Drupal\workflows\WorkflowInterface
|
||||
*/
|
||||
protected $workflow;
|
||||
|
||||
/**
|
||||
* The workflow transition being deleted.
|
||||
*
|
||||
* @var \Drupal\workflows\TransitionInterface
|
||||
*/
|
||||
protected $transition;
|
||||
|
||||
/**
|
||||
* The transition being deleted.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $transitionId;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'workflow_transition_delete_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete %transition from %workflow?', ['%transition' => $this->transition->label(), '%workflow' => $this->workflow->label()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return $this->workflow->toUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param \Drupal\workflows\WorkflowInterface $workflow
|
||||
* The workflow entity being edited.
|
||||
* @param string|null $workflow_transition
|
||||
* The workflow transition being deleted.
|
||||
*
|
||||
* @return array
|
||||
* The form structure.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, WorkflowInterface $workflow = NULL, $workflow_transition = NULL) {
|
||||
try {
|
||||
$this->transition = $workflow->getTransition($workflow_transition);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$this->workflow = $workflow;
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->workflow
|
||||
->deleteTransition($this->transition->id())
|
||||
->save();
|
||||
|
||||
drupal_set_message($this->t('%transition transition deleted.', ['%transition' => $this->transition->label()]));
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\workflows\State;
|
||||
|
||||
/**
|
||||
* Class WorkflowTransitionEditForm.
|
||||
*/
|
||||
class WorkflowTransitionEditForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The ID of the transition that is being edited.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $transitionId;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'workflow_transition_edit_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $workflow_transition = NULL) {
|
||||
$this->transitionId = $workflow_transition;
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->getEntity();
|
||||
$transition = $workflow->getTransition($this->transitionId);
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $transition->label(),
|
||||
'#description' => $this->t('Label for the transition.'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'value',
|
||||
'#value' => $this->transitionId,
|
||||
];
|
||||
|
||||
// @todo https://www.drupal.org/node/2830584 Add some ajax to ensure that
|
||||
// only valid transitions are selectable.
|
||||
$states = array_map([State::class, 'labelCallback'], $workflow->getStates());
|
||||
$form['from'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('From'),
|
||||
'#required' => TRUE,
|
||||
'#default_value' => array_keys($transition->from()),
|
||||
'#options' => $states,
|
||||
];
|
||||
$form['to'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('To'),
|
||||
'#required' => TRUE,
|
||||
'#default_value' => $transition->to()->id(),
|
||||
'#options' => $states,
|
||||
'#disabled' => TRUE,
|
||||
];
|
||||
|
||||
// Add additional form fields from the workflow type plugin.
|
||||
$form['type_settings'] = [
|
||||
$workflow->get('type') => $workflow->getTypePlugin()->buildTransitionConfigurationForm($form_state, $workflow, $transition),
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->getEntity();
|
||||
$values = $form_state->getValues();
|
||||
foreach (array_filter($values['from']) as $from_state_id) {
|
||||
if ($workflow->hasTransitionFromStateToState($from_state_id, $values['to'])) {
|
||||
$transition = $workflow->getTransitionFromStateToState($from_state_id, $values['to']);
|
||||
if ($transition->id() !== $values['id']) {
|
||||
$form_state->setErrorByName('from][' . $from_state_id, $this->t('The transition from %from to %to already exists.', [
|
||||
'%from' => $workflow->getState($from_state_id)->label(),
|
||||
'%to' => $workflow->getState($values['to'])->label(),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies top-level form values to entity properties
|
||||
*
|
||||
* This form can only change values for a state, which is part of workflow.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity the current form should operate upon.
|
||||
* @param array $form
|
||||
* A nested array of form elements comprising the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
if (!$form_state->isValidationComplete()) {
|
||||
// Only do something once form validation is complete.
|
||||
return;
|
||||
}
|
||||
/** @var \Drupal\workflows\WorkflowInterface $entity */
|
||||
$values = $form_state->getValues();
|
||||
$form_state->set('created_transition', FALSE);
|
||||
$entity->setTransitionLabel($values['id'], $values['label']);
|
||||
$entity->setTransitionFromStates($values['id'], array_filter($values['from']));
|
||||
if (isset($values['type_settings'])) {
|
||||
$configuration = $entity->getTypePlugin()->getConfiguration();
|
||||
$configuration['transitions'][$values['id']] = $values['type_settings'][$entity->getTypePlugin()->getPluginId()];
|
||||
$entity->set('type_settings', $configuration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entity;
|
||||
$workflow->save();
|
||||
drupal_set_message($this->t('Saved %label transition.', [
|
||||
'%label' => $workflow->getTransition($this->transitionId)->label(),
|
||||
]));
|
||||
$form_state->setRedirectUrl($workflow->toUrl('edit-form'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#submit' => ['::submitForm', '::save'],
|
||||
];
|
||||
|
||||
$actions['delete'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Delete'),
|
||||
// Deleting a transition is editing a workflow.
|
||||
'#access' => $this->entity->access('edit'),
|
||||
'#attributes' => [
|
||||
'class' => ['button', 'button--danger'],
|
||||
],
|
||||
'#url' => Url::fromRoute('entity.workflow.delete_transition_form', [
|
||||
'workflow' => $this->entity->id(),
|
||||
'workflow_transition' => $this->transitionId
|
||||
])
|
||||
];
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
148
web/core/modules/workflows/src/Plugin/WorkflowTypeBase.php
Normal file
148
web/core/modules/workflows/src/Plugin/WorkflowTypeBase.php
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginBase;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\workflows\StateInterface;
|
||||
use Drupal\workflows\TransitionInterface;
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
use Drupal\workflows\WorkflowTypeInterface;
|
||||
|
||||
/**
|
||||
* A base class for Workflow type plugins.
|
||||
*
|
||||
* @see \Drupal\workflows\Annotation\WorkflowType
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
abstract class WorkflowTypeBase extends PluginBase implements WorkflowTypeInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->setConfiguration($configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initializeWorkflow(WorkflowInterface $workflow) {
|
||||
return $workflow;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function label() {
|
||||
$definition = $this->getPluginDefinition();
|
||||
// The label can be an object.
|
||||
// @see \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
return $definition['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkWorkflowAccess(WorkflowInterface $entity, $operation, AccountInterface $account) {
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function decorateState(StateInterface $state) {
|
||||
return $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function deleteState($state_id) {
|
||||
unset($this->configuration['states'][$state_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function decorateTransition(TransitionInterface $transition) {
|
||||
return $transition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function deleteTransition($transition_id) {
|
||||
unset($this->configuration['transitions'][$transition_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildStateConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, StateInterface $state = NULL) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildTransitionConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, TransitionInterface $transition = NULL) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getConfiguration() {
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setConfiguration(array $configuration) {
|
||||
$this->configuration = NestedArray::mergeDeep(
|
||||
$this->defaultConfiguration(),
|
||||
$configuration
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRequiredStates() {
|
||||
return $this->getPluginDefinition()['required_states'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return [
|
||||
'states' => [],
|
||||
'transitions' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onDependencyRemoval(array $dependencies) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
118
web/core/modules/workflows/src/State.php
Normal file
118
web/core/modules/workflows/src/State.php
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
/**
|
||||
* A value object representing a workflow state.
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
class State implements StateInterface {
|
||||
|
||||
/**
|
||||
* The workflow the state is attached to.
|
||||
*
|
||||
* @var \Drupal\workflows\WorkflowInterface
|
||||
*/
|
||||
protected $workflow;
|
||||
|
||||
/**
|
||||
* The state's ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The state's label.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The state's weight.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $weight;
|
||||
|
||||
/**
|
||||
* State constructor.
|
||||
*
|
||||
* @param \Drupal\workflows\WorkflowInterface $workflow
|
||||
* The workflow the state is attached to.
|
||||
* @param string $id
|
||||
* The state's ID.
|
||||
* @param string $label
|
||||
* The state's label.
|
||||
* @param int $weight
|
||||
* The state's weight.
|
||||
*/
|
||||
public function __construct(WorkflowInterface $workflow, $id, $label, $weight = 0) {
|
||||
$this->workflow = $workflow;
|
||||
$this->id = $id;
|
||||
$this->label = $label;
|
||||
$this->weight = $weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function label() {
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function weight() {
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canTransitionTo($to_state_id) {
|
||||
return $this->workflow->hasTransitionFromStateToState($this->id, $to_state_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTransitionTo($to_state_id) {
|
||||
if (!$this->canTransitionTo($to_state_id)) {
|
||||
throw new \InvalidArgumentException("Can not transition to '$to_state_id' state");
|
||||
}
|
||||
return $this->workflow->getTransitionFromStateToState($this->id(), $to_state_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTransitions() {
|
||||
return $this->workflow->getTransitionsForState($this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to convert a list of states to labels
|
||||
*
|
||||
* @param \Drupal\workflows\StateInterface $state
|
||||
*
|
||||
* @return string
|
||||
* The label of the state.
|
||||
*/
|
||||
public static function labelCallback(StateInterface $state) {
|
||||
return $state->label();
|
||||
}
|
||||
|
||||
}
|
||||
73
web/core/modules/workflows/src/StateInterface.php
Normal file
73
web/core/modules/workflows/src/StateInterface.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
/**
|
||||
* An interface for state value objects.
|
||||
*
|
||||
* @see \Drupal\workflows\WorkflowTypeInterface::decorateState()
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
interface StateInterface {
|
||||
|
||||
/**
|
||||
* Gets the state's ID.
|
||||
*
|
||||
* @return string
|
||||
* The state's ID.
|
||||
*/
|
||||
public function id();
|
||||
|
||||
/**
|
||||
* Gets the state's label.
|
||||
*
|
||||
* @return string
|
||||
* The state's label.
|
||||
*/
|
||||
public function label();
|
||||
|
||||
/**
|
||||
* Gets the state's weight.
|
||||
*
|
||||
* @return int
|
||||
* The state's weight.
|
||||
*/
|
||||
public function weight();
|
||||
|
||||
/**
|
||||
* Determines if the state can transition to the provided state ID.
|
||||
*
|
||||
* @param $to_state_id
|
||||
* The state to transition to.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the state can transition to the provided state ID. FALSE, if not.
|
||||
*/
|
||||
public function canTransitionTo($to_state_id);
|
||||
|
||||
/**
|
||||
* Gets the Transition object for the provided state ID.
|
||||
*
|
||||
* @param $to_state_id
|
||||
* The state to transition to.
|
||||
*
|
||||
* @return \Drupal\workflows\TransitionInterface
|
||||
* The Transition object for the provided state ID.
|
||||
*
|
||||
* @throws \InvalidArgumentException()
|
||||
* Exception thrown when the provided state ID can not be transitioned to.
|
||||
*/
|
||||
public function getTransitionTo($to_state_id);
|
||||
|
||||
/**
|
||||
* Gets all the possible transition objects for the state.
|
||||
*
|
||||
* @return \Drupal\workflows\TransitionInterface[]
|
||||
* All the possible transition objects for the state.
|
||||
*/
|
||||
public function getTransitions();
|
||||
|
||||
}
|
||||
116
web/core/modules/workflows/src/Transition.php
Normal file
116
web/core/modules/workflows/src/Transition.php
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
/**
|
||||
* A transition value object that describes the transition between states.
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
class Transition implements TransitionInterface {
|
||||
|
||||
/**
|
||||
* The workflow that this transition is attached to.
|
||||
*
|
||||
* @var \Drupal\workflows\WorkflowInterface
|
||||
*/
|
||||
protected $workflow;
|
||||
|
||||
/**
|
||||
* The transition's ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The transition's label.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The transition's from state IDs.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fromStateIds;
|
||||
|
||||
/**
|
||||
* The transition's to state ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $toStateId;
|
||||
|
||||
/**
|
||||
* The transition's weight.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $weight;
|
||||
|
||||
/**
|
||||
* Transition constructor.
|
||||
*
|
||||
* @param \Drupal\workflows\WorkflowInterface $workflow
|
||||
* The workflow the state is attached to.
|
||||
* @param string $id
|
||||
* The transition's ID.
|
||||
* @param string $label
|
||||
* The transition's label.
|
||||
* @param array $from_state_ids
|
||||
* A list of from state IDs.
|
||||
* @param string $to_state_id
|
||||
* The to state ID.
|
||||
* @param int $weight
|
||||
* (optional) The transition's weight. Defaults to 0.
|
||||
*/
|
||||
public function __construct(WorkflowInterface $workflow, $id, $label, array $from_state_ids, $to_state_id, $weight = 0) {
|
||||
$this->workflow = $workflow;
|
||||
$this->id = $id;
|
||||
$this->label = $label;
|
||||
$this->fromStateIds = $from_state_ids;
|
||||
$this->toStateId = $to_state_id;
|
||||
$this->weight = $weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function label() {
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function from() {
|
||||
return $this->workflow->getStates($this->fromStateIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function to() {
|
||||
return $this->workflow->getState($this->toStateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function weight() {
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
}
|
||||
54
web/core/modules/workflows/src/TransitionInterface.php
Normal file
54
web/core/modules/workflows/src/TransitionInterface.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
/**
|
||||
* A transition value object that describes the transition between two states.
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
interface TransitionInterface {
|
||||
|
||||
/**
|
||||
* Gets the transition's ID.
|
||||
*
|
||||
* @return string
|
||||
* The transition's ID.
|
||||
*/
|
||||
public function id();
|
||||
|
||||
/**
|
||||
* Gets the transition's label.
|
||||
*
|
||||
* @return string
|
||||
* The transition's label.
|
||||
*/
|
||||
public function label();
|
||||
|
||||
/**
|
||||
* Gets the transition's from states.
|
||||
*
|
||||
* @return \Drupal\workflows\StateInterface[]
|
||||
* The transition's from states.
|
||||
*/
|
||||
public function from();
|
||||
|
||||
/**
|
||||
* Gets the transition's to state.
|
||||
*
|
||||
* @return \Drupal\workflows\StateInterface
|
||||
* The transition's to state.
|
||||
*/
|
||||
public function to();
|
||||
|
||||
/**
|
||||
* Gets the transition's weight.
|
||||
*
|
||||
* @return string
|
||||
* The transition's weight.
|
||||
*/
|
||||
public function weight();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Access controller for the Moderation State entity.
|
||||
*
|
||||
* @see \Drupal\workflows\Entity\Workflow.
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
class WorkflowAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
|
||||
|
||||
/**
|
||||
* The workflow type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $workflowTypeManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('plugin.manager.workflows.type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the workflow access control handler instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $workflow_type_manager
|
||||
* The workflow type plugin manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, PluginManagerInterface $workflow_type_manager) {
|
||||
parent::__construct($entity_type);
|
||||
$this->workflowTypeManager = $workflow_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
/** @var \Drupal\workflows\Entity\Workflow $entity */
|
||||
$workflow_type = $entity->getTypePlugin();
|
||||
if (strpos($operation, 'delete-state') === 0) {
|
||||
list(, $state_id) = explode(':', $operation, 2);
|
||||
// Deleting a state is editing a workflow, but also we should forbid
|
||||
// access if there is only one state.
|
||||
$admin_access = AccessResult::allowedIf(count($entity->getStates()) > 1)
|
||||
->andIf(parent::checkAccess($entity, 'edit', $account))
|
||||
->andIf(AccessResult::allowedIf(!in_array($state_id, $workflow_type->getRequiredStates(), TRUE)))
|
||||
->addCacheableDependency($entity);
|
||||
}
|
||||
else {
|
||||
$admin_access = parent::checkAccess($entity, $operation, $account);
|
||||
}
|
||||
return $workflow_type->checkWorkflowAccess($entity, $operation, $account)->orIf($admin_access);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
|
||||
$workflow_types_count = count($this->workflowTypeManager->getDefinitions());
|
||||
$admin_access = parent::checkCreateAccess($account, $context, $entity_bundle);
|
||||
// Allow access if there is at least one workflow type. Since workflow types
|
||||
// are provided by modules this is cacheable until extensions change.
|
||||
return $admin_access->andIf(AccessResult::allowedIf($workflow_types_count > 0))->addCacheTags(['config:core.extension']);
|
||||
}
|
||||
|
||||
}
|
||||
53
web/core/modules/workflows/src/WorkflowDeleteAccessCheck.php
Normal file
53
web/core/modules/workflows/src/WorkflowDeleteAccessCheck.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Provides a access checker for deleting a workflow state.
|
||||
*/
|
||||
class WorkflowDeleteAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* Checks access to deleting a workflow state for a particular route.
|
||||
*
|
||||
* The value of '_workflow_state_delete_access' is ignored. The route must
|
||||
* have the parameters 'workflow' and 'workflow_state'. For example:
|
||||
* @code
|
||||
* pattern: '/foo/{workflow}/bar/{workflow_state}/delete'
|
||||
* requirements:
|
||||
* _workflow_state_delete_access: 'true'
|
||||
* @endcode
|
||||
* @see \Drupal\Core\ParamConverter\EntityConverter
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $route
|
||||
* The route to check against.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The parametrized route
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The currently logged in account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) {
|
||||
// If there is valid entity of the given entity type, check its access.
|
||||
$parameters = $route_match->getParameters();
|
||||
if ($parameters->has('workflow') && $parameters->has('workflow_state')) {
|
||||
$entity = $parameters->get('workflow');
|
||||
if ($entity instanceof EntityInterface) {
|
||||
return $entity->access('delete-state:' . $parameters->get('workflow_state'), $account, TRUE);
|
||||
}
|
||||
}
|
||||
// No opinion, so other access checks should decide if access should be
|
||||
// allowed or not.
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
}
|
||||
289
web/core/modules/workflows/src/WorkflowInterface.php
Normal file
289
web/core/modules/workflows/src/WorkflowInterface.php
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for defining workflow entities.
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
interface WorkflowInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Adds a state to the workflow.
|
||||
*
|
||||
* @param string $state_id
|
||||
* The state's ID.
|
||||
* @param string $label
|
||||
* The state's label.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The workflow entity.
|
||||
*/
|
||||
public function addState($state_id, $label);
|
||||
|
||||
/**
|
||||
* Determines if the workflow has a state with the provided ID.
|
||||
*
|
||||
* @param string $state_id
|
||||
* The state's ID.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the workflow has a state with the provided ID, FALSE if not.
|
||||
*/
|
||||
public function hasState($state_id);
|
||||
|
||||
/**
|
||||
* Gets state objects for the provided state IDs.
|
||||
*
|
||||
* @param string[] $state_ids
|
||||
* A list of state IDs to get. If NULL then all states will be returned.
|
||||
*
|
||||
* @return \Drupal\workflows\StateInterface[]
|
||||
* An array of workflow states.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if $state_ids contains a state ID that does not exist.
|
||||
*/
|
||||
public function getStates($state_ids = NULL);
|
||||
|
||||
/**
|
||||
* Gets a workflow state.
|
||||
*
|
||||
* @param string $state_id
|
||||
* The state's ID.
|
||||
*
|
||||
* @return \Drupal\workflows\StateInterface
|
||||
* The workflow state.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if $state_id does not exist.
|
||||
*/
|
||||
public function getState($state_id);
|
||||
|
||||
/**
|
||||
* Sets a state's label.
|
||||
*
|
||||
* @param string $state_id
|
||||
* The state ID to set the label for.
|
||||
* @param string $label
|
||||
* The state's label.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The workflow entity.
|
||||
*/
|
||||
public function setStateLabel($state_id, $label);
|
||||
|
||||
/**
|
||||
* Sets a state's weight value.
|
||||
*
|
||||
* @param string $state_id
|
||||
* The state ID to set the weight for.
|
||||
* @param int $weight
|
||||
* The state's weight.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The workflow entity.
|
||||
*/
|
||||
public function setStateWeight($state_id, $weight);
|
||||
|
||||
/**
|
||||
* Deletes a state from the workflow.
|
||||
*
|
||||
* @param string $state_id
|
||||
* The state ID to delete.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The workflow entity.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if $state_id does not exist.
|
||||
*/
|
||||
public function deleteState($state_id);
|
||||
|
||||
/**
|
||||
* Gets the initial state for the workflow.
|
||||
*
|
||||
* @return \Drupal\workflows\StateInterface
|
||||
* The initial state.
|
||||
*/
|
||||
public function getInitialState();
|
||||
|
||||
/**
|
||||
* Adds a transition to the workflow.
|
||||
*
|
||||
* @param string $id
|
||||
* The transition ID.
|
||||
* @param string $label
|
||||
* The transition's label.
|
||||
* @param array $from_state_ids
|
||||
* The state IDs to transition from.
|
||||
* @param string $to_state_id
|
||||
* The state ID to transition to.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The workflow entity.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if either state does not exist.
|
||||
*/
|
||||
public function addTransition($id, $label, array $from_state_ids, $to_state_id);
|
||||
|
||||
/**
|
||||
* Gets a transition object for the provided transition ID.
|
||||
*
|
||||
* @param string $transition_id
|
||||
* A transition ID.
|
||||
*
|
||||
* @return \Drupal\workflows\TransitionInterface
|
||||
* The transition.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if $transition_id does not exist.
|
||||
*/
|
||||
public function getTransition($transition_id);
|
||||
|
||||
/**
|
||||
* Determines if a transition exists.
|
||||
*
|
||||
* @param string $transition_id
|
||||
* The transition ID.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the transition exists, FALSE if not.
|
||||
*/
|
||||
public function hasTransition($transition_id);
|
||||
|
||||
/**
|
||||
* Gets transition objects for the provided transition IDs.
|
||||
*
|
||||
* @param string[] $transition_ids
|
||||
* A list of transition IDs to get. If NULL then all transitions will be
|
||||
* returned.
|
||||
*
|
||||
* @return \Drupal\workflows\TransitionInterface[]
|
||||
* An array of transition objects.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if $transition_ids contains a transition ID that does not exist.
|
||||
*/
|
||||
public function getTransitions(array $transition_ids = NULL);
|
||||
|
||||
/**
|
||||
* Gets the transition IDs for a state for the provided direction.
|
||||
*
|
||||
* @param $state_id
|
||||
* The state to get transitions for.
|
||||
* @param string $direction
|
||||
* (optional) The direction of the transition. Defaults to 'from'. Possible
|
||||
* values are: 'from' and 'to'.
|
||||
*
|
||||
* @return array
|
||||
* The transition IDs for a state for the provided direction.
|
||||
*/
|
||||
public function getTransitionsForState($state_id, $direction = 'from');
|
||||
|
||||
/**
|
||||
* Gets a transition from state to state.
|
||||
*
|
||||
* @param string $from_state_id
|
||||
* The state ID to transition from.
|
||||
* @param string $to_state_id
|
||||
* The state ID to transition to.
|
||||
*
|
||||
* @return \Drupal\workflows\TransitionInterface
|
||||
* The transitions.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if the transition does not exist.
|
||||
*/
|
||||
public function getTransitionFromStateToState($from_state_id, $to_state_id);
|
||||
|
||||
/**
|
||||
* Determines if a transition from state to state exists.
|
||||
*
|
||||
* @param string $from_state_id
|
||||
* The state ID to transition from.
|
||||
* @param string $to_state_id
|
||||
* The state ID to transition to.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the transition exists, FALSE if not.
|
||||
*/
|
||||
public function hasTransitionFromStateToState($from_state_id, $to_state_id);
|
||||
|
||||
/**
|
||||
* Sets a transition's label.
|
||||
*
|
||||
* @param string $transition_id
|
||||
* The transition ID.
|
||||
* @param string $label
|
||||
* The transition's label.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The workflow entity.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if the transition does not exist.
|
||||
*/
|
||||
public function setTransitionLabel($transition_id, $label);
|
||||
|
||||
/**
|
||||
* Sets a transition's weight.
|
||||
*
|
||||
* @param string $transition_id
|
||||
* The transition ID.
|
||||
* @param int $weight
|
||||
* The transition's weight.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The workflow entity.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if the transition does not exist.
|
||||
*/
|
||||
public function setTransitionWeight($transition_id, $weight);
|
||||
|
||||
/**
|
||||
* Sets a transition's from states.
|
||||
*
|
||||
* @param string $transition_id
|
||||
* The transition ID.
|
||||
* @param array $from_state_ids
|
||||
* The state IDs to transition from.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The workflow entity.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if the transition does not exist or the states do not exist.
|
||||
*/
|
||||
public function setTransitionFromStates($transition_id, array $from_state_ids);
|
||||
|
||||
/**
|
||||
* Deletes a transition.
|
||||
*
|
||||
* @param string $transition_id
|
||||
* The transition ID.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The workflow entity.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if the transition does not exist.
|
||||
*/
|
||||
public function deleteTransition($transition_id);
|
||||
|
||||
/**
|
||||
* Gets the workflow type plugin.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowTypeInterface
|
||||
* The workflow type plugin.
|
||||
*/
|
||||
public function getTypePlugin();
|
||||
|
||||
}
|
||||
101
web/core/modules/workflows/src/WorkflowListBuilder.php
Normal file
101
web/core/modules/workflows/src/WorkflowListBuilder.php
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a listing of Workflow entities.
|
||||
*/
|
||||
class WorkflowListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* The workflow type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $workflowTypeManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity_type.manager')->getStorage($entity_type->id()),
|
||||
$container->get('plugin.manager.workflows.type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new WorkflowListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $workflow_type_manager
|
||||
* The workflow type plugin manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, PluginManagerInterface $workflow_type_manager) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
$this->workflowTypeManager = $workflow_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'workflow_admin_overview_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = $this->t('Workflow');
|
||||
$header['type'] = $this->t('Type');
|
||||
$header['states'] = $this->t('States');
|
||||
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
/** @var \Drupal\workflows\WorkflowInterface $entity */
|
||||
$row['label'] = $entity->label();
|
||||
|
||||
$row['type']['data'] = [
|
||||
'#markup' => $entity->getTypePlugin()->label()
|
||||
];
|
||||
|
||||
$items = array_map([State::class, 'labelCallback'], $entity->getStates());
|
||||
$row['states']['data'] = [
|
||||
'#theme' => 'item_list',
|
||||
'#context' => ['list_style' => 'comma-list'],
|
||||
'#items' => $items,
|
||||
];
|
||||
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
$build = parent::render();
|
||||
$workflow_types_count = count($this->workflowTypeManager->getDefinitions());
|
||||
if ($workflow_types_count === 0) {
|
||||
$build['table']['#empty'] = $this->t('There are no workflow types available. In order to create workflows you need to install a module that provides a workflow type. For example, the Content Moderation module provides a workflow type that enables workflows for content entities.');
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
||||
162
web/core/modules/workflows/src/WorkflowTypeInterface.php
Normal file
162
web/core/modules/workflows/src/WorkflowTypeInterface.php
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Component\Plugin\DerivativeInspectionInterface;
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* An interface for Workflow type plugins.
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
interface WorkflowTypeInterface extends PluginInspectionInterface, DerivativeInspectionInterface, ConfigurablePluginInterface {
|
||||
|
||||
/**
|
||||
* Initializes a workflow.
|
||||
*
|
||||
* Used to create required states and default transitions.
|
||||
*
|
||||
* @param \Drupal\workflows\WorkflowInterface $workflow
|
||||
* The workflow to initialize.
|
||||
*
|
||||
* @return \Drupal\workflows\WorkflowInterface
|
||||
* The initialized workflow.
|
||||
*
|
||||
* @see \Drupal\workflows\Form\WorkflowAddForm::save()
|
||||
*/
|
||||
public function initializeWorkflow(WorkflowInterface $workflow);
|
||||
|
||||
/**
|
||||
* Gets the label for the workflow type.
|
||||
*
|
||||
* @return string
|
||||
* The workflow type label.
|
||||
*/
|
||||
public function label();
|
||||
|
||||
/**
|
||||
* Performs access checks.
|
||||
*
|
||||
* @param \Drupal\workflows\WorkflowInterface $entity
|
||||
* The workflow entity for which to check access.
|
||||
* @param string $operation
|
||||
* The entity operation. Usually one of 'view', 'view label', 'update' or
|
||||
* 'delete'.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to check access.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function checkWorkflowAccess(WorkflowInterface $entity, $operation, AccountInterface $account);
|
||||
|
||||
/**
|
||||
* Decorates states so the WorkflowType can add additional information.
|
||||
*
|
||||
* @param \Drupal\workflows\StateInterface $state
|
||||
* The state object to decorate.
|
||||
*
|
||||
* @return \Drupal\workflows\StateInterface
|
||||
* The decorated state object.
|
||||
*/
|
||||
public function decorateState(StateInterface $state);
|
||||
|
||||
/**
|
||||
* React to the removal of a state from a workflow.
|
||||
*
|
||||
* @param string $state_id
|
||||
* The state ID of the state that is being removed.
|
||||
*/
|
||||
public function deleteState($state_id);
|
||||
|
||||
/**
|
||||
* Decorates transitions so the WorkflowType can add additional information.
|
||||
* @param \Drupal\workflows\TransitionInterface $transition
|
||||
* The transition object to decorate.
|
||||
*
|
||||
* @return \Drupal\workflows\TransitionInterface
|
||||
* The decorated transition object.
|
||||
*/
|
||||
public function decorateTransition(TransitionInterface $transition);
|
||||
|
||||
/**
|
||||
* React to the removal of a transition from a workflow.
|
||||
*
|
||||
* @param string $transition_id
|
||||
* The transition ID of the transition that is being removed.
|
||||
*/
|
||||
public function deleteTransition($transition_id);
|
||||
|
||||
/**
|
||||
* Builds a form to be added to the Workflow state edit form.
|
||||
*
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
* @param \Drupal\workflows\WorkflowInterface $workflow
|
||||
* The workflow the state is attached to.
|
||||
* @param \Drupal\workflows\StateInterface|null $state
|
||||
* The workflow state being edited. If NULL, a new state is being added.
|
||||
*
|
||||
* @return array
|
||||
* Form elements to add to a workflow state form for customisations to the
|
||||
* workflow.
|
||||
*
|
||||
* @see \Drupal\workflows\Form\WorkflowStateAddForm::form()
|
||||
* @see \Drupal\workflows\Form\WorkflowStateEditForm::form()
|
||||
*/
|
||||
public function buildStateConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, StateInterface $state = NULL);
|
||||
|
||||
/**
|
||||
* Builds a form to be added to the Workflow transition edit form.
|
||||
*
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
* @param \Drupal\workflows\WorkflowInterface $workflow
|
||||
* The workflow the state is attached to.
|
||||
* @param \Drupal\workflows\TransitionInterface|null $transition
|
||||
* The workflow transition being edited. If NULL, a new transition is being
|
||||
* added.
|
||||
*
|
||||
* @return array
|
||||
* Form elements to add to a workflow transition form for customisations to
|
||||
* the workflow.
|
||||
*
|
||||
* @see \Drupal\workflows\Form\WorkflowTransitionAddForm::form()
|
||||
* @see \Drupal\workflows\Form\WorkflowTransitionEditForm::form()
|
||||
*/
|
||||
public function buildTransitionConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, TransitionInterface $transition = NULL);
|
||||
|
||||
/**
|
||||
* Gets the required states of workflow type.
|
||||
*
|
||||
* This are usually configured in the workflow type annotation.
|
||||
*
|
||||
* @return array[]
|
||||
* The required states.
|
||||
*
|
||||
* @see \Drupal\workflows\Annotation\WorkflowType
|
||||
*/
|
||||
public function getRequiredStates();
|
||||
|
||||
/**
|
||||
* Informs the plugin that a dependency of the workflow will be deleted.
|
||||
*
|
||||
* @param array $dependencies
|
||||
* An array of dependencies that will be deleted keyed by dependency type.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the workflow settings have been changed, FALSE if not.
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigEntityInterface::onDependencyRemoval()
|
||||
*
|
||||
* @todo https://www.drupal.org/node/2579743 make part of a generic interface.
|
||||
*/
|
||||
public function onDependencyRemoval(array $dependencies);
|
||||
|
||||
}
|
||||
40
web/core/modules/workflows/src/WorkflowTypeManager.php
Normal file
40
web/core/modules/workflows/src/WorkflowTypeManager.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflows;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\workflows\Annotation\WorkflowType;
|
||||
|
||||
/**
|
||||
* Provides a Workflow type plugin manager.
|
||||
*
|
||||
* @see \Drupal\workflows\Annotation\WorkflowType
|
||||
* @see \Drupal\workflows\WorkflowTypeInterface
|
||||
* @see plugin_api
|
||||
*
|
||||
* @internal
|
||||
* The workflow system is currently experimental and should only be leveraged
|
||||
* by experimental modules and development releases of contributed modules.
|
||||
*/
|
||||
class WorkflowTypeManager extends DefaultPluginManager {
|
||||
|
||||
/**
|
||||
* Constructs a new class instance.
|
||||
*
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* Cache backend instance to use.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler to invoke the alter hook with.
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct('Plugin/WorkflowType', $namespaces, $module_handler, WorkflowTypeInterface::class, WorkflowType::class);
|
||||
$this->alterInfo('workflow_type_info');
|
||||
$this->setCacheBackend($cache_backend, 'workflow_type_info');
|
||||
}
|
||||
|
||||
}
|
||||
Reference in a new issue