workflow.api.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. <?php
  2. /**
  3. * @file
  4. * Hooks provided by the workflow module.
  5. */
  6. use Drupal\Core\Entity\EntityInterface;
  7. use Drupal\Core\Form\FormStateInterface;
  8. use Drupal\user\UserInterface;
  9. use Drupal\workflow\Entity\Workflow;
  10. use Drupal\workflow\Entity\WorkflowConfigTransition;
  11. use Drupal\workflow\Entity\WorkflowState;
  12. use Drupal\workflow\Entity\WorkflowTransitionInterface;
  13. /**
  14. * Implements hook_workflow_operations().
  15. *
  16. * Adds extra operations to ListBuilders.
  17. * - workflow_ui: Workflow, State;
  18. * - workflow: WorkflowTransition;
  19. *
  20. * @param string $op
  21. * 'top_actions': Allow modules to insert their own front page action links.
  22. * 'operations': Allow modules to insert their own workflow operations.
  23. * 'workflow': Allow modules to insert workflow operations.
  24. * 'state': Allow modules to insert state operations.
  25. * @param \Drupal\Core\Entity\EntityInterface|null $entity
  26. * The current workflow/state/transition object.
  27. *
  28. * @return array
  29. * The new actions, to be added to the entity list.
  30. */
  31. function hook_workflow_operations($op, EntityInterface $entity = NULL) {
  32. $operations = [];
  33. switch ($op) {
  34. case 'top_actions':
  35. // As of D8, below hook_workflow_operations is removed, in favour of core hooks.
  36. // @see file workflow_ui.links.action.yml for an example top action.
  37. return $operations;
  38. case 'operations':
  39. break;
  40. case 'workflow':
  41. // This example adds an operation to the 'operations column' of the Workflow List.
  42. /* @var $workflow Workflow */
  43. $workflow = $entity;
  44. $alt = t('Control content access for @wf', ['@wf' => $workflow->label()]);
  45. $attributes = ['alt' => $alt, 'title' => $alt];
  46. $operations['workflow_access_form'] = [
  47. 'title' => t('Access'),
  48. 'weight' => 50,
  49. 'url' => \Drupal\Core\Url::fromRoute('entity.workflow_type.access_form', ['workflow_type' => $workflow->id()]),
  50. 'query' => \Drupal::destination()->getAsArray(), // Add destination.
  51. ];
  52. return $operations;
  53. case 'state':
  54. /* @var $state WorkflowState */
  55. $state = $entity;
  56. break;
  57. case 'workflow_transition':
  58. // As of D8, below hook_workflow_operations is removed, in favour of core hooks.
  59. // @see EntityListBuilder::getOperations, workflow_operations, workflow.api.php.
  60. // Your module may add operations to the Entity list.
  61. /* @var $transition WorkflowTransitionInterface */
  62. $transition = $entity;
  63. break;
  64. default:
  65. break;
  66. }
  67. return $operations;
  68. }
  69. /**
  70. * Implements hook_workflow().
  71. *
  72. * NOTE: This hook may reside in the implementing module
  73. * or in a module.workflow.inc file.
  74. *
  75. * @param string $op
  76. * The current workflow operation.
  77. * E.g., 'transition pre', 'transition post'.
  78. * @param \Drupal\workflow\Entity\WorkflowTransitionInterface $transition
  79. * The transition, that contains all of the above.
  80. * @param \Drupal\user\UserInterface $user
  81. *
  82. * @return bool|void
  83. */
  84. function hook_workflow($op, WorkflowTransitionInterface $transition, UserInterface $user) {
  85. switch ($op) {
  86. case 'transition permitted':
  87. // As of version 8.x-1.x, this operation is never called to check if transition is permitted.
  88. // This was called in the following situations:
  89. // case 1. when building a workflow widget with list of available transitions;
  90. // case 2. when executing a transition, just before the 'transition pre';
  91. // case 3. when showing a 'revert state' link in a Views display.
  92. // Your module's implementation may return FALSE here and disallow
  93. // the execution, or avoid the presentation of the new State.
  94. // This may be user-dependent.
  95. // As of version 8.x-1.x:
  96. // case 1: use hook_workflow_permitted_state_transitions_alter();
  97. // case 2: use the 'transition pre' operation;
  98. // case 3: use the 'transition pre' operation;
  99. return TRUE;
  100. case 'transition revert':
  101. // Hook is called when showing the Transition Revert form.
  102. // Implement this hook if you need to control this.
  103. // If you return FALSE here, you will veto the transition.
  104. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, $op, '');
  105. break;
  106. case 'transition pre':
  107. // The workflow module does nothing during this operation.
  108. // Implement this hook if you need to change/do something BEFORE anything
  109. // is saved to the database.
  110. // If you return FALSE here, you will veto the transition.
  111. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, $op, '');
  112. break;
  113. case 'transition post':
  114. // In D7, this is called by Workflow Node during update of the state, directly
  115. // after updating the Workflow. Workflow Field does not call this,
  116. // since you can call a hook_entity_* event after saving the entity.
  117. // @see https://api.drupal.org/api/drupal/includes%21module.inc/group/hooks/7
  118. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, $op, '');
  119. break;
  120. case 'transition delete':
  121. case 'state delete':
  122. case 'workflow delete':
  123. // These hooks are removed in D8, in favour of the core hooks:
  124. // - workflow_entity_predelete(EntityInterface $entity)
  125. // - workflow_entity_delete(EntityInterface $entity)
  126. // See examples at the bottom of this file.
  127. break;
  128. }
  129. return;
  130. }
  131. /**
  132. * Implements hook_workflow_history_alter().
  133. *
  134. * In D8, hook_workflow_history_alter() is removed, in favour
  135. * of ListBuilder::getDefaultOperations
  136. * and hook_workflow_operations('workflow_transition').
  137. *
  138. * Allow other modules to add Operations to the most recent history change.
  139. * E.g., Workflow Revert implements an 'undo' operation.
  140. *
  141. * @param array $variables
  142. * The current workflow history information as an array.
  143. * 'old_sid' - The state ID of the previous state.
  144. * 'old_state_name' - The state name of the previous state.
  145. * 'sid' - The state ID of the current state.
  146. * 'state_name' - The state name of the current state.
  147. * 'history' - The row from the workflow_transition_history table.
  148. * 'transition' - a WorkflowTransition object, containing all of the above.
  149. */
  150. function hook_workflow_history_alter(array &$variables) {
  151. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, '', '');
  152. // The Workflow module does nothing with this hook.
  153. // For an example implementation, see the Workflow Revert add-on.
  154. }
  155. /**
  156. * Implements hook_workflow_comment_alter().
  157. *
  158. * Allow other modules to change the user comment when saving a state change.
  159. *
  160. * @param string $comment
  161. * The comment of the current state transition.
  162. * @param array $context
  163. * 'transition' - The current transition itself.
  164. */
  165. function hook_workflow_comment_alter(&$comment, array &$context) {
  166. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, '', '');
  167. /* @var $transition WorkflowTransitionInterface */
  168. $transition = $context['transition'];
  169. //$comment = $transition->getOwner()->getUsername() . ' says: ' . $comment;
  170. }
  171. /**
  172. * Implements hook_workflow_permitted_state_transitions_alter().
  173. *
  174. * @param array $transitions
  175. * An array of allowed transitions from the current state (as provided in
  176. * $context). They are already filtered by the settings in Admin UI.
  177. * @param array $context
  178. * An array of relevant objects. Currently:
  179. * $context = array(
  180. * 'user' => $user,
  181. * 'workflow' => $workflow,
  182. * 'state' => $current_state,
  183. * 'force' => $force,
  184. * );
  185. *
  186. * This hook allows you to add custom filtering of allowed target states, add
  187. * new custom states, change labels, etc.
  188. * It is invoked in WorkflowState::getOptions().
  189. */
  190. function hook_workflow_permitted_state_transitions_alter(array &$transitions, array $context) {
  191. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, '', '');
  192. $user = $context['user']; // user may have the custom role AUTHOR.
  193. // The following could be fetched from each transition.
  194. $workflow = $context['workflow'];
  195. $current_state = $context['state'];
  196. // The following could be fetched from the $user and $transition objects.
  197. $force = $context['force'];
  198. // Implement here own permission logic.
  199. foreach ($transitions as $key => $transition) {
  200. /* @var $transition WorkflowTransitionInterface */
  201. if (!$transition->isAllowed($user, $force)) {
  202. //unset($transitions[$key]);
  203. }
  204. }
  205. // This example creates a new custom target state.
  206. $values = [
  207. // Fixed values for new transition.
  208. 'wid' => $context['workflow']->id(),
  209. 'from_sid' => $context['state']->id(),
  210. // Custom values for new transition.
  211. // The ID must be an integer, due to db-table constraints.
  212. 'to_sid' => '998',
  213. 'label' => 'go to my new fantasy state',
  214. ];
  215. $new_transition = WorkflowConfigTransition::create($values);
  216. // $transitions[] = $new_transition;
  217. }
  218. /**********************************************************************
  219. * Hooks defined by core Form API: hooks to to alter the Workflow Form/Widget.
  220. */
  221. /**
  222. * Alter forms for field widgets provided by other modules.
  223. *
  224. * @param $element
  225. * The field widget form element as constructed by hook_field_widget_form().
  226. * @param $form_state
  227. * An associative array containing the current state of the form.
  228. * @param $context
  229. * An associative array containing the following key-value pairs, matching the
  230. * arguments received by hook_field_widget_form():
  231. * - form: The form structure to which widgets are being attached. This may be
  232. * a full form structure, or a sub-element of a larger form.
  233. * - field: The field structure.
  234. * - instance: The field instance structure.
  235. * - langcode: The language associated with $items.
  236. * - items: Array of default values for this field.
  237. * - delta: The order of this item in the array of subelements (0, 1, 2, etc).
  238. *
  239. * @see hook_field_widget_form()
  240. * @see hook_field_widget_WIDGET_TYPE_form_alter()
  241. */
  242. function hook_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
  243. // A hook for changing any widget. Better not use it: it is called on EVERY
  244. // Widget. (Even though the message is only shown once.)
  245. // D7: This hook is introduced in Drupal 7.8.
  246. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, '', '');
  247. // dpm($context['widget']->getPluginId());
  248. }
  249. function hook_field_widget_workflow_default_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
  250. // A hook specific for the 'workflow_default' widget.
  251. // D7: This hook is introduced in Drupal 7.8.
  252. // D8: This name is specified in the annotation of WorkflowDefaultWidget.
  253. workflow_debug(__FILE__, __FUNCTION__, __LINE__, '', '');
  254. // A widget on an entity form.
  255. if ('workflow_default' == $context['widget']->getPluginId()) {
  256. // This object contains all you need. You may find it in one of two locations.
  257. /* @var $transition WorkflowTransitionInterface */
  258. /* @var $transition WorkflowTransitionInterface */
  259. $transition = $element['#default_value'];
  260. // An example of customizing/overriding the workflow widget.
  261. // Beware, until now, you must do this twice: on the widget and on the form.
  262. if ($transition->getOwnerId() == 1) {
  263. drupal_set_message('(Test/Devel message) I got you, user 1, you will never schedule again,
  264. and you WILL document each state change!', 'warning');
  265. // Let's prohibit scheduling for user 1.
  266. $element['workflow_scheduling']['#access'] = FALSE;
  267. // Let's prohibit scheduling for user 1.
  268. if ($element['comment']['#access'] == TRUE) {
  269. $element['comment']['#required'] = TRUE;
  270. }
  271. }
  272. }
  273. }
  274. /**
  275. * Implements hook_form_BASE_FORM_ID_alter().
  276. *
  277. * Use this hook to alter the form.
  278. * It is only suited if you only use View Page or Workflow Tab.
  279. * If you change the state on the Entity Edit page (form), you need the hook
  280. * hook_form_alter(). See below for more info.
  281. */
  282. function hook_form_workflow_transition_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  283. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, $form_id, '');
  284. // The WorkflowTransitionForm (E.g., Workflow History tab, Block).
  285. // It has its own handling.
  286. // @todo: Populate the WorkflowTransitionForm with a Widget, so we have 1 way-of-working.
  287. // Let's take a (changeable) reference to the element.
  288. $workflow_element = &$form;
  289. // This object contains all you need. You may find it in one of two locations.
  290. /* @var $transition WorkflowTransitionInterface */
  291. $transition = $form['workflow_transition']['#value'];
  292. // dpm($transition);
  293. // An example of customizing/overriding the workflow widget.
  294. // Beware, until now, you must do this twice: on the widget and on the form.
  295. if ($transition->getOwnerId() == 1) {
  296. drupal_set_message('(Test/Devel message) I got you, user 1, you will never schedule again,
  297. and you WILL document each state change!', 'warning');
  298. // Let's prohibit scheduling for user 1.
  299. $workflow_element['workflow_scheduling']['#access'] = FALSE;
  300. // Let's prohibit scheduling for user 1.
  301. if ( $workflow_element['comment']['#access'] == TRUE) {
  302. $workflow_element['comment']['#required'] = TRUE;
  303. }
  304. }
  305. // Get the Entity.
  306. /* @var $entity \Drupal\Core\Entity\EntityInterface */
  307. $entity = NULL;
  308. //$entity = $form['workflow_entity']['#value'];
  309. $entity_type = 'node'; // $form['workflow_entity_type']['#value'];
  310. $entity_bundle = ''; // $entity->bundle();
  311. $sid = '';
  312. if ($entity) {
  313. $entity_type = $entity->getEntityTypeId();
  314. $entity_bundle = $entity->bundle();
  315. // Get the current State ID.
  316. $sid = workflow_node_current_state($entity, $field_name = NULL);
  317. // Get the State object, if needed.
  318. $state = WorkflowState::load($sid);
  319. }
  320. // Change the form, depending on the state ID.
  321. // In the upcoming version 7.x-2.4, States should have a machine_name, too.
  322. if ($entity_type == 'node' && $entity_bundle == 'MY_NODE_TYPE') {
  323. switch ($sid) {
  324. case '2':
  325. // Change form element, form validate and form submit for state '2'.
  326. break;
  327. case '3':
  328. // Change form element, form validate and form submit for state '3'.
  329. break;
  330. }
  331. }
  332. }
  333. /**
  334. * Implements hook_form_alter().
  335. *
  336. * Use this hook to alter the form on an Entity Form, Comment Form (Edit page).
  337. *
  338. * @see hook_form_workflow_transition_form_alter() for example code.
  339. */
  340. function hook_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  341. if (substr($form_id, 0, 8) == 'workflow') {
  342. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, $form_id, '');
  343. }
  344. }
  345. /**
  346. * Hooks defined by core Entity: hook_entity_CRUD.
  347. *
  348. * Instead of using hook_entity_OPERATION, better use hook_ENTITY_TYPE_OPERATION.
  349. *
  350. * @see hook_entity_create(), hook_entity_update(), etc.
  351. * @see hook_ENTITY_TYPE_create(), hook_ENTITY_TYPE_update(), etc.
  352. */
  353. function hook_entity_predelete(EntityInterface $entity) {
  354. if (substr($entity->getEntityTypeId(), 0, 8) == 'workflow') {
  355. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, 'pre-delete' , $entity->getEntityTypeId());
  356. }
  357. switch ($entity->getEntityTypeId()) {
  358. case 'workflow_config_transition':
  359. case 'workflow_state':
  360. case 'workflow_type':
  361. // Better use hook_ENTITY_TYPE_OPERATION.
  362. // E.g., hook_workflow_type_predelete
  363. break;
  364. }
  365. }
  366. function hook_entity_delete(EntityInterface $entity) {
  367. if (substr($entity->getEntityTypeId(), 0, 8) == 'workflow') {
  368. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, 'delete' , $entity->getEntityTypeId());
  369. }
  370. }
  371. function hook_workflow_type_delete(EntityInterface $entity) {
  372. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, 'delete' , $entity->getEntityTypeId());
  373. }
  374. function hook_workflow_config_transition_delete(EntityInterface $entity) {
  375. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, 'delete' , $entity->getEntityTypeId());
  376. }
  377. function hook_workflow_state_delete(EntityInterface $entity) {
  378. // workflow_debug(__FILE__, __FUNCTION__, __LINE__, 'delete' , $entity->getEntityTypeId());
  379. }