? workflow_history.patch
Index: workflow.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/workflow/workflow.install,v
retrieving revision 1.2
diff -u -p -r1.2 workflow.install
--- workflow.install	11 Mar 2006 05:37:40 -0000	1.2
+++ workflow.install	8 Apr 2006 21:41:00 -0000
@@ -39,6 +39,7 @@ QUERY
 CREATE TABLE IF NOT EXISTS {workflows} (
   wid int(10) unsigned NOT NULL default '0',
   name varchar(255) NOT NULL default '',
+  tab_roles varchar(60) NOT NULL default '',
   PRIMARY KEY  (wid)
 ) /*!40100 DEFAULT CHARACTER SET utf8 */;
 QUERY
@@ -81,9 +82,11 @@ QUERY
 <<<QUERY
 CREATE TABLE IF NOT EXISTS {workflow_node_history} (
   nid int(10) unsigned NOT NULL default '0',
+  old_sid int(10) unsigned NOT NULL default '0',
   sid int(10) unsigned NOT NULL default '0',
   uid int(10) unsigned NOT NULL default '0',
   stamp int(11) unsigned NOT NULL default '0',
+  comment longtext,
   KEY nid (nid,sid)
 ) /*!40100 DEFAULT CHARACTER SET utf8 */;
 QUERY
@@ -135,4 +138,14 @@ FROM {workflow_node}
   $ret[] = update_sql("ALTER TABLE {workflow_node} ADD PRIMARY KEY (nid)");
   
   return $ret;
-}
\ No newline at end of file
+}
+
+function workflow_update_2() {
+  $ret = array();
+
+  $ret[] = update_sql("ALTER TABLE {workflow_node_history} ADD old_sid int(10) unsigned AFTER nid");
+  $ret[] = update_sql("ALTER TABLE {workflow_node_history} ADD comment longtext");
+  $ret[] = update_sql("ALTER TABLE {workflows} ADD tab_roles varchar(60)");
+
+  return $ret;
+}
Index: workflow.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/workflow/workflow.module,v
retrieving revision 1.37
diff -u -p -r1.37 workflow.module
--- workflow.module	13 Mar 2006 22:36:36 -0000	1.37
+++ workflow.module	8 Apr 2006 21:41:02 -0000
@@ -86,9 +86,87 @@ function workflow_menu($may_cache) {
       'type'     => MENU_CALLBACK,
       'callback' => 'workflow_actions_remove_form');
   }
