diff --git a/rules.module b/rules.module index 3a460e4..8848eb4 100644 --- a/rules.module +++ b/rules.module @@ -9,9 +9,42 @@ require_once dirname(__FILE__) . '/modules/events.inc'; /** + * Implements hook_module_implements_alter(). + */ +function rules_module_implements_alter(&$implementations, $hook) { + // Ensures the invocation of hook_menu_get_item_alter() triggers + // rules_menu_get_item_alter() first so the rules invocation is ready for all + // sub-sequent hook implementations. + if ($hook == 'menu_get_item_alter' && array_key_exists('rules', $implementations)) { + $group = $implementations['rules']; + unset($implementations['rules']); + $implementations = array_merge(array('rules' => $group), $implementations); + } +} + +/** + * Implements hook_menu_get_item_alter(). + */ +function rules_menu_get_item_alter() { + // Make sure that event invocation is enabled before menu items are loaded. + // But make sure later calls to menu_get_item() won't automatically re-enabled + // the rules invocation. + // Example: modules that implement hook_entity_ENTITY_TYPE_load() might want + // to invoke Rules events in that load hook, which is also invoked for menu + // item loading. Since this can happen even before hook_init() we need to make + // sure that firing Rules events is enabled at that point. A typical use case + // for this is Drupal Commerce with commerce_cart_commerce_order_load(). + if (!drupal_static('rules_init', FALSE)) { + rules_event_invocation_enabled(TRUE); + } +} + +/** * Implements hook_init(). */ function rules_init() { + $rules_init = &drupal_static(__FUNCTION__, FALSE); + $rules_init = TRUE; // Enable event invocation once hook_init() was invoked for Rules. rules_event_invocation_enabled(TRUE); rules_invoke_event('init'); diff --git a/tests/rules.test b/tests/rules.test index 20f1c9d..3b034e8 100644 --- a/tests/rules.test +++ b/tests/rules.test @@ -2099,3 +2099,50 @@ class RulesEventDispatcherTestCase extends DrupalWebTestCase { } } } + +/** + * Test early bootstrap Rules invocation. + */ +class RulesInvocationEnabledTestCase extends DrupalWebTestCase { + + /** + * {@inheritdoc} + */ + public static function getInfo() { + return array( + 'name' => 'Rules invocation enabled', + 'description' => 'Tests that Rules events are enabled during menu item loads.', + 'group' => 'Rules', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp('dblog', 'rules', 'rules_test', 'rules_test_invocation'); + } + + /** + * Tests that a Rules event is triggered on node menu item loading. + * + * @see rules_test_invocation_node_load() + */ + public function testInvocationOnNodeMenuLoading() { + // Create a test node. + $node = $this->drupalCreateNode(array('title' => 'Test')); + // Enable Rules logging on the INFO level so that entries are written to + // dblog. + variable_set('rules_log_errors', RulesLog::INFO); + // Create an empty rule that will fire in our node load hook. + $rule = rules_reaction_rule(); + $rule->event('rules_test_event'); + $rule->save('test_rule'); + + // Visit the node page which should trigger the load hook. + $this->drupalGet('node/' . $node->nid); + $result = db_query("SELECT * FROM {watchdog} WHERE type = 'rules' AND message = 'Reacting on event %label.'")->fetch(); + $this->assertFalse(empty($result), 'Rules event was triggered and logged.'); + } + +} diff --git a/tests/rules_test_invocation.info b/tests/rules_test_invocation.info new file mode 100644 index 0000000..c4f43e6 --- /dev/null +++ b/tests/rules_test_invocation.info @@ -0,0 +1,5 @@ +name = "Rules Test invocation" +description = "Helper module to test Rules invocations." +package = Testing +core = 7.x +hidden = TRUE diff --git a/tests/rules_test_invocation.module b/tests/rules_test_invocation.module new file mode 100644 index 0000000..3664926 --- /dev/null +++ b/tests/rules_test_invocation.module @@ -0,0 +1,13 @@ +