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