+  else {
+    if (arg(0) == 'node' && is_numeric(arg(1))) {
+      $node = node_load(arg(1));
+      $wid = workflow_get_workflow_for_type($node->type);
+      if ($wid) { // workflow exists for this type
+        global $user;
+        $roles = array_merge(array('author'), array_keys($user->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);
+  $wid = workflow_get_workflow_for_type($node->type);
+  $states_per_page = 20;
+  $states = workflow_get_states($wid) + array(t('(creation)'));
+  $current = workflow_node_current_state($node);
+
+  $output = "<p>Current state: " . check_plain($states[$current]) . "</p>\n";
+
+  $choices = workflow_field_choices($node);
+  $min = $states[$current] == t('(creation)') ? 1 : 2;
+  if (count($choices) >= $min) { // bail out if user has no new target state(s)
+    ksort($choices);
+    $wid = workflow_get_workflow_for_type($node->type);
+    $name = check_plain(workflow_get_name($wid));
+
+    $form = array();
+    workflow_node_form($form, t('Change %s state', array('%s' => $name)), $name, $current, $choices);
+    $form['node'] = array(
+      '#type' => 'value',
+      '#value' => $node,
+    );
+
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Submit')
+    );
+
+    $output .= drupal_get_form('workflow_tab', $form);
+  }
+
+  $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 stamp DESC", $states_per_page, 0, NULL, $nid);
+  $rows = array();
+  while ($history = db_fetch_object($result)) {
+    $rows[] = array(
+      format_date($history->stamp),
+      check_plain($states[$history->old_sid]), 
+      check_plain($states[$history->sid]), 
+      theme('username', $history), 
+      check_plain($history->comment)
+    );
+  }
+  
+  $output .= theme('table', array(t('Date'), t('Old State'), t('New State'), t('By'), t('Comment')), $rows, array('class' => 'workflow_history'), t('Workflow History'));
+  $output .= theme('pager', $states_per_page);
+  return $output;
+}
+
+function workflow_tab_submit($form_id, $form_values) {
+  $node = $form_values['node'];
+  $sid = $form_values['workflow'];
+
+  // make sure new state is a valid choice
+  if (array_key_exists($sid, workflow_field_choices($node))) {
+    workflow_execute_transition($node, $sid, $form_values['workflow_comment']); // do transition
+  }
+}
+
 /**
  * Implementation of hook_nodeapi().
  * TODO: update this table for 4.7 as it is out of date.
@@ -133,7 +211,7 @@ function workflow_nodeapi(&$node, $op, $
 
       // make sure new state is a valid choice
       if (array_key_exists($sid, workflow_field_choices($node))) {
-        workflow_execute_transition($node, $sid); // do transition
+        workflow_execute_transition($node, $sid, $node->workflow_comment); // do transition
       }
       break;
 
@@ -144,6 +222,30 @@ function workflow_nodeapi(&$node, $op, $
 }
 
 /**
+ * Add the actual form widgets for workflow change to the passed in form.
+ *
+ * @param array $form 
+ * @param string $name
+ * @param string $current
+ * @param array $choices
+ */
+function workflow_node_form(&$form, $title, $name, $current, $choices) {
+  $form['workflow'][$name] = array(
+    '#type' => 'radios',
+    '#title' => $title,
+    '#options' => $choices,
+    '#name' => $name,
+    '#parents' => array('workflow'),
+    '#default_value' => $current
+  );
+  $form['workflow']['workflow_comment'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Comment'),
+    '#description' => t('Enter a comment to put in the workflow log; a transition is not required to enter a comment, nor is a comment required to enter a transition.'),
+  );
+}
+
+/**
  * Generate a forms API compliant workflow field.
  *
  * @param object &$node
@@ -175,14 +277,7 @@ function workflow_form_alter($form_id, &
       '#collapsible' => TRUE,
       '#collapsed' => FALSE,
     );
-    $form['workflow'][$name] = array(
-      '#type' => 'radios',
-      '#title' => $name,
-      '#options' => $choices,
-      '#name' => $name,
-      '#parents' => array('workflow'),
-      '#default_value' => $current
-    );
+    workflow_node_form($form, $name, $name, $current, $choices);
   }
 }
 
@@ -193,9 +288,13 @@ function workflow_form_alter($form_id, &
  * @param int $sid
  * @return int ID of new state.
  */
-function workflow_execute_transition($node, $sid) {
+function workflow_execute_transition($node, $sid, $comment = NULL) {
   $old_sid = workflow_node_current_state($node);
   if ($old_sid == $sid) { // stop if not going to a different state
+    // Write comment into history though.
+    if ($comment) {
+      _workflow_write_history($node, $sid, $comment);
+    }
     return;
   }
 
@@ -207,7 +306,7 @@ function workflow_execute_transition($no
       watchdog('workflow', t('Attempt to go to nonexistent transition (from %old to %new)', array('%old' => $old_sid, '%new' => $sid), WATCHDOG_ERROR));
       return;
     }
-    if (!workflow_transition_allowed($tid, array_keys($user->roles))) {
+    if (!workflow_transition_allowed($tid, array_merge(array_keys($user->roles), '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;
     }
@@ -221,7 +320,7 @@ function workflow_execute_transition($no
     return;
   }
 
-  _workflow_node_to_state($node, $sid); // change the state
+  _workflow_node_to_state($node, $sid, $comment); // change the state
 
   // Register state change with watchdog
   $state_name = db_result(db_query("SELECT state FROM {workflow_states} WHERE sid = %d", $sid));
@@ -496,14 +595,29 @@ function workflow_permissions($wid) {
  *
  */
 function workflow_edit($wid) {
-  $name = workflow_get_name($wid);
+//  $name = workflow_get_name($wid);
+  $workflow = db_fetch_object(db_query("SELECT * FROM {workflows} WHERE wid = %d", $wid));
   $form['wf_name'] = array(
     '#type' => 'textfield',
-    '#default_value' => $name,
+    '#default_value' => $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($wid);
   $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
   $form['wid'] = array('#type' => 'value', '#value' => $wid);
@@ -520,11 +634,7 @@ function theme_workflow_edit($form) {
   $wid = $form['wid']['#value'];
   $states = workflow_get_states($wid);
   if ($states) {
-    $result = db_query('SELECT * FROM {role} ORDER BY name');
-    $roles = array('author' => 'author');
-    while ($data = db_fetch_object($result)) {
-      $roles[$data->rid] = $data->name;
-    }
+    $roles = workflow_get_roles();
     $header = array(array('data' => t('From / To ') . '&nbsp;' . WORKFLOW_ARROW));
     $rows = array();
     foreach ($states as $state_id => $name) {
@@ -591,7 +701,7 @@ function workflow_edit_validate($form_id
 }
 
 function workflow_edit_submit($form_id, $form_values) {
-  workflow_update_name($form_values['wid'], $form_values['wf_name']);
+  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('Workflow updated.'));
   drupal_goto('admin/workflow');
@@ -712,11 +822,7 @@ function workflow_transition_delete_role
  */
 function workflow_transition_grid_form($wid) {
 
-  $result = db_query('SELECT * FROM {role} ORDER BY name');
-  $roles = array('author' => 'author');
-  while ($data = db_fetch_object($result)) {
-    $roles[$data->rid] = $data->name;
-  }
+  $roles = workflow_get_roles();
 
   $form = array();
   $states = workflow_get_states($wid);
@@ -1124,8 +1230,8 @@ function workflow_create($name) {
  *   The name of the workflow.
  *
  */
-function workflow_update_name($wid, $name) {
-  db_query("UPDATE {workflows} SET name = '%s' WHERE wid = %d", $name, $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);
 }
 
 /**
@@ -1338,7 +1444,7 @@ function workflow_actions_remove($tid, $
  * @param object $node
  * @param int $sid
  */
-function _workflow_node_to_state($node, $sid) {
+function _workflow_node_to_state($node, $sid, $comment = NULL) {
   global $user;
   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 WHERE nid = %d", $sid, $user->uid, $node->nid);    
@@ -1346,7 +1452,27 @@ function _workflow_node_to_state($node, 
   else {
     db_query("INSERT INTO {workflow_node} (nid, sid, uid) VALUES (%d, %d, %d)", $node->nid, $sid, $user->uid);
   }
-  db_query("INSERT INTO {workflow_node_history} (nid, sid, uid, stamp) VALUES (%d, %d, %d, %d)", $node->nid, $sid, $user->uid, time());
+  _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, time());
+}
+
+/**
+ * Function to get a list of roles. Used kind of often.
+ */
+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] = $data->name;
+    }
+  }
+  return $roles;
 }
 
 /**
@@ -1465,4 +1591,4 @@ function workflow_handler_arg_sid($op, &
       $state = db_fetch_object(db_query("SELECT state FROM {workflow_states} WHERE sid=%d", $query));
       return $state->state;
   }
-}
\ No newline at end of file
+}
