workflow_admin_ui.module 44 KB

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