· 8 years ago · Aug 17, 2018, 06:34 AM
1--- workflow.admin.inc 2010-03-02 11:32:54.000000000 -0700
2+++ workflow.admin.inc 2010-07-23 10:04:17.000000000 -0600
3@@ -122,7 +122,7 @@ function workflow_permissions($wid) {
4 $all[$role]['name'] = $value;
5 }
6 $result = db_query(
7- 'SELECT t.roles, s1.state AS state_name, s2.state AS target_state_name ' .
8+ 'SELECT t.roles, s1.state AS state_name, s1.sid as state_sid, s1.ref AS state_ref, s2.state AS target_state_name, s2.sid AS target_state_sid, s2.ref AS target_state_ref ' .
9 'FROM {workflow_transitions} t ' .
10 'INNER JOIN {workflow_states} s1 ON s1.sid = t.sid '.
11 'INNER JOIN {workflow_states} s2 ON s2.sid = t.target_sid '.
12@@ -132,12 +132,14 @@ function workflow_permissions($wid) {
13
14 while ($data = db_fetch_object($result)) {
15 foreach (explode(',', $data->roles) as $role) {
16- $all[$role]['transitions'][] = array(check_plain(t($data->state_name)), WORKFLOW_ARROW, check_plain(t($data->target_state_name)));
17+ $all[$role]['transitions'][] = array(
18+ 'from' => (object) array('sid' => $data->state_sid, 'name' => $data->state_name, 'ref' => $data->state_ref),
19+ 'to' => (object) array('sid' => $data->target_state_sid, 'name' => $data->target_state_name, 'ref' => $data->target_state_ref),
20+ );
21 }
22 }
23
24- $header = array(t('From'), '', t('To'));
25- return theme('workflow_permissions', $header, $all);
26+ return $all;
27 }
28
29 /**
30@@ -148,7 +150,11 @@ function theme_workflow_permissions($hea
31 foreach ($all as $role => $value) {
32 $output .= '<h3>'. t("%role may do these transitions:", array('%role' => $value['name'])) .'</h3>';
33 if (!empty($value['transitions'])) {
34- $output .= theme('table', $header, $value['transitions']) . '<p></p>';
35+ $rows = array();
36+ foreach ($value['transitions'] as $t) {
37+ $rows[] = array($t['from']->name, WORKFLOW_ARROW, $t['to']->name);
38+ }
39+ $output .= theme('table', $header, $rows) . '<p></p>';
40 }
41 else {
42 $output .= '<table><tbody><tr class="odd"><td>' . t('None') . '</td><td></tr></tbody></table><p></p>';
43@@ -225,7 +231,7 @@ function workflow_edit_form($form_state,
44 '#collapsible' => TRUE,
45 );
46 $form['permissions']['summary'] = array(
47- '#value' => workflow_permissions($workflow->wid),
48+ '#value' => theme('workflow_permissions', array(t('From'), '', t('To')), workflow_permissions($workflow->wid)),
49 );
50
51 return $form;
52@@ -378,6 +384,11 @@ function workflow_state_add_form(&$form_
53 '#default_value' => $state['weight'],
54 '#description' => t('In listings, the heavier states will sink and the lighter states will be positioned nearer the top.'),
55 );
56+ // Unique identifier for import/exporting.
57+ $form['ref'] = array(
58+ '#type' => 'hidden',
59+ '#value' => time(),
60+ );
61 $form['submit'] = array(
62 '#type' => 'submit',
63 '#value' => t('Save')
64--- workflow.features.inc 2010-07-23 10:48:16.000000000 -0600
65+++ workflow.features.inc 2010-07-23 10:43:08.000000000 -0600
66@@ -0,0 +1,360 @@
67+<?php
68+// $Id$
69+
70+/**
71+ * @file
72+ * The inc file which adds features support
73+ */
74+
75+/**
76+ * Implementation of hook_features_export().
77+ */
78+function workflow_features_export($data, &$export, $module_name) {
79+ $export['dependencies']['workflow'] = 'workflow';
80+
81+ foreach ($data as $machine_name) {
82+ $workflow = workflow_load($machine_name);
83+ // If the workflow requires workflow_access, add that as a dependency.
84+ if (workflow_export_requires_workflow_access($workflow->wid)) {
85+ $export['dependencies']['workflow_access'] = 'workflow_access';
86+ }
87+ // If the Workflow module created the workflow then the config is only in
88+ // the database, so add it as an exportable.
89+ if (in_array($workflow->module, array('workflow', $module_name))) {
90+ $export['features']['workflow'][$workflow->machine_name] = $workflow->machine_name;
91+ continue;
92+ }
93+ // If the workflow is implemented by a module, then add that module as a
94+ // dependency.
95+ else {
96+ $export['dependencies'][$workflow->module] = $workflow->module;
97+ }
98+ }
99+}
100+
101+/**
102+ * Implementation of hook_features_options().
103+ */
104+function workflow_features_export_options() {
105+ $options = array();
106+ foreach (workflow_get_all() as $wid => $name) {
107+ $workflow = workflow_load($wid);
108+ $options[$workflow->machine_name] = $workflow->name;
109+ }
110+ return $options;
111+}
112+
113+/**
114+ * Implementation of hook_features_export_render().
115+ */
116+function workflow_features_export_render($module_name = '', $data, $module_info = array()) {
117+ $code = ' $defaults = array();' . PHP_EOL;
118+ module_load_include('inc', 'workflow', 'workflow.admin');
119+ foreach ($data as $machine_name) {
120+ $workflow = workflow_load($machine_name);
121+ // Leave workflows implemented by other modules.
122+ if (!in_array($workflow->module, array('workflow', $module_name))) {
123+ continue;
124+ }
125+ $config = array(
126+ 'name' => $workflow->name,
127+ 'machine_name' => $machine_name,
128+ 'tab_roles' => workflow_export_tab_roles($workflow->tab_roles),
129+ 'options' => $workflow->options,
130+ 'states' => workflow_export_states($workflow->wid),
131+ 'roles' => workflow_export_permissions($workflow->wid),
132+ 'types' => workflow_export_types($workflow->wid),
133+ );
134+ foreach ($config['states'] as &$state) {
135+ // Ensure the module reference is correct.
136+ $state->module = $module_name;
137+ // We don't want to port sid or wid because that may cause unique reference conflict.
138+ if (module_exists('workflow_access')) {
139+ $state->access = workflow_export_workflow_access($state->sid);
140+ }
141+ unset($state->sid);
142+ unset($state->wid);
143+ }
144+ // Clean up the code by removing information we won't use.
145+ foreach ($config['roles'] as $idx => &$role) {
146+ if (!isset($role['transitions'])) {
147+ unset($config['roles'][$idx]);
148+ continue;
149+ }
150+ foreach ($role['transitions'] as &$t) {
151+ $t['from'] = $t['from']->ref;
152+ $t['to'] = $t['to']->ref;
153+ }
154+ }
155+ $code .= ' $defaults[] = ' . features_var_export($config, ' ') . ";\n";
156+ }
157+ $code .= ' return $defaults;' . PHP_EOL;
158+ return array('workflow_defaults' => $code);
159+}
160+
161+/**
162+ * Implementation of hook_features_revert().
163+ */
164+function workflow_features_revert($module) {
165+ workflow_features_rebuild($module);
166+}
167+
168+/**
169+ * Implementation of hook_features_rebuild().
170+ */
171+function workflow_features_rebuild($module) {
172+ $defaults = module_invoke($module, 'workflow_defaults');
173+ foreach ($defaults as $config) {
174+ $config['module'] = $module;
175+ // Retrieve the existing workflow if it exists.
176+ $wid = db_result(db_query("SELECT wid FROM {workflows} WHERE machine_name = '%s'", $config['machine_name']));
177+
178+ // At this point $wid will either have a value or be FALSE.
179+ workflow_revert_default($config, $wid);
180+ }
181+}
182+
183+/**
184+ * Get workflow roles which can view workflow tab.
185+ *
186+ * @param $tab_roles list of rid separated by comma
187+ *
188+ * @return an array of role name
189+ */
190+function workflow_export_tab_roles($tab_roles) {
191+ $roles = user_roles();
192+
193+ $tab_roles_name = array();
194+ $tab_roles_rid = explode(',', $tab_roles);
195+ foreach ($tab_roles_rid as $rid) {
196+ $tab_roles_name[] = $roles[$rid];
197+ }
198+
199+ return $tab_roles_name;
200+}
201+
202+/**
203+ * Get workflow roles which can view workflow tab.
204+ *
205+ * @param $tab_roles an array of role name
206+ *
207+ * @return an array of rid
208+ */
209+function workflow_import_tab_roles($tab_roles) {
210+ $roles = user_roles();
211+ $roles_flip = array_flip($roles);
212+
213+ $tab_roles_rid = array();
214+ foreach ($tab_roles as $role_name) {
215+ $tab_roles_rid[] = $roles_flip[$role_name];
216+ }
217+
218+ return $tab_roles_rid;
219+}
220+
221+/**
222+ * Get workflow states keyed by ref rather than sid.
223+ *
224+ * @param $wid workflow id
225+ */
226+function workflow_export_states($wid) {
227+ $result = db_query("SELECT * FROM {workflow_states} WHERE wid = %d ORDER BY weight, ref", $wid);
228+ while ($data = db_fetch_object($result)) {
229+ $states[$data->ref] = $data;
230+ }
231+ return $states;
232+}
233+
234+/**
235+ * Get workflow permissions keyed by role name rather than rid.
236+ *
237+ * @param $wid workflow id
238+ */
239+function workflow_export_permissions($wid) {
240+ $exported_permissions = array();
241+
242+ $permissions = workflow_permissions($wid);
243+ foreach ($permissions as $rid => $role) {
244+ $exported_permissions[addslashes($role['name'])] = $role;
245+ }
246+
247+ return $exported_permissions;
248+}
249+
250+/**
251+ * Helper function to revert database config back to code config.
252+ */
253+function workflow_revert_default($config, $wid = FALSE) {
254+ $config = (object) $config;
255+ if ($wid) {
256+ // Config already exists in database, we're just reverting back to the code.
257+ // Use ref as the database reference.
258+ db_query("UPDATE {workflows} SET name = '%s', machine_name = '%s', module = '%s' WHERE wid = %d", $config->name, $config->machine_name, $config->module, $wid);
259+ db_query("UPDATE {workflow_states} SET module = '%s' WHERE wid = %d", $config->module, $wid);
260+ $states = workflow_export_states($wid);
261+ foreach ($config->states as $state) {
262+ $state['module'] = $config->module;
263+ if (isset($states[$state['ref']])) {
264+ drupal_write_record('workflow_states', $state, array('ref', 'module'));
265+ $state['sid'] = $states[$state['ref']]->sid;
266+
267+ // Remove any states that have been deleted.
268+ if (!$state['status']) {
269+ workflow_state_delete($state['sid'], $new_sid);
270+ }
271+ unset($states[$state['ref']]);
272+ }
273+ // Create a new state that doesn't exist.
274+ else {
275+ $state['wid'] = $wid;
276+ drupal_write_record('workflow_states', $state);
277+ }
278+
279+ // Access
280+ workflow_import_access($state);
281+ }
282+ // Delete states that no longer exist.
283+ $sids = array();
284+ foreach ($states as $state) {
285+ // It would be better to move nodes to a different state rather than
286+ // orphan them, so lets move them to the next weight.
287+ $new_sid = db_result(db_query('SELECT sid FROM {workflow_states} WHERE wid = %d AND weight >= %d AND status = 1 ORDER BY weight, sid', $state->wid, $state->weight));
288+ workflow_state_delete($state->sid, $new_sid);
289+ $sids[] = $state->sid;
290+ }
291+ if (!empty($sids)) {
292+ db_query('DELETE FROM {workflow_states} WHERE sid IN (' . db_placeholders($sids) . ')', $sids);
293+ }
294+ }
295+ else {
296+ $wid = workflow_create($config->name, $config->module, $config->ref);
297+ foreach ($config->states as &$state) {
298+ $state['wid'] = $wid;
299+ $state['module'] = $config->module;
300+ // Update the already created (creation) state.
301+ if ($state['state'] == '(creation)') {
302+ drupal_write_record('workflow_states', $state, 'wid');
303+ continue;
304+ }
305+ // Otherwise create a new state.
306+ drupal_write_record('workflow_states', $state);
307+ workflow_import_access($state);
308+ }
309+ }
310+
311+ workflow_update($wid, $config->name, workflow_import_tab_roles($config->tab_roles), $config->options);
312+
313+ // Rebuild the transition permissions.
314+ $transitions = array();
315+ $states = array();
316+ $rs = db_query('SELECT ref, sid FROM {workflow_states} WHERE wid = %d', $wid);
317+ while ($row = db_fetch_object($rs)) {
318+ $states[$row->ref] = $row->sid;
319+ }
320+
321+ $user_roles = array('author' => 'author') + user_roles();
322+ $roles = array_combine(array_keys($user_roles), array_pad(array(), count($user_roles), 0));
323+
324+ // used to work with role name instead of rid during import
325+ $user_roles_flip = array_flip($user_roles);
326+
327+ // Build transition table.
328+ $targets = $states;
329+ foreach ($states as $state) {
330+ foreach ($targets as $target) {
331+ if ($target == $state) continue;
332+ $transitions[$state][$target] = $roles;
333+ }
334+ }
335+ foreach ($config->roles as $role_name => $perms) {
336+ if (!isset($perms['transitions'])) continue;
337+
338+ foreach ($perms['transitions'] as $transition) {
339+ $from = $states[$transition['from']];
340+ $to = $states[$transition['to']];
341+ $transitions[$from][$to][$user_roles_flip[$role_name]] = 1;
342+ }
343+ }
344+ workflow_update_transitions($transitions);
345+
346+ $types = $config->types;
347+ $current_types = array();
348+ $ct = db_query("SELECT type FROM {workflow_type_map} WHERE wid = %d", $wid);
349+ while($row = db_fetch_object($ct)){
350+ $current_types[$row->type] = $row->type;
351+ }
352+ $all_types = array_merge($types, $current_types);
353+ foreach ($types as $type) {
354+ $existing = workflow_get_workflow_for_type($type);
355+ if(!in_array($type, $types) && (!$existing || $existing == $wid)){
356+ db_query("DELETE FROM {workflow_type_map} WHERE type = '%s' AND wid = %d", $type, $wid);
357+ }
358+ elseif (!$existing) {
359+ db_query("DELETE FROM {workflow_type_map} WHERE type = '%s' AND wid = %d", $type, $wid);
360+ db_query("INSERT INTO {workflow_type_map} (type, wid) VALUES ('%s', %d)", $type, $wid);
361+ }
362+ elseif ($existing != $wid) {
363+ drupal_set_message(t('Type @type already has a workflow mapped to it. If you wish to change it, visit Workflow settings page', array('@type' => $type)));
364+ }
365+ }
366+}
367+
368+/**
369+ * Set workflow access data
370+ */
371+function workflow_import_access($state){
372+ if (module_exists('workflow_access')) {
373+ db_query('DELETE FROM {workflow_access} WHERE sid = %d', $state['sid']);
374+
375+ foreach ($state['access'] as $record) {
376+ if (!isset($record['rid'])) {
377+ $record['rid'] = db_result(db_query("SELECT rid FROM {role} WHERE name = '%s'", $record['role']));
378+ }
379+ if ($record['rid']) {
380+ $record['sid'] = $state['sid'];
381+ db_query('INSERT INTO {workflow_access} (sid, rid, grant_view, grant_update, grant_delete) VALUES (%d, %d, %d, %d, %d)', $record['sid'], $record['rid'], $record['grant_view'], $record['grant_update'], $record['grant_delete']);
382+ }
383+ }
384+ node_access_needs_rebuild(TRUE);
385+ }
386+}
387+
388+/**
389+ * Export type map
390+ */
391+function workflow_export_types($wid) {
392+ $result = db_query('SELECT type FROM {workflow_type_map} WHERE wid = %d', $wid);
393+ $types = array();
394+ while ($type = db_fetch_object($result)) {
395+ $types[$type->type] = $type->type;
396+ }
397+ return $types;
398+}
399+
400+/**
401+ * Export workflow access entries
402+ */
403+function workflow_export_workflow_access($sid) {
404+ $access = array();
405+ $result = db_query('SELECT roles.name as role, wa.rid, wa.grant_view, wa.grant_update, wa.grant_delete FROM {workflow_access} wa LEFT JOIN {role} roles ON wa.rid = roles.rid WHERE wa.sid = %d', $sid);
406+ while ($item = db_fetch_object($result)) {
407+ if ($item->rid == -1) {
408+ $item->role = 'author';
409+ }
410+ if ($item->rid > 2) {
411+ unset($item->rid);
412+ }
413+ $access[] = $item;
414+ }
415+ return $access;
416+}
417+
418+/**
419+ * Determine if the workflow requires the workflow_access module.
420+ */
421+function workflow_export_requires_workflow_access($wid) {
422+ $num_rows = db_result(db_query("SELECT COUNT(wa.sid) FROM {workflow_states} ws LEFT JOIN {workflow_access} wa ON ws.sid = wa.sid WHERE wid = %d", $wid));
423+ // If there's at least one row in the workflow_access table for this workflow,
424+ // then it requires the workflow_access module.
425+ return ($num_rows) ? TRUE : FALSE;
426+}
427diff -up ../workflow_old/workflow.install ./workflow.install
428--- workflow.install 2009-06-05 15:33:23.000000000 -0600
429+++ workflow.install 2010-07-23 10:04:17.000000000 -0600
430@@ -28,8 +28,14 @@ function workflow_schema() {
431 'fields' => array(
432 'wid' => array('type' => 'serial', 'not null' => TRUE),
433 'name' => array('type' => 'varchar', 'length' => '255', 'not null' => TRUE, 'default' => ''),
434+ 'machine_name' => array('type' => 'varchar', 'length' => '255', 'not null' => TRUE),
435+ 'module' => array('type' => 'varchar', 'length' => '128', 'not null' => TRUE, 'default' => 'workflow'),
436 'tab_roles' => array('type' => 'varchar', 'length' => '60', 'not null' => TRUE, 'default' => ''),
437- 'options' => array('type' => 'text', 'size' => 'big', 'not null' => FALSE)),
438+ 'options' => array('type' => 'text', 'size' => 'big', 'not null' => FALSE)
439+ ),
440+ 'unique keys' => array(
441+ 'machine_name' => array('machine_name'),
442+ ),
443 'primary key' => array('wid'),
444 );
445 $schema['workflow_type_map'] = array(
446@@ -54,11 +60,16 @@ function workflow_schema() {
447 'fields' => array(
448 'sid' => array('type' => 'serial', 'not null' => TRUE),
449 'wid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'disp-width' => '10'),
450+ 'ref' => array('type' => 'int', 'not null' => TRUE),
451+ 'module' => array('type' => 'varchar', 'length' => '128', 'not null' => TRUE, 'default' => 'workflow'),
452 'state' => array('type' => 'varchar', 'length' => '255', 'not null' => TRUE, 'default' => ''),
453 'weight' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, 'disp-width' => '4'),
454 'sysid' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, 'disp-width' => '4'),
455 'status' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1, 'disp-width' => '4')),
456 'primary key' => array('sid'),
457+ 'unique keys' => array(
458+ 'module_ref' => array('module', 'ref'),
459+ ),
460 'indexes' => array(
461 'sysid' => array('sysid'),
462 'wid' => array('wid')),
463@@ -421,4 +432,24 @@ function workflow_update_6101() {
464 db_change_field($ret, 'workflow_transitions', 'tid', 'tid', array('type' => 'serial', 'not null' => TRUE), array('primary key' => array('tid')));
465 }
466 return $ret;
467+}
468+
469+// Add features module support (exportables)
470+function workflow_update_6102() {
471+ $ret = array();
472+ db_add_field($ret, 'workflows', 'machine_name', array('type' => 'varchar', 'length' => '255'));
473+ db_add_field($ret, 'workflows', 'module', array('type' => 'varchar', 'length' => '128', 'not null' => TRUE, 'default' => 'workflow'));
474+ $rs = db_query('SELECT wid, name FROM {workflows}');
475+ while ($row = db_fetch_object($rs)) {
476+ $machine_name = drupal_strtolower(preg_replace('/[^\w\d]/', '_', $row->name));
477+ update_sql("UPDATE {workflows} SET machine_name = '%s' WHERE wid = %d", $machine_name, $row->wid);
478+ }
479+ db_add_unique_key($ret, 'workflows', 'machine_name', array('machine_name'));
480+
481+ db_add_field($ret, 'workflow_states', 'module', array('type' => 'varchar', 'length' => '128', 'not null' => TRUE, 'default' => 'workflow'));
482+ db_add_field($ret, 'workflow_states', 'ref', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
483+ $ret[] = update_sql('UPDATE {workflow_states} SET ref = sid');
484+ db_add_unique_key($ret, 'workflow_states', 'module_ref', array('module', 'ref'));
485+
486+ return $ret;
487 }
488\ No newline at end of file
489--- workflow.module 2010-03-03 11:17:02.000000000 -0700
490+++ workflow.module 2010-07-23 10:04:17.000000000 -0600
491@@ -195,6 +195,21 @@ function workflow_views_api() {
492 }
493
494 /**
495+ * Implementation of hook_features_api().
496+ */
497+function workflow_features_api() {
498+ return array(
499+ 'workflow' => array(
500+ 'name' => 'Workflow',
501+ 'default_hook' => 'workflow_defaults',
502+ 'default_file' => FEATURES_DEFAULTS_INCLUDED,
503+ 'features_source' => TRUE,
504+ 'file' => drupal_get_path('module', 'workflow') . '/workflow.features.inc',
505+ ),
506+ );
507+}
508+
509+/**
510 * Implementation of hook_nodeapi().
511 */
512 function workflow_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
513@@ -830,7 +845,13 @@ function workflow_workflow($op, $old_sta
514 * Object representing the workflow.
515 */
516 function workflow_load($wid) {
517- $workflow = db_fetch_object(db_query('SELECT * FROM {workflows} WHERE wid = %d', $wid));
518+ if (!is_numeric($wid)) {
519+ $where = "machine_name = '%s'";
520+ }
521+ else {
522+ $where = 'wid = %d';
523+ }
524+ $workflow = db_fetch_object(db_query('SELECT * FROM {workflows} WHERE ' . $where, $wid));
525 $workflow->options = unserialize($workflow->options);
526 return $workflow;
527 }
528@@ -1012,17 +1033,24 @@ function workflow_get_all() {
529 * @param $name
530 * The name of the workflow.
531 */
532-function workflow_create($name) {
533+function workflow_create($name, $module = 'workflow', $ref = FALSE) {
534 $workflow = array(
535 'name' => $name,
536 'options' => serialize(array('comment_log_node' => 1, 'comment_log_tab' => 1)),
537+ 'module' => $module,
538+ // machine_name is a unique identifier used for exporting and importing workflow.
539+ 'machine_name' => strtolower(preg_replace('/[^\w\d]/', '_', $name)),
540 );
541 drupal_write_record('workflows', $workflow);
542 workflow_state_save(array(
543 'wid' => $workflow['wid'],
544 'state' => t('(creation)'),
545 'sysid' => WORKFLOW_CREATION,
546- 'weight' => WORKFLOW_CREATION_DEFAULT_WEIGHT));
547+ 'weight' => WORKFLOW_CREATION_DEFAULT_WEIGHT,
548+ // Ref is a unique identifier used for exporting and importing workflow.
549+ 'ref' => $ref ? $ref : microtime(),
550+ 'module' => $module,
551+ ));
552 // Workflow creation affects tabs (local tasks), so force menu rebuild.
553 menu_rebuild();
554 return $workflow['wid'];