1<?php
2
3namespace Drupal\workflows\Form;
4
5use Drupal\Core\Form\SubformState;
6use Drupal\Core\Plugin\PluginFormFactoryInterface;
7use Drupal\workflows\Entity\Workflow;
8use Drupal\workflows\State;
9use Drupal\Core\Entity\EntityForm;
10use Drupal\Core\Entity\EntityInterface;
11use Drupal\Core\Form\FormStateInterface;
12use Drupal\Core\Url;
13use Drupal\workflows\WorkflowTypeInterface;
14use Symfony\Component\DependencyInjection\ContainerInterface;
15
16/**
17 * The form for editing workflows.
18 *
19 * @internal
20 */
21class WorkflowEditForm extends EntityForm {
22
23  /**
24   * The plugin form factory.
25   *
26   * @var \Drupal\Core\Plugin\PluginFormFactoryInterface
27   */
28  protected $pluginFormFactory;
29
30  /**
31   * Creates an instance of WorkflowStateEditForm.
32   *
33   * @param \Drupal\Core\Plugin\PluginFormFactoryInterface $pluginFormFactory
34   *   The plugin form factory.
35   */
36  public function __construct(PluginFormFactoryInterface $pluginFormFactory) {
37    $this->pluginFormFactory = $pluginFormFactory;
38  }
39
40  /**
41   * {@inheritdoc}
42   */
43  public static function create(ContainerInterface $container) {
44    return new static(
45      $container->get('plugin_form.factory')
46    );
47  }
48
49  /**
50   * {@inheritdoc}
51   */
52  public function form(array $form, FormStateInterface $form_state) {
53    $form = parent::form($form, $form_state);
54
55    /* @var \Drupal\workflows\WorkflowInterface $workflow */
56    $workflow = $this->entity;
57    $workflow_type = $workflow->getTypePlugin();
58    $form['#title'] = $this->t('Edit %label workflow', ['%label' => $workflow->label()]);
59
60    $form['label'] = [
61      '#type' => 'textfield',
62      '#title' => $this->t('Label'),
63      '#maxlength' => 255,
64      '#default_value' => $workflow->label(),
65      '#required' => TRUE,
66    ];
67
68    $form['id'] = [
69      '#type' => 'machine_name',
70      '#default_value' => $workflow->id(),
71      '#machine_name' => [
72        'exists' => [Workflow::class, 'load'],
73      ],
74      '#disabled' => TRUE,
75    ];
76
77    $header = [
78      'state' => $this->t('State'),
79      'weight' => $this->t('Weight'),
80      'operations' => $this->t('Operations'),
81    ];
82    $form['states_container'] = [
83      '#type' => 'details',
84      '#title' => $this->t('States'),
85      '#open' => TRUE,
86    ];
87    $form['states_container']['states'] = [
88      '#type' => 'table',
89      '#header' => $header,
90      '#title' => $this->t('States'),
91      '#empty' => $this->t('There are no states yet.'),
92      '#tabledrag' => [
93        [
94          'action' => 'order',
95          'relationship' => 'sibling',
96          'group' => 'state-weight',
97        ],
98      ],
99    ];
100
101    $states = $workflow->getTypePlugin()->getStates();
102
103    // Warn the user if there are no states.
104    if (empty($states)) {
105      $this->messenger()->addWarning(
106        $this->t(
107          'This workflow has no states and will be disabled until there is at least one, <a href=":add-state">add a new state.</a>',
108          [':add-state' => $workflow->toUrl('add-state-form')->toString()]
109        )
110      );
111    }
112
113    $state_weight_delta = round(count($states) / 2);
114    foreach ($states as $state) {
115      $links = [
116        'edit' => [
117          'title' => $this->t('Edit'),
118          'url' => Url::fromRoute('entity.workflow.edit_state_form', ['workflow' => $workflow->id(), 'workflow_state' => $state->id()]),
119        ],
120      ];
121      if ($this->entity->access('delete-state:' . $state->id())) {
122        $links['delete'] = [
123          'title' => $this->t('Delete'),
124          'url' => Url::fromRoute('entity.workflow.delete_state_form', [
125            'workflow' => $workflow->id(),
126            'workflow_state' => $state->id(),
127          ]),
128        ];
129      }
130      $form['states_container']['states'][$state->id()] = [
131        '#attributes' => ['class' => ['draggable']],
132        'state' => ['#markup' => $state->label()],
133        '#weight' => $state->weight(),
134        'weight' => [
135          '#type' => 'weight',
136          '#title' => $this->t('Weight for @title', ['@title' => $state->label()]),
137          '#title_display' => 'invisible',
138          '#default_value' => $state->weight(),
139          '#attributes' => ['class' => ['state-weight']],
140          '#delta' => $state_weight_delta,
141        ],
142        'operations' => [
143          '#type' => 'operations',
144          '#links' => $links,
145        ],
146      ];
147    }
148    $form['states_container']['state_add'] = [
149      '#markup' => $workflow->toLink($this->t('Add a new state'), 'add-state-form')->toString(),
150    ];
151
152    $header = [
153      'label' => $this->t('Label'),
154      'weight' => $this->t('Weight'),
155      'from' => $this->t('From'),
156      'to' => $this->t('To'),
157      'operations' => $this->t('Operations'),
158    ];
159    $form['transitions_container'] = [
160      '#type' => 'details',
161      '#title' => $this->t('Transitions'),
162      '#open' => TRUE,
163    ];
164    $form['transitions_container']['transitions'] = [
165      '#type' => 'table',
166      '#header' => $header,
167      '#title' => $this->t('Transitions'),
168      '#empty' => $this->t('There are no transitions yet.'),
169      '#tabledrag' => [
170        [
171          'action' => 'order',
172          'relationship' => 'sibling',
173          'group' => 'transition-weight',
174        ],
175      ],
176    ];
177
178    $transitions = $workflow->getTypePlugin()->getTransitions();
179    $transition_weight_delta = round(count($transitions) / 2);
180    foreach ($transitions as $transition) {
181      $links['edit'] = [
182        'title' => $this->t('Edit'),
183        'url' => Url::fromRoute('entity.workflow.edit_transition_form', ['workflow' => $workflow->id(), 'workflow_transition' => $transition->id()]),
184      ];
185      $links['delete'] = [
186        'title' => $this->t('Delete'),
187        'url' => Url::fromRoute('entity.workflow.delete_transition_form', ['workflow' => $workflow->id(), 'workflow_transition' => $transition->id()]),
188      ];
189      $form['transitions_container']['transitions'][$transition->id()] = [
190        '#attributes' => ['class' => ['draggable']],
191        'label' => ['#markup' => $transition->label()],
192        '#weight' => $transition->weight(),
193        'weight' => [
194          '#type' => 'weight',
195          '#title' => $this->t('Weight for @title', ['@title' => $transition->label()]),
196          '#title_display' => 'invisible',
197          '#default_value' => $transition->weight(),
198          '#attributes' => ['class' => ['transition-weight']],
199          '#delta' => $transition_weight_delta,
200        ],
201        'from' => [
202          '#theme' => 'item_list',
203          '#items' => array_map([State::class, 'labelCallback'], $transition->from()),
204          '#context' => ['list_style' => 'comma-list'],
205        ],
206        'to' => ['#markup' => $transition->to()->label()],
207        'operations' => [
208          '#type' => 'operations',
209          '#links' => $links,
210        ],
211      ];
212    }
213    $form['transitions_container']['transition_add'] = [
214      '#markup' => $workflow->toLink($this->t('Add a new transition'), 'add-transition-form')->toString(),
215    ];
216
217    if ($workflow_type->hasFormClass(WorkflowTypeInterface::PLUGIN_FORM_KEY)) {
218      $form['type_settings'] = [
219        '#tree' => TRUE,
220      ];
221      $subform_state = SubformState::createForSubform($form['type_settings'], $form, $form_state);
222      $form['type_settings'] += $this->pluginFormFactory
223        ->createInstance($workflow_type, WorkflowTypeInterface::PLUGIN_FORM_KEY)
224        ->buildConfigurationForm($form['type_settings'], $subform_state);
225    }
226
227    return $form;
228  }
229
230  /**
231   * {@inheritdoc}
232   */
233  public function validateForm(array &$form, FormStateInterface $form_state) {
234    /* @var \Drupal\workflows\WorkflowInterface $workflow */
235    $workflow = $this->entity;
236    $workflow_type = $workflow->getTypePlugin();
237
238    if ($workflow_type->hasFormClass(WorkflowTypeInterface::PLUGIN_FORM_KEY)) {
239      $subform_state = SubformState::createForSubform($form['type_settings'], $form, $form_state);
240      $this->pluginFormFactory
241        ->createInstance($workflow_type, WorkflowTypeInterface::PLUGIN_FORM_KEY)
242        ->validateConfigurationForm($form['type_settings'], $subform_state);
243    }
244  }
245
246  /**
247   * {@inheritdoc}
248   */
249  public function save(array $form, FormStateInterface $form_state) {
250    /* @var \Drupal\workflows\WorkflowInterface $workflow */
251    $workflow = $this->entity;
252    $workflow_type = $workflow->getTypePlugin();
253
254    if ($workflow_type->hasFormClass(WorkflowTypeInterface::PLUGIN_FORM_KEY)) {
255      $subform_state = SubformState::createForSubform($form['type_settings'], $form, $form_state);
256      $this->pluginFormFactory
257        ->createInstance($workflow_type, WorkflowTypeInterface::PLUGIN_FORM_KEY)
258        ->submitConfigurationForm($form['type_settings'], $subform_state);
259    }
260
261    $workflow->save();
262    $this->messenger()->addStatus($this->t('Saved the %label Workflow.', ['%label' => $workflow->label()]));
263  }
264
265  /**
266   * {@inheritdoc}
267   */
268  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
269    // This form can only set the workflow's ID, label and the weights for each
270    // state.
271    /** @var \Drupal\workflows\WorkflowInterface $entity */
272    $values = $form_state->getValues();
273    $entity->set('label', $values['label']);
274    $entity->set('id', $values['id']);
275    foreach ($values['states'] as $state_id => $state_values) {
276      $entity->getTypePlugin()->setStateWeight($state_id, $state_values['weight']);
277    }
278    foreach ($values['transitions'] as $transition_id => $transition_values) {
279      $entity->getTypePlugin()->setTransitionWeight($transition_id, $transition_values['weight']);
280    }
281  }
282
283}
284