Index: includes/actions.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/actions.inc,v
retrieving revision 1.17
diff -u -p -r1.17 actions.inc
--- includes/actions.inc	15 Sep 2008 16:07:47 -0000	1.17
+++ includes/actions.inc	16 Sep 2008 17:48:31 -0000
@@ -78,12 +78,16 @@ function actions_do($action_ids, $object
     foreach ($actions as $action_id => $params) {
       if (is_numeric($action_id)) { // Configurable actions need parameters.
         $function = $params['callback'];
-        $context = array_merge($context, $params);
-        $result[$action_id] = $function($object, $context, $a1, $a2);
+        if (drupal_function_exists($function)) {
+          $context = array_merge($context, $params);
+          $result[$action_id] = $function($object, $context, $a1, $a2);
+        }
       }
       // Singleton action; $action_id is the function name.
       else {
-        $result[$action_id] = $action_id($object, $context, $a1, $a2);
+        if (drupal_function_exists($action_id)) {
+          $result[$action_id] = $action_id($object, $context, $a1, $a2);
+        }
       }
     }
   }
@@ -93,12 +97,16 @@ function actions_do($action_ids, $object
     if (is_numeric($action_ids)) {
       $action = db_fetch_object(db_query("SELECT * FROM {actions} WHERE aid = '%s'", $action_ids));
       $function = $action->callback;
-      $context = array_merge($context, unserialize($action->parameters));
-      $result[$action_ids] = $function($object, $context, $a1, $a2);
+      if (drupal_function_exists($function)) {
+        $context = array_merge($context, unserialize($action->parameters));
+        $result[$action_ids] = $function($object, $context, $a1, $a2);
+      }
     }
     // Singleton action; $action_ids is the function name.
     else {
-      $result[$action_ids] = $action_ids($object, $context, $a1, $a2);
+      if (drupal_function_exists($action_ids)) {
+        $result[$action_ids] = $action_ids($object, $context, $a1, $a2);
+      }
     }
   }
   return $result;
@@ -235,8 +243,8 @@ function actions_function_lookup($hash) 
     }
   }
 
-  // Must be an instance; must check database.
-  $aid = db_result(db_query("SELECT aid FROM {actions} WHERE MD5(aid) = '%s' AND parameters <> ''", $hash));
+  // Must be an instance or an orphaned action; must check database.
+  $aid = db_result(db_query("SELECT aid FROM {actions} WHERE MD5(aid) = '%s'", $hash));
   return $aid;
 }
 
@@ -247,11 +255,18 @@ function actions_function_lookup($hash) 
  * This is necessary so that actions that do not require configuration can
  * receive action IDs. This is not necessarily the best approach,
  * but it is the most straightforward.
+ *
+ * @param $delete_orphans
+ *   If TRUE, any actions that exist in the database but are no longer found
+ *   in the code (for example, because the module that provides them has
+ *   been disabled) will be deleted.
+ *
+ * @return
+ *   If $delete_orphans is FALSE, array of orphaned action callbacks.
+ *   If $delete_orphans is TRUE, an array of actions that were deleted.
  */
