Only in ./workflow/: .svn
Only in /Users/mark/drupal/branches/drupal-4-7/contributions/modules/workflow/: CVS
Only in ./workflow/: LICENSE.txt
diff -u -p /Users/mark/drupal/branches/drupal-4-7/contributions/modules/workflow/README.txt ./workflow/README.txt
--- /Users/mark/drupal/branches/drupal-4-7/contributions/modules/workflow/README.txt	2006-03-09 22:05:06.000000000 -0600
+++ ./workflow/README.txt	2006-10-31 14:22:13.000000000 -0600
@@ -1,109 +1,109 @@
-********************************************************************
-                     D R U P A L    M O D U L E
-********************************************************************
-Name: Workflow Module
-Author: John VanDyk <jvandyk at iastate dot edu>
-Drupal: 4.7
-********************************************************************
-DESCRIPTION:
-
-The workflow module enables you to create arbitrary workflows in 
-Drupal and associate them with node types.
-
-Workflows are made up of workflow states.
-
-Moving from one state to another is called a transition.
-
-Actions are associated with transitions (actions.module must be
-installed for this).
-
-Alex Reisner introduced role-based permissions for workflow states
-and generally enhanced this module.
-
-********************************************************************
-INSTALLATION:
-
-1. Place the entire workflow directory into your Drupal modules/
-   directory.
-
-
-2. Enable the workflow module by navigating to:
-
-     administer > modules
-
-   Enabling the workflow module will create the necessary database 
-   tables for you.
-
-3. If you want anyone besides the administrative user to be able
-   to configure workflows (usually a bad idea), they must be given
-   the "administer workflow" access permission:
-   
-     administer > users > configure > permissions
-
-   When the module is enabled and the user has the "administer
-   workflow" permission, a "workflow" menu should appear in the 
-   menu system.
-
-********************************************************************
-GETTING STARTED:
-
-Let's create a new workflow. Click on administer -> workflow and click
-on the "add workflow" tab.
-
-We'll start simple. Call our workflow "Draft-Done" and click Add Workflow.
-
-Now lets add some workflow states to our workflow. Click "add state" and
-enter "draft" and click the Add State button. Do the same for "done".
-
-So we've got a workflow with two states, "draft" and "done". Now we
-have to tell each state which other states it can move to. With only
-two states, this is easy. Click on the "edit" link to edit the workflow
-and see its states.
-
-The "From / To -->" column lists all states. To the right are columns
-for each state. Within each cell is a list of roles with checkboxes.
-
-This is confusing. It's easiest to understand if you read rows
-across from the left. For example, we start with the creation
-state. Who may move a node from its creation state to the "draft"
-state? Well, the author of the node, for one. So check the "author"
-checkbox.
-
-Who may move the node from the "draft" state to the "done" state?
-This is up to you. If you want authors to be able to do this,
-check the "author" checkbox under the "done" state. If you had
-another role, say "editor", that you wanted to give the ability
-to decree a node as "done", you'd check the checkbox next to
-the "editor" role and not the author role. In this scenario authors
-would turn in drafts and editors would say when they are "done".
-
-Be sure to click the Save button to save your settings.
-
-Now let's tell Drupal which node types should use this workflow. Click
-on admin -> workflow. Let's assign the Draft-Done workflow
-to the story node type and click Save Workflow Mapping.
-
-Now we could add an action (previously configured using the actions
-module). If you have not configured your action yet, here's a quick
-guide:
-
-- go to administer -> actions
-- choose an action and click "Add new action"
-- configure the action and click Save. Note that the description field
-  here will be used later on
-
-OK, back in the workflow module, click on the actions link above
-your workflow. Add the action to the transition.
-
-Now create a new story by going to create content -> story. Note that
-there is no sign of workflow here because the story is in its
-initial state. Click submit to create the story.
-
-Now click the edit tab. Note that there is a select box for workflow
-with the "draft" state chosen.
-
-Changing the state to "done" and clicking Submit will fire the action
-you set up earlier.
-
-
+********************************************************************
+                     D R U P A L    M O D U L E
+********************************************************************
+Name: Workflow Module
+Author: John VanDyk <jvandyk at iastate dot edu>
+Drupal: 4.7
+********************************************************************
+DESCRIPTION:
+
+The workflow module enables you to create arbitrary workflows in 
+Drupal and associate them with node types.
+
+Workflows are made up of workflow states.
+
+Moving from one state to another is called a transition.
+
+Actions are associated with transitions (actions.module must be
+installed for this).
+
+Alex Reisner introduced role-based permissions for workflow states
+and generally enhanced this module.
+
+********************************************************************
+INSTALLATION:
+
+1. Place the entire workflow directory into your Drupal modules/
+   directory.
+
+
+2. Enable the workflow module by navigating to:
+
+     administer > modules
+
+   Enabling the workflow module will create the necessary database 
+   tables for you.
+
+3. If you want anyone besides the administrative user to be able
+   to configure workflows (usually a bad idea), they must be given
+   the "administer workflow" access permission:
+   
+     administer > users > configure > permissions
+
+   When the module is enabled and the user has the "administer
+   workflow" permission, a "workflow" menu should appear in the 
+   menu system.
+
+********************************************************************
+GETTING STARTED:
+
+Let's create a new workflow. Click on administer -> workflow and click
+on the "add workflow" tab.
+
+We'll start simple. Call our workflow "Draft-Done" and click Add Workflow.
+
+Now lets add some workflow states to our workflow. Click "add state" and
+enter "draft" and click the Add State button. Do the same for "done".
+
+So we've got a workflow with two states, "draft" and "done". Now we
+have to tell each state which other states it can move to. With only
+two states, this is easy. Click on the "edit" link to edit the workflow
+and see its states.
+
+The "From / To -->" column lists all states. To the right are columns
+for each state. Within each cell is a list of roles with checkboxes.
+
+This is confusing. It's easiest to understand if you read rows
+across from the left. For example, we start with the creation
+state. Who may move a node from its creation state to the "draft"
+state? Well, the author of the node, for one. So check the "author"
+checkbox.
+
+Who may move the node from the "draft" state to the "done" state?
+This is up to you. If you want authors to be able to do this,
+check the "author" checkbox under the "done" state. If you had
+another role, say "editor", that you wanted to give the ability
+to decree a node as "done", you'd check the checkbox next to
+the "editor" role and not the author role. In this scenario authors
+would turn in drafts and editors would say when they are "done".
+
+Be sure to click the Save button to save your settings.
+
+Now let's tell Drupal which node types should use this workflow. Click
+on admin -> workflow. Let's assign the Draft-Done workflow
+to the story node type and click Save Workflow Mapping.
+
+Now we could add an action (previously configured using the actions
+module). If you have not configured your action yet, here's a quick
+guide:
+
+- go to administer -> actions
+- choose an action and click "Add new action"
+- configure the action and click Save. Note that the description field
+  here will be used later on
+
+OK, back in the workflow module, click on the actions link above
+your workflow. Add the action to the transition.
+
+Now create a new story by going to create content -> story. Note that
+there is no sign of workflow here because the story is in its
+initial state. Click submit to create the story.
+
+Now click the edit tab. Note that there is a select box for workflow
+with the "draft" state chosen.
+
+Changing the state to "done" and clicking Submit will fire the action
+you set up earlier.
+
+
 ********************************************************************
