workflownode.module 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. <?php
  2. /**
  3. * @file
  4. * Hooks and functions for the 'conventional' (version D5/D6/D7.1) Workflow Node, remnants of nodeapi.
  5. */
  6. // Includes the hooks for the 'conventional' (version D5/D6/D7.1) Node API.
  7. require_once(dirname(__DIR__)) . '/workflow.node.type_map.inc';
  8. require_once(dirname(__DIR__)) . '/workflow.deprecated.inc';
  9. /**
  10. * Implements hook_ctools_plugin_directory().
  11. *
  12. * This lets ctools know to scan my module for a content_type plugin file
  13. * Detailed docks in ctools/ctools.api.php
  14. *
  15. * This is used to integrate WorkflowNode with Panels. See d.o. #1511694, #2187731
  16. */
  17. function workflownode_ctools_plugin_directory($owner, $plugin_type) {
  18. if ($owner == 'ctools' && !empty($plugin_type)) {
  19. return "plugins/$plugin_type";
  20. }
  21. }
  22. /**
  23. * Theme the current workflow state as top line of History tab.
  24. */
  25. function theme_workflow_current_state($variables) {
  26. $state = $variables['state'];
  27. return '<div class="workflow-current-state '
  28. . 'workflow-current-sid-' . intval($variables['sid']) . ' '
  29. . drupal_html_class($state)
  30. . '">'
  31. . t('Current state: <span class="state">@state</span>', array('@state' => $state))
  32. . '</div>';
  33. }
  34. /**
  35. * Implements hook_node_load().
  36. */
  37. function workflownode_node_load($nodes, $types) {
  38. // Get which types have workflows associated with them.
  39. $workflow_types = workflow_get_workflow_type_map();
  40. // Leave early if nodes do not have workflow.
  41. if (!$workflow_types || !array_intersect_key($workflow_types, array_flip($types))) {
  42. return;
  43. }
  44. // Read the current state for all nodes.
  45. $states = workflow_get_workflow_node_by_nid(array_keys($nodes));
  46. foreach ($nodes as $nid => $node) {
  47. // If it's not a workflow type, quit immediately.
  48. if (!array_key_exists($node->type, $workflow_types)) {
  49. continue;
  50. }
  51. // Nodes that existed before the workflow was defined may not have a state.
  52. $workflow_node = isset($states[$nid]) ? $states[$nid] : NULL;
  53. if (!$workflow_node) {
  54. if ($workflow = workflow_get_workflows_by_type($node->type, 'node')) {
  55. $node->workflow = $workflow->getCreationSid();
  56. $node->workflow_stamp = $node->created;
  57. }
  58. else {
  59. // Is this possible?
  60. }
  61. }
  62. else {
  63. $node->workflow = $workflow_node->sid;
  64. $node->workflow_stamp = $workflow_node->stamp;
  65. }
  66. }
  67. // As of workflow 7.x-2.x, the scheduled transition is not loaded. See issue #2138591.
  68. }
  69. /**
  70. * Implements hook_node_view().
  71. */
  72. function workflownode_node_view($node, $view_mode, $langcode) {
  73. global $user;
  74. $form = array();
  75. $entity_type = 'node'; // This hook is only for nodes.
  76. $entity_id = $node->nid;
  77. $field_name = ''; // This hook is only for workflow_node.
  78. $entity_bundle = $node->type;
  79. // Skip if there are no Node API workflows.
  80. if (!workflow_get_workflow_type_map_by_type($entity_bundle)) {
  81. return;
  82. }
  83. // Check permission, so that even with state change rights,
  84. // the form can be suppressed from the node view (#1893724).
  85. if (!user_access('show workflow state form', $user)) {
  86. return;
  87. }
  88. // Get the current sid. $field_name is updated with relevant value.
  89. $current_sid = workflow_node_current_state($node, $entity_type, $field_name);
  90. $current_state = workflow_state_load_single($current_sid);
  91. // Show current state at the top of the node display.
  92. $field = $instance = array();
  93. $node->content['workflow_current_state'] = workflow_state_formatter($entity_type, $node, $field, $instance, $current_sid);
  94. $node->content['workflow_current_state']['#weight'] = -99;
  95. if ($current_state && $current_state->showWidget('node', $node, '', $user, FALSE)) {
  96. $workflow = $current_state->getWorkflow();
  97. // Show the current state and the Workflow form to allow state changing.
  98. // N.B. This part is replicated in hook_node_view, workflow_tab_page, workflow_vbo.
  99. if ($workflow) {
  100. $field = _workflow_info_field($field_name, $workflow);
  101. $field_id = $field['id'];
  102. $instance = field_info_instance($entity_type, $field_name, $entity_bundle);
  103. // If no instance is found, restore the array.
  104. if (!is_array($instance)) {
  105. $instance = array();
  106. }
  107. if (!$field['id']) {
  108. // This is a Workflow Node workflow. Set widget options as in v7.x-1.2
  109. $field['settings']['widget']['comment'] = isset($workflow->options['comment_log_node']) ? $workflow->options['comment_log_node'] : 1; // vs. ['comment_log_tab'];
  110. $field['settings']['widget']['current_status'] = FALSE;
  111. }
  112. }
  113. // By including the nid in the form id. For backwards compatiblity, do not add entity_type, field_id.
  114. $form_id = implode('_', array('workflow_transition_form', $entity_id));
  115. $form += drupal_get_form($form_id, $field, $instance, $entity_type, $node);
  116. $form['#weight'] = 99;
  117. $node->content['workflow'] = $form;
  118. }
  119. }
  120. /**
  121. * Implements hook_node_insert().
  122. *
  123. * This is executed after saving data to the database.
  124. * We cannot use hook_node_presave, because workflow_execute_transition() needs the nid.
  125. */
  126. function workflownode_node_insert($node) {
  127. global $user;
  128. if (!isset($node->workflow_field)) {
  129. // Initializing the state of the node in case no widget on Node form.
  130. if ($workflow = workflow_get_workflows_by_type($node->type, 'node')) {
  131. $comment = t('Set to initial state.');
  132. $force = TRUE;
  133. $creation_sid = $workflow->getCreationSid();
  134. // Get the initial state for this node.
  135. // Due to permissions, it might be different for each user.
  136. $new_sid = $workflow->getFirstSid('node', $node, null, $user, $force);
  137. $transition = new WorkflowTransition();
  138. $transition->setValues('node', $node, null, $creation_sid, $new_sid, $user->uid, REQUEST_TIME, $comment);
  139. // Force it to transition to the first state and get a history record.
  140. workflow_execute_transition('node', $node, null, $transition, $force);
  141. }
  142. }
  143. return workflownode_node_update($node);
  144. }
  145. /**
  146. * Implements hook_node_update().
  147. */
  148. function workflownode_node_update($node) {
  149. if (!isset($node->workflow_field)) {
  150. // For this type_map, user did not want a form here.
  151. return;
  152. }
  153. $form = array();
  154. // Retrieve the data from the form.
  155. // Add form values (field, instance, entity_type, entity), then form input.
  156. $form_state = array();
  157. $form_state['values']['workflow_field'] = $node->workflow_field;
  158. $form_state['values']['workflow_instance'] = $node->workflow_instance;
  159. $form_state['values']['workflow_entity_type'] = 'node';
  160. // Careful: take the fresh node here, not the one that is in the form.
  161. $form_state['values']['workflow_entity'] = $node;
  162. // For some reason, the Workflow Form does not return the form in a 'workflow' array.
  163. // @todo: correct this (use '#tree => TRUE'), or filter on 'workflow' elements.
  164. $form_state['input'] = (array) $node;
  165. workflow_transition_form_submit($form, $form_state);
  166. }
  167. /**
  168. * Implements hook_node_delete().
  169. */
  170. function workflownode_node_delete($node) {
  171. if (!empty($node->workflow)) {
  172. // Call field_info_field().
  173. // Generates pseudo data for workflow_node to re-use Field API.
  174. $field = _workflow_info_field($field_name = '', $workflow = NULL);
  175. $instance = array();
  176. $items[0]['value'] = $node->workflow;
  177. $workflow_field = new WorkflowItem($field, $instance, 'node', $node);
  178. $workflow_field->delete($items);
  179. }
  180. }
  181. /**
  182. * Implements hook_comment_insert().
  183. */
  184. function workflownode_comment_insert($comment) {
  185. workflownode_comment_update($comment);
  186. }
  187. /**
  188. * Implements hook_comment_update().
  189. */
  190. function workflownode_comment_update($comment) {
  191. // Retrieve the data from the form.
  192. if (!isset($comment->workflow_field)) {
  193. // For this type_map, user did not want a form here.
  194. return;
  195. }
  196. $form = array();
  197. // Retrieve the data from the form.
  198. // Add form values (field, instance, entity_type, entity), then form input.
  199. $form_state = array();
  200. $form_state['values']['workflow_field'] = $comment->workflow_field;
  201. $form_state['values']['workflow_instance'] = $comment->workflow_instance;
  202. $form_state['values']['workflow_entity_type'] = $comment->workflow_entity_type;
  203. $form_state['values']['workflow_entity'] = $comment->workflow_entity;
  204. // For some reason, the Workflow Form does not return the form in a 'workflow' array.
  205. // @todo: correct this, or filter on 'workflow' elements.
  206. $form_state['input'] = (array) $comment;
  207. workflow_transition_form_submit($form, $form_state);
  208. }
  209. /**
  210. * Implements hook_field_extra_fields().
  211. */
  212. function workflownode_field_extra_fields() {
  213. $extra = array();
  214. // Get all workflows by content types.
  215. $types = array_filter(workflow_get_workflow_type_map());
  216. // Add the extra fields to each content type that has a workflow.
  217. foreach ($types as $type => $wid) {
  218. $extra['node'][$type] = array(
  219. 'form' => array(
  220. 'workflow' => array(
  221. 'label' => t('Workflow'),
  222. 'description' => t('Workflow module form'),
  223. 'weight' => 99, // Default to bottom.
  224. ),
  225. ),
  226. 'display' => array(
  227. 'workflow_current_state' => array(
  228. 'label' => t('Workflow: Current State'),
  229. 'description' => t('Current workflow state'),
  230. 'weight' => -99, // Default to top.
  231. ),
  232. 'workflow' => array(
  233. 'label' => t('Workflow: State Change Form'),
  234. 'description' => t('The form for controlling workflow state changes.'),
  235. 'weight' => 99, // Default to bottom.
  236. ),
  237. ),
  238. );
  239. }
  240. return $extra;
  241. }
  242. /**
  243. * Implements hook_form_BASE_FORM_ID_alter().
  244. *
  245. * Shows the form only on Node Edit forms.
  246. */
  247. function workflownode_form_node_form_alter(&$form, &$form_state, $form_id) {
  248. _workflownode_form_alter($form, $form_state, $form_id);
  249. }
  250. /**
  251. * Implements hook_form_BASE_FORM_ID_alter().
  252. *
  253. * Shows the form only on Comment forms.
  254. */
  255. function workflownode_form_comment_form_alter(&$form, &$form_state, $form_id) {
  256. _workflownode_form_alter($form, $form_state, $form_id);
  257. }
  258. /**
  259. * Helper function to implement hook_form_alter().
  260. *
  261. * Is now a subfunction of workflow_form_BASE_FORM_ID_alter().
  262. * This is more performant (compared to 7.x-1.2 and before), since it is called
  263. * only on form with correct BASE_FORM_ID.
  264. *
  265. * @see http://bryanbraun.com/2013/08/17/using-hook-form-base-form-id-alter
  266. */
  267. function _workflownode_form_alter(&$form, &$form_state, $form_id) {
  268. // Set node to #node if available or load from nid value.
  269. $node = isset($form['#node']) ? $form['#node'] : node_load($form['nid']['#value']);
  270. $bundle = $node->type;
  271. $entity = $node;
  272. $entity_type = 'node';
  273. $field_name = '';
  274. // Skip if there are no Node API workflows.
  275. if (!workflow_get_workflow_type_map_by_type($bundle)) {
  276. return;
  277. }
  278. if ($workflow = workflow_get_workflows_by_type($bundle, 'node')) {
  279. $workflow_entities = variable_get('workflow_' . $bundle, array());
  280. // Abort if the entity type of the form is not in the list that the user
  281. // wants to display the workflow form on.
  282. if (!in_array($form['#entity_type'], $workflow_entities)) {
  283. return;
  284. }
  285. /*
  286. $form['#wf'] = $workflow;
  287. $form['workflow'] = array(
  288. '#type' => 'fieldset',
  289. '#title' => check_plain($label),
  290. '#collapsible' => TRUE,
  291. '#collapsed' => FALSE,
  292. '#weight' => 10,
  293. );
  294. */
  295. // Emulate a Field API interface.
  296. // Show the current state and the Workflow form to allow state changing.
  297. // N.B. This part is replicated in hook_node_view, workflow_tab_page, etc.
  298. if ($workflow) {
  299. $field = _workflow_info_field($field_name, $workflow);
  300. $field_name = $field['field_name'];
  301. $field_id = $field['id'];
  302. $instance = field_info_instance($entity_type, $field_name, $bundle);
  303. if (!$field_id) {
  304. // This is a Workflow Node workflow. Set widget options as in v7.x-1.2
  305. $field['settings']['widget']['comment'] = isset($workflow->options['comment_log_node']) ? $workflow->options['comment_log_node'] : 1; // vs. ['comment_log_tab'];
  306. $field['settings']['widget']['current_status'] = TRUE;
  307. }
  308. }
  309. // Do not include the default 'Update Workflow' button, since we are already in an Edit form.
  310. $instance['widget']['settings']['submit_function'] = '';
  311. // Include the 'workflow form'. $form is modified by reference.
  312. $form = workflow_transition_form($form, $form_state, $field, $instance, $entity_type, $entity);
  313. }
  314. }