diff -ruP workflow.old/workflow.admin.inc workflow/workflow.admin.inc
--- workflow.old/workflow.admin.inc	2010-03-02 10:32:54.000000000 -0800
+++ workflow/workflow.admin.inc	2010-03-16 12:54:40.000000000 -0700
@@ -264,6 +264,9 @@
           foreach ($roles as $rid => $role_name) {
             $cell .= drupal_render($form['transitions'][$from][$to][$rid]);
           }
+          if (isset($form['transitions'][$from][$to]['module'])) {
+            $cell .= drupal_render($form['transitions'][$from][$to]['module']);
+          }
           $row[] = array('data' => $cell);
         }
         else {
@@ -472,6 +475,19 @@
       }
     }
   }
+  // Let modules declare that they implement dynamic transitions.
+  foreach (module_implements('workflow_transitions_info') as $module) {
+    $transitions = module_invoke($module, 'workflow_transitions_info');
+    foreach ($transitions as $src_sid => $dst) {
+      foreach ($dst as $dst_sid => $dst_description) {
+        $form[$src_sid][$dst_sid]['module'][] = array(
+          '#value' => t('!module: %description', array('!module' => $module, '%description' => $dst_description)),
+          '#prefix' => '<div class="form-item">',
+          '#suffix' => '</div>',
+        );
+      }
+    }
+  }
   return $form;
 }
 
@@ -697,4 +713,4 @@
   drupal_set_message(t('The workflow mapping was saved.'));
   menu_rebuild();
   $form_state['redirect'] = 'admin/build/workflow';
-}
\ No newline at end of file
+}
diff -ruP workflow.old/workflow.module workflow/workflow.module
--- workflow.old/workflow.module	2010-03-03 10:17:02.000000000 -0800
+++ workflow/workflow.module	2010-03-16 00:07:29.000000000 -0700
@@ -516,19 +516,28 @@
     return;
   }
 
-  $tid = workflow_get_transition_id($old_sid, $sid);
-  if (!$tid && !$force) {
-      watchdog('workflow', 'Attempt to go to nonexistent transition (from %old to %new)', array('%old' => $old_sid, '%new' => $sid, WATCHDOG_ERROR));
-      return;
-  }
-  // Make sure this transition is valid and allowed for the current user.
-  // Check allowability of state change if user is not superuser (might be cron).
-  if (($user->uid != 1) && !$force) {
-    if (!workflow_transition_allowed($tid, array_merge(array_keys($user->roles), array('author')))) {
-      watchdog('workflow', 'User %user not allowed to go from state %old to %new', array('%user' => $user->name, '%old' => $old_sid, '%new' => $sid, WATCHDOG_NOTICE));
-      return;
+  // Check if any module hands us a programmatic transition.
+  $transitions = array();
+  drupal_alter('workflow_transitions', $transitions, $old_sid, $user, $node);
+  if (!empty($transitions) && in_array($sid, array_keys($transitions))) { // Found a programmatic transition
+    // Do nothing.
+  }
+  else { // Check database transitions
+    $tid = workflow_get_transition_id($old_sid, $sid);
+    if (!$tid && !$force) {
+        watchdog('workflow', 'Attempt to go to nonexistent transition (from %old to %new)', array('%old' => $old_sid, '%new' => $sid, WATCHDOG_ERROR));
+        return;
+    }
+    // Make sure this transition is valid and allowed for the current user.
+    // Check allowability of state change if user is not superuser (might be cron).
+    if (($user->uid != 1) && !$force) {
+      if (!workflow_transition_allowed($tid, array_merge(array_keys($user->roles), array('author')))) {
+        watchdog('workflow', 'User %user not allowed to go from state %old to %new', array('%user' => $user->name, '%old' => $old_sid, '%new' => $sid, WATCHDOG_NOTICE));
+        return;
+      }
     }
   }
+
   // Invoke a callback indicating a transition is about to occur. Modules
   // may veto the transition by returning FALSE.
   $result = module_invoke_all('workflow', 'transition pre', $old_sid, $sid, $node);
@@ -712,7 +721,7 @@
     // Superuser is special.
     $roles = 'ALL';
   }
-  $transitions = workflow_allowable_transitions($current_sid, 'to', $roles);
+  $transitions = workflow_allowable_transitions($current_sid, 'to', $roles, $node);
 
   // Include current state if it is not the (creation) state.
   if ($current_sid == _workflow_creation_state($wid)) {
@@ -1250,7 +1259,7 @@
  * @return
  *   Associative array of states ($sid => $state_name pairs), excluding current state.
  */
-function workflow_allowable_transitions($sid, $dir = 'to', $roles = 'ALL') {
+function workflow_allowable_transitions($sid, $dir = 'to', $roles = 'ALL', $node = NULL) {
   $transitions = array();
 
   if ($dir == 'to') {
@@ -1282,6 +1291,8 @@
     }
   }
 
+  global $user;
+  drupal_alter('workflow_transitions', $transitions, $sid, $user, $node);
   return $transitions;
 }
 
