workflow_admin_ui.page.states.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. /**
  3. * @file
  4. * Provides an Admin UI page for the Workflow States.
  5. */
  6. /**
  7. * Menu callback.
  8. *
  9. * Creates the main workflow page, which gives an overview
  10. * of workflows and workflow states.
  11. * Replaced by http://drupal.org/node/1367530.
  12. */
  13. function workflow_admin_ui_states_form($form, &$form_state, $workflow, $op) {
  14. $form = array();
  15. $form['#tree'] = TRUE;
  16. $form['workflow'] = array(
  17. '#type' => 'value',
  18. '#value' => $workflow,
  19. );
  20. // Build select options for reassigning states.
  21. // We put a blank state first for validation.
  22. $state_list = array('' => ' ');
  23. $state_list += workflow_get_workflow_state_names($workflow->wid, $grouped = FALSE, $all = FALSE);
  24. // Is this the last state available?
  25. $form['#last_mohican'] = count($state_list) == 2;
  26. // Get the state objects as array ($sid => WorkflowState).
  27. $states = $workflow->getStates($all = TRUE);
  28. // Create a dummy object for new state item. It must NOT be saved to DB.
  29. $maxweight = $minweight = -50;
  30. // $wid = $workflow->wid;
  31. $dummy = $workflow->createState('', FALSE);
  32. $dummy->weight = $minweight;
  33. $states[$dummy->sid] = $dummy; // Although the index is 0, the state is appended at the end of the list.
  34. foreach ($states as $state) {
  35. // Allow modules to insert operations per state.
  36. $links = module_invoke_all('workflow_operations', 'state', $workflow, $state);
  37. $sid = $state->sid;
  38. $label = $state->label();
  39. $count = $state->count();
  40. // Make it impossible to reassign to the same state that is disabled.
  41. if ($state->isCreationState() || !$sid || !$state->isActive()) {
  42. $current_state = array();
  43. $current_state_list = array();
  44. }
  45. else {
  46. $current_state = array($sid => $state_list[$sid]);
  47. $current_state_list = array_diff($state_list, $current_state);
  48. }
  49. $form['states'][$sid] = array(
  50. 'state' => array(
  51. '#type' => 'textfield',
  52. '#size' => 30,
  53. '#maxlength' => 255,
  54. '#default_value' => $label,
  55. ),
  56. 'name' => array(
  57. '#type' => 'machine_name',
  58. '#size' => 30,
  59. '#maxlength' => 255,
  60. '#required' => FALSE,
  61. // '#disabled' => !empty($name), // If needed this would disable updating machine name, once set.
  62. '#default_value' => $state->getName(),
  63. '#machine_name' => array(
  64. 'exists' => 'workflow_admin_ui_states_validate_state_machine_name',
  65. 'source' => array('states', $sid, 'state'),
  66. 'replace_pattern' => '[^a-z0-9_()]+', // Added '()' characters from exclusion list since creation state has it.
  67. ),
  68. ),
  69. 'weight' => array(
  70. '#type' => 'weight',
  71. '#delta' => 20,
  72. '#default_value' => $state->weight,
  73. '#title-display' => 'invisible',
  74. '#attributes' => array('class' => array('state-weight')),
  75. ),
  76. 'status' => array(
  77. '#type' => 'checkbox',
  78. '#default_value' => $state->isActive(),
  79. ),
  80. // Save the original status for the validation handler.
  81. 'orig_status' => array(
  82. '#type' => 'value',
  83. '#value' => $state->isActive(),
  84. ),
  85. 'reassign' => array(
  86. '#type' => 'select',
  87. '#options' => $current_state_list,
  88. ),
  89. 'count' => array(
  90. '#type' => 'value',
  91. '#value' => $count,
  92. ),
  93. 'ops' => array(
  94. '#type' => 'markup',
  95. '#markup' => theme('links', array('links' => $links)),
  96. ),
  97. );
  98. // Don't let the creation state change weight or status or name.
  99. if ($state->isCreationState()) {
  100. $form['states'][$sid]['weight']['#value'] = $minweight;
  101. $form['states'][$sid]['sysid']['#value'] = 1;
  102. $form['states'][$sid]['state']['#disabled'] = TRUE;
  103. $form['states'][$sid]['name']['#disabled'] = TRUE;
  104. $form['states'][$sid]['status']['#disabled'] = TRUE;
  105. $form['states'][$sid]['reassign']['#type'] = 'hidden';
  106. $form['states'][$sid]['reassign']['#disabled'] = TRUE;
  107. }
  108. // New state and disabled states cannot be reassigned.
  109. if (!$sid || !$state->isActive() || ($count == 0) ) {
  110. $form['states'][$sid]['reassign']['#type'] = 'hidden';
  111. $form['states'][$sid]['reassign']['#disabled'] = TRUE;
  112. }
  113. // Disabled states cannot be renamed (and is a visual clue, too.).
  114. if (!$state->isActive()) {
  115. $form['states'][$sid]['state']['#disabled'] = TRUE;
  116. }
  117. // Set a proper weight to the new state.
  118. $maxweight = max($maxweight, $state->weight);
  119. if (!$sid) {
  120. $form['states'][$sid]['weight']['#default_value'] = $maxweight + 1;
  121. }
  122. }
  123. $form['actions'] = array('#type' => 'actions');
  124. $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save Changes'));
  125. return $form;
  126. }
  127. function theme_workflow_admin_ui_states_form($variables) {
  128. $form = $variables['form'];
  129. $output = '';
  130. $table_id = 'workflow_admin_ui_states';
  131. $table = array(
  132. 'rows' => array(),
  133. 'header' => array(
  134. t('State'),
  135. t('Weight'),
  136. t('Active'),
  137. t('Reassign'),
  138. t('Count'),
  139. array('data' => t('Operations'), 'class' => 'state-ops'),
  140. ),
  141. 'attributes' => array('id' => $table_id, 'style' => 'width: auto;'),
  142. );
  143. // The output needs to have the action links at the top.
  144. $output .= drupal_render($form['action-links']);
  145. // Iterate over each element in our $form['states'] array.
  146. foreach (element_children($form['states']) as $id) {
  147. // We are now ready to add each element of our $form data to the rows
  148. // array, so that they end up as individual table cells when rendered
  149. // in the final table. We run each element through the drupal_render()
  150. // function to generate the final html markup for that element.
  151. $table['rows'][] = array(
  152. 'data' => array(
  153. // Add our 'name' column.
  154. // array('data' => drupal_render($form['states'][$id]['state']), 'class' => 'state-name'),
  155. array('data' => drupal_render($form['states'][$id]['state']) . drupal_render($form['states'][$id]['name']), 'class' => 'state-name'),
  156. // Add our 'weight' column.
  157. drupal_render($form['states'][$id]['weight']),
  158. // Add our 'status' column.
  159. array('data' => drupal_render($form['states'][$id]['status']), 'class' => 'state-status'),
  160. // Add our 'reassign' column.
  161. array('data' => drupal_render($form['states'][$id]['reassign']), 'class' => 'state-reassign'),
  162. // Add our 'count' column.
  163. array('data' => $form['states'][$id]['count']['#value'], 'class' => 'state-count'),
  164. // Add our 'operations' column.
  165. array('data' => drupal_render($form['states'][$id]['ops']), 'class' => 'state-ops'),
  166. ),
  167. // To support the tabledrag behavior, we need to assign each row of the
  168. // table a class attribute of 'draggable'. This will add the 'draggable'
  169. // class to the <tr> element for that row when the final table is
  170. // rendered.
  171. 'class' => array('draggable'),
  172. );
  173. }
  174. $output .= theme('table', $table);
  175. // And then render any remaining form elements (such as our submit button).
  176. $output .= drupal_render_children($form);
  177. // We now call the drupal_add_tabledrag() function in order to add the
  178. // tabledrag.js goodness onto our page.
  179. //
  180. // For a basic sortable table, we need to pass it:
  181. // - the $table_id of our <table> element,
  182. // - the $action to be performed on our form items ('order'),
  183. // - a string describing where $action should be applied ('siblings'),
  184. // - and the class of the element containing our 'weight' element.
  185. drupal_add_tabledrag($table_id, 'order', 'sibling', 'state-weight');
  186. return $output;
  187. }
  188. /**
  189. * Validation handler for the state form.
  190. */
  191. function workflow_admin_ui_states_form_validate($form, &$form_state) {
  192. // Because the form elements were keyed with the item ids from the database,
  193. // we can simply iterate through the submitted values.
  194. foreach ($form_state['values']['states'] as $sid => $item) {
  195. // Reload $state from db, in case the states were changed by anyone else. And it is just as fast.
  196. $state = workflow_state_load_single($sid);
  197. // Does user want to deactivate the state (reassign current nodes)?
  198. if ($sid > 0 && $item['status'] == 0 && $state->isActive()) {
  199. $args = array('%state' => $state->getName()); // check_plain() is run by t().
  200. // Does that state have nodes in it?
  201. if ($item['count'] > 0 && empty($item['reassign'])) {
  202. if ($form['#last_mohican']) {
  203. $message = 'Since you are deleting the last available workflow state
  204. in this workflow, all content items which are in that state will have their
  205. workflow state removed.';
  206. drupal_set_message(t($message, $args), 'warning');
  207. }
  208. else {
  209. $message = 'The %state state has content; you must reassign the content to another state.';
  210. form_set_error("states'][$sid]['reassign'", t($message, $args));
  211. }
  212. }
  213. }
  214. }
  215. }
  216. /**
  217. * Submission handler for the state form.
  218. */
  219. function workflow_admin_ui_states_form_submit($form, &$form_state) {
  220. // Get the workflow id, then save it for the next round.
  221. $workflow = $form_state['values']['workflow'];
  222. $wid = $workflow->wid;
  223. // Because the form elements were keyed with the item ids from the database,
  224. // we can simply iterate through the submitted values.
  225. foreach ($form_state['values']['states'] as $sid => $item) {
  226. $item['sid'] = $sid;
  227. $item['wid'] = $wid;
  228. // Is there not a new state name?
  229. if (empty($item['state'])) {
  230. // No new state entered, so skip it.
  231. continue;
  232. }
  233. // Reload $state from db, in case the states were changed by anyone else. And it is just as fast.
  234. $state = ($sid) ? workflow_state_load_single($sid) : $workflow->createState('');
  235. // Does user want to deactivate the state (reassign current nodes)?
  236. if ($sid > 0 && $item['status'] == 0 && $state->isActive()) {
  237. $new_sid = $item['reassign'];
  238. $new_state = workflow_state_load_single($new_sid);
  239. $args = array(
  240. '%workflow' => $workflow->getName(), // check_plain() is run by t().
  241. '%old_state' => $state->getName(),
  242. '%new_state' => isset($new_state) ? $new_state->getName() : '',
  243. );
  244. if ($item['count'] > 0) {
  245. if ($form['#last_mohican']) {
  246. $new_sid = NULL; // Do not reassign to new state.
  247. $message = 'Removing workflow states from content in the %workflow.';
  248. drupal_set_message(t($message, $args));
  249. }
  250. else {
  251. // Prepare the state delete function.
  252. $message = 'Reassigning content from %old_state to %new_state.';
  253. drupal_set_message(t($message, $args));
  254. }
  255. }
  256. // Delete the old state without orphaning nodes, move them to the new state.
  257. $state->deactivate($new_sid);
  258. $message = 'Deactivated workflow state %old_state in %workflow.';
  259. watchdog('workflow', $message, $args);
  260. drupal_set_message(t($message, $args));
  261. }
  262. $state->state = $item['state'];
  263. $state->name = $item['name'];
  264. $state->status = $item['status'];
  265. $state->weight = $item['weight'];
  266. $state->save();
  267. }
  268. drupal_set_message(t('The workflow was updated.'));
  269. // $form_state['redirect'] = WORKFLOW_ADMIN_UI_PATH;
  270. }
  271. /**
  272. * Validate duplicate machine names. Function registered in 'name' form element.
  273. */
  274. function workflow_admin_ui_states_validate_state_machine_name($name, $element, $form_state) {
  275. // @todo: Should $name be checked against DB?
  276. $state_names = array();
  277. foreach ($form_state['values']['states'] as $sid => $item) {
  278. $state_names[] = $item['name'];
  279. }
  280. $state_names = array_map('strtolower', $state_names);
  281. $result = array_unique(array_diff_assoc($state_names, array_unique($state_names)));
  282. if (in_array($name, $result)) {
  283. return TRUE;
  284. }
  285. return FALSE;
  286. }