View source
<?php
define('WORKFLOW_CREATION', 1);
define('WORKFLOW_DELETION', -1);
define('WORKFLOW_CREATION_DEFAULT_WEIGHT', -50);
define('WORKFLOW_ARROW', '→');
function workflow_help($section) {
switch ($section) {
case strstr($section, 'admin/build/workflow/edit'):
return t('You are currently viewing the possible transitions to and from workflow states. The state is shown in the left column; 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. For example, if only the editor role may move a node from Review state to the Published state, check the box next to editor. The author role is built in and refers to the user who authored the node. For a summary of which role may do which transition, look at the bottom of this page.');
case 'admin/build/workflow/add':
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 during node editing.');
case strstr($section, 'admin/build/workflow/state') && !strstr($section, 'delete'):
return t('Enter the name for a state in your workflow. For example, if you were doing a meal workflow it may include states like <em>shop</em>, <em>prepare food</em>, <em>eat</em>, and <em>clean up</em>.');
case strstr($section, 'admin/build/workflow/actions') && count($section) == 22:
return t('Use this page to set actions to happen when transitions occur. To <a href="@link">configure actions</a>, use the actions module.', array(
'@link' => url('admin/actions'),
));
}
}
function workflow_perm() {
return array(
'administer workflow',
'schedule workflow transitions',
);
}
function workflow_menu($may_cache) {
$items = array();
$access = user_access('administer workflow');
if ($may_cache) {
$items[] = array(
'path' => 'admin/build/workflow',
'title' => t('Workflow'),
'access' => $access,
'callback' => 'workflow_overview',
'description' => t('Allows the creation and assignment of arbitrary workflows to content types.'),
);
$items[] = array(
'path' => 'admin/build/workflow/edit',
'title' => t('Edit workflow'),
'type' => MENU_CALLBACK,
'callback' => 'drupal_get_form',
'callback arguments' => array(
'workflow_edit_form',
),
);
$items[] = array(
'path' => 'admin/build/workflow/list',
'title' => t('List'),
'weight' => -10,
'callback' => 'workflow_page',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/build/workflow/add',
'title' => t('Add workflow'),
'weight' => -8,
'callback' => 'drupal_get_form',
'callback arguments' => array(
'workflow_add_form',
),
'type' => MENU_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/build/workflow/state',
'title' => t('Add state'),
'type' => MENU_CALLBACK,
'callback' => 'drupal_get_form',
'callback arguments' => array(
'workflow_state_add_form',
),
);
$items[] = array(
'path' => 'admin/build/workflow/state/delete',
'title' => t('Delete State'),
'type' => MENU_CALLBACK,
'callback' => 'drupal_get_form',
'callback arguments' => array(
'workflow_state_delete_form',
),
);
$items[] = array(
'path' => 'admin/build/workflow/delete',
'title' => t('Delete workflow'),
'type' => MENU_CALLBACK,
'callback' => 'drupal_get_form',
'callback arguments' => array(
'workflow_delete_form',
),
);
$items[] = array(
'path' => 'admin/build/trigger/workflow',
'title' => t('Workflow'),
'callback' => 'actions_assign',
'callback arguments' => array(
'workflow',
),
'access' => $access,
'type' => MENU_LOCAL_TASK,
);
if (workflow_get_states()) {
$result = db_query("SELECT wid, name FROM {workflows}");
$default_assigned = FALSE;
while ($data = db_fetch_object($result)) {
$items[] = array(
'path' => 'admin/build/trigger/workflow/' . $data->wid,
'title' => check_plain($data->name),
'callback' => 'actions_assign',
'callback arguments' => array(
'workflow',
),
'access' => $access,
'type' => MENU_LOCAL_TASK,
);
$default_assigned = TRUE;
}
}
}
else {
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
$wid = workflow_get_workflow_for_type($node->type);
if ($wid) {
global $user;
$roles = array_keys($user->roles);
if ($node->uid == $user->uid) {
$roles = array_merge(array(
'author',
), $roles);
}
$workflow = db_fetch_object(db_query("SELECT * FROM {workflows} WHERE wid = %d", $wid));
$allowed_roles = $workflow->tab_roles ? explode(',', $workflow->tab_roles) : array();
$items[] = array(
'path' => "node/{$node->nid}/workflow",
'title' => t('Workflow'),
'access' => array_intersect($roles, $allowed_roles) || user_access('administer nodes'),
'type' => MENU_LOCAL_TASK,
'weight' => 2,
'callback' => 'workflow_tab_page',
'callback arguments' => arg(1),
);
}
}
}
return $items;
}
function workflow_tab_page($nid) {
$node = node_load($nid);
drupal_set_title(check_plain($node->title));
$wid = workflow_get_workflow_for_type($node->type);
$states_per_page = variable_get('workflow_states_per_page', 20);
$result = db_query("SELECT sid, state FROM {workflow_states} WHERE status = 1 ORDER BY sid");
while ($data = db_fetch_object($result)) {
$states[$data->sid] = $data->state;
}
$deleted_states = array();
$result = db_query("SELECT sid, state FROM {workflow_states} WHERE status = 0 ORDER BY sid");
while ($data = db_fetch_object($result)) {
$deleted_states[$data->sid] = $data->state;
}
$current = workflow_node_current_state($node);
$output = '<p>' . t('Current state: !state', array(
'!state' => theme('workflow_current_state', $states[$current]),
)) . "</p>\n";
$output .= drupal_get_form('workflow_tab_form', $node, $wid, $states, $current);
$result = pager_query("SELECT h.*, u.name FROM {workflow_node_history} h LEFT JOIN {users} u ON h.uid = u.uid WHERE nid = %d ORDER BY hid DESC", $states_per_page, 0, NULL, $nid);
$rows = array();
while ($history = db_fetch_object($result)) {
if ($history->sid == $current && !isset($deleted_states[$history->sid]) && !isset($current_themed)) {
$state_name = theme('workflow_current_state', $states[$history->sid]);
$current_themed = TRUE;
}
elseif (isset($deleted_states[$history->sid])) {
$state_name = theme('workflow_deleted_state', $deleted_states[$history->sid]);
$footer_needed = TRUE;
}
else {
$state_name = check_plain(t($states[$history->sid]));
}
if (isset($deleted_states[$history->old_sid])) {
$old_state_name = theme('workflow_deleted_state', $deleted_states[$history->old_sid]);
$footer_needed = TRUE;
}
else {
$old_state_name = check_plain(t($states[$history->old_sid]));
}
$rows[] = theme('workflow_history_table_row', $history, $old_state_name, $state_name);
}
$output .= theme('workflow_history_table', $rows, !empty($footer_needed));
$output .= theme('pager', $states_per_page);
return $output;
}
function theme_workflow_history_table_row($history, $old_state_name, $state_name) {
return array(
format_date($history->stamp),
$old_state_name,
$state_name,
theme('username', $history),
filter_xss($history->comment, array(
'a',
'em',
'strong',
)),
);
}
function theme_workflow_history_table($rows, $footer) {
$output = theme('table', array(
t('Date'),
t('Old State'),
t('New State'),
t('By'),
t('Comment'),
), $rows, array(
'class' => 'workflow_history',
), t('Workflow History'));
if ($footer) {
$output .= t('*State is no longer available.');
}
return $output;
}
function theme_workflow_current_state($state_name) {
return '<strong>' . check_plain(t($state_name)) . '</strong>';
}
function theme_workflow_deleted_state($state_name) {
return check_plain(t($state_name)) . '*';
}
function workflow_tab_form(&$node, $wid, $states, $current) {
$form = array();
$choices = workflow_field_choices($node);
$min = $states[$current] == t('(creation)') ? 1 : 2;
if (count($choices) >= $min) {
$wid = workflow_get_workflow_for_type($node->type);
$name = check_plain(t(workflow_get_name($wid)));
if ($node->_workflow_scheduled_timestamp && $node->_workflow_scheduled_sid) {
global $user;
if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
$timezone = $user->timezone;
}
else {
$timezone = variable_get('date_default_timezone', 0);
}
$current = $node->_workflow_scheduled_sid;
$timestamp = $node->_workflow_scheduled_timestamp;
$comment = $node->_workflow_scheduled_comment;
}
workflow_node_form($form, t('Change %s state', array(
'%s' => $name,
)), $name, $current, $choices, $timestamp, $comment);
$form['node'] = array(
'#type' => 'value',
'#value' => $node,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
}
return $form;
}
function workflow_tab_form_submit($form_id, $form_values) {
$node = $form_values['node'];
$node->workflow = $form_values['workflow'];
$node->workflow_comment = $form_values['workflow_comment'];
$node->workflow_scheduled = $form_values['workflow_scheduled'];
$node->workflow_scheduled_date = $form_values['workflow_scheduled_date'];
$node->workflow_scheduled_hour = $form_values['workflow_scheduled_hour'];
node_save($node);
return 'node/' . $node->nid;
}
function workflow_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
switch ($op) {
case 'load':
$node->_workflow = workflow_node_current_state($node);
$res = db_query('SELECT * FROM {workflow_scheduled_transition} WHERE nid = %d', $node->nid);
if ($row = db_fetch_object($res)) {
$node->_workflow_scheduled_sid = $row->sid;
$node->_workflow_scheduled_timestamp = $row->scheduled;
$node->_workflow_scheduled_comment = $row->comment;
}
break;
case 'insert':
if (empty($node->workflow)) {
$choices = workflow_field_choices($node);
$keys = array_keys($choices);
$sid = array_shift($keys);
}
case 'update':
$wid = workflow_get_workflow_for_type($node->type);
if (!$wid) {
break;
}
if (!isset($sid)) {
$sid = $node->workflow;
}
workflow_transition($node, $sid);
break;
case 'delete':
db_query("DELETE FROM {workflow_node} WHERE nid = %d", $node->nid);
_workflow_write_history($node, WORKFLOW_DELETION, t('Node deleted'));
break;
}
}
function workflow_comment($a1, $op) {
if (($op == 'insert' || $op == 'update') && isset($a1['workflow'])) {
$node = node_load($a1['nid']);
$sid = $a1['workflow'];
$node->workflow_comment = $a1['workflow_comment'];
workflow_transition($node, $sid);
}
}
function workflow_transition($node, $sid) {
if (array_key_exists($sid, workflow_field_choices($node))) {
if (!$node->workflow_scheduled) {
workflow_execute_transition($node, $sid, $node->workflow_comment);
}
else {
$comment = $node->workflow_comment;
$old_sid = workflow_node_current_state($node);
if ($node->workflow_scheduled_date['day'] < 10) {
$node->workflow_scheduled_date['day'] = '0' . $node->workflow_scheduled_date['day'];
}
if ($node->workflow_scheduled_date['month'] < 10) {
$node->workflow_scheduled_date['month'] = '0' . $node->workflow_scheduled_date['month'];
}
if (!$node->workflow_scheduled_hour) {
$node->workflow_scheduled_hour = '00:00';
}
$scheduled = $node->workflow_scheduled_date['year'] . $node->workflow_scheduled_date['month'] . $node->workflow_scheduled_date['day'] . ' ' . $node->workflow_scheduled_hour . 'Z';
if ($scheduled = strtotime($scheduled)) {
global $user;
if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
$timezone = $user->timezone;
}
else {
$timezone = variable_get('date_default_timezone', 0);
}
$scheduled = $scheduled - $timezone;
db_query("DELETE FROM {workflow_scheduled_transition} WHERE nid = %d", $node->nid);
db_query("INSERT INTO {workflow_scheduled_transition} VALUES (%d, %d, %d, %d, '%s')", $node->nid, $old_sid, $sid, $scheduled, $comment);
drupal_set_message(t("@node_title is scheduled for state change on !scheduled_date", array(
"@node_title" => $node->title,
"!scheduled_date" => format_date($scheduled),
)));
}
}
}
}
function workflow_node_form(&$form, $title, $name, $current, $choices, $timestamp = NULL, $comment = NULL) {
if (sizeof($choices) == 1) {
$form['workflow'][$name] = array(
'#type' => 'hidden',
'#value' => $current,
);
}
else {
$form['workflow'][$name] = array(
'#type' => 'radios',
'#title' => $title,
'#options' => $choices,
'#name' => $name,
'#parents' => array(
'workflow',
),
'#default_value' => $current,
);
if (!(arg(0) == 'node' && arg(1) == 'add') && user_access('schedule workflow transitions')) {
$scheduled = $timestamp ? 1 : 0;
$timestamp = $scheduled ? $timestamp : time();
$form['workflow']['workflow_scheduled'] = array(
'#type' => 'radios',
'#title' => t('Schedule'),
'#options' => array(
t('Immediately'),
t('Schedule for state change at:'),
),
'#default_value' => $scheduled,
);
$form['workflow']['workflow_scheduled_date'] = array(
'#type' => 'date',
'#default_value' => array(
'day' => format_date($timestamp, 'custom', 'j'),
'month' => format_date($timestamp, 'custom', 'n'),
'year' => format_date($timestamp, 'custom', 'Y'),
),
);
$hours = format_date($timestamp, 'custom', 'H:i');
$form['workflow']['workflow_scheduled_hour'] = array(
'#type' => 'textfield',
'#description' => t('Please enter a time in 24 hour (eg. HH:MM) format. If no time is included, the default will be midnight on the specified date. The current time is: ') . format_date(time()),
'#default_value' => $scheduled ? $hours : NULL,
);
}
$form['workflow']['workflow_comment'] = array(
'#type' => 'textarea',
'#title' => t('Comment'),
'#description' => t('A comment to put in the workflow log.'),
'#default_value' => $comment,
'#rows' => 2,
);
}
}
function workflow_form_alter($form_id, &$form) {
if ($form_id == 'comment_form' || isset($form['type']) && $form['#base'] == 'node_form') {
if (isset($form['#node'])) {
$node = $form['#node'];
if (!in_array('node', variable_get('workflow_' . $node->type, array(
'node',
)))) {
return;
}
}
else {
$type = db_result(db_query("SELECT type FROM {node} WHERE nid = %d", $form['nid']['#value']));
if (!in_array('comment', variable_get('workflow_' . $type, array(
'node',
)))) {
return;
}
$node = node_load($form['nid']['#value']);
}
$choices = workflow_field_choices($node);
$wid = workflow_get_workflow_for_type($node->type);
$states = workflow_get_states($wid);
$current = workflow_node_current_state($node);
$min = $states[$current] == t('(creation)') ? 1 : 2;
if (count($choices) < $min) {
return;
}
$name = check_plain(t(workflow_get_name($wid)));
if (!isset($choices[$current])) {
$array = array_keys($choices);
$current = $array[0];
}
if (sizeof($choices) > 1) {
$form['workflow'] = array(
'#type' => 'fieldset',
'#title' => $name,
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#weight' => 10,
);
}
if ($node->_workflow_scheduled_timestamp && $node->_workflow_scheduled_sid) {
global $user;
if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
$timezone = $user->timezone;
}
else {
$timezone = variable_get('date_default_timezone', 0);
}
$current = $node->_workflow_scheduled_sid;
$timestamp = $node->_workflow_scheduled_timestamp;
$comment = $node->_workflow_scheduled_comment;
}
workflow_node_form($form, $name, $name, $current, $choices, $timestamp, $comment);
}
}
function workflow_execute_transition(&$node, $sid, $comment = NULL, $force = FALSE) {
$old_sid = workflow_node_current_state($node);
if ($old_sid == $sid) {
if ($comment && !$node->_workflow_scheduled_comment) {
$node->workflow_stamp = time();
db_query("UPDATE {workflow_node} SET stamp = %d WHERE nid = %d", $node->workflow_stamp, $node->nid);
$result = module_invoke_all('workflow', 'transition pre', $old_sid, $sid, $node);
_workflow_write_history($node, $sid, $comment);
}
$result = module_invoke_all('workflow', 'transition post', $old_sid, $sid, $node);
return;
}
$tid = workflow_get_transition_id($old_sid, $sid);
if (!$tid) {
watchdog('workflow', t('Attempt to go to nonexistent transition (from %old to %new)', array(
'%old' => $old_sid,
'%new' => $sid,
)), WATCHDOG_ERROR);
return;
}
global $user;
if ($user->uid != 1 && !$force) {
if (!workflow_transition_allowed($tid, array_merge(array_keys($user->roles), array(
'author',
)))) {
watchdog('workflow', t('User %user not allowed to go from state %old to %new', array(
'%user' => $user->name,
'%old' => $old_sid,
'%new' => $sid,
)), WATCHDOG_NOTICE);
return;
}
}
$result = module_invoke_all('workflow', 'transition pre', $old_sid, $sid, $node);
if (in_array(FALSE, $result)) {
watchdog('workflow', t('Transition vetoed by module.'));
return;
}
_workflow_node_to_state($node, $sid, $comment);
$node->_workflow = $sid;
$state_name = db_result(db_query('SELECT state FROM {workflow_states} WHERE sid = %d', $sid));
$type = node_get_types('name', $node->type);
watchdog('workflow', t('State of @type %node_title set to @state_name', array(
'@type' => $type,
'%node_title' => $node->title,
'@state_name' => $state_name,
)), WATCHDOG_NOTICE, l('view', 'node/' . $node->nid));
module_invoke_all('workflow', 'transition post', $old_sid, $sid, $node);
db_query('DELETE FROM {workflow_scheduled_transition} WHERE nid = %d', $node->nid);
}
function workflow_action_info() {
return array(
'workflow_select_next_state_action' => array(
'type' => 'node',
'description' => t('Change workflow state of post to next state'),
'configurable' => FALSE,
'hooks' => array(
'nodeapi' => array(
'presave',
),
'comment' => array(
'insert',
'update',
),
'workflow' => array(
'any',
),
),
),
'workflow_select_given_state_action' => array(
'type' => 'node',
'description' => t('Change workflow state of post to new state'),
'configurable' => TRUE,
'hooks' => array(
'nodeapi' => array(
'presave',
),
'comment' => array(
'insert',
'update',
),
'workflow' => array(
'any',
),
),
),
);
}
function workflow_select_next_state_action($node, $context) {
if (!isset($node->workflow) && !isset($node->_workflow)) {
watchdog('workflow', t('Unable to get current workflow state of node %nid.', array(
'%nid' => $node->nid,
)));
return;
}
$current_state = isset($node->workflow) ? $node->workflow : $node->_workflow;
$choices = workflow_field_choices($node);
foreach ($choices as $sid => $name) {
if (isset($flag)) {
$new_state = $sid;
$new_state_name = $name;
break;
}
if ($sid == $current_state) {
$flag = TRUE;
}
}
workflow_execute_transition($node, $new_state);
}
function workflow_select_given_state_action($node, $context) {
$comment = t($context['workflow_comment'], array(
'%title' => check_plain($node->title),
'%state' => check_plain($context['state_name']),
));
workflow_execute_transition($node, $context['target_sid'], $comment, $context['force']);
}
function workflow_select_given_state_action_form($context) {
$result = db_query("SELECT * FROM {workflow_states} ws LEFT JOIN {workflows} w ON ws.wid = w.wid WHERE ws.sysid = 0 AND ws.status = 1 ORDER BY ws.wid, ws.weight");
$previous_workflow = '';
$options = array();
while ($data = db_fetch_object($result)) {
$options[$data->name][$data->sid] = check_plain(t($data->state));
}
$form['target_sid'] = array(
'#type' => 'select',
'#title' => t('Target state'),
'#description' => t('Please select that state that should be assigned when this action runs.'),
'#default_value' => isset($context['target_sid']) ? $context['target_sid'] : '',
'#options' => $options,
);
$form['force'] = array(
'#type' => 'checkbox',
'#title' => t('Force transition'),
'#description' => t('If this box is checked, the new state will be assigned even if workflow permissions disallow it.'),
'#default_value' => isset($context['force']) ? $context['force'] : '',
);
$form['workflow_comment'] = array(
'#type' => 'textfield',
'#title' => t('Message'),
'#description' => t('This message will be written into the workflow history log when the action runs. You may include the following variables: %state, %title'),
'#default_value' => isset($context['workflow_history']) ? $context['workflow_history'] : t('Action set %title to %state.'),
);
return $form;
}
function workflow_select_given_state_action_submit($form_id, $form_values) {
$state_name = db_result(db_query("SELECT state FROM {workflow_states} WHERE sid = %d", $form_values['target_sid']));
return array(
'target_sid' => $form_values['target_sid'],
'state_name' => check_plain(t($state_name)),
'force' => $form_values['force'],
'workflow_comment' => $form_values['workflow_comment'],
);
}
function workflow_field_choices($node) {
global $user;
$wid = workflow_get_workflow_for_type($node->type);
if (!$wid) {
return array();
}
$states = workflow_get_states($wid);
$roles = array_keys($user->roles);
$current_sid = workflow_node_current_state($node);
if ($user->uid == $node->uid && $node->uid > 0 || arg(0) == 'node' && arg(1) == 'add') {
$roles += array(
'author' => 'author',
);
}
if ($user->uid == 1) {
$roles = 'ALL';
}
$transitions = workflow_allowable_transitions($current_sid, 'to', $roles);
if ($current_sid == _workflow_creation_state($wid)) {
unset($transitions[$current_sid]);
}
return $transitions;
}
function workflow_node_current_state($node) {
$sid = db_result(db_query('SELECT sid FROM {workflow_node} WHERE nid = %d', $node->nid));
if (!$sid) {
$wid = workflow_get_workflow_for_type($node->type);
$sid = _workflow_creation_state($wid);
}
return $sid;
}
function _workflow_creation_state($wid) {
static $cache;
if (!isset($cache[$wid])) {
$result = db_result(db_query("SELECT sid FROM {workflow_states} WHERE wid = %d AND sysid = %d", $wid, WORKFLOW_CREATION));
$cache[$wid] = $result;
}
return $cache[$wid];
}
function workflow_workflow($op, $old_state, $new_state, $node) {
switch ($op) {
case 'transition pre':
break;
case 'transition post':
if (!function_exists('actions_do')) {
break;
}
$tid = workflow_get_transition_id($old_state, $new_state);
$op = 'workflow-' . $node->type . '-' . $tid;
$aids = _actions_get_hook_aids('workflow', $op);
if ($aids) {
$context = array(
'hook' => 'workflow',
'op' => $op,
);
foreach ($aids as $aid => $action_info) {
if ($action_info['type'] != 'node') {
if (!isset($objects[$action_info['type']])) {
$objects[$action_info['type']] = _actions_normalize_node_context($action_info['type'], $node);
}
$context['node'] = $node;
$result = actions_do($aid, $objects[$action_info['type']], $context);
}
else {
actions_do($aid, $node, $context);
}
}
}
break;
}
}
function workflow_add_form($name = NULL) {
$form = array();
$form['wf_name'] = array(
'#type' => 'textfield',
'#title' => t('Workflow Name'),
'#maxlength' => '254',
'#default_value' => $name,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Add Workflow'),
);
return $form;
}
function workflow_add_form_validate($form_id, $form_values) {
$workflow_name = $form_values['wf_name'];
$workflows = array_flip(workflow_get_all());
if ($workflow_name == '') {
form_set_error('wf_name', t('Please provide a nonblank name for the new workflow.'));
}
if (array_key_exists($workflow_name, $workflows)) {
form_set_error('wf_name', t('A workflow with the name %name already exists. Please enter another name for your new workflow.', array(
'%name' => $workflow_name,
)));
}
}
function workflow_add_form_submit($form_id, $form_values) {
$workflow_name = $form_values['wf_name'];
if (array_key_exists('wf_name', $form_values) && $workflow_name != '') {
workflow_create($workflow_name);
watchdog('workflow', t('Created workflow %name', array(
'%name' => $workflow_name,
)));
drupal_set_message(t('The workflow %name was created. You should now add states to your workflow.', array(
'%name' => $workflow_name,
)), 'warning');
return 'admin/build/workflow';
}
}
function workflow_delete_form($wid, $sid = NULL) {
if (isset($sid)) {
return workflow_state_delete_form($wid, $sid);
}
$form = array();
$form['wid'] = array(
'#type' => 'value',
'#value' => $wid,
);
return confirm_form($form, t('Are you sure you want to delete %title? All nodes that have a workflow state associated with this workflow will have those workflow states removed.', array(
'%title' => workflow_get_name($wid),
)), $_GET['destination'] ? $_GET['destination'] : 'admin/build/workflow', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}
function workflow_delete_form_submit($form_id, $form_values) {
if ($form_values['confirm'] == 1) {
$workflow_name = workflow_get_name($form_values['wid']);
workflow_deletewf($form_values['wid']);
watchdog('workflow', t('Deleted workflow %name with all its states', array(
'%name' => $workflow_name,
)));
drupal_set_message(t('The workflow %name with all its states was deleted.', array(
'%name' => $workflow_name,
)));
return 'admin/build/workflow';
}
}
function workflow_permissions($wid) {
$name = workflow_get_name($wid);
$all = array();
$roles = array(
'author' => t('author'),
) + user_roles();
foreach ($roles as $role => $value) {
$all[$role]['name'] = $value;
}
$result = db_query("SELECT t.roles, s1.state AS state_name, s2.state AS target_state_name " . "FROM {workflow_transitions} t " . "INNER JOIN {workflow_states} s1 ON s1.sid = t.sid " . "INNER JOIN {workflow_states} s2 ON s2.sid = t.target_sid " . "WHERE s1.wid = %d AND s1.status = 1 " . "ORDER BY s1.weight ASC , s1.state ASC , s2.weight ASC , s2.state ASC", $wid);
while ($data = db_fetch_object($result)) {
foreach (explode(',', $data->roles) as $role) {
$all[$role]['transitions'][] = array(
check_plain(t($data->state_name)),
WORKFLOW_ARROW,
check_plain(t($data->target_state_name)),
);
}
}
$output = '';
$header = array(
t('From'),
'',
t('To'),
);
foreach ($all as $role => $value) {
if ($role == 'author') {
$output .= '<h3>' . t("The author of the post may do these transitions:") . '</h3>';
}
else {
$output .= '<h3>' . t("The role %role may do these transitions:", array(
'%role' => $value['name'],
)) . '</h3>';
}
if ($value['transitions']) {
$output .= theme('table', $header, $value['transitions']) . '<p></p>';
}
else {
$output .= '<table><tbody><tr class="even"><td>' . t('None') . '</td><td></tr></tbody></table><p></p>';
}
}
return $output;
}
function workflow_edit_form($wid) {
$workflow = db_fetch_object(db_query('SELECT * FROM {workflows} WHERE wid = %d', $wid));
$form['wid'] = array(
'#type' => 'value',
'#value' => $workflow->wid,
);
$form['basic'] = array(
'#type' => 'fieldset',
'#title' => t('Workflow information'),
);
$form['basic']['wf_name'] = array(
'#type' => 'textfield',
'#default_value' => check_plain(t($workflow->name)),
'#title' => t('Workflow Name'),
'#size' => '16',
'#maxlength' => '254',
);
$form['tab'] = array(
'#type' => 'fieldset',
'#title' => t('Workflow tab permissions'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['tab']['tab_roles'] = array(
'#type' => 'checkboxes',
'#options' => workflow_get_roles(),
'#default_value' => explode(',', $workflow->tab_roles),
'#description' => t('Select any roles that should have access to the workflow tab on nodes that have a workflow.'),
);
$form['transitions'] = workflow_transition_grid_form($workflow->wid);
$form['transitions']['#tree'] = TRUE;
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
$form['permissions'] = array(
'#type' => 'fieldset',
'#title' => t('Permissions Summary'),
);
$form['permissions']['summary'] = array(
'#value' => workflow_permissions($workflow->wid),
);
return $form;
}
function theme_workflow_edit_form($form) {
$output = drupal_render($form['wf_name']);
$wid = $form['wid']['#value'];
$states = workflow_get_states($wid);
drupal_set_title(t('Edit workflow %name', array(
'%name' => workflow_get_name($wid),
)));
if ($states) {
$roles = workflow_get_roles();
$header = array(
array(
'data' => t('From / To ') . ' ' . WORKFLOW_ARROW,
),
);
$rows = array();
foreach ($states as $state_id => $name) {
if ($name != t('(creation)')) {
$header[] = array(
'data' => t($name),
);
}
$row = array(
array(
'data' => $name,
),
);
foreach ($states as $nested_state_id => $nested_name) {
if ($nested_name == t('(creation)')) {
continue;
}
if ($nested_state_id != $state_id) {
$from = $state_id;
$to = $nested_state_id;
$cell = '';
foreach ($roles as $rid => $role_name) {
$cell .= drupal_render($form['transitions'][$from][$to][$rid]);
}
$row[] = array(
'data' => $cell,
);
}
else {
$row[] = array(
'data' => '',
);
}
}
$rows[] = $row;
}
$output .= theme('table', $header, $rows);
}
else {
$output = t('There are no states defined for this workflow.');
}
$output .= drupal_render($form);
return $output;
}
function workflow_edit_form_validate($form_id, $form_values) {
$wid = $form_values['wid'];
if (array_key_exists('wf_name', $form_values) && $form_values['wf_name'] != '') {
$workflow_name = $form_values['wf_name'];
$workflows = array_flip(workflow_get_all());
if (array_key_exists($workflow_name, $workflows) && $wid != $workflows[$workflow_name]) {
form_set_error('wf_name', t('A workflow with the name %name already exists. Please enter another name for this workflow.', array(
'%name' => $workflow_name,
)));
}
}
else {
form_set_error('wf_name', t('Please provide a nonblank name for this workflow.'));
}
$creation_id = _workflow_creation_state($wid);
if (is_array($form_values['transitions'][$creation_id])) {
foreach ($form_values['transitions'][$creation_id] as $to => $roles) {
if ($roles['author']) {
$author_has_permission = true;
break;
}
}
}
$state_count = db_result(db_query("SELECT COUNT(sid) FROM {workflow_states} WHERE wid = %d", $wid));
if (!$author_has_permission && $state_count > 1) {
form_set_error('transitions', t('Please give the author permission to go from %creation to at least one state!', array(
'%creation' => '(creation)',
)));
}
}
function workflow_edit_form_submit($form_id, $form_values) {
workflow_update($form_values['wid'], $form_values['wf_name'], array_filter($form_values['tab_roles']));
workflow_update_transitions($form_values['transitions']);
drupal_set_message(t('The workflow was updated.'));
return 'admin/build/workflow';
}
function workflow_state_add_form($wid, $sid = NULL) {
$form = array();
$form['wid'] = array(
'#type' => 'value',
'#value' => $wid,
);
if (isset($sid)) {
$state = workflow_get_state($sid);
if (isset($state) && $state['status']) {
drupal_set_title(t('Edit workflow state %state', array(
'%state' => $state['state'],
)));
$form['sid'] = array(
'#type' => 'value',
'#value' => $sid,
);
}
}
if (!isset($state) || $state === FALSE) {
$state = array(
'state' => '',
'weight' => 0,
);
drupal_set_title(t('Add a new state to workflow %workflow', array(
'%workflow' => workflow_get_name($wid),
)));
}
$form['state'] = array(
'#type' => 'textfield',
'#title' => t('State name'),
'#default_value' => $state['state'],
'#size' => '16',
'#maxlength' => '254',
'#required' => TRUE,
'#description' => t('Enter the name for a state in your workflow. For example, if you were doing a meal workflow ' . 'it may include states like <em>shop</em>, <em>prepare food</em>, <em>eat</em>, and <em>clean up</em>.'),
);
$form['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight'),
'#default_value' => $state['weight'],
'#description' => t('In listings, the heavier states will sink and the lighter states will be positioned nearer the top.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
function workflow_state_add_form_validate($form_id, $form_values) {
$state_name = $form_values['state'];
$wf_states = array_flip(workflow_get_states($form_values['wid']));
if (array_key_exists('sid', $form_values)) {
if ($state_name == '') {
form_set_error('state', t('Please provide a nonblank name for this state.'));
}
if (array_key_exists($state_name, $wf_states) && $form_values['sid'] != $wf_states[$state_name]) {
form_set_error('state', t('A state with the name %state already exists in this workflow. ' . 'Please enter another name for this state.', array(
'%state' => $state_name,
)));
}
}
else {
if ($state_name == '') {
form_set_error('state', t('Please provide a nonblank name for the new state.'));
}
if (array_key_exists($state_name, $wf_states)) {
form_set_error('state', t('A state with the name %state already exists in this workflow. ' . 'Please enter another name for your new state.', array(
'%state' => $state_name,
)));
}
}
}
function workflow_state_add_form_submit($form_id, $form_values) {
workflow_state_save($form_values);
if (array_key_exists('sid', $form_values)) {
drupal_set_message(t('The workflow state was updated.'));
}
else {
watchdog('workflow', t('Created workflow state %name', array(
'%name' => $form_values['state'],
)));
drupal_set_message(t('The workflow state %name was created.', array(
'%name' => $form_values['state'],
)));
}
return 'admin/build/workflow';
}
function workflow_update_transitions($transitions = array()) {
if (!$transitions) {
return;
}
foreach ($transitions as $from => $to_data) {
foreach ($to_data as $to => $role_data) {
foreach ($role_data as $role => $can_do) {
if ($can_do) {
workflow_transition_add_role($from, $to, $role);
}
else {
workflow_transition_delete_role($from, $to, $role);
}
}
}
}
db_query("DELETE FROM {workflow_transitions} WHERE roles=''");
}
function workflow_transition_add_role($from, $to, $role) {
$tid = workflow_get_transition_id($from, $to);
if ($tid) {
$roles = db_result(db_query("SELECT roles FROM {workflow_transitions} WHERE tid=%d", $tid));
$roles = explode(',', $roles);
if (array_search($role, $roles) === FALSE) {
$roles[] = $role;
db_query("UPDATE {workflow_transitions} SET roles='%s' WHERE tid=%d", implode(',', $roles), $tid);
}
}
else {
db_query("INSERT INTO {workflow_transitions} (tid, sid, target_sid, roles) VALUES (%d, %d, %d, '%s')", db_next_id('{workflow_transitions}_tid'), $from, $to, $role);
}
}
function workflow_transition_delete_role($from, $to, $role) {
$tid = workflow_get_transition_id($from, $to);
if ($tid) {
$roles = db_result(db_query("SELECT roles FROM {workflow_transitions} WHERE tid=%d", $tid));
$roles = explode(',', $roles);
if (($i = array_search($role, $roles)) !== FALSE) {
unset($roles[$i]);
db_query("UPDATE {workflow_transitions} SET roles='%s' WHERE tid=%d", implode(',', $roles), $tid);
}
}
}
function workflow_transition_grid_form($wid) {
$form = array();
$roles = workflow_get_roles();
$states = workflow_get_states($wid);
if (!$states) {
$form = array(
'#type' => 'markup',
'#value' => t('There are no states defined for this workflow.'),
);
return $form;
}
foreach ($states as $state_id => $name) {
foreach ($states as $nested_state_id => $nested_name) {
if ($nested_name == t('(creation)')) {
continue;
}
if ($nested_state_id != $state_id) {
$from = $state_id;
$to = $nested_state_id;
foreach ($roles as $rid => $role_name) {
$tid = workflow_get_transition_id($from, $to);
$form[$from][$to][$rid] = array(
'#type' => 'checkbox',
'#title' => $role_name,
'#default_value' => $tid ? workflow_transition_allowed($tid, $rid) : FALSE,
);
}
}
}
}
return $form;
}
function workflow_transition_allowed($tid, $role = null) {
$allowed = db_result(db_query("SELECT roles FROM {workflow_transitions} WHERE tid=%d", $tid));
$allowed = explode(',', $allowed);
if ($role) {
if (!is_array($role)) {
$role = array(
$role,
);
}
return array_intersect($role, $allowed) == TRUE;
}
else {
return $allowed == TRUE;
}
}
function workflow_overview() {
$workflows = workflow_get_all();
$row = array();
foreach ($workflows as $wid => $name) {
$links = array(
'workflow_overview_add_state' => array(
'title' => t('Add state'),
'href' => "admin/build/workflow/state/{$wid}",
),
'workflow_overview_actions' => array(
'title' => t('Actions'),
'href' => "admin/build/trigger/workflow/{$wid}",
),
'workflow_overview_edit' => array(
'title' => t('Edit'),
'href' => "admin/build/workflow/edit/{$wid}",
),
'workflow_overview_delete' => array(
'title' => t('Delete'),
'href' => "admin/build/workflow/delete/{$wid}",
),
);
$links = array_merge($links, module_invoke_all('workflow_operations', 'workflow', $wid));
$states = workflow_get_states($wid);
if (!module_exists('actions')) {
unset($links['workflow_overview_actions']);
}
$row[] = array(
check_plain(t($name)),
theme('links', $links),
);
$subrows = array();
foreach ($states as $sid => $state_name) {
$state_links = array();
if (!workflow_is_system_state(check_plain(t($state_name)))) {
$state_links = array(
'workflow_overview_edit_state' => array(
'title' => t('Edit'),
'href' => "admin/build/workflow/state/{$wid}/{$sid}",
),
'workflow_overview_delete_state' => array(
'title' => t('Delete'),
'href' => "admin/build/workflow/state/delete/{$wid}/{$sid}",
),
);
}
$state_links = array_merge($state_links, module_invoke_all('workflow_operations', 'state', $wid, $sid));
$subrows[] = array(
check_plain(t($state_name)),
theme('links', $state_links),
);
unset($state_links);
}
$subheader_state = array(
'data' => t('State'),
'style' => 'width: 30%',
);
$subheader_operations = array(
'data' => t('Operations'),
'style' => 'width: 70%',
);
$subheader_style = array(
'style' => 'width: 100%; margin: 3px 20px 20px;',
);
$subtable = theme('table', array(
$subheader_state,
$subheader_operations,
), $subrows, $subheader_style);
$row[] = array(
array(
'data' => $subtable,
'colspan' => '2',
),
);
}
if ($row) {
$output = theme('table', array(
t('Workflow'),
t('Operations'),
), $row);
$output .= drupal_get_form('workflow_types_form');
}
else {
$output = '<p>' . t('No workflows have been added. Would you like to <a href="@link">add a workflow</a>?', array(
'@link' => url('admin/build/workflow/add'),
)) . '</p>';
}
return $output;
}
function workflow_is_system_state($state) {
static $states;
if (!isset($states)) {
$states = array(
t('(creation)') => TRUE,
);
}
return isset($states[$state]);
}
function workflow_state_delete_form($wid, $sid) {
$states = workflow_get_states($wid);
$state_name = $states[$sid];
if ($count = db_result(db_query("SELECT COUNT(nid) FROM {workflow_node} WHERE sid = %d", $sid))) {
$key = array_search(t('(creation)'), $states);
unset($states[$key]);
unset($states[$sid]);
$form['new_sid'] = array(
'#type' => 'select',
'#title' => t('State to be assigned to orphaned nodes'),
'#description' => format_plural($count, 'Since you are deleting a workflow state, @count node which is in that state will be orphaned, and must be reassigned to a new state. Please choose the new state.', 'Since you are deleting a workflow state, @count nodes which are in that state will be orphaned, and must be reassigned to a new state. Please choose the new state.'),
'#options' => $states,
);
}
$form['wid'] = array(
'#type' => 'value',
'#value' => $wid,
);
$form['sid'] = array(
'#type' => 'value',
'#value' => $sid,
);
return confirm_form($form, t('Are you sure you want to delete %title (and all its transitions)?', array(
'%title' => $state_name,
)), $_GET['destination'] ? $_GET['destination'] : 'admin/build/workflow', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}
function workflow_state_delete_form_submit($form_id, $form_values) {
$states = workflow_get_states($form_values['wid']);
$state_name = $states[$form_values['sid']];
$new_sid = isset($form_values['new_sid']) ? $form_values['new_sid'] : NULL;
if ($form_values['confirm'] == 1) {
workflow_state_delete($form_values['sid'], $new_sid);
watchdog('workflow', t('Deleted workflow state %name', array(
'%name' => $state_name,
)));
drupal_set_message(t('The workflow state %name was deleted.', array(
'%name' => $state_name,
)));
}
return 'admin/build/workflow';
}
function workflow_types_form() {
$form = array();
$workflows = array(
'<' . t('None') . '>',
) + workflow_get_all();
if (count($workflows) == 0) {
return $form;
}
$type_map = array();
$result = db_query("SELECT wid, type FROM {workflow_type_map}");
while ($data = db_fetch_object($result)) {
$type_map[$data->type] = $data->wid;
}
$form['#theme'] = 'workflow_types_form';
$form['#tree'] = TRUE;
$form['help'] = array(
'#type' => 'item',
'#value' => t('Each content type may have a separate workflow. The form for changing workflow state can be displayed when editing a node, editing a comment for a node, or both.'),
);
foreach (node_get_types('names') as $type => $name) {
$form[$type]['workflow'] = array(
'#type' => 'select',
'#title' => $name,
'#options' => $workflows,
'#default_value' => isset($type_map[$type]) ? $type_map[$type] : 0,
);
$form[$type]['placement'] = array(
'#type' => 'checkboxes',
'#options' => array(
'node' => t('Post'),
'comment' => t('Comment'),
),
'#default_value' => variable_get('workflow_' . $type, array(
'node',
)),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save Workflow Mapping'),
);
return $form;
}
function theme_workflow_types_form($form) {
$header = array(
t('Content Type'),
t('Workflow'),
t('Display Workflow Form for:'),
);
$rows = array();
foreach (node_get_types('names') as $type => $name) {
$name = $form[$type]['workflow']['#title'];
unset($form[$type]['workflow']['#title']);
$rows[] = array(
$name,
drupal_render($form[$type]['workflow']),
drupal_render($form[$type]['placement']),
);
}
$output = drupal_render($form['help']);
$output .= theme('table', $header, $rows);
return $output . drupal_render($form);
}
function workflow_types_form_submit($form_id, $form_values) {
workflow_types_save($form_values);
drupal_set_message(t('The workflow mapping was saved.'));
return 'admin/build/workflow';
}
function workflow_get_name($wid) {
$name = db_result(db_query("SELECT name FROM {workflows} WHERE wid = %d", $wid));
return $name;
}
function workflow_get_workflow_for_type($type) {
static $cache;
if (!isset($cache[$type])) {
$wid = db_result(db_query("SELECT wid FROM {workflow_type_map} WHERE type = '%s'", $type));
$cache[$type] = $wid;
}
else {
$wid = $cache[$type];
}
return $wid > 0 ? $wid : FALSE;
}
function workflow_get_all() {
$workflows = array();
$result = db_query("SELECT wid, name FROM {workflows} ORDER BY name ASC");
while ($data = db_fetch_object($result)) {
$workflows[$data->wid] = check_plain(t($data->name));
}
return $workflows;
}
function workflow_create($name) {
$wid = db_next_id('{workflows}_wid');
db_query("INSERT INTO {workflows} (wid, name) VALUES (%d, '%s')", $wid, $name);
workflow_state_save(array(
'wid' => $wid,
'state' => t('(creation)'),
'sysid' => WORKFLOW_CREATION,
'weight' => WORKFLOW_CREATION_DEFAULT_WEIGHT,
));
cache_clear_all('*', 'cache_menu', TRUE);
menu_rebuild();
return $wid;
}
function workflow_update($wid, $name, $tab_roles) {
db_query("UPDATE {workflows} SET name = '%s', tab_roles = '%s' WHERE wid = %d", $name, implode(',', $tab_roles), $wid);
cache_clear_all('*', 'cache_menu', TRUE);
menu_rebuild();
}
function workflow_deletewf($wid) {
$wf = workflow_get_name($wid);
$result = db_query('SELECT sid FROM {workflow_states} WHERE wid = %d', $wid);
while ($data = db_fetch_object($result)) {
workflow_state_delete($data->sid);
db_query('DELETE FROM {workflow_node} WHERE sid = %d', $data->sid);
}
workflow_types_delete($wid);
db_query('DELETE FROM {workflows} WHERE wid = %d', $wid);
cache_clear_all('*', 'cache_menu', TRUE);
menu_rebuild();
}
function workflow_get_states($wid = NULL) {
$states = array();
if (isset($wid)) {
$result = db_query("SELECT sid, state FROM {workflow_states} WHERE wid = %d AND status = 1 ORDER BY weight, sid", $wid);
}
else {
$result = db_query("SELECT sid, state FROM {workflow_states} WHERE status = 1 ORDER BY sid");
}
while ($data = db_fetch_object($result)) {
$states[$data->sid] = check_plain(t($data->state));
}
return $states;
}
function workflow_get_state($sid) {
$state = array();
$result = db_query('SELECT wid, state, weight, sysid, status FROM {workflow_states} WHERE sid = %d', $sid);
while ($data = db_fetch_object($result)) {
$state['wid'] = $data->wid;
$state['state'] = $data->state;
$state['weight'] = $data->weight;
$state['sysid'] = $data->sysid;
$state['status'] = $data->status;
}
return $state;
}
function workflow_state_save($edit) {
$defaults = array(
'weight' => 0,
'sysid' => 0,
);
$edit = array_merge($defaults, $edit);
if (!isset($edit['sid'])) {
$edit['sid'] = db_next_id('{workflow_states}_sid');
db_query("INSERT INTO {workflow_states} (sid, wid, state, sysid, weight) VALUES (%d, %d, '%s', %d, %d)", $edit['sid'], $edit['wid'], $edit['state'], $edit['sysid'], $edit['weight']);
}
else {
db_query("UPDATE {workflow_states} SET wid = %d, state = '%s', sysid = %d, weight = %d WHERE sid = %d", $edit['wid'], $edit['state'], $edit['sysid'], $edit['weight'], $edit['sid']);
}
return $edit['sid'];
}
function workflow_state_create($wid, $name) {
return workflow_state_save(array(
'wid' => $wid,
'state' => $name,
));
}
function workflow_state_delete($sid, $new_sid = NULL) {
if ($new_sid) {
$node = new stdClass();
$node->workflow_stamp = time();
$result = db_query("SELECT nid FROM {workflow_node} WHERE sid = %d", $sid);
while ($data = db_fetch_object($result)) {
$node->nid = $data->nid;
$node->_workflow = $sid;
_workflow_write_history($node, $new_sid, t('Previous state deleted'));
db_query("UPDATE {workflow_node} SET sid = %d WHERE nid = %d AND sid = %d", $new_sid, $data->nid, $sid);
}
}
else {
db_query('DELETE from {workflow_node} WHERE sid = %d', $sid);
}
$preexisting = array();
$result = db_query("SELECT sid, target_sid FROM {workflow_transitions} WHERE sid = %d OR target_sid = %d", $sid, $sid);
while ($data = db_fetch_object($result)) {
$preexisting[$data->sid][$data->target_sid] = TRUE;
}
foreach ($preexisting as $from => $array) {
foreach (array_keys($array) as $target_id) {
$tid = workflow_get_transition_id($from, $target_id);
workflow_transition_delete($tid);
}
}
db_query("UPDATE {workflow_states} SET status = 0 WHERE sid = %d", $sid);
}
function workflow_transition_delete($tid) {
$aids = workflow_get_actions($tid);
foreach ($aids as $aid => $description) {
workflow_actions_remove($tid, $aid, $description);
}
db_query("DELETE FROM {workflow_transitions} WHERE tid = %d", $tid);
}
function workflow_allowable_transitions($sid, $dir = 'to', $roles = 'ALL') {
$transitions = array();
if ($dir == 'to') {
$field = 'target_sid';
$field_where = 'sid';
}
else {
$field = 'sid';
$field_where = 'target_sid';
}
$result = db_query("(SELECT t.tid, t.%s as state_id, s.state AS state_name, s.weight AS state_weight " . "FROM {workflow_transitions} t " . "INNER JOIN {workflow_states} s " . "ON s.sid = t.%s " . "WHERE t.%s = %d AND s.status = 1 " . "ORDER BY state_weight) " . "UNION " . "(SELECT s.sid as tid, s.sid as state_id, s.state as state_name, s.weight as state_weight " . "FROM {workflow_states} s " . "WHERE s.sid = %d AND s.status = 1) " . "ORDER BY state_weight, state_id", $field, $field, $field_where, $sid, $sid);
while ($t = db_fetch_object($result)) {
if ($roles == 'ALL' || $sid == $t->state_id || workflow_transition_allowed($t->tid, $roles)) {
$transitions[$t->state_id] = check_plain(t($t->state_name));
}
}
return $transitions;
}
function workflow_types_save($form_values) {
db_query("DELETE FROM {workflow_type_map}");
foreach (node_get_types() as $type => $name) {
db_query("INSERT INTO {workflow_type_map} (type, wid) VALUES ('%s', %d)", $type, $form_values[$type]['workflow']);
variable_set('workflow_' . $type, array_keys(array_filter($form_values[$type]['placement'])));
}
}
function workflow_types_delete($wid) {
db_query("DELETE FROM {workflow_type_map} WHERE wid = %d", $wid);
}
function workflow_get_actions($tid) {
$actions = array();
if (!function_exists('actions_do')) {
watchdog('workflow', t('Unable to get actions associated with a transition because the actions module is not enabled.'), WATCHDOG_WARNING);
return $actions;
}
$op = 'workflow-' . $tid;
$actions = _actions_get_hook_actions('workflow', $op);
return $actions;
}
function workflow_get_transition_id($from, $to) {
return db_result(db_query("SELECT tid FROM {workflow_transitions} WHERE sid=%d AND target_sid=%d", $from, $to));
}
function workflow_actions_remove($tid, $aid, $description) {
db_query("DELETE FROM {actions_assignments} WHERE hook = '%s' AND op = '%s' AND aid = '%s'", 'workflow', 'workflow' . $tid, $aid);
watchdog('workflow', t('Action %action has been unassigned.', array(
'%action' => $description,
)));
}
function _workflow_node_to_state($node, $sid, $comment = NULL) {
global $user;
$node->workflow_stamp = time();
if (db_result(db_query("SELECT nid FROM {workflow_node} WHERE nid = %d", $node->nid))) {
db_query("UPDATE {workflow_node} SET sid = %d, uid = %d, stamp = %d WHERE nid = %d", $sid, $user->uid, $node->workflow_stamp, $node->nid);
}
else {
db_query("INSERT INTO {workflow_node} (nid, sid, uid, stamp) VALUES (%d, %d, %d, %d)", $node->nid, $sid, $user->uid, $node->workflow_stamp);
}
_workflow_write_history($node, $sid, $comment);
}
function _workflow_write_history($node, $sid, $comment) {
global $user;
db_query("INSERT INTO {workflow_node_history} (nid, old_sid, sid, uid, comment, stamp) VALUES (%d, %d, %d, %d, '%s', %d)", $node->nid, $node->_workflow, $sid, $user->uid, $comment, $node->workflow_stamp);
}
function workflow_get_roles() {
static $roles = NULL;
if (!$roles) {
$result = db_query('SELECT * FROM {role} ORDER BY name');
$roles = array(
'author' => 'author',
);
while ($data = db_fetch_object($result)) {
$roles[$data->rid] = check_plain($data->name);
}
}
return $roles;
}
function workflow_views_tables() {
$table = array(
'name' => 'workflow_node',
'provider' => 'workflow',
'join' => array(
'left' => array(
'table' => 'node',
'field' => 'nid',
),
'right' => array(
'field' => 'nid',
),
),
'filters' => array(
'sid' => array(
'name' => t('Workflow: state'),
'operator' => 'views_handler_operator_andor',
'list' => 'workflow_handler_filter_sid',
'list-type' => 'list',
'value-type' => 'array',
'help' => t('Include only nodes in the selected workflow states.'),
),
),
);
$tables[$table['name']] = $table;
$table = array(
'name' => 'workflow_states',
'provider' => 'workflow',
'join' => array(
'left' => array(
'table' => 'workflow_node',
'field' => 'sid',
),
'right' => array(
'field' => 'sid',
),
),
"sorts" => array(
'weight' => array(
'name' => t('Workflow: state'),
'field' => array(
'weight',
'state',
),
'help' => t('Order nodes by workflow state.'),
),
),
"fields" => array(
'state' => array(
'name' => t('Workflow: state'),
'sortable' => TRUE,
'help' => t('Display the workflow state of the node.'),
),
),
);
$tables[$table['name']] = $table;
$table = array(
'name' => 'workflow_node_history',
'provider' => 'workflow',
'join' => array(
'left' => array(
'table' => 'workflow_node',
'field' => 'nid',
),
'right' => array(
'field' => 'nid',
),
'extra' => array(
'stamp = workflow_node.stamp' => NULL,
),
),
"fields" => array(
'comment' => array(
'name' => t('Workflow: comment'),
'sortable' => false,
'help' => t('Display the most recent workflow comment of the node.'),
),
),
);
$tables[$table['name']] = $table;
return $tables;
}
function workflow_views_arguments() {
$arguments = array(
'workflow_state' => array(
'name' => t('Workflow: state'),
'handler' => 'workflow_handler_arg_sid',
'help' => t('The work flow argument allows users to filter a view by workflow state.'),
),
);
return $arguments;
}
function workflow_handler_filter_sid() {
$result = db_query("SELECT sid, state FROM {workflow_states} ORDER BY weight, state");
while ($data = db_fetch_object($result)) {
$states[$data->sid] = check_plain(t($data->state));
}
return $states;
}
function workflow_handler_arg_sid($op, &$query, $argtype, $arg = '') {
switch ($op) {
case 'summary':
$query
->add_table('workflow_states', TRUE);
$fieldinfo['field'] = "workflow_states.sid";
$query
->add_field('sid', 'workflow_states');
$query
->add_field('state', 'workflow_states');
$query
->add_where('workflow_node.sid IS NOT NULL');
return $fieldinfo;
break;
case 'filter':
$query
->add_table('workflow_states', TRUE);
if (is_numeric($arg)) {
$query
->add_where("workflow_states.sid = %d", $arg);
}
else {
$query
->add_where("workflow_states.state = '%s'", $arg);
}
break;
case 'link':
return l($query->state, "{$arg}/{$query->sid}");
case 'title':
$state = db_fetch_object(db_query("SELECT state FROM {workflow_states} WHERE sid = %d", $query));
return check_plain(t($state->state));
}
}
function workflow_cron() {
$clear_cache = FALSE;
$nodes = db_query('SELECT * FROM {workflow_scheduled_transition} s WHERE s.scheduled > 0 AND s.scheduled < %d', time());
while ($row = db_fetch_object($nodes)) {
$node = node_load($row->nid);
if ($node->_workflow == $row->old_sid) {
workflow_execute_transition($node, $row->sid, $row->comment, TRUE);
watchdog('content', t('%type: scheduled transition of %title.', array(
'%type' => t($node->type),
'%title' => $node->title,
)), WATCHDOG_NOTICE, l(t('view'), 'node/' . $node->nid));
$clear_cache = TRUE;
}
}
if ($clear_cache) {
cache_clear_all();
}
}
function workflow_token_values($type, $object = NULL) {
$values = array();
switch ($type) {
case 'node':
case 'workflow':
$node = (object) $object;
if ($wid = workflow_get_workflow_for_type($node->type)) {
$values['workflow-name'] = workflow_get_name($wid);
$states = workflow_get_states($wid);
}
else {
break;
}
$result = db_query_range("SELECT h.* FROM {workflow_node_history} h WHERE nid = %d ORDER BY stamp DESC", $node->nid, 0, 1);
if ($row = db_fetch_object($result)) {
$account = user_load(array(
'uid' => $row->uid,
));
$comment = $row->comment;
}
$values['workflow-current-state-name'] = $states[$row->sid];
$values['workflow-old-state-name'] = $states[$row->old_sid];
$values['workflow-current-state-date-iso'] = date('Ymdhis', $row->stamp);
$values['workflow-current-state-date-tstamp'] = $row->stamp;
$values['workflow-current-state-date-formatted'] = date('M d, Y h:i:s', $row->stamp);
$values['workflow-current-state-updating-user-name'] = $account->uid ? check_plain($account->name) : variable_get('anonymous', 'Anonymous');
$values['workflow-current-state-updating-user-uid'] = $account->uid;
$values['workflow-current-state-updating-user-mail'] = $account->uid ? check_plain($account->mail) : '';
$values['workflow-current-state-log-entry'] = filter_xss($row->comment, array(
'a',
'em',
'strong',
));
break;
}
return $values;
}
function workflow_token_list($type = 'all') {
if ($type == 'workflow' || $type == 'node' || $type == 'all') {
$tokens['workflow']['workflow-name'] = 'Name of workflow appied to this node';
$tokens['workflow']['workflow-current-state-name'] = 'Current state of content';
$tokens['workflow']['workflow-old-state-name'] = 'Old state of content';
$tokens['workflow']['workflow-current-state-date-iso'] = 'Date of last state change (ISO)';
$tokens['workflow']['workflow-current-state-date-tstamp'] = 'Date of last state change (timestamp)';
$tokens['workflow']['workflow-current-state-date-formatted'] = 'Date of last state change (formated - M d, Y h:i:s)';
$tokens['workflow']['workflow-current-state-updating-user-name'] = 'Username of last state changer';
$tokens['workflow']['workflow-current-state-updating-user-uid'] = 'uid of last state changer';
$tokens['workflow']['workflow-current-state-updating-user-mail'] = 'email of last state changer';
$tokens['workflow']['workflow-current-state-log-entry'] = 'Last workflow comment log';
$tokens['node'] = $tokens['workflow'];
}
return $tokens;
}
function workflow_hook_info() {
$states = workflow_get_states();
if (!$states) {
return;
}
$trigger_page = substr($_GET['q'], 0, 28) == 'admin/build/trigger/workflow';
if ($trigger_page && ($wid = arg(4))) {
$result = db_query("SELECT tm.type, w.wid, w.name, ws.state, wt.tid, wt.sid, wt.target_sid FROM {workflow_type_map} tm LEFT JOIN {workflows} w ON tm.wid = w.wid LEFT JOIN {workflow_states} ws ON w.wid = ws.wid LEFT JOIN {workflow_transitions} wt ON ws.sid = wt.sid WHERE w.wid = %d AND wt.target_sid IS NOT NULL ORDER BY tm.type, ws.weight", $wid);
}
else {
$result = db_query("SELECT tm.type, w.wid, w.name, ws.state, wt.tid, wt.sid, wt.target_sid FROM {workflow_type_map} tm LEFT JOIN {workflows} w ON tm.wid = w.wid LEFT JOIN {workflow_states} ws ON w.wid = ws.wid LEFT JOIN {workflow_transitions} wt ON ws.sid = wt.sid WHERE wt.target_sid IS NOT NULL ORDER BY tm.type, ws.weight");
}
while ($data = db_fetch_object($result)) {
$pseudohooks['workflow-' . $data->type . '-' . $data->tid] = array(
'runs when' => t('When %type moves from %state to %target_state', array(
'%type' => $data->type,
'%state' => $states[$data->sid],
'%target_state' => $states[$data->target_sid],
)),
);
}
if (isset($pseudohooks)) {
return array(
'workflow' => array(
'workflow' => $pseudohooks,
),
);
}
if ($trigger_page) {
drupal_set_message(t('No transitions have been set up. To enable the assignment of actions, edit the workflow to assign permissions for roles to do transitions. After that is completed, transitions will appear here and you will be able to assign actions to them. The workflow must also be assigned to at least one content type.'));
}
}