-function actions_synchronize($actions_in_code = array(), $delete_orphans = FALSE) {
-  if (!$actions_in_code) {
-    $actions_in_code = actions_list(TRUE);
-  }
+function actions_synchronize($delete_orphans = FALSE) {
+  $actions_in_code = actions_list(TRUE);
   $actions_in_db = array();
   $result = db_query("SELECT * FROM {actions} WHERE parameters = ''");
   while ($action = db_fetch_object($result)) {
@@ -288,17 +303,21 @@ function actions_synchronize($actions_in
     $orphans = implode(', ', $orphaned);
 
     if ($delete_orphans) {
+      $deleted = array();
       $placeholders = implode(', ', $placeholder);
       $results = db_query("SELECT a.aid, a.description FROM {actions} a WHERE callback IN ($placeholders)", $orphaned);
       while ($action = db_fetch_object($results)) {
         actions_delete($action->aid);
-        watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => filter_xss_admin($action->description)));
+        $deleted[] = $action;
+        watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => check_plain($action->description)));
       }
+      return $deleted;
     }
     else {
-      $link = l(t('Remove orphaned actions'), 'admin/build/actions/orphan');
+      $link = l(t('Remove orphaned actions'), 'admin/settings/actions/orphan');
       $count = count($actions_in_db);
       watchdog('actions', format_plural($count, 'One orphaned action (%orphans) exists in the actions table. !link', '@count orphaned actions (%orphans) exist in the actions table. !link'), array('@count' => $count, '%orphans' => $orphans, '!link' => $link), WATCHDOG_WARNING);
+      return $orphaned;
     }
   }
 }
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.619
diff -u -p -r1.619 system.module
--- modules/system/system.module	15 Sep 2008 08:49:40 -0000	1.619
+++ modules/system/system.module	16 Sep 2008 17:48:33 -0000
@@ -1483,8 +1483,16 @@ function system_action_info() {
  */
 function system_actions_manage() {
   $output = '';
+
+  // Warn user of any orphaned actions.
+  $orphans = actions_synchronize();
+  $count = count($orphans);
+  $link = l(t('Remove orphaned actions'), 'admin/settings/actions/orphan');
+  if ($orphans) {
+    drupal_set_message(t(format_plural($count, 'One orphaned action (%orphans) exists in the actions table. !link', '@count orphaned actions (%orphans) exist in the actions table. !link'), array('@count' => $count, '%orphans' => implode(', ', $orphans), '!link' => $link)), 'error', FALSE);
+  }
+
   $actions = actions_list();
-  actions_synchronize($actions);
   $actions_map = actions_actions_map($actions);
   $options = array(t('Choose an advanced action'));
   $unconfigurable = array();
@@ -1717,28 +1725,21 @@ function system_actions_delete_form_subm
   $action = actions_load($aid);
   actions_delete($aid);
   $description = check_plain($action->description);
+  drupal_set_message(t('Action %action was deleted.', array('%action' => $description)));
   watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $description));
-  drupal_set_message(t('Action %action was deleted', array('%action' => $description)));
   $form_state['redirect'] = 'admin/settings/actions/manage';
 }
 
 /**
- * Post-deletion operations for deleting action orphans.
- *
- * @param $orphaned
- *   An array of orphaned actions.
- */
-function system_action_delete_orphans_post($orphaned) {
-  foreach ($orphaned as $callback) {
-    drupal_set_message(t("Deleted orphaned action (%action).", array('%action' => $callback)));
-  }
-}
-
-/**
  * Remove actions that are in the database but not supported by any enabled module.
  */
 function system_actions_remove_orphans() {
-  actions_synchronize(actions_list(), TRUE);
+  if ($deleted = actions_synchronize(TRUE)) {
+    foreach ($deleted as $action) {
+      $description = check_plain($action->description);
+      drupal_set_message(t("Orphaned action %action was deleted.", array('%action' => $description)));
+    }
+  }
   drupal_goto('admin/settings/actions/manage');
 }
 
Index: modules/trigger/trigger.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/trigger.admin.inc,v
retrieving revision 1.7
diff -u -p -r1.7 trigger.admin.inc
--- modules/trigger/trigger.admin.inc	16 Jul 2008 21:59:28 -0000	1.7
+++ modules/trigger/trigger.admin.inc	16 Sep 2008 17:48:33 -0000
@@ -24,6 +24,14 @@ function trigger_assign($type = NULL) {
     $type = 'nodeapi';
   }
 
+  // Warn user of any orphaned actions.
+  $orphans = actions_synchronize();
+  $count = count($orphans);
+  $link = l(t('Remove orphaned actions'), 'admin/settings/actions/orphan');
+  if ($orphans) {
+    drupal_set_message(t(format_plural($count, 'One orphaned action (%orphans) exists in the actions table. !link', '@count orphaned actions (%orphans) exist in the actions table. !link'), array('@count' => $count, '%orphans' => implode(', ', $orphans), '!link' => $link)), 'error', FALSE);
+  }
+
   $output = '';
   $hooks = module_invoke_all('hook_info');
   foreach ($hooks as $module => $hook) {
Index: modules/trigger/trigger.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/trigger.install,v
retrieving revision 1.5
diff -u -p -r1.5 trigger.install
--- modules/trigger/trigger.install	28 Dec 2007 12:02:52 -0000	1.5
+++ modules/trigger/trigger.install	16 Sep 2008 17:48:33 -0000
@@ -9,7 +9,7 @@ function trigger_install() {
   drupal_install_schema('trigger');
 
   // Do initial synchronization of actions in code and the database.
-  actions_synchronize(actions_list());
+  actions_synchronize();
 }
 
 /**