\ No newline at end of file
Common subdirectories: /Users/mark/drupal/branches/drupal-4-7/contributions/modules/workflow/po and ./workflow/po
diff -u -p /Users/mark/drupal/branches/drupal-4-7/contributions/modules/workflow/workflow.install ./workflow/workflow.install
--- /Users/mark/drupal/branches/drupal-4-7/contributions/modules/workflow/workflow.install	2006-09-11 08:54:51.000000000 -0500
+++ ./workflow/workflow.install	2006-10-31 14:22:13.000000000 -0600
@@ -93,6 +93,19 @@ CREATE TABLE {workflow_node_history} (
 ) /*!40100 DEFAULT CHARACTER SET utf8 */;
 QUERY
       );
+      
+        $result[] = db_query(
+<<<QUERY
+CREATE TABLE {workflow_scheduled_transition} (
+  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',
+  scheduled int(10) unsigned NOT NULL default '0',
+  comment longtext,
+  KEY nid (nid,sid)
+) /*!40100 DEFAULT CHARACTER SET utf8 */;
+QUERY
+      );
       break;
 
     case 'pgsql':
@@ -214,6 +227,23 @@ QUERY
 CREATE INDEX {workflow_node_history}_nid_sid_idx ON {workflow_node_history}(nid,sid);
 QUERY
       );
