workflow_admin_ui.module 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
  1. <?php
  2. /**
  3. * @file
  4. * Provides administrative UI for workflow.
  5. * Why it's own module? Lower code footprint and better performance.
  6. * Additional creadit to gcassie ( http://drupal.org/user/80260 ) for
  7. * the initial push to split UI out of core workflow.
  8. * We're moving workflow in a API direction, so UI and the like - out.
  9. */
  10. define('WORKFLOW_ARROW', '&#8594;');
  11. /**
  12. * Implements hook_help().
  13. */
  14. function workflow_admin_ui_help($path, $arg) {
  15. switch ($path) {
  16. case 'admin/config/workflow/workflow/edit/%':
  17. return t('You are currently viewing the possible transitions to and from workflow states. The state is shown in the left column; ' .
  18. 'the state to be moved to is to the right. For each transition, check the box next to the role(s) that may initiate the transition. ' .
  19. 'For example, if only the "production editor" role may move a node from Review state to the Published state, check the box next to ' .
  20. '"production editor". The author role is built in and refers to the user who authored the node.');
  21. case 'admin/config/workflow/workflow/add':
  22. return t('To get started, provide a name for your workflow. This name will be used as a label when the workflow status is shown ' .
  23. 'during node editing.');
  24. }
  25. }
  26. /**
  27. * Implements hook_permission().
  28. */
  29. function workflow_admin_ui_permission() {
  30. return array(
  31. 'administer workflow' => array(
  32. 'title' => t('Administer workflow'),
  33. 'description' => t('Administer workflow configurations.'),
  34. ),
  35. 'participate in workflow' => array(
  36. 'title' => t('Participate in workflow'),
  37. 'description' => t('Role is shown on workflow admin pages.'),
  38. ),
  39. );
  40. }
  41. /**
  42. * Implements hook_menu().
  43. */
  44. function workflow_admin_ui_menu() {
  45. $items['admin/config/workflow/workflow'] = array(
  46. 'title' => 'Workflow',
  47. 'access arguments' => array('administer workflow'),
  48. 'page callback' => 'drupal_get_form',
  49. 'page arguments' => array('workflow_admin_ui_types_form'),
  50. 'description' => 'Allows the creation and assignment of arbitrary workflows to node types.',
  51. );
  52. $items['admin/config/workflow/workflow/%workflow'] = array(
  53. 'access arguments' => array('administer workflow'),
  54. 'page callback' => 'drupal_get_form',
  55. 'page arguments' => array('workflow_admin_ui_overview_form', 4),
  56. 'type' => MENU_CALLBACK,
  57. );
  58. $items['admin/config/workflow/workflow/add'] = array(
  59. 'access arguments' => array('administer workflow'),
  60. 'page callback' => 'drupal_get_form',
  61. 'page arguments' => array('workflow_admin_ui_add_form'),
  62. 'type' => MENU_CALLBACK,
  63. );
  64. $items['admin/config/workflow/workflow/edit/%workflow'] = array(
  65. 'title' => 'Edit',
  66. 'access arguments' => array('administer workflow'),
  67. 'page callback' => 'drupal_get_form',
  68. 'page arguments' => array('workflow_admin_ui_edit_form', 5),
  69. 'type' => MENU_CALLBACK,
  70. );
  71. $items["admin/config/workflow/workflow/delete/%workflow"] = array(
  72. 'title' => 'Delete',
  73. 'access arguments' => array('administer workflow'),
  74. 'page callback' => 'drupal_get_form',
  75. 'page arguments' => array('workflow_admin_ui_delete_form',5),
  76. 'type' => MENU_CALLBACK,
  77. );
  78. $items["admin/config/workflow/workflow/transitions/%workflow"] = array(
  79. 'title' => 'Transitions',
  80. 'access arguments' => array('administer workflow'),
  81. 'page callback' => 'drupal_get_form',
  82. 'page arguments' => array('workflow_admin_ui_transitions_form',5),
  83. 'type' => MENU_CALLBACK,
  84. );
  85. $items["admin/config/workflow/workflow/perm_summary/%workflow"] = array(
  86. 'title' => 'Permission Summary',
  87. 'access arguments' => array('administer workflow'),
  88. 'page callback' => 'workflow_admin_ui_view_permissions',
  89. 'page arguments' => array(5),
  90. 'type' => MENU_CALLBACK,
  91. );
  92. return $items;
  93. }
  94. /**
  95. * Implements hook_theme().
  96. */
  97. function workflow_admin_ui_theme() {
  98. return array(
  99. 'workflow_admin_ui_transitions_form' => array('render element' => 'form'),
  100. 'workflow_admin_ui_edit_form' => array('render element' => 'form'),
  101. 'workflow_admin_ui_types_form' => array('render element' => 'form'),
  102. 'workflow_admin_ui_overview_form' => array('render element' => 'form'),
  103. );
  104. }
  105. /**
  106. * Form builder. Create the form for adding/editing a workflow.
  107. *
  108. * @param $name
  109. * Name of the workflow if editing.
  110. * @param $add
  111. * Boolean, if true edit workflow name.
  112. *
  113. * @return
  114. * HTML form.
  115. */
  116. function workflow_admin_ui_add_form($form, &$form_state, $name = NULL) {
  117. $form = array();
  118. $form['wf_name'] = array(
  119. '#type' => 'textfield',
  120. '#title' => t('Workflow Name'),
  121. '#maxlength' => '254',
  122. '#required' => TRUE,
  123. '#default_value' => $name,
  124. );
  125. $form['submit'] = array(
  126. '#type' => 'submit',
  127. '#value' => t('Add Workflow'),
  128. );
  129. return $form;
  130. }
  131. /**
  132. * Validate the workflow add form.
  133. *
  134. * @see workflow_add_form()
  135. */
  136. function workflow_admin_ui_add_form_validate($form, &$form_state) {
  137. $workflow_name = $form_state['values']['wf_name'];
  138. // Make sure workflow name is not a duplicate.
  139. $workflows = array();
  140. foreach (workflow_get_workflows() as $data) {
  141. $workflows[$data->wid] = check_plain(t($data->name));
  142. }
  143. if (!empty($workflows)) {
  144. $workflows = array_flip($workflows);
  145. if (array_key_exists($workflow_name, $workflows)) {
  146. form_set_error('wf_name', t('A workflow with the name %name already exists. Please enter another name for your new workflow.',
  147. array('%name' => $workflow_name)));
  148. }
  149. }
  150. }
  151. /**
  152. * Submit handler for the workflow add form.
  153. *
  154. * @see workflow_add_form()
  155. */
  156. function workflow_admin_ui_add_form_submit($form, &$form_state) {
  157. $workflow_name = $form_state['values']['wf_name'];
  158. $workflow = array(
  159. 'name' => $workflow_name,
  160. 'tab_roles' => '',
  161. 'options' => serialize(array()),
  162. );
  163. workflow_update_workflows($workflow);
  164. watchdog('workflow', 'Created workflow %name', array('%name' => $workflow->name));
  165. drupal_set_message(t('The workflow %name was created. You should set the options for your workflow.',
  166. array('%name' => $workflow->name)), 'status');
  167. $form_state['wid'] = $workflow->wid;
  168. $form_state['redirect'] = 'admin/config/workflow/workflow/edit/' . $workflow->wid;
  169. }
  170. /**
  171. * Form builder. Create form for confirmation of workflow deletion.
  172. *
  173. * @param $wid
  174. * The workflow object to delete.
  175. * @return
  176. * Form definition array.
  177. *
  178. */
  179. function workflow_admin_ui_delete_form($form, &$form_state, $workflow) {
  180. // If we don't have a workflow that goes with this, return to the admin pg.
  181. if ($workflow) {
  182. // Let's add some breadcrumbs.
  183. workflow_admin_ui_breadcrumbs($workflow);
  184. $form['wid'] = array('#type' => 'value', '#value' => $workflow->wid);
  185. return confirm_form(
  186. $form,
  187. t('Are you sure you want to delete %title? All nodes that have a workflow state associated with this workflow will ' .
  188. 'have those workflow states removed.', array('%title' => $workflow->name)),
  189. !empty($_GET['destination']) ? $_GET['destination'] : 'admin/config/workflow/workflow',
  190. t('This action cannot be undone.'),
  191. t('Delete ' . $workflow->name), // This seems to get check_plain'ed.
  192. t('Cancel')
  193. );
  194. }
  195. else {
  196. drupal_goto('admin/config/workflow/workflow');
  197. }
  198. }
  199. /**
  200. * Submit handler for workflow deletion form.
  201. *
  202. * @see workflow_delete_form()
  203. */
  204. function workflow_admin_ui_delete_form_submit($form, &$form_state) {
  205. if ($form_state['values']['confirm'] == 1
  206. && $workflow = workflow_get_workflows_by_wid($form_state['values']['wid'])) {
  207. workflow_delete_workflows_by_wid($form_state['values']['wid']);
  208. watchdog('workflow', 'Deleted workflow %name with all its states', array('%name' => $workflow->name));
  209. drupal_set_message(t('The workflow %name with all its states was deleted.', array('%name' => $workflow->name)));
  210. $form_state['redirect'] = 'admin/config/workflow/workflow';
  211. }
  212. }
  213. /**
  214. * View workflow permissions by role
  215. *
  216. * @param $workflow
  217. * The workflow object.
  218. */
  219. function workflow_admin_ui_view_permissions($workflow) {
  220. // If we don't have a workflow at this point, go back to admin pg.
  221. if ($workflow) {
  222. // Place some breadcrumbs.
  223. workflow_admin_ui_breadcrumbs($workflow);
  224. $name = $workflow->name;
  225. $all = array();
  226. $roles = workflow_admin_ui_get_roles();
  227. foreach ($roles as $role => $value) {
  228. $all[$role]['name'] = $value;
  229. }
  230. // TODO return to this, this looks similar to actions stuff (transitions) - should be it's own function.
  231. $result = db_query(
  232. 'SELECT t.roles, s1.state AS state_name, s2.state AS target_state_name ' .
  233. 'FROM {workflow_transitions} t ' .
  234. 'INNER JOIN {workflow_states} s1 ON s1.sid = t.sid ' .
  235. 'INNER JOIN {workflow_states} s2 ON s2.sid = t.target_sid ' .
  236. 'WHERE s1.wid = :wid ' .
  237. 'ORDER BY s1.weight ASC , s1.state ASC , s2.weight ASC , s2.state ASC',
  238. array(':wid' => $workflow->wid));
  239. foreach ($result as $data) {
  240. foreach (explode(',', $data->roles) as $role) {
  241. $all[$role]['transitions'][] = array(check_plain(t($data->state_name)), WORKFLOW_ARROW, check_plain(t($data->target_state_name)));
  242. }
  243. }
  244. $header = array(t('From'), '', t('To'));
  245. $output = '';
  246. // TODO we should theme out the html here.
  247. foreach ($all as $role => $value) {
  248. if (!empty($value['name'])) {
  249. $output .= '<h3>' . t('%role may do these transitions:', array('%role' => $value['name'])) . '</h3>';
  250. }
  251. if (!empty($value['transitions'])) {
  252. $output .= theme('table', array('header' => $header, 'rows' => $value['transitions'])) . '<p></p>';
  253. }
  254. else {
  255. $output .= '<table><tbody><tr class="odd"><td>' . t('None') . '</td><td></tr></tbody></table><p></p>';
  256. }
  257. }
  258. return $output;
  259. }
  260. else {
  261. drupal_goto('admin/config/workflow/workflow');
  262. }
  263. }
  264. /**
  265. * Theme the workflow permissions view.
  266. */
  267. function theme_workflow_admin_ui_view_permissions($variables) {
  268. $header = $variables['header'];
  269. $all = $variables['all'];
  270. $output = '';
  271. foreach ($all as $role => $value) {
  272. if (!empty($value['name'])) {
  273. $output .= '<h3>' . t('%role may do these transitions:', array('%role' => $value['name'])) . '</h3>';
  274. }
  275. if (!empty($value['transitions'])) {
  276. $output .= theme('table', array('header' => $header, 'rows' => $value['transitions'])) . '<p></p>';
  277. }
  278. else {
  279. $output .= '<table><tbody><tr class="odd"><td>' . t('None') . '</td><td></tr></tbody></table><p></p>';
  280. }
  281. }
  282. return $output;
  283. }
  284. /**
  285. * Helper function. Create breadcrumbs.
  286. *
  287. * @param $workflow
  288. * The workflow object.
  289. * @param $extra (optional)
  290. * The link to the extra item to add to the end of the breadcrumbs.
  291. * @return
  292. * none.
  293. */
  294. function workflow_admin_ui_breadcrumbs($workflow, $extra = NULL) {
  295. $bc = array(l(t('Home'), '<front>'));
  296. $bc[] = l(t('Configuration'), 'admin/config');
  297. $bc[] = l(t('Workflow'), 'admin/config/workflow');
  298. $bc[] = l(t('Workflow'), 'admin/config/workflow/workflow');
  299. $bc[] = l(t($workflow->name), "admin/config/workflow/workflow/$workflow->wid");
  300. if ($extra) {
  301. $bc[] = $extra;
  302. }
  303. drupal_set_breadcrumb($bc);
  304. }
  305. /**
  306. * Menu callback. Edit a workflow's properties.
  307. *
  308. * @param $wid
  309. * The workflow object..
  310. * @return
  311. * HTML form and permissions table.
  312. */
  313. function workflow_admin_ui_edit_form($form, $form_state, $workflow = NULL) {
  314. $form = array();
  315. // If we don't have a workflow by this point, we need to go back
  316. // to creating one at admin/config/workflow/workflow/add
  317. // I think the menu loader won't allow this to happen.
  318. if (!$workflow) {
  319. drupal_goto('admin/config/workflow/workflow/add');
  320. }
  321. // Create breadcrumbs.
  322. workflow_admin_ui_breadcrumbs($workflow);
  323. $form['wid'] = array('#type' => 'value', '#value' => $workflow->wid);
  324. $form['#workflow'] = $workflow;
  325. $form['basic'] = array(
  326. '#type' => 'fieldset',
  327. '#title' => t('Workflow information'),
  328. );
  329. $form['basic']['wf_name'] = array(
  330. '#type' => 'textfield',
  331. '#default_value' => $workflow->name,
  332. '#title' => t('Workflow Name'),
  333. '#maxlength' => '254',
  334. '#required' => TRUE,
  335. );
  336. $form['basic']['name_as_title'] = array(
  337. '#type' => 'checkbox',
  338. '#title' => t('Use the workflow name as the title of the workflow form.'),
  339. '#default_value' => isset($workflow->options['name_as_title']) ? $workflow->options['name_as_title'] : 0,
  340. '#description' => t('The workflow section of the editing form is in its own fieldset. Checking the box will add the workflow ' .
  341. 'name as the title of workflow section of the editing form.'),
  342. );
  343. $form['comment'] = array(
  344. '#type' => 'fieldset',
  345. '#title' => t('Comment for Workflow Log'),
  346. );
  347. $form['comment']['comment_log_node'] = array(
  348. '#type' => 'checkbox',
  349. '#title' => t('Show a comment field in the workflow section of the editing form.'),
  350. '#default_value' => isset($workflow->options['comment_log_node']) ? $workflow->options['comment_log_node'] : 0,
  351. '#description' => t('On the node editing form, a Comment form can be shown so that the person making the state change can record ' .
  352. 'reasons for doing so. The comment is then included in the node\'s workflow history.'),
  353. );
  354. $form['comment']['comment_log_tab'] = array(
  355. '#type' => 'checkbox',
  356. '#title' => t('Show a comment field in the workflow section of the workflow tab form.'),
  357. '#default_value' => isset($workflow->options['comment_log_tab']) ? $workflow->options['comment_log_tab'] : 0,
  358. '#description' => t('On the workflow tab, a Comment form can be shown so that the person making the state change can record ' .
  359. 'reasons for doing so. The comment is then included in the node\'s workflow history.'),
  360. );
  361. $form['tab'] = array(
  362. '#type' => 'fieldset',
  363. '#title' => t('Workflow tab permissions'),
  364. '#collapsible' => TRUE,
  365. '#collapsed' => FALSE,
  366. );
  367. $form['tab']['tab_roles'] = array(
  368. '#type' => 'checkboxes',
  369. '#options' => workflow_admin_ui_get_roles(),
  370. '#default_value' => explode(',', $workflow->tab_roles),
  371. '#description' => t('Select any roles that should have access to the workflow tab on nodes that have a workflow.'),
  372. );
  373. $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
  374. return $form;
  375. }
  376. /**
  377. * Theme the workflow editing form.
  378. *
  379. * @see workflow_edit_form()
  380. */
  381. function theme_workflow_admin_ui_edit_form($variables) {
  382. $output = '';
  383. $form = $variables['form'];
  384. $wid = $form['wid']['#value'];
  385. $workflow = $form['#workflow'];
  386. // If we don't have a workflow here, we need to go back to admin.
  387. if ($workflow) {
  388. drupal_set_title(t('Edit workflow %name', array('%name' => $workflow->name)), PASS_THROUGH);
  389. $output .= drupal_render($form['wf_name']);
  390. $output .= drupal_render_children($form);
  391. return $output;
  392. }
  393. else {
  394. drupal_goto('admin/config/workflow/workflow');
  395. }
  396. }
  397. /**
  398. * Validate the workflow editing form.
  399. *
  400. * @see workflow_edit_form()
  401. */
  402. function workflow_admin_ui_edit_form_validate($form_id, $form_state) {
  403. $wid = $form_state['values']['wid'];
  404. // Make sure workflow name is not a duplicate.
  405. if (array_key_exists('wf_name', $form_state['values']) /*&& $form_state['values']['wf_name'] != ''*/) {
  406. $workflow_name = $form_state['values']['wf_name'];
  407. $workflows = array();
  408. foreach (workflow_get_workflows() as $data) {
  409. $workflows[$data->wid] = check_plain(t($data->name));
  410. }
  411. $workflows = array_flip($workflows);
  412. if (array_key_exists($workflow_name, $workflows) && $wid != $workflows[$workflow_name]) {
  413. form_set_error('wf_name', t('A workflow with the name %name already exists. Please enter another name for this workflow.',
  414. array('%name' => $workflow_name)));
  415. }
  416. }
  417. }
  418. /**
  419. * Submit handler for the workflow editing form.
  420. *
  421. * @see workflow_edit_form()
  422. */
  423. function workflow_admin_ui_edit_form_submit($form, &$form_state) {
  424. if (isset($form_state['values']['transitions'])) {
  425. workflow_update_transitions($form_state['values']['transitions']);
  426. }
  427. $options = array(
  428. 'comment_log_node' => $form_state['values']['comment_log_node'],
  429. 'comment_log_tab' => $form_state['values']['comment_log_tab'],
  430. 'name_as_title' => $form_state['values']['name_as_title'],
  431. );
  432. $workflow = array(
  433. 'wid' => $form_state['values']['wid'],
  434. 'name' => $form_state['values']['wf_name'],
  435. 'tab_roles' => array_filter($form_state['values']['tab_roles']),
  436. 'options' => serialize($options),
  437. );
  438. workflow_update_workflows($workflow);
  439. drupal_set_message(t('The workflow was updated.'));
  440. $form_state['redirect'] = 'admin/config/workflow/workflow/' . $form_state['values']['wid'];
  441. }
  442. /**
  443. * Menu callback. Edit a workflow's transitions.
  444. *
  445. * @param $wid
  446. * The workflow object.
  447. * @return
  448. * HTML form and permissions table.
  449. */
  450. function workflow_admin_ui_transitions_form($form, $form_state, $workflow) {
  451. // Make sure we have a workflow.
  452. if ($workflow) {
  453. // Create breadcrumbs.
  454. workflow_admin_ui_breadcrumbs($workflow);
  455. $form = array();
  456. $form['wid'] = array('#type' => 'value', '#value' => $workflow->wid);
  457. $form['#workflow'] = $workflow;
  458. $form['transitions'] = workflow_admin_ui_transition_grid_form($workflow);
  459. $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
  460. return $form;
  461. }
  462. }
  463. /**
  464. * Validate the workflow editing form.
  465. *
  466. * @see workflow_edit_form()
  467. */
  468. function workflow_admin_ui_transitions_form_validate($form, $form_state) {
  469. $wid = $form['wid']['#value'];
  470. $workflow = $form['#workflow'];
  471. // Make sure 'author' is checked for (creation) -> [something].
  472. $creation_id = workflow_get_creation_state_by_wid($wid);
  473. if (isset($form_state['values']['transitions'][$creation_id]) && is_array($form_state['values']['transitions'][$creation_id])) {
  474. foreach ($form_state['values']['transitions'][$creation_id] as $to => $roles) {
  475. if ($roles['author']) {
  476. $author_has_permission = TRUE;
  477. break;
  478. }
  479. }
  480. }
  481. $state_count = db_query('SELECT COUNT(sid) FROM {workflow_states} WHERE wid = :wid', array(':wid' => $wid))->fetchField();
  482. if (empty($author_has_permission) && $state_count > 1) {
  483. form_set_error('transitions', t('Please give the author permission to go from %creation to at least one state!',
  484. array('%creation' => '(creation)')));
  485. }
  486. }
  487. /**
  488. * Theme the workflow editing form.
  489. *
  490. * @see workflow_edit_form()
  491. */
  492. function theme_workflow_admin_ui_transitions_form($variables) {
  493. $output = '';
  494. $form = $variables['form'];
  495. $wid = $form['wid']['#value'];
  496. $workflow = $form['#workflow'];
  497. // If we don't have a workflow here, we need to go back to admin.
  498. if ($workflow) {
  499. drupal_set_title(t('Edit workflow %name transitions', array('%name' => $workflow->name)), PASS_THROUGH);
  500. $output .= drupal_render($form['wf_name']);
  501. $states = workflow_get_workflow_states_by_wid($wid, array('status' => 1));
  502. if ($states) {
  503. $roles = workflow_admin_ui_get_roles();
  504. $header = array(array('data' => t('From / To') . ' &nbsp;' . WORKFLOW_ARROW));
  505. $rows = array();
  506. foreach ($states as $state) {
  507. $state_id = $state->sid;
  508. $name = $state->state;
  509. // Don't allow transition TO (creation).
  510. if ($name != t('(creation)')) {
  511. $header[] = array('data' => check_plain(t($name)));
  512. }
  513. $row = array(array('data' => check_plain(t($name))));
  514. foreach ($states as $nested_state) {
  515. $nested_state_id = $nested_state->sid;
  516. $nested_name = $nested_state->state;
  517. if ($nested_name == t('(creation)')) {
  518. // Don't allow transition TO (creation).
  519. continue;
  520. }
  521. if ($nested_state_id != $state_id) {
  522. // Need to render checkboxes for transition from $state to $nested_state.
  523. $from = $state_id;
  524. $to = $nested_state_id;
  525. $cell = '';
  526. foreach ($roles as $rid => $role_name) {
  527. $cell .= drupal_render($form['transitions'][$from][$to][$rid]);
  528. }
  529. $row[] = array('data' => $cell);
  530. }
  531. else {
  532. $row[] = array('data' => '');
  533. }
  534. }
  535. $rows[] = $row;
  536. }
  537. $output .= theme('table', array('header' => $header, 'rows' => $rows));
  538. }
  539. else {
  540. $output = t('There are no states defined for this workflow.');
  541. }
  542. $output .= drupal_render_children($form);
  543. return $output;
  544. }
  545. }
  546. /**
  547. * Submit handler for the workflow editing form.
  548. *
  549. * @see workflow_edit_form()
  550. */
  551. function workflow_admin_ui_transitions_form_submit($form, &$form_state) {
  552. $workflow = $form['#workflow'];
  553. if (isset($form_state['values']['transitions'])) {
  554. workflow_update_transitions($form_state['values']['transitions']);
  555. }
  556. drupal_set_message(t('The workflow was updated.'));
  557. $form_state['redirect'] = 'admin/config/workflow/workflow/' . $form_state['values']['wid'];
  558. }
  559. /**
  560. * Form builder. Build the grid of transitions for defining a workflow.
  561. *
  562. * @param int $wid
  563. * The workflow object.
  564. */
  565. function workflow_admin_ui_transition_grid_form($workflow) {
  566. $form = array('#tree' => TRUE);
  567. $roles = workflow_admin_ui_get_roles();
  568. $states = workflow_get_workflow_states_by_wid($workflow->wid, array('status' => 1));
  569. if (!$states) {
  570. $form['error'] = array(
  571. '#type' => 'markup',
  572. '#value' => t('There are no states defined for this workflow.'),
  573. );
  574. return $form;
  575. }
  576. foreach ($states as $state1) {
  577. $state_id = $state1->sid;
  578. $name = $state1->state;
  579. foreach ($states as $state2) {
  580. $nested_state_id = $state2->sid;
  581. $nested_name = $state2->state;
  582. if ($nested_name == t('(creation)')) {
  583. // Don't allow transition TO (creation).
  584. continue;
  585. }
  586. if ($nested_state_id != $state_id) {
  587. // Need to generate checkboxes for transition from $state to $nested_state.
  588. $from = $state_id;
  589. $to = $nested_state_id;
  590. foreach ($roles as $rid => $role_name) {
  591. $checked = FALSE;
  592. if ($transition = workflow_get_workflow_transitions_by_sid_target_sid($from, $to)) {
  593. $checked = workflow_transition_allowed($transition->tid, $rid);
  594. }
  595. $form[$from][$to][$rid] = array(
  596. '#type' => 'checkbox',
  597. '#title' => check_plain($role_name),
  598. '#default_value' => $checked,
  599. );
  600. }
  601. }
  602. }
  603. }
  604. return $form;
  605. }
  606. /**
  607. * Implements hook_workflow_operations().
  608. * Might as well eat our own cooking.
  609. */
  610. function workflow_admin_ui_workflow_operations($op, $workflow = NULL, $state = NULL) {
  611. switch ($op) {
  612. case 'top_actions':
  613. // Build a link to each workflow.
  614. $workflows = workflow_get_workflows();
  615. $alt = t('Add a new workflow');
  616. $actions = array(
  617. 'add-workflow' => array(
  618. 'title' => t('Add workflow'),
  619. // @TODO: It might be more sane to go to the "settings" page.
  620. 'href' => 'admin/config/workflow/workflow/add',
  621. 'attributes' => array('alt' => $alt, 'title' => $alt),
  622. ),
  623. );
  624. foreach ($workflows as $workflow) {
  625. $alt = t('Work with @wf', array('@wf' => $workflow->name));
  626. $actions[drupal_html_class($workflow->name)] = array(
  627. 'title' => t($workflow->name),
  628. 'href' => "admin/config/workflow/workflow/$workflow->wid",
  629. 'attributes' => array('alt' => $alt, 'title' => $alt),
  630. );
  631. }
  632. return $actions;
  633. case 'workflow':
  634. $actions = array(
  635. 'workflow_settings' => array(
  636. 'title' => t('Settings'),
  637. 'href' => "admin/config/workflow/workflow/edit/$workflow->wid",
  638. 'attributes' => array('alt' => t('Edit the @wf settings', array('@wf' => $workflow->name))),
  639. ),
  640. 'workflow_transitions' => array(
  641. 'title' => t('Transitions'),
  642. 'href' => "admin/config/workflow/workflow/transitions/$workflow->wid",
  643. 'attributes' => array('alt' => t('Edit the @wf transitios', array('@wf' => $workflow->name))),
  644. ),
  645. 'workflow_permission_summary' => array(
  646. 'title' => t('Summary'),
  647. 'href' => "admin/config/workflow/workflow/perm_summary/$workflow->wid",
  648. 'attributes' => array('alt' => t('See a summary of the @wf transitios', array('@wf' => $workflow->name))),
  649. ),
  650. 'workflow_overview_delete' => array(
  651. 'title' => t('Delete'),
  652. 'href' => "admin/config/workflow/workflow/delete/$workflow->wid",
  653. 'attributes' => array('alt' => t('Delete the @wf', array('@wf' => $workflow->name))),
  654. ),
  655. );
  656. foreach ($actions as $name => $link) {
  657. $actions[$name]['attributes']['title'] = $actions[$name]['attributes']['alt'];
  658. }
  659. return $actions;
  660. }
  661. }
  662. /**
  663. * Menu callback. Create the main workflow page, which gives an overview
  664. * of workflows and workflow states.
  665. * Replaced by http://drupal.org/node/1367530.
  666. */
  667. function workflow_admin_ui_overview_form($form, $form_state, $workflow) {
  668. $bc = array(l(t('Home'), '<front>'));
  669. $bc[] = l(t('Configuration'), 'admin/config');
  670. $bc[] = l(t('Workflow'), 'admin/config/workflow');
  671. $bc[] = l(t('Workflow'), 'admin/config/workflow/workflow');
  672. drupal_set_breadcrumb($bc);
  673. $form = array('#tree' => TRUE);
  674. $form['wid'] = array('#type' => 'value', '#value' => $workflow->wid);
  675. $form['#wf_name'] = $workflow->name;
  676. // Get the state objects and make the array keys the same as the sids.
  677. $states = workflow_get_workflow_states_by_wid($workflow->wid);
  678. $sids = array();
  679. foreach ($states as $state) {
  680. $sids[] = $state->sid;
  681. }
  682. $states = array_combine($sids, $states);
  683. // Save the list in the form for later.
  684. $form['#states'] = $states;
  685. // Allow modules to insert their own workflow operations.
  686. $links = module_invoke_all('workflow_operations', 'workflow', $workflow);
  687. drupal_set_title(t('Workflow: ') . t($workflow->name));
  688. $links_args = array(
  689. 'links' => $links,
  690. 'attributes' => array('class' => array('inline', 'action-links')),
  691. );
  692. $form['action-links'] = array(
  693. '#type' => 'markup',
  694. '#markup' => theme('links', $links_args),
  695. );
  696. // Build select options for reassigning states.
  697. // We put a blank state first for validation.
  698. $state_list = array('' => ' ');
  699. foreach ($states as $sid => $state) {
  700. // Leave out the creation state and any inactive states.
  701. if ($state->sysid == 0 && $state->status == 1) {
  702. $state_list[$sid] = $state->state;
  703. }
  704. }
  705. // Is this the last state available?
  706. $form['#last'] = count($state_list) == 2;
  707. // Dummy object for new state item.
  708. $states[] = (object) array(
  709. 'sid' => 0,
  710. 'state' => '',
  711. 'status' => 0,
  712. 'sysid' => 0,
  713. 'weight' => 99,
  714. );
  715. foreach ($states as $state) {
  716. // Allow modules to insert state operations.
  717. $state_links = module_invoke_all('workflow_operations', 'state', $workflow, $state);
  718. $form['states'][$state->sid] = array(
  719. 'state' => array(
  720. '#type' => 'textfield',
  721. '#size' => 30,
  722. '#maxlength' => 255,
  723. '#default_value' => $state->state,
  724. ),
  725. 'weight' => array(
  726. '#type' => 'weight',
  727. '#delta' => 20,
  728. '#default_value' => $state->weight,
  729. '#title-display' => 'invisible',
  730. '#attributes' => array('class' => array('state-weight')),
  731. ),
  732. 'status' => array(
  733. '#type' => 'checkbox',
  734. '#default_value' => $state->status,
  735. ),
  736. // Save the original status for the validation handler.
  737. 'orig_status' => array(
  738. '#type' => 'value',
  739. '#value' => $state->status,
  740. ),
  741. 'reassign' => array(
  742. '#type' => 'select',
  743. '#options' => $state_list,
  744. ),
  745. 'count' => array(
  746. '#type' => 'value',
  747. '#value' => count(workflow_get_workflow_node_by_sid($state->sid)),
  748. ),
  749. 'ops' => array(
  750. '#type' => 'markup',
  751. '#markup' => theme('links', array('links' => $state_links)),
  752. ),
  753. 'sysid' => array(
  754. '#type' => 'value',
  755. '#value' => 0,
  756. ),
  757. );
  758. // Don't let the creation state change weight or status.
  759. if ($state->state == '(creation)') {
  760. $form['states'][$state->sid]['weight']['#value'] = -50;
  761. $form['states'][$state->sid]['sysid']['#value'] = 1;
  762. $form['states'][$state->sid]['status']['#disabled'] = TRUE;
  763. $form['states'][$state->sid]['reassign']['#disabled'] = TRUE;
  764. }
  765. }
  766. $form['instructions'] = array(
  767. '#type' => 'markup',
  768. '#markup' => '<p>' . t('You may enter a new state name in the empty space.
  769. Check the "Active" box to make it effective. You may also drag it to the appropriate position.') . '</p>'
  770. . '<p>' . t('A state not marked as active will not be shown as available in the workflow settings.') . '</p>'
  771. . '<p>' . t('If you wish to inactivate a state that has content (i.e. count is not zero),
  772. then you need to select a state to which to reassign that content.') . '</p>'
  773. );
  774. $form['actions'] = array('#type' => 'actions');
  775. $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Save Changes'));
  776. return $form;
  777. }
  778. function theme_workflow_admin_ui_overview_form($variables) {
  779. $form = $variables['form'];
  780. $output = '';
  781. $table_id = 'workflow_admin_ui_overview';
  782. $table = array(
  783. 'rows' => array(),
  784. 'header' => array(
  785. t('State'),
  786. t('Weight'),
  787. t('Active'),
  788. t('Reassign'),
  789. t('Count'),
  790. array('data' => t('Operations'), 'class' => 'state-ops'),
  791. ),
  792. 'attributes' => array('id' => $table_id, 'style' => 'width: auto;'),
  793. );
  794. // The out put needs to have the action links at the top.
  795. $output .= drupal_render($form['action-links']);
  796. // Iterate over each element in our $form['states'] array
  797. foreach (element_children($form['states']) as $id) {
  798. // We are now ready to add each element of our $form data to the rows
  799. // array, so that they end up as individual table cells when rendered
  800. // in the final table. We run each element through the drupal_render()
  801. // function to generate the final html markup for that element.
  802. $table['rows'][] = array(
  803. 'data' => array(
  804. // Add our 'name' column
  805. array('data' => drupal_render($form['states'][$id]['state']), 'class' => 'state-name'),
  806. // Add our 'weight' column
  807. drupal_render($form['states'][$id]['weight']),
  808. // Add our 'status' column
  809. array('data' => drupal_render($form['states'][$id]['status']), 'class' => 'state-status'),
  810. // Add our 'reassign' column
  811. array('data' => drupal_render($form['states'][$id]['reassign']), 'class' => 'state-reassign'),
  812. // Add our 'count' column
  813. array('data' => $form['states'][$id]['count']['#value'], 'class' => 'state-count'),
  814. // Add our 'operations' column
  815. array('data' => drupal_render($form['states'][$id]['ops']), 'class' => 'state-ops'),
  816. // Add our 'sysid' column
  817. drupal_render($form['states'][$id]['sysid']),
  818. ),
  819. // To support the tabledrag behaviour, we need to assign each row of the
  820. // table a class attribute of 'draggable'. This will add the 'draggable'
  821. // class to the <tr> element for that row when the final table is
  822. // rendered.
  823. 'class' => array('draggable'),
  824. );
  825. }
  826. $output .= theme('table', $table);
  827. // And then render any remaining form elements (such as our submit button)
  828. $output .= drupal_render_children($form);
  829. // We now call the drupal_add_tabledrag() function in order to add the
  830. // tabledrag.js goodness onto our page.
  831. //
  832. // For a basic sortable table, we need to pass it:
  833. // - the $table_id of our <table> element,
  834. // - the $action to be performed on our form items ('order'),
  835. // - a string describing where $action should be applied ('siblings'),
  836. // - and the class of the element containing our 'weight' element.
  837. drupal_add_tabledrag($table_id, 'order', 'sibling', 'state-weight');
  838. return $output;
  839. }
  840. /**
  841. * Validation handler for the state form.
  842. */
  843. function workflow_admin_ui_overview_form_validate($form, &$form_state) {
  844. // Get the workflow id.
  845. $wid = $form_state['values']['wid'];
  846. // Get the state objects.
  847. $states = $form['#states'];
  848. // Because the form elements were keyed with the item ids from the database,
  849. // we can simply iterate through the submitted values.
  850. foreach ($form_state['values']['states'] as $sid => $item) {
  851. // Do they want to deactivate the state?
  852. if (($item['status'] != $item['orig_status']) && ($item['status'] == 0)) {
  853. // Does that state have nodes in it?
  854. if ($item['count'] > 0 && empty($item['reassign'])) {
  855. if ($form['#last']) {
  856. drupal_set_message(t('Since you are deleting the last available workflow state
  857. in this workflow, all content items which are in that state will have their
  858. workflow state removed.'), 'warning');
  859. }
  860. else {
  861. form_set_error("states'][$sid]['reassign'",
  862. t('The %state state has content; you must reassign the content to another state.',
  863. array('%state' => $states[$sid]->state)));
  864. }
  865. }
  866. }
  867. }
  868. }
  869. /**
  870. * Submission handler for the state form.
  871. */
  872. function workflow_admin_ui_overview_form_submit($form, &$form_state) {
  873. // Get the workflow id, then save it for the next round.
  874. $wid = $form_state['values']['wid'];
  875. $wf_name = $form['#wf_name'];
  876. // Get the state objects.
  877. $states = $form['#states'];
  878. // Because the form elements were keyed with the item ids from the database,
  879. // we can simply iterate through the submitted values.
  880. foreach ($form_state['values']['states'] as $id => $item) {
  881. $item['sid'] = $id;
  882. $item['wid'] = $wid;
  883. // Is there not a new state name?
  884. if (empty($item['state'])) {
  885. // No new state entered, so skip it.
  886. continue;
  887. }
  888. // Is this a new state?
  889. if ($id == 0) {
  890. // Remove the key for the updating.
  891. unset($item['sid']);
  892. }
  893. // Do they want to deactivate the state?
  894. if ($item['status'] == 0) {
  895. // Does that state have nodes in it?
  896. if ($item['count'] > 0) {
  897. if ($form['#last']) {
  898. $new_sid = NULL;
  899. drupal_set_message(t('Removing workflow states from content in the %workflow.',
  900. array('%workflow' => $wf_name)));
  901. }
  902. else {
  903. // Prepare the state delete function.
  904. $new_sid = $item['reassign'];
  905. drupal_set_message(t('Reassigning content from %old_state to %new_state.',
  906. array('%old_state' => $states[$id]->state, '%new_state' => $states[$new_sid]->state)));
  907. }
  908. // Delete the old state without orphaning nodes, move them to the new state.
  909. workflow_delete_workflow_states_by_sid($id, $new_sid);
  910. $args = array(
  911. '%state' => $states[$id]->state,
  912. '%workflow' => $wf_name,
  913. );
  914. watchdog('workflow', 'Deactivated workflow state %state in %workflow.', $args);
  915. drupal_set_message(t('The workflow state %state was deleted from %workflow.', $args));
  916. }
  917. }
  918. // Call the update function.
  919. workflow_update_workflow_states($item);
  920. }
  921. $form_state['redirect'] = "admin/config/workflow/workflow/$wid";
  922. }
  923. /**
  924. * Form builder. Allow administrator to map workflows to content types
  925. * and determine placement.
  926. */
  927. function workflow_admin_ui_types_form($form) {
  928. $form = array();
  929. // Allow modules to insert their own action links.
  930. $links = module_invoke_all('workflow_operations', 'top_actions');
  931. $links_args = array(
  932. 'links' => $links,
  933. 'attributes' => array('class' => array('inline', 'action-links')),
  934. );
  935. $form['action-links'] = array(
  936. '#type' => 'markup',
  937. '#markup' => theme('links', $links_args),
  938. );
  939. $these_workflows = array();
  940. foreach (workflow_get_workflows() as $data) {
  941. // Don't allow workflows with no states.
  942. $states = workflow_get_workflow_states_by_wid($data->wid, array('status' => 1));
  943. // There should always be a creation state.
  944. if (count($states) == 1) {
  945. // That's all, so let's remind them to create some states.
  946. drupal_set_message(t('%workflow has no states defined, so it cannot be assigned to content yet.',
  947. array('%workflow' => ucwords($data->name))), 'warning');
  948. // Skip allowing this workflow.
  949. continue;
  950. }
  951. // Also check for transitions at least out of the creation state.
  952. // This always gets at least the "from" state.
  953. $transitions = workflow_allowable_transitions($data->creation_state, 'to');
  954. if (count($transitions) == 1) {
  955. // That's all, so let's remind them to create some transitions.
  956. drupal_set_message(t('%workflow has no transitions defined, so it cannot be assigned to content yet.',
  957. array('%workflow' => ucwords($data->name))), 'warning');
  958. // Skip allowing this workflow.
  959. continue;
  960. }
  961. // Add this workflow to the allowed list.
  962. $these_workflows[$data->wid] = t($data->name);
  963. }
  964. $workflows = array(t('None')) + $these_workflows;
  965. if (count($workflows) == 1) {
  966. drupal_set_message(t('You must create at least one workflow before content can be assigned to a workflow.'));
  967. return $form;
  968. }
  969. $type_map = workflow_get_workflow_type_map();
  970. $form['#tree'] = TRUE;
  971. $form['help'] = array(
  972. '#type' => 'item',
  973. '#title' => '<h3>' . t('Content Type Mapping') . '</h3>',
  974. '#markup' => t('Each content type may have a separate workflow. The form for changing workflow state can be '
  975. . 'displayed when editing a node, editing a comment for a node, or both.'),
  976. );
  977. $placement_options = array(
  978. 'node' => t('Post'),
  979. 'comment' => t('Comment'),
  980. );
  981. foreach (node_type_get_names() as $type => $name) {
  982. $form[$type]['workflow'] = array(
  983. '#type' => 'select',
  984. '#options' => $workflows,
  985. '#default_value' => isset($type_map[$type]) ? $type_map[$type] : 0,
  986. );
  987. $form[$type]['placement'] = array(
  988. '#type' => 'checkboxes',
  989. '#options' => $placement_options,
  990. '#default_value' => variable_get('workflow_' . $type, array()),
  991. );
  992. }
  993. $form['submit'] = array(
  994. '#type' => 'submit',
  995. '#value' => t('Save workflow mapping'),
  996. );
  997. return $form;
  998. }
  999. /**
  1000. * Theme the workflow type mapping form.
  1001. */
  1002. function theme_workflow_admin_ui_types_form($variables) {
  1003. $output = '';
  1004. $form = $variables['form'];
  1005. // Do the action links at the top.
  1006. $output .= drupal_render($form['action-links']);
  1007. $header = array(t('Content Type'), t('Workflow'), t('Display Workflow Form on:'));
  1008. $rows = array();
  1009. foreach (node_type_get_names() as $type => $name) {
  1010. $rows[] = array(
  1011. check_plain(t($name)),
  1012. drupal_render($form[$type]['workflow']),
  1013. drupal_render($form[$type]['placement']),
  1014. );
  1015. }
  1016. $output .= drupal_render($form['help']);
  1017. $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('style' => 'width: auto;')));
  1018. return $output . drupal_render_children($form);
  1019. }
  1020. /**
  1021. * Submit handler for workflow type mapping form.
  1022. *
  1023. * @see workflow_types_form()
  1024. */
  1025. function workflow_admin_ui_types_form_submit($form, &$form_state) {
  1026. workflow_admin_ui_types_save($form_state['values']);
  1027. drupal_set_message(t('The workflow mapping was saved.'));
  1028. }
  1029. /**
  1030. * Get a list of roles.
  1031. *
  1032. * @return
  1033. * Array of role names keyed by role ID, including the 'author' role.
  1034. */
  1035. function workflow_admin_ui_get_roles() {
  1036. static $roles = NULL;
  1037. if (!$roles) {
  1038. $roles = array('author' => 'author');
  1039. $list = user_roles(FALSE, 'participate in workflow');
  1040. foreach ($list as $rid => $name) {
  1041. $roles[$rid] = check_plain($name);
  1042. }
  1043. }
  1044. return $roles;
  1045. }
  1046. /**
  1047. * Save mapping of workflow to node type. E.g., the story node type is using the Foo workflow.
  1048. *
  1049. * @param $form_state['values']
  1050. */
  1051. function workflow_admin_ui_types_save($form_values) {
  1052. // Empty the table so that types no longer under workflow go away.
  1053. workflow_delete_workflow_type_map_all();
  1054. $node_types = node_type_get_names();
  1055. foreach ($node_types as $type => $name) {
  1056. $data = array(
  1057. 'type' => $type,
  1058. 'wid' => $form_values[$type]['workflow'],
  1059. );
  1060. workflow_insert_workflow_type_map($data);
  1061. variable_set('workflow_' . $type, array_keys(array_filter(($form_values[$type]['placement']))));
  1062. // If this type uses workflow, make sure pre-existing nodes are set
  1063. // to the workflow's creation state.
  1064. if ($form_values[$type]['workflow']) {
  1065. workflow_initialize_nodes($type, $name);
  1066. }
  1067. }
  1068. }
  1069. /**
  1070. * Initialize all pre-existing nodes of a type to their first state..
  1071. *
  1072. * @param $type - node type
  1073. * @param $name - node type name
  1074. */
  1075. function workflow_initialize_nodes($type, $name) {
  1076. // Build the select query.
  1077. // We want all published nodes of this type that don't already have a workflow state.
  1078. $query = db_select('node', 'n');
  1079. $query->leftJoin('workflow_node', 'wn', 'wn.nid = n.nid');
  1080. // Add the fields.
  1081. $query->addField('n', 'nid');
  1082. // Add conditions.
  1083. $query->condition('n.type', $type);
  1084. $query->condition('n.status', 1);
  1085. $query->isNull('wn.sid');
  1086. $nids = $query->execute()->fetchCol();
  1087. $how_many = count($nids);
  1088. if ($how_many == 0) {
  1089. return;
  1090. }
  1091. $comment = t('Pre-existing content set to initial state.');
  1092. // Get the initial state for this content type.
  1093. $first_state = db_query("SELECT s.sid "
  1094. . "FROM {workflow_type_map} m "
  1095. . "INNER JOIN {workflow_states} s ON s.wid = m.wid "
  1096. . "WHERE m.type = :type AND s.sysid <> :creation "
  1097. . "ORDER BY s.weight ASC ",
  1098. array(':type' => $type, ':creation' => WORKFLOW_CREATION)
  1099. )->fetchField(0);
  1100. // Load them all up.
  1101. $nodes = node_load_multiple($nids);
  1102. foreach ($nodes as $node) {
  1103. // Force it to transition to the first state and get a history record.
  1104. workflow_execute_transition($node, $first_state, $comment, TRUE);
  1105. }
  1106. return;
  1107. drupal_set_message(t('!count @type nodes have been initialized.', array('@type' => $name, '!count' => $how_many)));
  1108. }
  1109. /**
  1110. * Update the transitions for a workflow.
  1111. *
  1112. * @param array $transitions from values.
  1113. * Transitions, for example:
  1114. * 18 => array(
  1115. * 20 => array(
  1116. * 'author' => 1,
  1117. * 1 => 0,
  1118. * 2 => 1,
  1119. * )
  1120. * )
  1121. * means the transition from state 18 to state 20 can be executed by
  1122. * the node author or a user in role 2. The $transitions array should
  1123. * contain ALL transitions for the workflow.
  1124. */
  1125. function workflow_update_transitions($transitions = array()) {
  1126. // Empty string is sometimes passed in instead of an array.
  1127. if (!$transitions) {
  1128. return;
  1129. }
  1130. foreach ($transitions as $from => $to_data) {
  1131. foreach ($to_data as $to => $role_data) {
  1132. foreach ($role_data as $role => $can_do) {
  1133. if ($can_do) {
  1134. $transition = array(
  1135. 'sid' => $from,
  1136. 'target_sid' => $to,
  1137. 'roles' => $role,
  1138. );
  1139. workflow_update_workflow_transitions($transition);
  1140. }
  1141. else {
  1142. $roles = array();
  1143. if ($transition = workflow_get_workflow_transitions_by_sid_target_sid($from, $to)) {
  1144. $roles = explode(',', $transition->roles);
  1145. $tid = $transition->tid;
  1146. if (($i = array_search($role, $roles)) !== FALSE) {
  1147. unset($roles[$i]);
  1148. workflow_update_workflow_transitions_roles($tid, $roles);
  1149. }
  1150. }
  1151. }
  1152. }
  1153. }
  1154. }
  1155. workflow_delete_workflow_transitions_by_roles('');
  1156. }