workflow.form.inc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. /**
  3. * @file
  4. * Contains helper functions for WorkflowTransitionForm.
  5. */
  6. use Drupal\Component\Utility\Html;
  7. use Drupal\workflow\Entity\WorkflowTransitionInterface;
  8. /**
  9. * Getter/Setter to tell if the action buttons are used.
  10. *
  11. * @param string $button_type
  12. * Options. If empty, value is only getted, else the button type.
  13. *
  14. * @return string
  15. * Previous value. If 'dropbutton'||'buttons', action buttons must be created.
  16. *
  17. * @see workflow_form_alter()
  18. * @see WorkflowDefaultWidget::formElement()
  19. *
  20. * Used to save some expensive operations on every form.
  21. */
  22. function _workflow_use_action_buttons($button_type = '') {
  23. global $_workflow_use_actions_buttons;
  24. // Reset value if requested.
  25. if ($button_type) {
  26. $_workflow_use_actions_buttons = $button_type;
  27. }
  28. return $_workflow_use_actions_buttons;
  29. }
  30. /**
  31. * Implements hook_form_alter().
  32. *
  33. * Form builder. Move action buttons next to the 'Save'/'Delete' buttons.
  34. *
  35. * This is only used if the set the 'options widget' to 'action buttons'.
  36. * Do not use with multiple workflows per entity: confusing UX.
  37. * ATM this works for:
  38. * - Workflow Field: create, edit, view, workflow tab, comment;
  39. * - Workflow Node: view, workflow tab;
  40. * (For forms with Workflow Node, the form_alter() is AFTER formElement(). )
  41. *
  42. * @todo: move this to WorkflowTransitionForm::_addActionButtons();
  43. */
  44. function workflow_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  45. // N.B. Keep code aligned: workflow_form_alter(), WorkflowTransitionForm::actions().
  46. // Use a fast, custom way to check if we need to do this.
  47. // @todo: Make this work with multiple workflows per entity.
  48. if (!_workflow_use_action_buttons()) {
  49. return;
  50. }
  51. if (isset($form_state->getBuildInfo()['base_form_id']) && $form_state->getBuildInfo()['base_form_id'] == 'workflow_transition_form') {
  52. // The WorkflowTransitionForm::actions() has its own handling.
  53. // E.g., Workflow History tab, Block.
  54. return;
  55. }
  56. // Find the first workflow.
  57. // (So this won't work with multiple workflows per entity.)
  58. $workflow_element = _workflow_transition_form_get_first_workflow_element($form);
  59. // Quit if there is no Workflow on this page.
  60. if (!$workflow_element ) {
  61. return;
  62. }
  63. // Quit if there are no Workflow Action buttons.
  64. // (If user has only 1 workflow option, there are no Action buttons.)
  65. if (count($workflow_element['to_sid']['#options']) <= 1) {
  66. return;
  67. }
  68. // Find the default submit button and replace with our own action buttons.
  69. if (isset($form['actions']['submit'])) {
  70. $default_submit_action = $form['actions']['submit'];
  71. // @todo D8-port: on Node-form, this is not sufficient.
  72. unset($form['actions']['submit']);
  73. }
  74. elseif (isset($form['actions']['save'])) {
  75. $default_submit_action = $form['actions']['save'];
  76. unset($form['actions']['save']);
  77. }
  78. else {
  79. // @todo test: when does this happen?
  80. $default_submit_action = [];
  81. }
  82. if (isset($default_submit_action)) {
  83. $actions = _workflow_transition_form_get_action_buttons($form, $workflow_element, $default_submit_action);
  84. // Place the button with the other action buttons.
  85. $form['actions'] = isset($form['actions']) ? $form['actions'] : [];
  86. $form['actions'] += $actions;
  87. }
  88. }
  89. /**
  90. * Fetches the first workflow_element from one of the Workflow Fields.
  91. *
  92. * @param array $form
  93. *
  94. * @return array
  95. */
  96. function _workflow_transition_form_get_first_workflow_element(&$form) {
  97. $workflow_element = [];
  98. // Find the first workflow.
  99. // (So this won't work with multiple workflows per entity.)
  100. foreach (\Drupal\Core\Render\Element::children($form) as $key) {
  101. if (isset($form[$key]['widget'][0]['#default_value'])) {
  102. $transition = $form[$key]['widget'][0]['#default_value'];
  103. if (is_object($transition) && $transition instanceof WorkflowTransitionInterface ) {
  104. $workflow_element = $form[$key]['widget'][0];
  105. break;
  106. }
  107. }
  108. }
  109. return $workflow_element;
  110. }
  111. /**
  112. * Returns the action buttons from the options widget.
  113. *
  114. * @param array $form
  115. * @param array $workflow_element
  116. * @param array $default_submit_action
  117. *
  118. * @return array $actions
  119. */
  120. function _workflow_transition_form_get_action_buttons(array $form, array $workflow_element, array $default_submit_action) {
  121. $actions = [];
  122. $current_sid = $workflow_element['to_sid']['#default_value'];
  123. /* @var $transition WorkflowTransitionInterface */
  124. $transition = $workflow_element['workflow_transition']['#value'];
  125. $field_name = $transition->getFieldName();
  126. // Find the default submit button and add our action buttons before it.
  127. // Get the min weight for our buttons.
  128. $option_weight = isset($default_submit_action['#weight']) ? $default_submit_action['#weight'] : 0;
  129. $option_weight = $option_weight - count($workflow_element['to_sid']['#options']);
  130. $min_weight = $option_weight;
  131. foreach ($workflow_element['to_sid']['#options'] as $sid => $option_name) {
  132. // Make the workflow button act exactly like the original submit button.
  133. $same_state_button = ($sid == $current_sid);
  134. $workflow_submit_action = $default_submit_action;
  135. // Add target State ID and Field name, to set correct value in validate_buttons callback.
  136. $workflow_submit_action['#workflow'] = [
  137. 'field_name' => $field_name,
  138. 'to_sid' => $sid,
  139. ];
  140. // Keep option order. Put current state first.
  141. $workflow_submit_action['#weight'] = ($same_state_button) ? $min_weight : ++$option_weight;
  142. // Add/Overwrite some other settings.
  143. $workflow_submit_action['#access'] = TRUE;
  144. $workflow_submit_action['#value'] = $option_name;
  145. // Use one drop button, instead of several action buttons.
  146. if ('dropbutton' == _workflow_use_action_buttons()) {
  147. $workflow_submit_action['#dropbutton'] = 'save';
  148. }
  149. $workflow_submit_action['#attributes'] = ($same_state_button) ? ['class' => ['form-save-default-button']] : [];
  150. $workflow_submit_action['#button_type'] = ($same_state_button) ? 'primary' : ''; // @todo: works for node form and workflow tab, not for workflow block.
  151. //$workflow_submit_action['#executes_submit_callback'] = TRUE;
  152. // Add class to workflow button.
  153. $workflow_submit_action['#attributes']['class'][] = Html::getClass('workflow_button_' . $option_name);
  154. // Append the form's #validate function, or it won't be called upon submit,
  155. // because the workflow buttons have its own #validate.
  156. $workflow_submit_action['#validate'] = [];
  157. $workflow_submit_action['#validate'][] = '_workflow_transition_form_validate_buttons';
  158. if (isset($default_submit_action['#validate'])) {
  159. $workflow_submit_action['#validate'] = $default_submit_action['#validate'];
  160. }
  161. elseif (isset($form['#validate'])) {
  162. $workflow_submit_action['#validate'] = $form['#validate'];
  163. }
  164. // Append the submit-buttons's #submit function, or it won't be called upon submit.
  165. if (isset($default_submit_action['#submit'])) {
  166. $workflow_submit_action['#submit'] = $default_submit_action['#submit'];
  167. }
  168. elseif (isset($form['#submit'])) {
  169. $workflow_submit_action['#submit'] = $form['#submit'];
  170. }
  171. // Hide the same-state button in some cases.
  172. if ($same_state_button) {
  173. if (isset($form['#form_id']) && substr($form['#form_id'], 0, 24) == 'workflow_transition_form') {
  174. // Hide same-state-button on the transition-form (that is:
  175. // view page or workflow history tab) if there is nothing to do.
  176. // However, a Transition may be fieldable (have attached fields).
  177. if ($form['comment']['#access'] == FALSE) {
  178. $workflow_submit_action['#access'] = FALSE;
  179. }
  180. }
  181. elseif (isset($form['#id']) && $form['#id'] == 'comment-form') {
  182. // On comment-form, the button must stay, since you can comment to same state.
  183. }
  184. else {
  185. // On a entity edit page, the button must stay.
  186. }
  187. }
  188. // Place the button with the other action buttons.
  189. $actions['workflow_' . $sid] = $workflow_submit_action;
  190. }
  191. return $actions;
  192. }
  193. /**
  194. * Get the Workflow parameter from the button, pressed by the user.
  195. * @param \Drupal\Core\Form\FormStateInterface $form_state
  196. * The form state.
  197. * @return array
  198. * A $field_name => $to_sid array.
  199. */
  200. function _workflow_transition_form_get_triggering_button(\Drupal\Core\Form\FormStateInterface $form_state) {
  201. $result = ['field_name' => '', 'to_sid' => ''];
  202. $triggering_element = $form_state->getTriggeringElement();
  203. if (isset($triggering_element['#workflow'])) {
  204. $result['field_name'] = $triggering_element['#workflow']['field_name'];
  205. $result['to_sid'] = $triggering_element['#workflow']['to_sid'];
  206. }
  207. return $result;
  208. }
  209. /**
  210. * Submit callback function for the Workflow Form / DefaultWidget.
  211. *
  212. * Validate form data for 'time' element.
  213. * @param $element
  214. * @param \Drupal\Core\Form\FormStateInterface $form_state
  215. * @param $form
  216. */
  217. function _workflow_transition_form_element_validate_time($element, \Drupal\Core\Form\FormStateInterface &$form_state, $form) {
  218. if (!strtotime($element['#value'])) {
  219. $form_state->setError($element, t('Please enter a valid value for time.'));
  220. }
  221. }
  222. /**
  223. * Submit callback function for the Workflow Form / DefaultWidget.
  224. *
  225. * This is only used when using action buttons in the widget.
  226. * It sets the new state to proper
  227. * element and sets a submit function if needed, making sure the action is
  228. * executed, influencing function core/includes/form.inc/form_execute_handlers().
  229. * (While constructing the Workflow form, we were not yet aware of the submit
  230. * buttons of the complete form. We try to correct this here, without adding
  231. * another hook_form_alter. We guess the first button is the Save button.
  232. */
  233. function _workflow_transition_form_validate_buttons($form, \Drupal\Core\Form\FormStateInterface &$form_state) {
  234. // Retrieve the data from the form.
  235. $transition = $form_state->getValue('workflow_transition');
  236. if ($transition) {
  237. // On WorkflowTransitionForm :
  238. // D7: $form_state['input']['to_sid'] = $new_sid;
  239. // D7: $form_state['values'][$field_name][$langcode][0]['to_sid'] = $new_sid;
  240. $values = $form_state->getValues();
  241. $to_sid = $form_state->getTriggeringElement()['#workflow']['to_sid'];
  242. $values['to_sid'] = $to_sid;
  243. // Update the form_state.
  244. $form_state->setValues($values);
  245. }
  246. else {
  247. // On edit form : See $form_state->getTriggeringElement() in WorkflowDefaultWidget;
  248. }
  249. }