+      
+      $result[] = db_query(
+<<<QUERY
+CREATE TABLE {workflow_scheduled_transition} (
+  nid integer NOT NULL default '0',
+  old_sid integer NOT NULL default '0',
+  sid integer NOT NULL default '0',
+  scheduled integer NOT NULL default '0',
+  comment text
+);
+QUERY
+      );
+      $result[] = db_query(
+<<<QUERY
+CREATE INDEX {workflow_scheduled_transition}_nid_sid_idx ON {workflow_scheduled_transition}(nid,sid);
+QUERY
+      );
       break;
   }            
 
@@ -339,3 +369,47 @@ function _workflow_fix_seq($old_name, $n
   $new_name = db_prefix_tables($new_name);
   return update_sql("UPDATE {sequences} SET name = '" . $new_name . "' WHERE name = '" . $old_name . "'");
 }
+
+// Add scheduling tables
+function workflow_update_5() {
+  $ret = array();
+  
+  switch ($GLOBALS['db_type']) {
+    case 'mysqli':
+    case 'mysql':
+        $ret[] = db_query(
+<<<QUERY
+CREATE TABLE {workflow_scheduled_transition} (
+  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',
+  scheduled int(10) unsigned NOT NULL default '0',
+  comment longtext,
+  KEY nid (nid,sid)
+) /*!40100 DEFAULT CHARACTER SET utf8 */;
+QUERY
+      );
+      break;
+    case 'pgsql':
+      $ret[] = db_query(
+<<<QUERY
+CREATE TABLE {workflow_scheduled_transition} (
+  nid integer NOT NULL default '0',
+  old_sid integer NOT NULL default '0',
+  sid integer NOT NULL default '0',
+  scheduled integer NOT NULL default '0',
+  comment text
+);
+QUERY
+      );
+      $ret [] = db_query(
+<<<QUERY
+CREATE INDEX {workflow_scheduled_transition}_nid_sid_idx ON {workflow_scheduled_transition}(nid,sid);
+QUERY
+      );
+
+     break;
+  }
+  
+  return $ret;
+}
diff -u -p /Users/mark/drupal/branches/drupal-4-7/contributions/modules/workflow/workflow.module ./workflow/workflow.module
--- /Users/mark/drupal/branches/drupal-4-7/contributions/modules/workflow/workflow.module	2006-07-27 16:08:13.000000000 -0500
+++ ./workflow/workflow.module	2006-11-08 13:20:31.000000000 -0600
@@ -128,9 +128,20 @@ function workflow_tab_page($nid) {
     ksort($choices);
     $wid = workflow_get_workflow_for_type($node->type);
     $name = check_plain(workflow_get_name($wid));
-
+    // see if scheduling information is present
+    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; // the default value should be the upcoming sid
+      $timestamp = $node->_workflow_scheduled_timestamp;
+    }
     $form = array();
-    workflow_node_form($form, t('Change %s state', array('%s' => $name)), $name, $current, $choices);
+    workflow_node_form($form, t('Change %s state', array('%s' => $name)), $name, $current, $choices, $timestamp);
     $form['node'] = array(
       '#type' => 'value',
       '#value' => $node,
@@ -163,15 +174,21 @@ function workflow_tab_page($nid) {
 
 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
-  }
+  
+  // mockup a node so we don't need to repeat the code for processing this
+  $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'];
+  
+  // call node save to make sure all saving properties run on this node
+  node_save($node);
+  
   return 'node/' . $node->nid;
 }
 
