Index: modules/trigger/trigger.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/trigger.admin.inc,v
retrieving revision 1.21
diff -u -p -r1.21 trigger.admin.inc
--- modules/trigger/trigger.admin.inc	6 Nov 2009 03:59:06 -0000	1.21
+++ modules/trigger/trigger.admin.inc	26 Feb 2010 21:59:26 -0000
@@ -153,10 +153,20 @@ function trigger_assign_form($form, $for
   $form[$hook]['assigned']['#type'] = 'value';
   $form[$hook]['assigned']['#value'] = array();
   foreach ($actions as $aid => $info) {
-    $form[$hook]['assigned']['#value'][$aid] = array(
-      'label' => $info['label'],
-      'link' => l(t('unassign'), "admin/structure/trigger/unassign/$module/$hook/" . md5($aid)),
-    );
+    // If action is defined unassign it otherwise offer to delete all orphaned
+    // actions.
+    if (actions_function_lookup(md5($aid))) {
+      $form[$hook]['assigned']['#value'][$aid] = array(
+        'label' => $info['label'],
+        'link' => l(t('unassign'), "admin/structure/trigger/unassign/$module/$hook/" . md5($aid)),
+      );
+    }
+    else {
+      $form[$hook]['assigned']['#value'][$aid] = array(
+        'label' => $info['label'],
+        'link' => l(t('Remove orphaned actions'), "admin/config/system/actions/orphan"),
+      );
+    }
   }
 
   $form[$hook]['parent'] = array(
Index: modules/trigger/trigger.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/trigger.module,v
retrieving revision 1.58
diff -u -p -r1.58 trigger.module
--- modules/trigger/trigger.module	26 Dec 2009 16:50:09 -0000	1.58
+++ modules/trigger/trigger.module	26 Feb 2010 21:59:26 -0000
@@ -610,7 +610,7 @@ function trigger_actions_delete($aid) {
  */
 function _trigger_get_all_info() {
   static $triggers = NULL;
-  if( $triggers ) {
+  if ( $triggers ) {
     return $triggers;
   }
 
Index: modules/trigger/trigger.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/trigger.test,v
retrieving revision 1.26
diff -u -p -r1.26 trigger.test
--- modules/trigger/trigger.test	9 Feb 2010 12:29:39 -0000	1.26
+++ modules/trigger/trigger.test	26 Feb 2010 21:59:26 -0000
@@ -299,3 +299,57 @@ class TriggerOtherTestCase extends Drupa
     $this->assertTrue(variable_get($action_id, FALSE), t('Check that creating a taxonomy term triggered the action.'));
   }
 }
+
+/**
+ * Test that orphaned actions are properly handled.
+ */
+class TriggerOrphanedActionsTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Trigger orphaned actions',
+      'description' => 'Test triggering an action that has since been removed.' ,
+      'group' => 'Trigger',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('trigger', 'trigger_test');
+  }
+
+  function testActionsOrphaned() {
+    $action = 'trigger_test_generic_any_action';
+    $hash = md5($action);
+
+    // Test 1: Assign an action from a disable-able module to a trigger, 
+    // then pull the trigger, and make sure the actions fire.
+    $test_user = $this->drupalCreateUser(array('administer actions'));
+    $this->drupalLogin($test_user);
+    $edit = array('aid' => $hash);
+    $this->drupalPost('admin/structure/trigger/node', $edit, t('Assign'));
+
+    // Create an unpublished node.
+    $web_user = $this->drupalCreateUser(array('create page content', 'edit own page content', 'access content', 'administer nodes'));
+    $this->drupalLogin($web_user);
+    $edit = array();
+    $langcode = LANGUAGE_NONE;
+    $edit["title"] = '!SimpleTest test node! ' . $this->randomName(10);
+    $edit["body[$langcode][0][value]"] = '!SimpleTest test body! ' . $this->randomName(32) . ' ' . $this->randomName(32);
+    $this->drupalPost('node/add/page', $edit, t('Save'));
+    $this->assertRaw(t('!post %title has been created.', array('!post' => 'Basic page', '%title' => $edit["title"])), t('Make sure the Basic page has actually been created'));
+
+    // Action should have been fired.
+    $this->assertTrue(variable_get('trigger_test_generic_any_action', FALSE), t('Trigger test action successfully fired.'));
+
+    // Test 2: Disable the module that provides the action and make sure the 
+    // trigger doesn't white screen.
+    module_disable(array('trigger_test'));
+    $loaded_node = $this->drupalGetNodeByTitle($edit["title"]);
+    $edit["body[$langcode][0][value]"] = '!SimpleTest test body! ' . $this->randomName(32) . ' ' . $this->randomName(32);
+    $this->drupalPost("node/$loaded_node->nid/edit", $edit, t('Save'));
+
+    // If the node body was updated successfully we have dealt with the 
+    // unavailable action.
+    $this->assertRaw(t('!post %title has been updated.', array('!post' => 'Basic page', '%title' => $edit["title"])), t('Make sure the Basic page can be updated with the missing trigger function.'));
+  }
+}
Index: modules/trigger/tests/trigger_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/tests/trigger_test.module,v
retrieving revision 1.5
diff -u -p -r1.5 trigger_test.module
--- modules/trigger/tests/trigger_test.module	4 Dec 2009 16:49:47 -0000	1.5
+++ modules/trigger/tests/trigger_test.module	26 Feb 2010 21:59:26 -0000
@@ -63,12 +63,12 @@ function trigger_test_trigger_info() {
   return array(
     'node' => array(
       'node_triggertest' => array(
-        'runs when' => t('A test trigger is fired'),
+        'label' => t('A test trigger is fired'),
       ),
     ),
     'trigger_test' => array(
       'trigger_test_triggertest' => array(
-        'runs when' => t('Another test trigger is fired'),
+        'label' => t('Another test trigger is fired'),
       ),
     ),
   );
Index: includes/actions.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/actions.inc,v
retrieving revision 1.35
diff -u -p -r1.35 actions.inc
--- includes/actions.inc	26 Feb 2010 16:55:18 -0000	1.35
+++ includes/actions.inc	26 Feb 2010 21:59:26 -0000
@@ -129,7 +129,12 @@ function actions_do($action_ids, $object
     }
     // Singleton action; $action_ids is the function name.
     else {
-      $actions_result[$action_ids] = $action_ids($object, $context, $a1, $a2);
+      if (function_exists($action_ids)) {
+        $actions_result[$action_ids] = $action_ids($object, $context, $a1, $a2);
+      }
+      else {
+        $actions_result[$action_ids] = FALSE;
+      }
     }
   }
   $stack--;
@@ -153,7 +158,7 @@ function actions_do($action_ids, $object
  * @see hook_action_info()
  */
 function actions_list($reset = FALSE) {
-  static $actions;
+  $actions = &drupal_static(__FUNCTION__);
   if (!isset($actions) || $reset) {
     $actions = module_invoke_all('action_info');
     drupal_alter('action_info', $actions);