+
 /**
  * Implementation of hook_nodeapi().
  * Summary of nodeapi ops we can see (Drupal 4.7):
@@ -193,10 +210,19 @@ function workflow_nodeapi(&$node, $op, $
 
   case 'load':
     $node->_workflow = workflow_node_current_state($node);
-      break;
+    
+    // scheduling information
+    $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;
+    }
+    
+    break;
 
     case 'insert':
     case 'update':
+
       // stop if no workflow for this node type
       $wid = workflow_get_workflow_for_type($node->type);
       if (!$wid) {
@@ -213,7 +239,43 @@ 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, $node->workflow_comment); // do transition
+        // check to see if this is an immediate change or a scheduled change
+        if (!$node->workflow_scheduled) {
+          workflow_execute_transition($node, $sid, $node->workflow_comment); // do transition
+        }
+        else { // schedule the the time to change the state
+          $nid = $node->nid;
+          $comment = $node->workflow_comment;
+          $old_sid = workflow_node_current_state($node);
+          // TODO use the user's TZ
+          if ($node->workflow_scheduled_date['day'] < 10) {
+            $node->workflow_scheduled_date['day'] = '0' . 
+              $node->workflow_scheduled_date['day'];
+          }
+          $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)) {
+
+            // adjust for user and site timezone settings
+            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;
+            
+            // clear previous entries and insert
+            db_query("DELETE FROM {workflow_scheduled_transition} WHERE nid = $nid");
+            db_query('INSERT INTO {workflow_scheduled_transition} VALUES (%d, %d, %d, %d,\'%s\')', $nid, $old_sid, $sid, $scheduled, $comment);
+            
+            drupal_set_message("$node->title is scheduled for state change on " . format_date($scheduled));
+          }
+        }
       }
       break;
 
@@ -231,7 +293,7 @@ function workflow_nodeapi(&$node, $op, $
  * @param string $current
  * @param array $choices
  */
-function workflow_node_form(&$form, $title, $name, $current, $choices) {
+function workflow_node_form(&$form, $title, $name, $current, $choices, $timestamp = NULL) {
   if (sizeof($choices) == 1) {
     $form['workflow'][$name] = array(
       '#type' => 'hidden',
@@ -239,6 +301,7 @@ function workflow_node_form(&$form, $tit
     );
   }
   else {
+    
     $form['workflow'][$name] = array(
       '#type' => 'radios',
       '#title' => $title,
@@ -247,6 +310,34 @@ function workflow_node_form(&$form, $tit
       '#parents' => array('workflow'),
       '#default_value' => $current
     );
+    
+    if (!workflow_is_system_state($current)) {
+      $scheduled = $timestamp ? 1 : 0;
+      $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' => $scheduled ? array('day' => format_date($timestamp, 'custom', 'j'),
+                              'month' => format_date($timestamp, 'custom', 'n'),
+                              'year' => format_date($timestamp, 'custom', 'Y')) : NULL,
+      );
+  
+      $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 date is included, the default will be midnight on the specified date.'),
+        '#default_value' => $scheduled ? $hours : NULL,
+      );
+    }
+    
     $form['workflow']['workflow_comment'] = array(
       '#type' => 'textarea',
       '#title' => t('Comment'),
@@ -291,7 +382,20 @@ function workflow_form_alter($form_id, &
         '#collapsed' => FALSE,
       );
     }
-    workflow_node_form($form, $name, $name, $current, $choices);
+    
+    // see if scheduling information is present
+    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; // the default value should be the upcoming sid
+      $timestamp = $node->_workflow_scheduled_timestamp;
+    }
+    workflow_node_form($form, $name, $name, $current, $choices, $timestamp);
   }
 }
 
@@ -345,6 +449,9 @@ function workflow_execute_transition($no
   // Notify modules that transition has occurred. Actions should take place
   // in response to this callback, not the previous one.
   module_invoke_all('workflow', 'transition post', $old_sid, $sid, $node);
+  
+  // clear any references in the scheduled listing
+  db_query('DELETE FROM {workflow_scheduled_transition} WHERE nid = %d', $node->nid);
 }
 
 /**
@@ -1595,4 +1702,33 @@ 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
+}
+
+/**
+ * Implementation of hook_cron
+ */
+function workflow_cron() {
+  $clear_cache = FALSE;
+  
+  //if the time now is greater than the time to publish a node, publish it
+  $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);
+    
+    // make sure transition is still valid
+    if ($node->_workflow == $row->old_sid) {
+      // do transistion 
+      workflow_execute_transition($node, $row->sid, $row->comment); 
+      
+      watchdog('content', t('%type: scheduled transition of %title.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title))), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
+      $clear_cache = TRUE;
+      
+    }
+  }
+  
+  if ($clear_cache) {
+    // clear the cache so an anonymous poster can see the node being published or unpublished
+    cache_clear_all();
+  }
+}
Only in ./workflow/: workflow.pgsql
