1 |
85ad3d82
|
Assos Assos
|
<?php
|
2 |
|
|
|
3 |
|
|
/**
|
4 |
|
|
* @file
|
5 |
|
|
* Rules tests.
|
6 |
|
|
*/
|
7 |
|
|
|
8 |
|
|
class RulesTestCase extends DrupalWebTestCase {
|
9 |
|
|
|
10 |
|
|
static function getInfo() {
|
11 |
|
|
return array(
|
12 |
|
|
'name' => 'Rules Engine tests',
|
13 |
|
|
'description' => 'Test using the rules API to create and evaluate rules.',
|
14 |
|
|
'group' => 'Rules',
|
15 |
|
|
);
|
16 |
|
|
}
|
17 |
|
|
|
18 |
|
|
function setUp() {
|
19 |
|
|
parent::setUp('rules', 'rules_test');
|
20 |
|
|
RulesLog::logger()->clear();
|
21 |
|
|
variable_set('rules_debug_log', 1);
|
22 |
|
|
}
|
23 |
|
|
|
24 |
|
|
/**
|
25 |
|
|
* Calculates the output of t() given an array of placeholders to replace.
|
26 |
|
|
*/
|
27 |
|
|
static function t($text, $strings) {
|
28 |
|
|
$placeholders = array();
|
29 |
|
|
foreach ($strings as $key => $string) {
|
30 |
|
|
$key = !is_numeric($key) ? $key : $string;
|
31 |
|
|
$placeholders['%' . $key] = drupal_placeholder($string);
|
32 |
|
|
}
|
33 |
|
|
return strtr($text, $placeholders);
|
34 |
|
|
}
|
35 |
|
|
|
36 |
|
|
protected function createTestRule() {
|
37 |
|
|
$rule = rule();
|
38 |
|
|
$rule->condition('rules_test_condition_true')
|
39 |
|
|
->condition('rules_test_condition_true')
|
40 |
|
|
->condition(rules_or()
|
41 |
|
|
->condition(rules_condition('rules_test_condition_true')->negate())
|
42 |
|
|
->condition('rules_test_condition_false')
|
43 |
|
|
->condition(rules_and()
|
44 |
|
|
->condition('rules_test_condition_false')
|
45 |
|
|
->condition('rules_test_condition_true')
|
46 |
|
|
->negate()
|
47 |
|
|
)
|
48 |
|
|
);
|
49 |
|
|
$rule->action('rules_test_action');
|
50 |
|
|
return $rule;
|
51 |
|
|
}
|
52 |
|
|
|
53 |
|
|
/**
|
54 |
|
|
* Tests creating a rule and iterating over the rule elements.
|
55 |
|
|
*/
|
56 |
|
|
function testRuleCreation() {
|
57 |
|
|
$rule = $this->createTestRule();
|
58 |
|
|
$rule->integrityCheck();
|
59 |
|
|
$rule->execute();
|
60 |
|
|
$log = RulesLog::logger()->get();
|
61 |
|
|
$last = array_pop($log);
|
62 |
|
|
$last = array_pop($log);
|
63 |
|
|
$last = array_pop($log);
|
64 |
|
|
$this->assertEqual($last[0], 'action called', 'Action called');
|
65 |
|
|
RulesLog::logger()->checkLog();
|
66 |
|
|
|
67 |
|
|
// Make sure condition and action iterators are working.
|
68 |
|
|
$it = new RecursiveIteratorIterator($rule->conditions(), RecursiveIteratorIterator::SELF_FIRST);
|
69 |
|
|
$this->assertEqual(iterator_count($it), 8, 'Iterated over all conditions and condition containers');
|
70 |
|
|
$it = new RecursiveIteratorIterator($rule->conditions());
|
71 |
|
|
$this->assertEqual(iterator_count($it), 6, 'Iterated over all conditions');
|
72 |
|
|
$this->assertEqual(iterator_count($rule->actions()), 1, 'Iterated over all actions');
|
73 |
|
|
$this->assertEqual(iterator_count($rule->elements()), 10, 'Iterated over all rule elements.');
|
74 |
|
|
|
75 |
|
|
// Test getting dependencies and the integrity check.
|
76 |
|
|
$rule->integrityCheck();
|
77 |
|
|
$this->assertTrue($rule->dependencies() === array('rules_test'), 'Dependencies correctly returned.');
|
78 |
|
|
}
|
79 |
|
|
|
80 |
|
|
/**
|
81 |
|
|
* Test handling dependencies.
|
82 |
|
|
*/
|
83 |
|
|
function testdependencies() {
|
84 |
|
|
$action = rules_action('rules_node_publish_action');
|
85 |
|
|
$this->assertEqual($action->dependencies(), array('rules_test'), 'Providing module is returned as dependency.');
|
86 |
|
|
|
87 |
|
|
$container = new RulesTestContainer();
|
88 |
|
|
$this->assertEqual($container->dependencies(), array('rules_test'), 'Providing module for container plugin is returned as dependency.');
|
89 |
|
|
|
90 |
|
|
// Test handling unmet dependencies.
|
91 |
|
|
$rule = rules_config_load('rules_export_test');
|
92 |
|
|
$this->assertTrue(in_array('comment', $rule->dependencies) && !$rule->dirty, 'Dependencies have been imported.');
|
93 |
|
|
|
94 |
|
|
// Remove the required comment module and make sure the rule is dirty then.
|
95 |
|
|
module_disable(array('comment'));
|
96 |
|
|
rules_clear_cache();
|
97 |
|
|
$rule = rules_config_load('rules_export_test');
|
98 |
|
|
$this->assertTrue($rule->dirty, 'Rule has been marked as dirty');
|
99 |
|
|
|
100 |
|
|
// Now try re-enabling.
|
101 |
|
|
module_enable(array('comment'));
|
102 |
|
|
rules_clear_cache();
|
103 |
|
|
$rule = rules_config_load('rules_export_test');
|
104 |
|
|
$this->assertTrue(!$rule->dirty, 'Rule has been marked as not dirty again.');
|
105 |
|
|
|
106 |
|
|
// Test it with components.
|
107 |
|
|
module_enable(array('path'));
|
108 |
|
|
$action_set = rules_action_set(array('node' => array('type' => 'node')));
|
109 |
|
|
$action_set->action('node_path_alias');
|
110 |
|
|
$action_set->save('rules_test_alias');
|
111 |
|
|
|
112 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
113 |
|
|
$rule->action('component_rules_test_alias');
|
114 |
|
|
$rule->integrityCheck();
|
115 |
|
|
$rule->save('rules_test_rule');
|
116 |
|
|
|
117 |
|
|
$rule = rules_config_load('rules_test_rule');
|
118 |
|
|
$component = rules_config_load('rules_test_alias');
|
119 |
|
|
$this->assertTrue(in_array('path', $component->dependencies) && !$rule->dirty && !$component->dirty, 'Component has path module dependency.');
|
120 |
|
|
|
121 |
|
|
// Now disable path module and make sure both configs are marked as dirty.
|
122 |
|
|
module_disable(array('path'));
|
123 |
|
|
rules_clear_cache();
|
124 |
|
|
$rule = rules_config_load('rules_test_rule');
|
125 |
|
|
$component = rules_config_load('rules_test_alias');
|
126 |
|
|
|
127 |
|
|
$this->assertTrue($component->dirty, 'Component has been marked as dirty');
|
128 |
|
|
$node = $this->drupalCreateNode();
|
129 |
|
|
$result = rules_invoke_component('rules_test_alias', $node);
|
130 |
|
|
$this->assertTrue($result === FALSE, 'Unable to execute a dirty component.');
|
131 |
|
|
|
132 |
|
|
// When the rule is evaluated, the broken component is detected and the
|
133 |
|
|
// rule should be marked as dirty too.
|
134 |
|
|
$rule->execute($node);
|
135 |
|
|
$this->assertTrue($rule->dirty, 'Rule has been marked as dirty');
|
136 |
|
|
|
137 |
|
|
module_enable(array('path'));
|
138 |
|
|
rules_clear_cache();
|
139 |
|
|
|
140 |
|
|
// Trigger rebuilding the cache, so configs are checked again.
|
141 |
|
|
rules_get_cache();
|
142 |
|
|
|
143 |
|
|
$rule = rules_config_load('rules_test_rule');
|
144 |
|
|
$component = rules_config_load('rules_test_alias');
|
145 |
|
|
$this->assertTrue(!$component->dirty, 'Component has been marked as not dirty again.');
|
146 |
|
|
$this->assertTrue(!$rule->dirty, 'Rule has been marked as not dirty again.');
|
147 |
|
|
}
|
148 |
|
|
|
149 |
|
|
/**
|
150 |
|
|
* Test setting up an action with some action_info and serializing and
|
151 |
|
|
* executing it.
|
152 |
|
|
*/
|
153 |
|
|
function testActionSetup() {
|
154 |
|
|
$action = rules_action('rules_node_publish_action');
|
155 |
|
|
|
156 |
|
|
$s = serialize($action);
|
157 |
|
|
$action2 = unserialize($s);
|
158 |
|
|
$node = (object) array('status' => 0, 'type' => 'page');
|
159 |
|
|
$node->title = 'test';
|
160 |
|
|
|
161 |
|
|
$action2->execute($node);
|
162 |
|
|
$this->assertEqual($node->status, 1, 'Action executed correctly');
|
163 |
|
|
|
164 |
|
|
$this->assertTrue(in_array('node', array_keys($action2->parameterInfo())), 'Parameter info returned.');
|
165 |
|
|
|
166 |
|
|
$node->status = 0;
|
167 |
|
|
$action2->integrityCheck();
|
168 |
|
|
$action2->executeByArgs(array('node' => $node));
|
169 |
|
|
$this->assertEqual($node->status, 1, 'Action executed correctly');
|
170 |
|
|
|
171 |
|
|
// Test calling an extended + overriden method.
|
172 |
|
|
$this->assertEqual($action2->help(), 'custom', 'Using custom help callback.');
|
173 |
|
|
|
174 |
|
|
// Inspect the cache
|
175 |
|
|
//$this->pass(serialize(rules_get_cache()));
|
176 |
|
|
RulesLog::logger()->checkLog();
|
177 |
|
|
}
|
178 |
|
|
|
179 |
|
|
/**
|
180 |
|
|
* Test executing with wrong arguments.
|
181 |
|
|
*/
|
182 |
|
|
function testActionExecutionFails() {
|
183 |
|
|
$action = rules_action('rules_node_publish_action');
|
184 |
|
|
try {
|
185 |
|
|
$action->execute();
|
186 |
|
|
$this->fail("Execution hasn't created an exception.");
|
187 |
|
|
}
|
188 |
|
|
catch (RulesEvaluationException $e) {
|
189 |
|
|
$this->pass("RulesEvaluationException was thrown: ". $e);
|
190 |
|
|
}
|
191 |
|
|
}
|
192 |
|
|
|
193 |
|
|
/**
|
194 |
|
|
* Test setting up a rule and mapping variables.
|
195 |
|
|
*/
|
196 |
|
|
function testVariableMapping() {
|
197 |
|
|
$rule = rule(array(
|
198 |
|
|
'node' => array('type' => 'node'),
|
199 |
|
|
'node_unchanged' => array('type' => 'node'),
|
200 |
|
|
));
|
201 |
|
|
$rule->condition(rules_condition('rules_condition_content_is_published')->negate())
|
202 |
|
|
->condition('rules_condition_content_is_type', array('type' => array('page', 'story')))
|
203 |
|
|
->action('rules_node_publish_action', array('node:select' => 'node_unchanged'));
|
204 |
|
|
|
205 |
|
|
$node1 = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
|
206 |
|
|
$node2 = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
|
207 |
|
|
$rule->integrityCheck();
|
208 |
|
|
$rule->execute($node1, $node2);
|
209 |
|
|
$this->assertEqual($node2->status, 1, 'Action executed correctly on node2.');
|
210 |
|
|
$this->assertEqual($node1->status, 0, 'Action not executed on node1.');
|
211 |
|
|
|
212 |
|
|
RulesLog::logger()->checkLog();
|
213 |
|
|
}
|
214 |
|
|
|
215 |
|
|
/**
|
216 |
|
|
* Tests making use of class based actions.
|
217 |
|
|
*/
|
218 |
|
|
function testClassBasedActions() {
|
219 |
|
|
$cache = rules_get_cache();
|
220 |
|
|
$this->assertTrue(!empty($cache['action_info']['rules_test_class_action']), 'Action has been discovered.');
|
221 |
|
|
$action = rules_action('rules_test_class_action');
|
222 |
|
|
|
223 |
|
|
$parameters = $action->parameterInfo();
|
224 |
|
|
$this->assertTrue($parameters['node'], 'Action parameter needs a value.');
|
225 |
|
|
|
226 |
|
|
$node = $this->drupalCreateNode();
|
227 |
|
|
$action->execute($node);
|
228 |
|
|
$log = RulesLog::logger()->get();
|
229 |
|
|
$last = array_pop($log);
|
230 |
|
|
$last = array_pop($log);
|
231 |
|
|
$this->assertEqual($last[0], 'Action called with node ' . $node->nid, 'Action called');
|
232 |
|
|
RulesLog::logger()->checkLog();
|
233 |
|
|
}
|
234 |
|
|
|
235 |
|
|
/**
|
236 |
|
|
* Tests CRUD functionality.
|
237 |
|
|
*/
|
238 |
|
|
function testRulesCRUD() {
|
239 |
|
|
$rule = $this->createTestRule();
|
240 |
|
|
$rule->integrityCheck()->save('test');
|
241 |
|
|
|
242 |
|
|
$this->assertEqual(TRUE, $rule->active, 'Rule is active.');
|
243 |
|
|
$this->assertEqual(0, $rule->weight, 'Rule weight is zero.');
|
244 |
|
|
|
245 |
|
|
$results = entity_load('rules_config', array('test'));
|
246 |
|
|
$rule2 = array_pop($results);
|
247 |
|
|
$this->assertEqual($rule->id, $rule2->id, 'Rule created and loaded');
|
248 |
|
|
$this->assertEqual(get_class($rule2), get_class($rule), 'Class properly instantiated.');
|
249 |
|
|
$rule2->execute();
|
250 |
|
|
// Update.
|
251 |
|
|
$rule2->save();
|
252 |
|
|
|
253 |
|
|
// Make sure all rule elements are still here.
|
254 |
|
|
$it = new RecursiveIteratorIterator($rule2->conditions(), RecursiveIteratorIterator::SELF_FIRST);
|
255 |
|
|
$this->assertEqual(iterator_count($it), 8, 'Iterated over all conditions and condition containers');
|
256 |
|
|
$it = new RecursiveIteratorIterator($rule2->conditions());
|
257 |
|
|
$this->assertEqual(iterator_count($it), 6, 'Iterated over all conditions');
|
258 |
|
|
$this->assertEqual(iterator_count($rule2->actions()), 1, 'Iterated over all actions');
|
259 |
|
|
|
260 |
|
|
// Delete.
|
261 |
|
|
$rule2->delete();
|
262 |
|
|
$this->assertEqual(entity_load('rules_config', FALSE, array('id' => $rule->id)), array(), 'Deleted.');
|
263 |
|
|
|
264 |
|
|
// Tests CRUD for tags - making sure the tags are stored properly..
|
265 |
|
|
$rule = $this->createTestRule();
|
266 |
|
|
$tag = $this->randomString();
|
267 |
|
|
$rule->tags = array($tag);
|
268 |
|
|
$rule->save();
|
269 |
|
|
$result = db_select('rules_tags')
|
270 |
|
|
->fields('rules_tags', array('tag'))
|
271 |
|
|
->condition('id', $rule->id)
|
272 |
|
|
->execute();
|
273 |
|
|
$this->assertEqual($result->fetchField(), $tag, 'Associated tag has been saved.');
|
274 |
|
|
// Try updating.
|
275 |
|
|
$rule->tags = array($this->randomName(), $this->randomName());
|
276 |
|
|
$rule->integrityCheck()->save();
|
277 |
|
|
$result = db_select('rules_tags')
|
278 |
|
|
->fields('rules_tags', array('tag'))
|
279 |
|
|
->condition('id', $rule->id)
|
280 |
|
|
->execute()
|
281 |
|
|
->fetchCol();
|
282 |
|
|
$this->assertTrue(in_array($rule->tags[0], $result) && in_array($rule->tags[1], $result), 'Updated associated tags.');
|
283 |
|
|
// Try loading multiple rules by tags.
|
284 |
|
|
$rule2 = $this->createTestRule();
|
285 |
|
|
$rule2->tags = array($this->randomName());
|
286 |
|
|
$rule2->save();
|
287 |
|
|
$loaded = entity_load('rules_config', FALSE, array('tags' => array($rule->tags[0], $rule2->tags[0])));
|
288 |
|
|
$this->assertTrue($loaded[$rule->id]->id == $rule->id && $loaded[$rule2->id]->id == $rule2->id, 'Loading configs by tags');
|
289 |
|
|
// Try deleting.
|
290 |
|
|
$rule->delete();
|
291 |
|
|
$result = db_select('rules_tags')
|
292 |
|
|
->fields('rules_tags', array('tag'))
|
293 |
|
|
->condition('id', $rule->id)
|
294 |
|
|
->execute();
|
295 |
|
|
$this->assertEqual($result->fetchField(), FALSE, 'Deleted associated tags.');
|
296 |
|
|
}
|
297 |
|
|
|
298 |
|
|
/**
|
299 |
|
|
* Test automatic saving of variables.
|
300 |
|
|
*/
|
301 |
|
|
function testActionSaving() {
|
302 |
|
|
// Test saving a parameter.
|
303 |
|
|
$action = rules_action('rules_node_publish_action_save');
|
304 |
|
|
$node = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
|
305 |
|
|
$action->executeByArgs(array('node' => $node));
|
306 |
|
|
|
307 |
|
|
$this->assertEqual($node->status, 1, 'Action executed correctly on node.');
|
308 |
|
|
// Sync node_load cache with node_save
|
309 |
|
|
entity_get_controller('node')->resetCache();
|
310 |
|
|
|
311 |
|
|
$node = node_load($node->nid);
|
312 |
|
|
$this->assertEqual($node->status, 1, 'Node has been saved.');
|
313 |
|
|
|
314 |
|
|
// Now test saving a provided variable, which is renamed and modified before
|
315 |
|
|
// it is saved.
|
316 |
|
|
$title = $this->randomName();
|
317 |
|
|
$rule = rule();
|
318 |
|
|
$rule->action('entity_create', array(
|
319 |
|
|
'type' => 'node',
|
320 |
|
|
'param_type' => 'article',
|
321 |
|
|
'param_author:select' => 'site:current-user',
|
322 |
|
|
'param_title' => $title,
|
323 |
|
|
'entity_created:var' => 'node',
|
324 |
|
|
));
|
325 |
|
|
$rule->action('data_set', array(
|
326 |
|
|
'data:select' => 'node:body',
|
327 |
|
|
'value' => array('value' => 'body'),
|
328 |
|
|
));
|
329 |
|
|
$rule->integrityCheck();
|
330 |
|
|
$rule->execute();
|
331 |
|
|
|
332 |
|
|
$node = $this->drupalGetNodeByTitle($title);
|
333 |
|
|
$this->assertTrue(!empty($node) && $node->body[LANGUAGE_NONE][0]['value'] == 'body', 'Saved a provided variable');
|
334 |
|
|
RulesLog::logger()->checkLog();
|
335 |
|
|
}
|
336 |
|
|
|
337 |
|
|
/**
|
338 |
|
|
* Test adding a variable and optional parameters.
|
339 |
|
|
*/
|
340 |
|
|
function testVariableAdding() {
|
341 |
|
|
$node = $this->drupalCreateNode();
|
342 |
|
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
343 |
|
|
$rule->condition('rules_test_condition_true')
|
344 |
|
|
->action('rules_action_load_node')
|
345 |
|
|
->action('rules_action_delete_node', array('node:select' => 'node_loaded'))
|
346 |
|
|
->execute($node->nid);
|
347 |
|
|
|
348 |
|
|
$this->assertEqual(FALSE, node_load($node->nid), 'Variable added and skipped optional parameter.');
|
349 |
|
|
RulesLog::logger()->checkLog();
|
350 |
|
|
|
351 |
|
|
$vars = $rule->conditions()->offsetGet(0)->availableVariables();
|
352 |
|
|
$this->assertEqual(!isset($vars['node_loaded']), 'Loaded variable is not available to conditions.');
|
353 |
|
|
|
354 |
|
|
|
355 |
|
|
// Test adding a variable with a custom variable name.
|
356 |
|
|
$node = $this->drupalCreateNode();
|
357 |
|
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
358 |
|
|
$rule->action('rules_action_load_node', array('node_loaded:var' => 'node'))
|
359 |
|
|
->action('rules_action_delete_node')
|
360 |
|
|
->execute($node->nid);
|
361 |
|
|
|
362 |
|
|
$this->assertEqual(FALSE, node_load($node->nid), 'Variable with custom name added.');
|
363 |
|
|
RulesLog::logger()->checkLog();
|
364 |
|
|
}
|
365 |
|
|
|
366 |
|
|
/**
|
367 |
|
|
* Test custom access for using component actions/conditions.
|
368 |
|
|
*/
|
369 |
|
|
function testRuleComponentAccess() {
|
370 |
|
|
// Create a normal user.
|
371 |
|
|
$normal_user = $this->drupalCreateUser();
|
372 |
|
|
// Create a role for granting access to the rule component.
|
373 |
|
|
$this->normal_role = $this->drupalCreateRole(array(), 'test_role');
|
374 |
|
|
$normal_user->roles[$this->normal_role] = 'test_role';
|
375 |
|
|
user_save($normal_user, array('roles' => $normal_user->roles));
|
376 |
|
|
// Create an 'action set' rule component making use of a permission.
|
377 |
|
|
$action_set = rules_action_set(array('node' => array('type' => 'node')));
|
378 |
|
|
$action_set->access_exposed = TRUE;
|
379 |
|
|
$action_set->save('rules_test_roles');
|
380 |
|
|
|
381 |
|
|
// Set the global user to be the current one as access is checked for the
|
382 |
|
|
// global user.
|
383 |
|
|
global $user;
|
384 |
|
|
$user = user_load($normal_user->uid);
|
385 |
|
|
$this->assertFalse(rules_action('component_rules_test_roles')->access(), 'Authenticated user without the correct role can\'t use the rule component.');
|
386 |
|
|
|
387 |
|
|
// Assign the role that will have permissions for the rule component.
|
388 |
|
|
user_role_change_permissions($this->normal_role, array('use Rules component rules_test_roles' => TRUE));
|
389 |
|
|
$this->assertTrue(rules_action('component_rules_test_roles')->access(), 'Authenticated user with the correct role can use the rule component.');
|
390 |
|
|
|
391 |
|
|
// Reset global user to anonyous.
|
392 |
|
|
$user = user_load(0);
|
393 |
|
|
$this->assertFalse(rules_action('component_rules_test_roles')->access(), 'Anonymous user can\'t use the rule component.');
|
394 |
|
|
}
|
395 |
|
|
|
396 |
|
|
/**
|
397 |
|
|
* Test passing arguments by reference to an action.
|
398 |
|
|
*/
|
399 |
|
|
function testPassingByReference() {
|
400 |
|
|
// Keeping references of variables is unsupported, though the
|
401 |
|
|
// EntityMetadataArrayObject may be used to achieve that.
|
402 |
|
|
$array = array('foo' => 'bar');
|
403 |
|
|
$data = new EntityMetadataArrayObject($array);
|
404 |
|
|
rules_action('rules_action_test_reference')->execute($data);
|
405 |
|
|
$this->assertTrue($data['changed'], 'Parameter has been passed by reference');
|
406 |
|
|
}
|
407 |
|
|
|
408 |
|
|
/**
|
409 |
|
|
* Test sorting rule elements.
|
410 |
|
|
*/
|
411 |
|
|
function testSorting() {
|
412 |
|
|
$rule = $this->createTestRule();
|
413 |
|
|
$conditions = $rule->conditions();
|
414 |
|
|
$conditions[0]->weight = 10;
|
415 |
|
|
$conditions[2]->weight = 10;
|
416 |
|
|
$id[0] = $conditions[0]->elementId();
|
417 |
|
|
$id[1] = $conditions[1]->elementId();
|
418 |
|
|
$id[2] = $conditions[2]->elementId();
|
419 |
|
|
// For testing use a deep sort, even if not necessary here.
|
420 |
|
|
$rule->sortChildren(TRUE);
|
421 |
|
|
$conditions = $rule->conditions();
|
422 |
|
|
$this->assertEqual($conditions[0]->elementId(), $id[1], 'Condition sorted correctly.');
|
423 |
|
|
$this->assertEqual($conditions[1]->elementId(), $id[0], 'Condition sorted correctly.');
|
424 |
|
|
$this->assertEqual($conditions[2]->elementId(), $id[2], 'Condition sorted correctly.');
|
425 |
|
|
}
|
426 |
|
|
|
427 |
|
|
/**
|
428 |
|
|
* Tests using data selectors.
|
429 |
|
|
*/
|
430 |
|
|
function testDataSelectors() {
|
431 |
|
|
$body[LANGUAGE_NONE][0] = array('value' => '<b>The body & nothing.</b>');
|
432 |
|
|
$node = $this->drupalCreateNode(array('body' => $body, 'type' => 'page', 'summary' => ''));
|
433 |
|
|
|
434 |
|
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
435 |
|
|
$rule->action('rules_action_load_node')
|
436 |
|
|
->action('drupal_message', array('message:select' => 'node_loaded:body:value'))
|
437 |
|
|
->execute($node->nid);
|
438 |
|
|
|
439 |
|
|
RulesLog::logger()->checkLog();
|
440 |
|
|
$msg = drupal_get_messages('status');
|
441 |
|
|
$last_msg = array_pop($msg['status']);
|
442 |
|
|
$wrapper = entity_metadata_wrapper('node', $node);
|
443 |
|
|
$this->assertEqual($last_msg, $wrapper->body->value->value(array('sanitize' => TRUE)), 'Data selector for getting parameter applied.');
|
444 |
|
|
|
445 |
|
|
// Get a "reference" on the same object as returned by node_load().
|
446 |
|
|
$node = node_load($node->nid);
|
447 |
|
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
448 |
|
|
$rule->action('rules_action_load_node')
|
449 |
|
|
->action('data_set', array('data:select' => 'node_loaded:title', 'value' => 'Test title'))
|
450 |
|
|
// Use two actions and make sure the node get saved only once.
|
451 |
|
|
->action('data_set', array('data:select' => 'node_loaded:title', 'value' => 'Test title2'))
|
452 |
|
|
->execute($node->nid);
|
453 |
|
|
|
454 |
|
|
$wrapper = entity_metadata_wrapper('node', $node);
|
455 |
|
|
$this->assertEqual('Test title2', $wrapper->title->value(), 'Data has been modified and saved.');
|
456 |
|
|
|
457 |
|
|
RulesLog::logger()->checkLog();
|
458 |
|
|
$text = RulesLog::logger()->render();
|
459 |
|
|
$msg = RulesTestCase::t('Saved %node_loaded of type %node.', array('node_loaded', 'node'));
|
460 |
|
|
if ($pos1 = strpos($text, $msg)) {
|
461 |
|
|
$pos2 = strpos($text, $msg, $pos1 + 1);
|
462 |
|
|
}
|
463 |
|
|
$this->assertTrue($pos1 && $pos2 === FALSE, 'Data has been saved only once.');
|
464 |
|
|
|
465 |
|
|
// Test validation.
|
466 |
|
|
try {
|
467 |
|
|
rules_action('data_set', array('data' => 'no-selector', 'value' => ''))->integrityCheck();
|
468 |
|
|
$this->fail("Validation hasn't created an exception.");
|
469 |
|
|
}
|
470 |
|
|
catch (RulesIntegrityException $e) {
|
471 |
|
|
$this->pass("Validation error correctly detected: ". $e);
|
472 |
|
|
}
|
473 |
|
|
|
474 |
|
|
// Test auto creation of nested data structures, like the node body field.
|
475 |
|
|
// I.e. if $node->body is not set, it is automatically initialized to an
|
476 |
|
|
// empty array, so that the nested value can be set and the wrappers do not
|
477 |
|
|
// complain about missing parent data structures.
|
478 |
|
|
$rule = rule();
|
479 |
|
|
$rule->action('entity_create', array(
|
480 |
|
|
'type' => 'node',
|
481 |
|
|
'param_type' => 'page',
|
482 |
|
|
'param_title' => 'foo',
|
483 |
|
|
'param_author' => $GLOBALS['user'],
|
484 |
|
|
));
|
485 |
|
|
$rule->action('data_set', array('data:select' => 'entity_created:body:value', 'value' => 'test content'))
|
486 |
|
|
->execute();
|
487 |
|
|
try {
|
488 |
|
|
RulesLog::logger()->checkLog();
|
489 |
|
|
$this->pass('Auto creation of nested data structures.');
|
490 |
|
|
}
|
491 |
|
|
catch (Exception $e) {
|
492 |
|
|
$this->fail('Auto creation of nested data structures.');
|
493 |
|
|
}
|
494 |
|
|
|
495 |
|
|
// Make sure variables that are passed wrapped work.
|
496 |
|
|
$result = rules_condition('rules_test_condition_node_wrapped')->execute($node->nid);
|
497 |
|
|
$this->assertTrue($result, 'Condition receiving wrapped parameter.');
|
498 |
|
|
|
499 |
|
|
// Make sure wrapped parameters are checked for containing NULL values.
|
500 |
|
|
$rule = rule(array('node' => array('type' => 'node', 'optional' => TRUE)));
|
501 |
|
|
$rule->condition('rules_test_condition_node_wrapped', array('node:select' => 'node'));
|
502 |
|
|
$rule->execute(entity_metadata_wrapper('node'));
|
503 |
|
|
$text = RulesLog::logger()->render();
|
504 |
|
|
$msg = RulesTestCase::t('The variable or parameter %node is empty.', array('node'));
|
505 |
|
|
$this->assertTrue(strpos($text, $msg) !== FALSE, 'Evaluation aborted due to an empty argument value.');
|
506 |
|
|
}
|
507 |
|
|
|
508 |
|
|
/**
|
509 |
|
|
* Tests making use of rule sets.
|
510 |
|
|
*/
|
511 |
|
|
function testRuleSets() {
|
512 |
|
|
$set = rules_rule_set(array(
|
513 |
|
|
'node' => array('type' => 'node', 'label' => 'node'),
|
514 |
|
|
));
|
515 |
|
|
$set->rule(rule()->action('drupal_message', array('message:select' => 'node:title')))
|
516 |
|
|
->rule(rule()->condition('rules_condition_content_is_published')
|
517 |
|
|
->action('drupal_message', array('message' => 'Node is published.'))
|
518 |
|
|
);
|
519 |
|
|
$set->integrityCheck()->save('rules_test_set_1');
|
520 |
|
|
|
521 |
|
|
$node = $this->drupalCreateNode(array('title' => 'The title.', 'status' => 1));
|
522 |
|
|
// Execute.
|
523 |
|
|
rules_invoke_component('rules_test_set_1', $node);
|
524 |
|
|
|
525 |
|
|
$msg = drupal_get_messages();
|
526 |
|
|
$this->assertEqual($msg['status'][0], 'The title.', 'First rule evaluated.');
|
527 |
|
|
$this->assertEqual($msg['status'][1], 'Node is published.', 'Second rule evaluated.');
|
528 |
|
|
|
529 |
|
|
// Test a condition set.
|
530 |
|
|
$set = rules_or(array(
|
531 |
|
|
'node' => array('type' => 'node', 'label' => 'node'),
|
532 |
|
|
));
|
533 |
|
|
$set->condition('data_is', array('data:select' => 'node:author:name', 'value' => 'notthename'))
|
534 |
|
|
->condition('data_is', array('data:select' => 'node:nid', 'value' => $node->nid))
|
535 |
|
|
->integrityCheck()
|
536 |
|
|
->save('test', 'rules_test');
|
537 |
|
|
// Load and execute condition set.
|
538 |
|
|
$set = rules_config_load('test');
|
539 |
|
|
$this->assertTrue($set->execute($node), 'Set has been correctly evaluated.');
|
540 |
|
|
RulesLog::logger()->checkLog();
|
541 |
|
|
}
|
542 |
|
|
|
543 |
|
|
/**
|
544 |
|
|
* Tests invoking components from the action.
|
545 |
|
|
*/
|
546 |
|
|
function testComponentInvocations() {
|
547 |
|
|
$set = rules_rule_set(array(
|
548 |
|
|
'node1' => array('type' => 'node', 'label' => 'node'),
|
549 |
|
|
));
|
550 |
|
|
$set->rule(rule()->condition('node_is_published', array('node:select' => 'node1'))
|
551 |
|
|
->action('node_unpublish', array('node:select' => 'node1'))
|
552 |
|
|
);
|
553 |
|
|
$set->integrityCheck()->save('rules_test_set_2');
|
554 |
|
|
|
555 |
|
|
// Use different names for the variables to ensure they are properly mapped
|
556 |
|
|
// when taking over the variables to be saved.
|
557 |
|
|
$rule = rule(array(
|
558 |
|
|
'node2' => array('type' => 'node', 'label' => 'node'),
|
559 |
|
|
));
|
560 |
|
|
$rule->action('component_rules_test_set_2', array('node1:select' => 'node2'));
|
561 |
|
|
$rule->action('node_make_sticky', array('node:select' => 'node2'));
|
562 |
|
|
|
563 |
|
|
$node = $this->drupalCreateNode(array('title' => 'The title.', 'status' => 1, 'sticky' => 0));
|
564 |
|
|
$rule->execute($node);
|
565 |
|
|
|
566 |
|
|
$node = node_load($node->nid, NULL, TRUE);
|
567 |
|
|
$this->assertFalse($node->status, 'The component changes have been saved correctly.');
|
568 |
|
|
$this->assertTrue($node->sticky, 'The action changes have been saved correctly.');
|
569 |
|
|
|
570 |
|
|
// Check that we have saved the changes only once.
|
571 |
|
|
$text = RulesLog::logger()->render();
|
572 |
|
|
// Make sure both saves are handled in one save operation.
|
573 |
|
|
$this->assertEqual(substr_count($text, 'Saved'), 1, 'Changes have been saved in one save operation.');
|
574 |
|
|
RulesLog::logger()->checkLog();
|
575 |
|
|
|
576 |
|
|
// Test recursion prevention on components by invoking the component from
|
577 |
|
|
// itself, what should be prevented.
|
578 |
|
|
$set->action('component_rules_test_set_2', array('node1:select' => 'node1'))
|
579 |
|
|
->save();
|
580 |
|
|
|
581 |
|
|
$rule->execute($node);
|
582 |
|
|
$text1 = RulesLog::logger()->render();
|
583 |
|
|
$text2 = RulesTestCase::t('Not evaluating rule set %rules_test_set_2 to prevent recursion.', array('rules_test_set_2'));
|
584 |
|
|
$this->assertTrue((strpos($text1, $text2) !== FALSE), "Recursion of component invocation prevented.");
|
585 |
|
|
|
586 |
|
|
// Test executing the component provided in code via the action. This makes
|
587 |
|
|
// sure the component in code has been properly picked up.
|
588 |
|
|
$node->status = 0;
|
589 |
|
|
node_save($node);
|
590 |
|
|
rules_action('component_rules_test_action_set')->execute($node);
|
591 |
|
|
$this->assertTrue($node->status == 1, 'Component provided in code has been executed.');
|
592 |
|
|
}
|
593 |
|
|
|
594 |
|
|
|
595 |
|
|
/**
|
596 |
|
|
* Test asserting metadata, customizing action info and make sure integrity
|
597 |
|
|
* is checked.
|
598 |
|
|
*/
|
599 |
|
|
function testMetadataAssertion() {
|
600 |
|
|
$action = rules_action('rules_node_make_sticky_action');
|
601 |
|
|
|
602 |
|
|
// Test failing integrity check.
|
603 |
|
|
try {
|
604 |
|
|
$rule = rule(array('node' => array('type' => 'entity')));
|
605 |
|
|
$rule->action($action);
|
606 |
|
|
// Fails due to the 'node' variable not matching the node type.
|
607 |
|
|
$rule->integrityCheck();
|
608 |
|
|
$this->fail('Integrity check has not thrown an exception.');
|
609 |
|
|
}
|
610 |
|
|
catch (RulesIntegrityException $e) {
|
611 |
|
|
$this->pass('Integrity check has thrown exception: ' . $e->getMessage());
|
612 |
|
|
}
|
613 |
|
|
|
614 |
|
|
// Test asserting additional metadata.
|
615 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
616 |
|
|
// Customize action info using the settings.
|
617 |
|
|
$rule->condition('data_is', array('data:select' => 'node:type', 'value' => 'page'))
|
618 |
|
|
// Configure an condition using the body. As the body is a field,
|
619 |
|
|
// tis requires the bundle to be correctly asserted.
|
620 |
|
|
->condition(rules_condition('data_is', array('data:select' => 'node:body:value', 'value' => 'foo'))->negate())
|
621 |
|
|
// The action also requires the page bundle in order to work.
|
622 |
|
|
->action($action);
|
623 |
|
|
// Make sure the integrity check doesn't throw an exception.
|
624 |
|
|
$rule->integrityCheck();
|
625 |
|
|
// Test the rule.
|
626 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
627 |
|
|
$rule->execute($node);
|
628 |
|
|
$this->assertTrue($node->sticky, 'Rule with asserted metadata executed.');
|
629 |
|
|
|
630 |
|
|
|
631 |
|
|
// Test asserting metadata on a derived property, i.e. not a variable.
|
632 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
633 |
|
|
$rule->condition('entity_is_of_type', array('entity:select' => 'node:reference', 'type' => 'node'))
|
634 |
|
|
->condition('data_is', array('data:select' => 'node:reference:type', 'value' => 'page'))
|
635 |
|
|
->action('rules_node_page_make_sticky_action', array('node:select' => 'node:reference'));
|
636 |
|
|
$rule->integrityCheck();
|
637 |
|
|
$rule->execute($node);
|
638 |
|
|
|
639 |
|
|
// Test asserting an entity field.
|
640 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
641 |
|
|
$rule->condition('entity_has_field', array('entity:select' => 'node:reference', 'field' => 'field_tags'))
|
642 |
|
|
->action('data_set', array('data:select' => 'node:reference:field-tags', 'value' => array()));
|
643 |
|
|
$rule->integrityCheck();
|
644 |
|
|
$rule->execute($node);
|
645 |
|
|
|
646 |
|
|
// Make sure an asserted bundle can be used as argument.
|
647 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
648 |
|
|
$rule->condition('entity_is_of_type', array('entity:select' => 'node:reference', 'type' => 'node'))
|
649 |
|
|
->condition('node_is_of_type', array('node:select' => 'node:reference', 'type' => array('page')))
|
650 |
|
|
->action('rules_node_page_make_sticky_action', array('node:select' => 'node:reference'));
|
651 |
|
|
$rule->integrityCheck();
|
652 |
|
|
$rule->execute($node);
|
653 |
|
|
|
654 |
|
|
// Test asserting metadata on a derived property being a list item.
|
655 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
656 |
|
|
$rule->condition('node_is_of_type', array('node:select' => 'node:ref-nodes:0', 'type' => array('article')))
|
657 |
|
|
->action('data_set', array('data:select' => 'node:ref-nodes:0:field-tags', 'value' => array()));
|
658 |
|
|
$rule->integrityCheck();
|
659 |
|
|
$rule->execute($node);
|
660 |
|
|
|
661 |
|
|
// Give green lights if there were no exceptions and check rules-log errors.
|
662 |
|
|
$this->pass('Rules asserting metadata on a derived property pass integrity checks.');
|
663 |
|
|
RulesLog::logger()->checkLog();
|
664 |
|
|
|
665 |
|
|
// Make sure assertions of a one list item are not valid for another item.
|
666 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
667 |
|
|
$rule->condition('node_is_of_type', array('node:select' => 'node:ref-nodes:0', 'type' => array('article')))
|
668 |
|
|
->action('data_set', array('data:select' => 'node:ref-nodes:1:field-tags', 'value' => array()));
|
669 |
|
|
try {
|
670 |
|
|
$rule->integrityCheck();
|
671 |
|
|
$this->fail('Assertion of a list item is not valid for another item.');
|
672 |
|
|
}
|
673 |
|
|
catch (RulesException $e) {
|
674 |
|
|
$this->pass('Assertion of a list item is not valid for another item.');
|
675 |
|
|
}
|
676 |
|
|
}
|
677 |
|
|
|
678 |
|
|
/**
|
679 |
|
|
* Test using loops.
|
680 |
|
|
*/
|
681 |
|
|
function testLoops() {
|
682 |
|
|
// Test passing the list parameter as argument to ensure that is working
|
683 |
|
|
// generally for plugin container too.
|
684 |
|
|
drupal_get_messages(NULL, TRUE);
|
685 |
|
|
$loop = rules_loop();
|
686 |
|
|
$loop->action('drupal_message', array('message' => 'test'));
|
687 |
|
|
$arg_info = $loop->parameterInfo();
|
688 |
|
|
$this->assert($arg_info['list']['type'] == 'list', 'Argument info contains list.');
|
689 |
|
|
$loop->execute(array(1, 2));
|
690 |
|
|
|
691 |
|
|
// Ensure the action has been executed twice, once for each list item.
|
692 |
|
|
$msg = drupal_get_messages();
|
693 |
|
|
$this->assert($msg['status'][0] == 'test' && $msg['status'][1], 'Loop has been properly executed');
|
694 |
|
|
|
695 |
|
|
// Now test looping over nodes.
|
696 |
|
|
$node1 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
697 |
|
|
$node2 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
698 |
|
|
$node3 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
699 |
|
|
|
700 |
|
|
$rule = rule(array(
|
701 |
|
|
'list' => array(
|
702 |
|
|
'type' => 'list<node>',
|
703 |
|
|
'label' => 'A list of nodes',
|
704 |
|
|
)
|
705 |
|
|
));
|
706 |
|
|
$loop = rules_loop(array('list:select' => 'list', 'item:var' => 'node'));
|
707 |
|
|
$loop->action('data_set', array('data:select' => 'node:sticky', 'value' => TRUE));
|
708 |
|
|
$rule->action($loop);
|
709 |
|
|
// Test using a list with data selectors, just output the last nodes type.
|
710 |
|
|
$rule->action('drupal_message', array('message:select' => 'list:2:type'));
|
711 |
|
|
|
712 |
|
|
$rule->execute(array($node1->nid, $node2->nid, $node3->nid));
|
713 |
|
|
$text = RulesLog::logger()->render();
|
714 |
|
|
$save_msg = RulesTestCase::t('Saved %node of type %node.', array('node', 'node'));
|
715 |
|
|
$this->assertTrue(substr_count($text, $save_msg) == 3, 'List item variables have been saved.');
|
716 |
|
|
RulesLog::logger()->checkLog();
|
717 |
|
|
}
|
718 |
|
|
|
719 |
|
|
/**
|
720 |
|
|
* Test access checks.
|
721 |
|
|
*/
|
722 |
|
|
function testAccessCheck() {
|
723 |
|
|
$rule = rule();
|
724 |
|
|
// Try to set a property which is provided by the test module and is not
|
725 |
|
|
// accessible, so the access check has to return FALSE.
|
726 |
|
|
$rule->action('data_set', array('data:select' => 'site:no-access-user', 'value' => 'foo'));
|
727 |
|
|
$this->assertTrue($rule->access() === FALSE, 'Access check is working.');
|
728 |
|
|
}
|
729 |
|
|
|
730 |
|
|
/**
|
731 |
|
|
* Test returning provided variables.
|
732 |
|
|
*/
|
733 |
|
|
function testReturningVariables() {
|
734 |
|
|
$node = $this->drupalCreateNode();
|
735 |
|
|
$action = rules_action('entity_fetch', array('type' => 'node', 'id' => $node->nid));
|
736 |
|
|
list($node2) = $action->execute();
|
737 |
|
|
$this->assertTrue($node2->nid == $node->nid, 'Action returned a variable.');
|
738 |
|
|
|
739 |
|
|
// Create a simple set that just passed through the given node.
|
740 |
|
|
$set = rules_rule_set(array('node' => array('type' => 'node')), array('node'));
|
741 |
|
|
$set->integrityCheck()->save('rules_test_set_1');
|
742 |
|
|
|
743 |
|
|
$provides = $set->providesVariables();
|
744 |
|
|
$this->assertTrue($provides['node']['type'] == 'node', 'Rule set correctly passed through the node.');
|
745 |
|
|
|
746 |
|
|
list($node2) = $set->execute($node);
|
747 |
|
|
$this->assertTrue($node2->nid == $node->nid, 'Rule set returned a variable.');
|
748 |
|
|
|
749 |
|
|
// Create an action set returning a variable that is no parameter.
|
750 |
|
|
$set = rules_action_set(array(
|
751 |
|
|
'node' => array(
|
752 |
|
|
'type' => 'node',
|
753 |
|
|
'parameter' => FALSE,
|
754 |
|
|
)), array('node'));
|
755 |
|
|
$set->action('entity_fetch', array('type' => 'node', 'id' => $node->nid))
|
756 |
|
|
->action('data_set', array('data:select' => 'node', 'value:select' => 'entity_fetched'));
|
757 |
|
|
$set->integrityCheck();
|
758 |
|
|
list($node3) = $set->execute();
|
759 |
|
|
$this->assertTrue($node3->nid == $node->nid, 'Action set returned a variable that has not been passed as parameter.');
|
760 |
|
|
|
761 |
|
|
// Test the same again with a variable holding a not wrapped data type.
|
762 |
|
|
$set = rules_action_set(array(
|
763 |
|
|
'number' => array(
|
764 |
|
|
'type' => 'integer',
|
765 |
|
|
'parameter' => FALSE,
|
766 |
|
|
)), array('number'));
|
767 |
|
|
$set->action('data_set', array('data:select' => 'number', 'value' => 3));
|
768 |
|
|
$set->integrityCheck();
|
769 |
|
|
list($number) = $set->execute();
|
770 |
|
|
$this->assertTrue($number == 3, 'Actions set returned a number.');
|
771 |
|
|
}
|
772 |
|
|
|
773 |
|
|
/**
|
774 |
|
|
* Tests using input evaluators.
|
775 |
|
|
*/
|
776 |
|
|
function testInputEvaluators() {
|
777 |
|
|
$node = $this->drupalCreateNode(array('title' => '<b>The body & nothing.</b>', 'type' => 'page'));
|
778 |
|
|
|
779 |
|
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
780 |
|
|
$rule->action('rules_action_load_node')
|
781 |
|
|
->action('drupal_message', array('message' => 'Title: [node_loaded:title]'))
|
782 |
|
|
->execute($node->nid);
|
783 |
|
|
|
784 |
|
|
RulesLog::logger()->checkLog();
|
785 |
|
|
$msg = drupal_get_messages();
|
786 |
|
|
$this->assertEqual(array_pop($msg['status']), 'Title: ' . check_plain('<b>The body & nothing.</b>'), 'Token input evaluator applied.');
|
787 |
|
|
|
788 |
|
|
// Test token replacements on a list of text values.
|
789 |
|
|
$component = rules_action_set(array('var' => array('type' => 'list<text>', 'label' => 'var')), array('var'));
|
790 |
|
|
$component->save('rules_test_input');
|
791 |
|
|
|
792 |
|
|
$action = rules_action('component_rules_test_input', array('var' => array('uid: [site:current-user:uid]')));
|
793 |
|
|
list($var) = $action->execute();
|
794 |
|
|
$uid = $GLOBALS['user']->uid;
|
795 |
|
|
$this->assertEqual(array("uid: $uid"), $var, 'Token replacements on a list of values applied.');
|
796 |
|
|
}
|
797 |
|
|
|
798 |
|
|
/**
|
799 |
|
|
* Test importing and exporting a rule.
|
800 |
|
|
*/
|
801 |
|
|
function testRuleImportExport() {
|
802 |
|
|
$rule = rule(array('nid' => array('type' => 'integer')));
|
803 |
|
|
$rule->name = "rules_export_test";
|
804 |
|
|
$rule->action('rules_action_load_node')
|
805 |
|
|
->action('drupal_message', array('message' => 'Title: [node_loaded:title]'));
|
806 |
|
|
|
807 |
|
|
$export =
|
808 |
|
|
'{ "rules_export_test" : {
|
809 |
|
|
"PLUGIN" : "rule",
|
810 |
|
|
"REQUIRES" : [ "rules_test", "rules" ],
|
811 |
|
|
"USES VARIABLES" : { "nid" : { "type" : "integer" } },
|
812 |
|
|
"DO" : [
|
813 |
|
|
{ "rules_action_load_node" : { "PROVIDE" : { "node_loaded" : { "node_loaded" : "Loaded content" } } } },
|
814 |
|
|
{ "drupal_message" : { "message" : "Title: [node_loaded:title]" } }
|
815 |
|
|
]
|
816 |
|
|
}
|
817 |
|
|
}';
|
818 |
|
|
$this->assertEqual($export, $rule->export(), 'Rule has been exported correctly.');
|
819 |
|
|
|
820 |
|
|
// Test importing a rule which makes use of almost all features.
|
821 |
|
|
$export = _rules_export_get_test_export();
|
822 |
|
|
$rule = rules_import($export);
|
823 |
|
|
$this->assertTrue(!empty($rule) && $rule->integrityCheck(), 'Rule has been imported.');
|
824 |
|
|
|
825 |
|
|
// Test loading the same export provided as default rule.
|
826 |
|
|
$rule = rules_config_load('rules_export_test');
|
827 |
|
|
$this->assertTrue(!empty($rule) && $rule->integrityCheck(), 'Export has been provided in code.');
|
828 |
|
|
|
829 |
|
|
// Export it and make sure the same export is generated again.
|
830 |
|
|
$this->assertEqual($export, $rule->export(), 'Export of imported rule equals original export.');
|
831 |
|
|
|
832 |
|
|
// Now try importing a rule set.
|
833 |
|
|
$export =
|
834 |
|
|
'{ "rules_test_set" : {
|
835 |
|
|
"LABEL" : "Test set",
|
836 |
|
|
"PLUGIN" : "rule set",
|
837 |
|
|
"REQUIRES" : [ "rules" ],
|
838 |
|
|
"USES VARIABLES" : { "node" : { "label" : "Test node", "type" : "node" } },
|
839 |
|
|
"RULES" : [
|
840 |
|
|
{ "RULE" : {
|
841 |
|
|
"IF" : [ { "NOT data_is" : { "data" : [ "node:title" ], "value" : "test" } } ],
|
842 |
|
|
"DO" : [ { "data_set" : { "data" : [ "node:title" ], "value" : "test" } } ],
|
843 |
|
|
"LABEL" : "Test Rule"
|
844 |
|
|
}
|
845 |
|
|
},
|
846 |
|
|
{ "RULE" : {
|
847 |
|
|
"DO" : [ { "drupal_message" : { "message" : "hi" } } ],
|
848 |
|
|
"LABEL" : "Test Rule 2"
|
849 |
|
|
}
|
850 |
|
|
}
|
851 |
|
|
]
|
852 |
|
|
}
|
853 |
|
|
}';
|
854 |
|
|
$set = rules_import($export);
|
855 |
|
|
$this->assertTrue(!empty($set) && $set->integrityCheck(), 'Rule set has been imported.');
|
856 |
|
|
// Export it and make sure the same export is generated again.
|
857 |
|
|
$this->assertEqual($export, $set->export(), 'Export of imported rule set equals original export.');
|
858 |
|
|
|
859 |
|
|
// Try executing the imported rule set.
|
860 |
|
|
$node = $this->drupalCreateNode();
|
861 |
|
|
$set->execute($node);
|
862 |
|
|
$this->assertEqual($node->title, 'test', 'Imported rule set has been executed.');
|
863 |
|
|
RulesLog::logger()->checkLog();
|
864 |
|
|
|
865 |
|
|
// Try import / export for a rule component providing a variable.
|
866 |
|
|
$rule = rule(array(
|
867 |
|
|
'number' => array(
|
868 |
|
|
'type' => 'integer',
|
869 |
|
|
'label' => 'Number',
|
870 |
|
|
'parameter' => FALSE,
|
871 |
|
|
)), array('number'));
|
872 |
|
|
$rule->action('data_set', array('data:select' => 'number', 'value' => 3));
|
873 |
|
|
$rule->name = 'rules_test_provides';
|
874 |
|
|
|
875 |
|
|
$export = '{ "rules_test_provides" : {
|
876 |
|
|
"PLUGIN" : "rule",
|
877 |
|
|
"REQUIRES" : [ "rules" ],
|
878 |
|
|
"USES VARIABLES" : { "number" : { "type" : "integer", "label" : "Number", "parameter" : false } },
|
879 |
|
|
"DO" : [ { "data_set" : { "data" : [ "number" ], "value" : 3 } } ],
|
880 |
|
|
"PROVIDES VARIABLES" : [ "number" ]
|
881 |
|
|
}
|
882 |
|
|
}';
|
883 |
|
|
$this->assertEqual($export, $rule->export(), 'Rule 2 has been exported correctly.');
|
884 |
|
|
$imported_rule = rules_import($rule->export());
|
885 |
|
|
|
886 |
|
|
$this->assertTrue(!empty($imported_rule) && $imported_rule->integrityCheck(), 'Rule 2 has been imported.');
|
887 |
|
|
$this->assertEqual($export, $imported_rule->export(), 'Export of imported rule 2 equals original export.');
|
888 |
|
|
|
889 |
|
|
// Test importing a negated condition component.
|
890 |
|
|
$export = '{ "rules_negated_component" : {
|
891 |
|
|
"LABEL" : "negated_component",
|
892 |
|
|
"PLUGIN" : "or",
|
893 |
|
|
"REQUIRES" : [ "rules" ],
|
894 |
|
|
"NOT OR" : [ { "data_is_empty" : { "data" : [ "site:slogan" ] } } ]
|
895 |
|
|
}
|
896 |
|
|
}';
|
897 |
|
|
$or = rules_import($export);
|
898 |
|
|
$this->assertTrue($or->integrityCheck() && $or->isNegated(), 'Negated condition component imported.');
|
899 |
|
|
}
|
900 |
|
|
|
901 |
|
|
/**
|
902 |
|
|
* Test the named parameter mode.
|
903 |
|
|
*/
|
904 |
|
|
function testNamedParameters() {
|
905 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
906 |
|
|
$rule->action('rules_action_node_set_title', array('title' => 'foo'));
|
907 |
|
|
$rule->integrityCheck();
|
908 |
|
|
|
909 |
|
|
// Test the rule.
|
910 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
|
911 |
|
|
$rule->execute($node);
|
912 |
|
|
$this->assertTrue($node->title == 'foo', 'Action with named parameters has been correctly executed.');
|
913 |
|
|
RulesLog::logger()->checkLog();
|
914 |
|
|
}
|
915 |
|
|
|
916 |
|
|
/**
|
917 |
|
|
* Make sure Rules aborts when NULL values are used.
|
918 |
|
|
*/
|
919 |
|
|
function testAbortOnNULLValues() {
|
920 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
921 |
|
|
$rule->action('drupal_message', array('message:select' => 'node:log'));
|
922 |
|
|
$rule->integrityCheck();
|
923 |
|
|
|
924 |
|
|
// Test the rule.
|
925 |
|
|
$node = $this->drupalCreateNode();
|
926 |
|
|
$node->log = NULL;
|
927 |
|
|
$rule->execute($node);
|
928 |
|
|
|
929 |
|
|
$text = RulesLog::logger()->render();
|
930 |
|
|
$msg = RulesTestCase::t('The variable or parameter %message is empty.', array('message'));
|
931 |
|
|
$this->assertTrue(strpos($text, $msg) !== FALSE, 'Evaluation aborted due to an empty argument value.');
|
932 |
|
|
}
|
933 |
|
|
}
|
934 |
|
|
|
935 |
|
|
/**
|
936 |
|
|
* Test rules data wrappers.
|
937 |
|
|
*/
|
938 |
|
|
class RulesTestDataCase extends DrupalWebTestCase {
|
939 |
|
|
|
940 |
|
|
static function getInfo() {
|
941 |
|
|
return array(
|
942 |
|
|
'name' => 'Rules Data tests',
|
943 |
|
|
'description' => 'Tests rules data saving and type matching.',
|
944 |
|
|
'group' => 'Rules',
|
945 |
|
|
);
|
946 |
|
|
}
|
947 |
|
|
|
948 |
|
|
function setUp() {
|
949 |
|
|
parent::setUp('rules', 'rules_test');
|
950 |
|
|
variable_set('rules_debug_log', 1);
|
951 |
|
|
// Make sure we don't ran over issues with the node_load static cache.
|
952 |
|
|
entity_get_controller('node')->resetCache();
|
953 |
|
|
}
|
954 |
|
|
|
955 |
|
|
/**
|
956 |
|
|
* Tests intelligently saving data.
|
957 |
|
|
*/
|
958 |
|
|
function testDataSaving() {
|
959 |
|
|
$node = $this->drupalCreateNode();
|
960 |
|
|
$state = new RulesState(rule());
|
961 |
|
|
$state->addVariable('node', $node, array('type' => 'node'));
|
962 |
|
|
$wrapper = $state->get('node');
|
963 |
|
|
$node->title = 'test';
|
964 |
|
|
$wrapper->set($node);
|
965 |
|
|
$state->saveChanges('node', $wrapper, FALSE);
|
966 |
|
|
|
967 |
|
|
$this->assertFalse($this->drupalGetNodeByTitle('test'), 'Changes have not been saved.');
|
968 |
|
|
$state->saveChanges('node', $wrapper, TRUE);
|
969 |
|
|
$this->assertTrue($this->drupalGetNodeByTitle('test'), 'Changes have been saved.');
|
970 |
|
|
|
971 |
|
|
// Test skipping saving.
|
972 |
|
|
$state->addVariable('node2', $node, array(
|
973 |
|
|
'type' => 'node',
|
974 |
|
|
'skip save' => TRUE,
|
975 |
|
|
));
|
976 |
|
|
$wrapper = $state->get('node2');
|
977 |
|
|
$node->title = 'test2';
|
978 |
|
|
$wrapper->set($node);
|
979 |
|
|
$state->saveChanges('node2', $wrapper, TRUE);
|
980 |
|
|
$this->assertFalse($this->drupalGetNodeByTitle('test2'), 'Changes have not been saved.');
|
981 |
|
|
|
982 |
|
|
// Try saving a non-entity wrapper, which should result in saving the
|
983 |
|
|
// parent entity containing the property.
|
984 |
|
|
$wrapper = $state->get('node');
|
985 |
|
|
$wrapper->title->set('test3');
|
986 |
|
|
$state->saveChanges('node:title', $wrapper, TRUE);
|
987 |
|
|
$this->assertTrue($this->drupalGetNodeByTitle('test3'), 'Parent entity has been saved.');
|
988 |
|
|
}
|
989 |
|
|
|
990 |
|
|
/**
|
991 |
|
|
* Test type matching
|
992 |
|
|
*/
|
993 |
|
|
function testTypeMatching() {
|
994 |
|
|
$entity = array('type' => 'entity');
|
995 |
|
|
$node = array('type' => 'node');
|
996 |
|
|
$this->assertTrue(RulesData::typesMatch($node, $entity), 'Types match.');
|
997 |
|
|
$this->assertFalse(RulesData::typesMatch($entity, $node), 'Types don\'t match.');
|
998 |
|
|
|
999 |
|
|
$this->assertTrue(RulesData::typesMatch($node + array('bundle' => 'page'), $node), 'Types match.');
|
1000 |
|
|
$this->assertTrue(RulesData::typesMatch($node + array('bundle' => 'page'), $entity), 'Types match.');
|
1001 |
|
|
$this->assertTrue(RulesData::typesMatch(array('type' => 'list<node>'), array('type' => 'list')), 'Types match.');
|
1002 |
|
|
$this->assertTrue(RulesData::typesMatch($node + array('bundle' => 'page'), $node + array('bundles' => array('page', 'story'))), 'Types match.');
|
1003 |
|
|
$this->assertFalse(RulesData::typesMatch($node, $node + array('bundles' => array('page', 'story'))), 'Types don\'t match.');
|
1004 |
|
|
|
1005 |
|
|
// Test that a type matches its grand-parent type (text > decimal > integer)
|
1006 |
|
|
$this->assertTrue(RulesData::typesMatch(array('type' => 'integer'), array('type' => 'text')), 'Types match.');
|
1007 |
|
|
$this->assertFalse(RulesData::typesMatch(array('type' => 'text'), array('type' => 'integer')), 'Types don\'t match.');
|
1008 |
|
|
}
|
1009 |
|
|
|
1010 |
|
|
/**
|
1011 |
|
|
* Tests making use of custom wrapper classes.
|
1012 |
|
|
*/
|
1013 |
|
|
function testCustomWrapperClasses() {
|
1014 |
|
|
// Test loading a vocabulary by name, which is done by a custom wrapper.
|
1015 |
|
|
$set = rules_action_set(array('vocab' => array('type' => 'taxonomy_vocabulary')), array('vocab'));
|
1016 |
|
|
$set->action('drupal_message', array('message:select' => 'vocab:name'));
|
1017 |
|
|
$set->integrityCheck();
|
1018 |
|
|
list($vocab) = $set->execute('tags');
|
1019 |
|
|
$this->assertTrue($vocab->machine_name == 'tags', 'Loaded vocabulary by name.');
|
1020 |
|
|
|
1021 |
|
|
// Now test wrapper creation for a direct input argument value.
|
1022 |
|
|
$set = rules_action_set(array('term' => array('type' => 'taxonomy_term')));
|
1023 |
|
|
$set->action('data_set', array('data:select' => 'term:vocabulary', 'value' => 'tags'));
|
1024 |
|
|
$set->integrityCheck();
|
1025 |
|
|
|
1026 |
|
|
$vocab = entity_create('taxonomy_vocabulary', array(
|
1027 |
|
|
'name' => 'foo',
|
1028 |
|
|
'machine_name' => 'foo',
|
1029 |
|
|
));
|
1030 |
|
|
entity_save('taxonomy_vocabulary', $vocab);
|
1031 |
|
|
$term_wrapped = entity_property_values_create_entity('taxonomy_term', array(
|
1032 |
|
|
'name' => $this->randomName(),
|
1033 |
|
|
'vocabulary' => $vocab,
|
1034 |
|
|
))->save();
|
1035 |
|
|
$set->execute($term_wrapped);
|
1036 |
|
|
$this->assertEqual($term_wrapped->vocabulary->machine_name->value(), 'tags', 'Vocabulary name used as direct input value.');
|
1037 |
|
|
RulesLog::logger()->checkLog();
|
1038 |
|
|
}
|
1039 |
|
|
|
1040 |
|
|
/**
|
1041 |
|
|
* Makes sure the RulesIdentifiableDataWrapper is working correctly.
|
1042 |
|
|
*/
|
1043 |
|
|
function testRulesIdentifiableDataWrapper() {
|
1044 |
|
|
$node = $this->drupalCreateNode();
|
1045 |
|
|
$wrapper = new RulesTestTypeWrapper('rules_test_type', $node);
|
1046 |
|
|
$this->assertTrue($wrapper->value() == $node, 'Data correctly wrapped.');
|
1047 |
|
|
|
1048 |
|
|
// Test serializing and make sure only the id is stored.
|
1049 |
|
|
$this->assertTrue(strpos(serialize($wrapper), $node->title) === FALSE, 'Data has been correctly serialized.');
|
1050 |
|
|
$this->assertEqual(unserialize(serialize($wrapper))->value()->title, $node->title, 'Serializing works right.');
|
1051 |
|
|
|
1052 |
|
|
$wrapper2 = unserialize(serialize($wrapper));
|
1053 |
|
|
// Test serializing the unloaded wrapper.
|
1054 |
|
|
$this->assertEqual(unserialize(serialize($wrapper2))->value()->title, $node->title, 'Serializing works right.');
|
1055 |
|
|
|
1056 |
|
|
// Test loading a not more existing node.
|
1057 |
|
|
$s = serialize($wrapper2);
|
1058 |
|
|
node_delete($node->nid);
|
1059 |
|
|
$this->assertFalse(node_load($node->nid), 'Node deleted.');
|
1060 |
|
|
try {
|
1061 |
|
|
unserialize($s)->value();
|
1062 |
|
|
$this->fail("Loading hasn't created an exception.");
|
1063 |
|
|
}
|
1064 |
|
|
catch (EntityMetadataWrapperException $e) {
|
1065 |
|
|
$this->pass("Exception was thrown: ". $e->getMessage());
|
1066 |
|
|
}
|
1067 |
|
|
|
1068 |
|
|
// Test saving a savable custom, identifiable wrapper.
|
1069 |
|
|
$action = rules_action('test_type_save');
|
1070 |
|
|
$node = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
|
1071 |
|
|
$node->status = 1;
|
1072 |
|
|
$action->execute($node);
|
1073 |
|
|
|
1074 |
|
|
// Load the node fresh from the db.
|
1075 |
|
|
$node = node_load($node->nid, NULL, TRUE);
|
1076 |
|
|
$this->assertEqual($node->status, 1, 'Savable non-entity has been saved.');
|
1077 |
|
|
}
|
1078 |
|
|
}
|
1079 |
|
|
|
1080 |
|
|
/**
|
1081 |
|
|
* Test triggering rules.
|
1082 |
|
|
*/
|
1083 |
|
|
class RulesTriggerTestCase extends DrupalWebTestCase {
|
1084 |
|
|
|
1085 |
|
|
static function getInfo() {
|
1086 |
|
|
return array(
|
1087 |
|
|
'name' => 'Reaction Rules',
|
1088 |
|
|
'description' => 'Tests triggering reactive rules.',
|
1089 |
|
|
'group' => 'Rules',
|
1090 |
|
|
);
|
1091 |
|
|
}
|
1092 |
|
|
|
1093 |
|
|
function setUp() {
|
1094 |
|
|
parent::setUp('rules', 'rules_test');
|
1095 |
|
|
RulesLog::logger()->clear();
|
1096 |
|
|
variable_set('rules_debug_log', 1);
|
1097 |
|
|
}
|
1098 |
|
|
|
1099 |
|
|
protected function createTestRule($action = TRUE, $event = 'node_presave') {
|
1100 |
|
|
$rule = rules_reaction_rule();
|
1101 |
|
|
$rule->event($event)
|
1102 |
|
|
->condition(rules_condition('data_is', array('data:select' => 'node:status', 'value' => TRUE))->negate())
|
1103 |
|
|
->condition('data_is', array('data:select' => 'node:type', 'value' => 'page'));
|
1104 |
|
|
if ($action) {
|
1105 |
|
|
$rule->action('rules_action_delete_node');
|
1106 |
|
|
}
|
1107 |
|
|
return $rule;
|
1108 |
|
|
}
|
1109 |
|
|
|
1110 |
|
|
/**
|
1111 |
|
|
* Tests CRUD for reaction rules - making sure the events are stored properly.
|
1112 |
|
|
*/
|
1113 |
|
|
function testReactiveRuleCreation() {
|
1114 |
|
|
$rule = $this->createTestRule();
|
1115 |
|
|
$rule->save();
|
1116 |
|
|
$result = db_query("SELECT event FROM {rules_trigger} WHERE id = :id", array(':id' => $rule->id));
|
1117 |
|
|
$this->assertEqual($result->fetchField(), 'node_presave', 'Associated event has been saved.');
|
1118 |
|
|
// Try updating.
|
1119 |
|
|
$rule->removeEvent('node_presave');
|
1120 |
|
|
$rule->event('node_insert');
|
1121 |
|
|
$rule->event('node_update');
|
1122 |
|
|
$rule->active = FALSE;
|
1123 |
|
|
$rule->integrityCheck()->save();
|
1124 |
|
|
$result = db_query("SELECT event FROM {rules_trigger} WHERE id = :id", array(':id' => $rule->id));
|
1125 |
|
|
$this->assertEqual($result->fetchCol(), array_values($rule->events()), 'Updated associated events.');
|
1126 |
|
|
// Try deleting.
|
1127 |
|
|
$rule->delete();
|
1128 |
|
|
$result = db_query("SELECT event FROM {rules_trigger} WHERE id = :id", array(':id' => $rule->id));
|
1129 |
|
|
$this->assertEqual($result->fetchField(), FALSE, 'Deleted associated events.');
|
1130 |
|
|
}
|
1131 |
|
|
|
1132 |
|
|
/**
|
1133 |
|
|
* Tests creating and triggering a basic reaction rule.
|
1134 |
|
|
*/
|
1135 |
|
|
function testBasicReactionRule() {
|
1136 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page'));
|
1137 |
|
|
$rule = $this->createTestRule();
|
1138 |
|
|
$rule->integrityCheck()->save();
|
1139 |
|
|
// Test the basics of the event set work right.
|
1140 |
|
|
$event = rules_get_cache('event_node_presave');
|
1141 |
|
|
$this->assertEqual(array_keys($event->parameterInfo()), array('node'), 'EventSet returns correct argument info.');
|
1142 |
|
|
|
1143 |
|
|
// Trigger the rule by updating the node.
|
1144 |
|
|
$nid = $node->nid;
|
1145 |
|
|
$node->status = 0;
|
1146 |
|
|
node_save($node);
|
1147 |
|
|
|
1148 |
|
|
RulesLog::logger()->checkLog();
|
1149 |
|
|
$this->assertFalse(node_load($nid), 'Rule successfully triggered and executed');
|
1150 |
|
|
//debug(RulesLog::logger()->render());
|
1151 |
|
|
}
|
1152 |
|
|
|
1153 |
|
|
/**
|
1154 |
|
|
* Test a rule using a handler to load a variable.
|
1155 |
|
|
*/
|
1156 |
|
|
function testVariableHandler() {
|
1157 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1158 |
|
|
$rule = $this->createTestRule(FALSE, 'node_update');
|
1159 |
|
|
$rule->action('rules_node_publish_action_save', array('node:select' => 'node_unchanged'));
|
1160 |
|
|
// Test without recursion prevention to make sure recursive invocations
|
1161 |
|
|
// work right too. This rule won't ran in an infinite loop anyway.
|
1162 |
|
|
$rule->recursion = TRUE;
|
1163 |
|
|
$rule->label = 'rule 1';
|
1164 |
|
|
$rule->integrityCheck()->save();
|
1165 |
|
|
|
1166 |
|
|
$node->status = 0;
|
1167 |
|
|
$node->sticky = 1;
|
1168 |
|
|
node_save($node);
|
1169 |
|
|
|
1170 |
|
|
RulesLog::logger()->checkLog();
|
1171 |
|
|
entity_get_controller('node')->resetCache();
|
1172 |
|
|
$node = node_load($node->nid);
|
1173 |
|
|
|
1174 |
|
|
$this->assertFalse($node->sticky, 'Parameter has been loaded and saved.');
|
1175 |
|
|
$this->assertTrue($node->status, 'Action has been executed.');
|
1176 |
|
|
|
1177 |
|
|
// Ensure the rule was evaluated a second time
|
1178 |
|
|
$text = RulesLog::logger()->render();
|
1179 |
|
|
$msg = RulesTestCase::t('Evaluating conditions of rule %rule 1', array('rule 1'));
|
1180 |
|
|
$pos = strpos($text, $msg);
|
1181 |
|
|
$pos = ($pos !== FALSE) ? strpos($text, $msg, $pos) : FALSE;
|
1182 |
|
|
$this->assertTrue($pos !== FALSE, "Recursion prevented.");
|
1183 |
|
|
//debug(RulesLog::logger()->render());
|
1184 |
|
|
}
|
1185 |
|
|
|
1186 |
|
|
/**
|
1187 |
|
|
* Test aborting silently when handlers are not able to load.
|
1188 |
|
|
*/
|
1189 |
|
|
function testVariableHandlerFailing() {
|
1190 |
|
|
$rule = $this->createTestRule(FALSE, 'node_presave');
|
1191 |
|
|
$rule->action('rules_node_publish_action_save', array('node:select' => 'node_unchanged'));
|
1192 |
|
|
$rule->integrityCheck()->save();
|
1193 |
|
|
|
1194 |
|
|
// On insert it's not possible to get the unchanged node during presave.
|
1195 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1196 |
|
|
|
1197 |
|
|
//debug(RulesLog::logger()->render());
|
1198 |
|
|
$text = RulesTestCase::t('Unable to load variable %node_unchanged, aborting.', array('node_unchanged'));
|
1199 |
|
|
$this->assertTrue(strpos(RulesLog::logger()->render(), $text) !== FALSE, "Aborted evaluation.");
|
1200 |
|
|
}
|
1201 |
|
|
|
1202 |
|
|
/**
|
1203 |
|
|
* Tests preventing recursive rule invocations by creating a rule that reacts
|
1204 |
|
|
* on node-update and generates a node update that would trigger it itself.
|
1205 |
|
|
*/
|
1206 |
|
|
function testRecursionPrevention() {
|
1207 |
|
|
$rule = $this->createTestRule(FALSE, 'node_update');
|
1208 |
|
|
$rule->action('rules_node_make_sticky_action');
|
1209 |
|
|
$rule->integrityCheck()->save();
|
1210 |
|
|
|
1211 |
|
|
// Now trigger the rule.
|
1212 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1213 |
|
|
node_save($node);
|
1214 |
|
|
|
1215 |
|
|
$text = RulesTestCase::t('Not evaluating reaction rule %label to prevent recursion.', array('label' => $rule->name));
|
1216 |
|
|
//debug(RulesLog::logger()->render());
|
1217 |
|
|
$this->assertTrue((strpos(RulesLog::logger()->render(), $text) !== FALSE), "Recursion prevented.");
|
1218 |
|
|
//debug(RulesLog::logger()->render());
|
1219 |
|
|
}
|
1220 |
|
|
|
1221 |
|
|
/**
|
1222 |
|
|
* Ensure the recursion prevention still allows to let the rule trigger again
|
1223 |
|
|
* during evaluation of the same event set, if the event isn't caused by the
|
1224 |
|
|
* rule itself - thus we won't run in an infinte loop.
|
1225 |
|
|
*/
|
1226 |
|
|
function testRecursionOnDifferentArguments() {
|
1227 |
|
|
// Create rule1 - which might recurse.
|
1228 |
|
|
$rule = $this->createTestRule(FALSE, 'node_update');
|
1229 |
|
|
$rule->action('rules_node_make_sticky_action');
|
1230 |
|
|
$rule->label = 'rule 1';
|
1231 |
|
|
$rule->integrityCheck()->save();
|
1232 |
|
|
|
1233 |
|
|
// Create rule2 - which triggers rule1 on another node.
|
1234 |
|
|
$node2 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1235 |
|
|
$rule2 = $this->createTestRule(FALSE, 'node_update');
|
1236 |
|
|
$rule2->action('rules_action_load_node', array('nid' => $node2->nid))
|
1237 |
|
|
->action('rules_node_make_sticky_action', array('node:select' => 'node_loaded'));
|
1238 |
|
|
$rule2->label = 'rule 2';
|
1239 |
|
|
$rule2->save();
|
1240 |
|
|
|
1241 |
|
|
// Now trigger both rules by generating the event.
|
1242 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1243 |
|
|
node_save($node);
|
1244 |
|
|
|
1245 |
|
|
//debug(RulesLog::logger()->render());
|
1246 |
|
|
$text = RulesLog::logger()->render();
|
1247 |
|
|
$pos = strpos($text, RulesTestCase::t('Evaluating conditions of rule %rule 1', array('rule 1')));
|
1248 |
|
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Evaluating conditions of rule %rule 2', array('rule 2')), $pos) : FALSE;
|
1249 |
|
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Saved %node_loaded of type %node.', array('node_loaded', 'node')), $pos) : FALSE;
|
1250 |
|
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Evaluating conditions of rule %rule 1', array('rule 1')), $pos) : FALSE;
|
1251 |
|
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Not evaluating reaction rule %rule 2 to prevent recursion', array('rule 2')), $pos) : FALSE;
|
1252 |
|
|
$this->assertTrue($pos !== FALSE, 'Rule1 was triggered on the event caused by Rule2.');
|
1253 |
|
|
}
|
1254 |
|
|
|
1255 |
|
|
/**
|
1256 |
|
|
* Tests the provided default rule 'rules_test_default_1'.
|
1257 |
|
|
*/
|
1258 |
|
|
function testDefaultRule() {
|
1259 |
|
|
$rule = rules_config_load('rules_test_default_1');
|
1260 |
|
|
$this->assertTrue($rule->status & ENTITY_IN_CODE && !($rule->status & ENTITY_IN_DB), 'Default rule can be loaded and has the right status.');
|
1261 |
|
|
// Enable.
|
1262 |
|
|
$rule->active = TRUE;
|
1263 |
|
|
$rule->save();
|
1264 |
|
|
|
1265 |
|
|
// Create a node that triggers the rule.
|
1266 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1267 |
|
|
// Clear messages.
|
1268 |
|
|
drupal_get_messages();
|
1269 |
|
|
// Let event node_update occur.
|
1270 |
|
|
node_save($node);
|
1271 |
|
|
|
1272 |
|
|
$msg = drupal_get_messages();
|
1273 |
|
|
$this->assertEqual($msg['status'][0], 'A node has been updated.', 'Default rule has been triggered.');
|
1274 |
|
|
}
|
1275 |
|
|
|
1276 |
|
|
/**
|
1277 |
|
|
* Tests creating and triggering a reaction rule with event settings.
|
1278 |
|
|
*/
|
1279 |
|
|
function testEventSettings() {
|
1280 |
|
|
$rule = rules_reaction_rule();
|
1281 |
|
|
$rule->event('node_presave', array('bundle' => 'article'))
|
1282 |
|
|
->condition('data_is_empty', array('data:select' => 'node:field-tags'))
|
1283 |
|
|
->action('node_publish', array('node:select' => 'node'));
|
1284 |
|
|
$rule->integrityCheck()->save();
|
1285 |
|
|
|
1286 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'status' => 0));
|
1287 |
|
|
$this->assertEqual($node->status, 0, 'Rule has not been triggered.');
|
1288 |
|
|
$node = $this->drupalCreateNode(array('type' => 'article', 'status' => 0));
|
1289 |
|
|
$this->assertEqual($node->status, 1, 'Rule has been triggered.');
|
1290 |
|
|
RulesLog::logger()->checkLog();
|
1291 |
|
|
|
1292 |
|
|
// Make sure an invalid bundle raises integrity problems.
|
1293 |
|
|
$rule->event('node_presave', array('bundle' => 'invalid'));
|
1294 |
|
|
try {
|
1295 |
|
|
$rule->integrityCheck();
|
1296 |
|
|
$this->fail('Integrity check failed.');
|
1297 |
|
|
}
|
1298 |
|
|
catch (RulesIntegrityException $e) {
|
1299 |
|
|
$this->pass('Integrity check failed: ' . $e);
|
1300 |
|
|
}
|
1301 |
|
|
}
|
1302 |
|
|
}
|
1303 |
|
|
|
1304 |
|
|
/**
|
1305 |
|
|
* Tests provided module integration.
|
1306 |
|
|
*/
|
1307 |
|
|
class RulesIntegrationTestCase extends DrupalWebTestCase {
|
1308 |
|
|
|
1309 |
|
|
static function getInfo() {
|
1310 |
|
|
return array(
|
1311 |
|
|
'name' => 'Rules Core Integration',
|
1312 |
|
|
'description' => 'Tests provided integration for drupal core.',
|
1313 |
|
|
'group' => 'Rules',
|
1314 |
|
|
);
|
1315 |
|
|
}
|
1316 |
|
|
|
1317 |
|
|
function setUp() {
|
1318 |
|
|
parent::setUp('rules', 'rules_test', 'php', 'path');
|
1319 |
|
|
RulesLog::logger()->clear();
|
1320 |
|
|
variable_set('rules_debug_log', 1);
|
1321 |
|
|
}
|
1322 |
|
|
|
1323 |
|
|
/**
|
1324 |
|
|
* Just make sure the access callback run without errors.
|
1325 |
|
|
*/
|
1326 |
|
|
function testAccessCallbacks() {
|
1327 |
|
|
$cache = rules_get_cache();
|
1328 |
|
|
foreach (array('action', 'condition', 'event') as $type) {
|
1329 |
|
|
foreach (rules_fetch_data($type . '_info') as $name => $info) {
|
1330 |
|
|
if (isset($info['access callback'])) {
|
1331 |
|
|
$info['access callback']($type, $name);
|
1332 |
|
|
}
|
1333 |
|
|
}
|
1334 |
|
|
}
|
1335 |
|
|
}
|
1336 |
|
|
|
1337 |
|
|
/**
|
1338 |
|
|
* Test data integration.
|
1339 |
|
|
*/
|
1340 |
|
|
function testDataIntegration() {
|
1341 |
|
|
// Test data_create action.
|
1342 |
|
|
$action = rules_action('data_create', array(
|
1343 |
|
|
'type' => 'log_entry',
|
1344 |
|
|
'param_type' => 'rules_test',
|
1345 |
|
|
'param_message' => 'Rules test log message',
|
1346 |
|
|
'param_severity' => WATCHDOG_WARNING,
|
1347 |
|
|
'param_request_uri' => 'http://example.com',
|
1348 |
|
|
'param_link' => '',
|
1349 |
|
|
));
|
1350 |
|
|
$action->access();
|
1351 |
|
|
$action->execute();
|
1352 |
|
|
$text = RulesLog::logger()->render();
|
1353 |
|
|
$pos = strpos($text, RulesTestCase::t('Added the provided variable %data_created of type %log_entry', array('data_created', 'log_entry')));
|
1354 |
|
|
$this->assertTrue($pos !== FALSE, 'Data of type log entry has been created.');
|
1355 |
|
|
|
1356 |
|
|
|
1357 |
|
|
// Test variable_add action.
|
1358 |
|
|
$action = rules_action('variable_add', array(
|
1359 |
|
|
'type' => 'text_formatted',
|
1360 |
|
|
'value' => array(
|
1361 |
|
|
'value' => 'test text',
|
1362 |
|
|
'format' => 1,
|
1363 |
|
|
)
|
1364 |
|
|
));
|
1365 |
|
|
$action->access();
|
1366 |
|
|
$action->execute();
|
1367 |
|
|
$text = RulesLog::logger()->render();
|
1368 |
|
|
$pos = strpos($text, RulesTestCase::t('Added the provided variable %variable_added of type %text_formatted', array('variable_added', 'text_formatted')));
|
1369 |
|
|
$this->assertTrue($pos !== FALSE, 'Data of type text formatted has been created.');
|
1370 |
|
|
|
1371 |
|
|
|
1372 |
|
|
// Test using the list actions.
|
1373 |
|
|
$rule = rule(array(
|
1374 |
|
|
'list' => array(
|
1375 |
|
|
'type' => 'list<text>',
|
1376 |
|
|
'label' => 'A list of text',
|
1377 |
|
|
)
|
1378 |
|
|
));
|
1379 |
|
|
$rule->action('list_add', array('list:select' => 'list', 'item' => 'bar2'));
|
1380 |
|
|
$rule->action('list_add', array('list:select' => 'list', 'item' => 'bar', 'pos' => 'start'));
|
1381 |
|
|
$rule->action('list_add', array('list:select' => 'list', 'item' => 'bar', 'unique' => TRUE));
|
1382 |
|
|
$rule->action('list_remove', array('list:select' => 'list', 'item' => 'bar2'));
|
1383 |
|
|
$list = entity_metadata_wrapper('list', array('foo', 'foo2'));
|
1384 |
|
|
$rule->execute($list);
|
1385 |
|
|
RulesLog::logger()->checkLog();
|
1386 |
|
|
$this->assertEqual($list->value(), array('bar', 'foo', 'foo2'), 'List items removed and added.');
|
1387 |
|
|
$this->assertFalse(rules_condition('list_contains')->execute($list, 'foo-bar'), 'Condition "List item contains" evaluates to FALSE');
|
1388 |
|
|
$this->assertTrue(rules_condition('list_contains')->execute($list, 'foo'), 'Condition "List item contains" evaluates to TRUE');
|
1389 |
|
|
//debug(RulesLog::logger()->render());
|
1390 |
|
|
|
1391 |
|
|
// Test data_is condition with IN operation.
|
1392 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
1393 |
|
|
$rule->condition('data_is', array('data:select' => 'node:title', 'op' => 'IN', 'value' => array('foo', 'bar')));
|
1394 |
|
|
$rule->action('data_set', array('data:select' => 'node:title', 'value' => 'bar'));
|
1395 |
|
|
$rule->integrityCheck();
|
1396 |
|
|
|
1397 |
|
|
$node = $this->drupalCreateNode(array('title' => 'foo'));
|
1398 |
|
|
$rule->execute($node);
|
1399 |
|
|
$this->assertEqual($node->title, 'bar', "Data comparision using IN operation evaluates to TRUE.");
|
1400 |
|
|
|
1401 |
|
|
|
1402 |
|
|
// Test Condition: Data is empty.
|
1403 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
1404 |
|
|
$rule->condition('data_is_empty', array('data:select' => 'node:title'));
|
1405 |
|
|
$rule->action('data_set', array('data:select' => 'node:title', 'value' => 'bar'));
|
1406 |
|
|
$rule->integrityCheck();
|
1407 |
|
|
|
1408 |
|
|
// Data is empty condition evaluates to TRUE
|
1409 |
|
|
// for node with empty title, action sets title to 'bar'.
|
1410 |
|
|
$node = $this->drupalCreateNode(array('title' => '', 'type' => 'article'));
|
1411 |
|
|
$rule->execute($node);
|
1412 |
|
|
$this->assertEqual($node->title, 'bar', "Data is empty condition evaluates to TRUE for node with empty title, action sets title to 'bar'.");
|
1413 |
|
|
|
1414 |
|
|
// Data is empty condition evaluates to FALSE
|
1415 |
|
|
// for node with title 'foo', action is not executed.
|
1416 |
|
|
$node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article'));
|
1417 |
|
|
$rule->execute($node);
|
1418 |
|
|
$this->assertEqual($node->title, 'foo', "Data is empty condition evaluates to FALSE for node with title 'foo', action is not executed.");
|
1419 |
|
|
|
1420 |
|
|
// Data is empty condition evaluates to TRUE for the parent of a
|
1421 |
|
|
// not existing term in the tags field of the node.
|
1422 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
1423 |
|
|
$rule->condition('node_is_of_type', array('type' => array('article')));
|
1424 |
|
|
$rule->condition('data_is_empty', array('data:select' => 'node:field-tags:0:parent'));
|
1425 |
|
|
$rule->action('data_set', array('data:select' => 'node:title', 'value' => 'bar'));
|
1426 |
|
|
$rule->integrityCheck();
|
1427 |
|
|
$node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article'));
|
1428 |
|
|
$rule->execute($node);
|
1429 |
|
|
$this->assertEqual($node->title, 'bar', "Data is empty condition evaluates to TRUE for not existing data structures");
|
1430 |
|
|
|
1431 |
|
|
// Test Action: Calculate a value.
|
1432 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
1433 |
|
|
$rule->action('data_calc', array('input_1:select' => 'node:nid', 'op' => '*', 'input_2' => 2));
|
1434 |
|
|
$rule->action('data_set', array('data:select' => 'node:title', 'value:select' => 'result'));
|
1435 |
|
|
$rule->integrityCheck();
|
1436 |
|
|
$rule->execute($node);
|
1437 |
|
|
$this->assertEqual($node->title, $node->nid * 2, "Value has been calculated.");
|
1438 |
|
|
|
1439 |
|
|
// Test moving a date.
|
1440 |
|
|
$action_set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
|
1441 |
|
|
$action_set->action('data_calc', array('input_1:select' => 'date', 'op' => '+', 'input_2' => 3600))
|
1442 |
|
|
->action('data_set', array('data:select' => 'date', 'value:select' => 'result'));
|
1443 |
|
|
$action_set->integrityCheck();
|
1444 |
|
|
list($result) = $action_set->execute(REQUEST_TIME);
|
1445 |
|
|
$this->assertEqual($result, REQUEST_TIME + 3600, 'Used data calculation action to move a date by an hour.');
|
1446 |
|
|
|
1447 |
|
|
// Test data type conversion action.
|
1448 |
|
|
$set = rules_action_set(array('result' => array('type' => 'text', 'parameter' => FALSE)), array('result'));
|
1449 |
|
|
$set->action('data_convert', array('type' => 'text', 'value:select' => 'site:login-url'));
|
1450 |
|
|
$set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
|
1451 |
|
|
list($result) = $set->execute();
|
1452 |
|
|
$set->integrityCheck();
|
1453 |
|
|
$this->assertEqual($result, url('user', array('absolute' => TRUE)), 'Converted URI to text.');
|
1454 |
|
|
|
1455 |
|
|
$set = rules_action_set(array(
|
1456 |
|
|
'result' => array('type' => 'integer', 'parameter' => FALSE),
|
1457 |
|
|
'source' => array('type' => 'text'),
|
1458 |
|
|
), array('result'));
|
1459 |
|
|
$set->action('data_convert', array('type' => 'integer', 'value:select' => 'source'));
|
1460 |
|
|
$set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
|
1461 |
|
|
list($result) = $set->execute('9.4');
|
1462 |
|
|
$this->assertEqual($result, 9, 'Converted decimal to integer using rounding.');
|
1463 |
|
|
|
1464 |
|
|
$set = rules_action_set(array(
|
1465 |
|
|
'result' => array('type' => 'integer', 'parameter' => FALSE),
|
1466 |
|
|
'source' => array('type' => 'text'),
|
1467 |
|
|
), array('result'));
|
1468 |
|
|
$set->action('data_convert', array('type' => 'integer', 'value:select' => 'source', 'rounding_behavior' => 'down'));
|
1469 |
|
|
$set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
|
1470 |
|
|
list($result) = $set->execute('9.6');
|
1471 |
|
|
$this->assertEqual($result, 9, 'Converted decimal to integer using roundin behavio down.');
|
1472 |
|
|
|
1473 |
|
|
$set = rules_action_set(array(
|
1474 |
|
|
'result' => array('type' => 'integer', 'parameter' => FALSE),
|
1475 |
|
|
'source' => array('type' => 'text'),
|
1476 |
|
|
), array('result'));
|
1477 |
|
|
$set->action('data_convert', array('type' => 'integer', 'value:select' => 'source', 'rounding_behavior' => 'up'));
|
1478 |
|
|
$set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
|
1479 |
|
|
list($result) = $set->execute('9.4');
|
1480 |
|
|
$this->assertEqual($result, 10, 'Converted decimal to integer using rounding behavior up.');
|
1481 |
|
|
|
1482 |
|
|
// Test text matching condition.
|
1483 |
|
|
$result = rules_condition('text_matches')->execute('my-text', 'text', 'contains');
|
1484 |
|
|
$result2 = rules_condition('text_matches')->execute('my-text', 'tex2t', 'contains');
|
1485 |
|
|
$this->assertTrue($result && !$result2, 'Text matching condition using operation contain evaluated.');
|
1486 |
|
|
|
1487 |
|
|
$result = rules_condition('text_matches')->execute('my-text', 'my', 'starts');
|
1488 |
|
|
$result2 = rules_condition('text_matches')->execute('my-text', 'text', 'starts');
|
1489 |
|
|
$this->assertTrue($result && !$result2, 'Text matching condition using operation starts evaluated.');
|
1490 |
|
|
|
1491 |
|
|
$result = rules_condition('text_matches')->execute('my-text', 'text', 'ends');
|
1492 |
|
|
$result2 = rules_condition('text_matches')->execute('my-text', 'my', 'ends');
|
1493 |
|
|
$this->assertTrue($result && !$result2, 'Text matching condition using operation ends evaluated.');
|
1494 |
|
|
|
1495 |
|
|
$result = rules_condition('text_matches')->execute('my-text', 'me?y-texx?t', 'regex');
|
1496 |
|
|
$result2 = rules_condition('text_matches')->execute('my-text', 'me+y-texx?t', 'regex');
|
1497 |
|
|
$this->assertTrue($result && !$result2, 'Text matching condition using operation regex evaluated.');
|
1498 |
|
|
}
|
1499 |
|
|
|
1500 |
|
|
/**
|
1501 |
|
|
* Tests entity related integration.
|
1502 |
|
|
*/
|
1503 |
|
|
function testEntityIntegration() {
|
1504 |
|
|
global $user;
|
1505 |
|
|
|
1506 |
|
|
$page = $this->drupalCreateNode(array('type' => 'page'));
|
1507 |
|
|
$article = $this->drupalCreateNode(array('type' => 'article'));
|
1508 |
|
|
|
1509 |
|
|
$result = rules_condition('entity_field_access')
|
1510 |
|
|
->execute(entity_metadata_wrapper('node', $article), 'field_tags');
|
1511 |
|
|
$this->assertTrue($result);
|
1512 |
|
|
|
1513 |
|
|
// Test entiy_is_of_bundle condition.
|
1514 |
|
|
$result = rules_condition('entity_is_of_bundle', array(
|
1515 |
|
|
'type' => 'node',
|
1516 |
|
|
'bundle' => array('article'),
|
1517 |
|
|
))->execute(entity_metadata_wrapper('node', $page));
|
1518 |
|
|
$this->assertFalse($result, 'Entity is of bundle condition has not been met.');
|
1519 |
|
|
$result = rules_condition('entity_is_of_bundle', array(
|
1520 |
|
|
'type' => 'node',
|
1521 |
|
|
'bundle' => array('article'),
|
1522 |
|
|
))->execute(entity_metadata_wrapper('node', $article));
|
1523 |
|
|
$this->assertTrue($result, 'Entity is of bundle condition has been met.');
|
1524 |
|
|
|
1525 |
|
|
// Also test a full rule so the integrity check must work.
|
1526 |
|
|
$term_wrapped = entity_property_values_create_entity('taxonomy_term', array(
|
1527 |
|
|
'name' => $this->randomName(),
|
1528 |
|
|
'vocabulary' => 1,
|
1529 |
|
|
))->save();
|
1530 |
|
|
$rule = rule(array(
|
1531 |
|
|
'node' => array('type' => 'node'),
|
1532 |
|
|
));
|
1533 |
|
|
$rule->condition('entity_is_of_bundle', array(
|
1534 |
|
|
'entity:select' => 'node',
|
1535 |
|
|
'bundle' => array('article'),
|
1536 |
|
|
));
|
1537 |
|
|
$rule->action('data_set', array('data:select' => 'node:field_tags', 'value' => array($term_wrapped->getIdentifier())));
|
1538 |
|
|
$rule->integrityCheck();
|
1539 |
|
|
$rule->execute($article);
|
1540 |
|
|
$this->assertEqual($term_wrapped->getIdentifier(), $article->field_tags[LANGUAGE_NONE][0]['tid'], 'Entity is of bundle condition has been met.');
|
1541 |
|
|
|
1542 |
|
|
// Test again using an entity variable.
|
1543 |
|
|
$article = $this->drupalCreateNode(array('type' => 'article'));
|
1544 |
|
|
$rule = rule(array(
|
1545 |
|
|
'entity' => array('type' => 'entity'),
|
1546 |
|
|
));
|
1547 |
|
|
$rule->condition('entity_is_of_bundle', array(
|
1548 |
|
|
'entity:select' => 'entity',
|
1549 |
|
|
'type' => 'node',
|
1550 |
|
|
'bundle' => array('article'),
|
1551 |
|
|
));
|
1552 |
|
|
$rule->action('data_set', array('data:select' => 'entity:field_tags', 'value' => array($term_wrapped->getIdentifier())));
|
1553 |
|
|
$rule->integrityCheck();
|
1554 |
|
|
$rule->execute(entity_metadata_wrapper('node', $article));
|
1555 |
|
|
$this->assertEqual($term_wrapped->getIdentifier(), $article->field_tags[LANGUAGE_NONE][0]['tid'], 'Entity is of bundle condition has been met.');
|
1556 |
|
|
|
1557 |
|
|
// Test CRUD actions.
|
1558 |
|
|
$action = rules_action('entity_create', array(
|
1559 |
|
|
'type' => 'node',
|
1560 |
|
|
'param_type' => 'page',
|
1561 |
|
|
'param_title' => 'foo',
|
1562 |
|
|
'param_author' => $GLOBALS['user'],
|
1563 |
|
|
));
|
1564 |
|
|
$action->access();
|
1565 |
|
|
$action->execute();
|
1566 |
|
|
$text = RulesLog::logger()->render();
|
1567 |
|
|
$pos = strpos($text, RulesTestCase::t('Added the provided variable %entity_created of type %node', array('entity_created', 'node')));
|
1568 |
|
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Saved %entity_created of type %node.', array('entity_created', 'node')), $pos) : FALSE;
|
1569 |
|
|
$this->assertTrue($pos !== FALSE, 'Data has been created and saved.');
|
1570 |
|
|
|
1571 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
|
1572 |
|
|
$rule = rule();
|
1573 |
|
|
$rule->action('entity_fetch', array('type' => 'node', 'id' => $node->nid, 'entity_fetched:var' => 'node'));
|
1574 |
|
|
$rule->action('entity_save', array('data:select' => 'node', 'immediate' => TRUE));
|
1575 |
|
|
$rule->action('entity_delete', array('data:select' => 'node'));
|
1576 |
|
|
$rule->access();
|
1577 |
|
|
$rule->integrityCheck()->execute();
|
1578 |
|
|
|
1579 |
|
|
$text = RulesLog::logger()->render();
|
1580 |
|
|
$pos = strpos($text, RulesTestCase::t('Evaluating the action %entity_fetch.', array('entity_fetch')));
|
1581 |
|
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Added the provided variable %node of type %node', array('node')), $pos) : FALSE;
|
1582 |
|
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Saved %node of type %node.', array('node')), $pos) : FALSE;
|
1583 |
|
|
$pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Evaluating the action %entity_delete.', array('entity_delete')), $pos) : FALSE;
|
1584 |
|
|
$this->assertTrue($pos !== FALSE, 'Data has been fetched, saved and deleted.');
|
1585 |
|
|
//debug(RulesLog::logger()->render());
|
1586 |
|
|
|
1587 |
|
|
|
1588 |
|
|
|
1589 |
|
|
$node = entity_property_values_create_entity('node', array(
|
1590 |
|
|
'type' => 'article',
|
1591 |
|
|
'author' => $user,
|
1592 |
|
|
'title' => 'foo',
|
1593 |
|
|
))->value();
|
1594 |
|
|
$term_wrapped = entity_property_values_create_entity('taxonomy_term', array(
|
1595 |
|
|
'name' => $this->randomName(),
|
1596 |
|
|
'vocabulary' => 1,
|
1597 |
|
|
))->save();
|
1598 |
|
|
|
1599 |
|
|
// Test asserting the field and using it afterwards.
|
1600 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
1601 |
|
|
$rule->condition('entity_has_field', array('entity:select' => 'node', 'field' => 'field_tags'));
|
1602 |
|
|
$rule->condition('entity_is_new', array('entity:select' => 'node'));
|
1603 |
|
|
$rule->action('list_add', array('list:select' => 'node:field-tags', 'item' => $term_wrapped));
|
1604 |
|
|
$rule->integrityCheck();
|
1605 |
|
|
$rule->execute($node);
|
1606 |
|
|
|
1607 |
|
|
$tid = $term_wrapped->getIdentifier();
|
1608 |
|
|
$this->assertEqual(array_values($node->field_tags[LANGUAGE_NONE]), array(0 => array('tid' => $tid)), 'Entity has field conditions evaluted.');
|
1609 |
|
|
|
1610 |
|
|
// Test loading a non-node entity.
|
1611 |
|
|
$action = rules_action('entity_fetch', array('type' => 'taxonomy_term', 'id' => $tid));
|
1612 |
|
|
list($term) = $action->execute();
|
1613 |
|
|
$this->assertEqual($term->tid, $tid, 'Fetched a taxonomy term using "entity_fetch".');
|
1614 |
|
|
|
1615 |
|
|
// Test the entity is of type condition.
|
1616 |
|
|
$rule = rule(array('entity' => array('type' => 'entity', 'label' => 'entity')));
|
1617 |
|
|
$rule->condition('entity_is_of_type', array('type' => 'node'));
|
1618 |
|
|
$rule->action('data_set', array('data:select' => 'entity:title', 'value' => 'bar'));
|
1619 |
|
|
$rule->integrityCheck();
|
1620 |
|
|
$rule->execute(entity_metadata_wrapper('node', $node));
|
1621 |
|
|
|
1622 |
|
|
$this->assertEqual(entity_metadata_wrapper('node', $node->nid)->title->value(), 'bar', 'Entity is of type condition correctly asserts the entity type.');
|
1623 |
|
|
|
1624 |
|
|
|
1625 |
|
|
// Test the entity_query action.
|
1626 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'title' => 'foo2'));
|
1627 |
|
|
$rule = rule();
|
1628 |
|
|
$rule->action('entity_query', array('type' => 'node', 'property' => 'title', 'value' => 'foo2'))
|
1629 |
|
|
->action('data_set', array('data:select' => 'entity_fetched:0:title', 'value' => 'bar'));
|
1630 |
|
|
$rule->access();
|
1631 |
|
|
$rule->integrityCheck();
|
1632 |
|
|
$rule->execute();
|
1633 |
|
|
$node = node_load($node->nid);
|
1634 |
|
|
$this->assertEqual('bar', $node->title, 'Fetched a node by title and modified it.');
|
1635 |
|
|
|
1636 |
|
|
RulesLog::logger()->checkLog();
|
1637 |
|
|
}
|
1638 |
|
|
|
1639 |
|
|
/**
|
1640 |
|
|
* Test integration for the taxonomy module.
|
1641 |
|
|
*/
|
1642 |
|
|
function testTaxonomyIntegration() {
|
1643 |
|
|
$term = entity_property_values_create_entity('taxonomy_term', array(
|
1644 |
|
|
'name' => $this->randomName(),
|
1645 |
|
|
'vocabulary' => 1,
|
1646 |
|
|
))->value();
|
1647 |
|
|
$term2 = clone $term;
|
1648 |
|
|
taxonomy_term_save($term);
|
1649 |
|
|
taxonomy_term_save($term2);
|
1650 |
|
|
|
1651 |
|
|
$tags[LANGUAGE_NONE][0]['tid'] = $term->tid;
|
1652 |
|
|
$node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article', 'field_tags' => $tags));
|
1653 |
|
|
|
1654 |
|
|
// Test assigning and remove a term from an article.
|
1655 |
|
|
$rule = rule(array('node' => array('type' => 'node', 'bundle' => 'article')));
|
1656 |
|
|
$term_wrapped = rules_wrap_data($term->tid, array('type' => 'taxonomy_term'));
|
1657 |
|
|
$term_wrapped2 = rules_wrap_data($term2->tid, array('type' => 'taxonomy_term'));
|
1658 |
|
|
$rule->action('list_add', array('list:select' => 'node:field-tags', 'item' => $term_wrapped2));
|
1659 |
|
|
$rule->action('list_remove', array('list:select' => 'node:field-tags', 'item' => $term_wrapped));
|
1660 |
|
|
$rule->execute($node);
|
1661 |
|
|
RulesLog::logger()->checkLog();
|
1662 |
|
|
$this->assertEqual(array_values($node->field_tags[LANGUAGE_NONE]), array(0 => array('tid' => $term2->tid)), 'Term removed and added from a node.');
|
1663 |
|
|
|
1664 |
|
|
// Test using the taxonomy term reference field on a term object.
|
1665 |
|
|
$field_name = drupal_strtolower($this->randomName() . '_field_name');
|
1666 |
|
|
$field = field_create_field(array(
|
1667 |
|
|
'field_name' => $field_name,
|
1668 |
|
|
'type' => 'taxonomy_term_reference',
|
1669 |
|
|
// Set cardinality to unlimited for tagging.
|
1670 |
|
|
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
|
1671 |
|
|
'settings' => array(
|
1672 |
|
|
'allowed_values' => array(
|
1673 |
|
|
array(
|
1674 |
|
|
'vocabulary' => 'tags',
|
1675 |
|
|
'parent' => 0,
|
1676 |
|
|
),
|
1677 |
|
|
),
|
1678 |
|
|
),
|
1679 |
|
|
));
|
1680 |
|
|
$instance = array(
|
1681 |
|
|
'field_name' => $field_name,
|
1682 |
|
|
'entity_type' => 'taxonomy_term',
|
1683 |
|
|
'bundle' => 'tags', // Machine name of vocabulary.
|
1684 |
|
|
'label' => $this->randomName() . '_label',
|
1685 |
|
|
'description' => $this->randomName() . '_description',
|
1686 |
|
|
'weight' => mt_rand(0, 127),
|
1687 |
|
|
'widget' => array(
|
1688 |
|
|
'type' => 'taxonomy_autocomplete',
|
1689 |
|
|
'weight' => -4,
|
1690 |
|
|
),
|
1691 |
|
|
'display' => array(
|
1692 |
|
|
'default' => array(
|
1693 |
|
|
'type' => 'taxonomy_term_reference_link',
|
1694 |
|
|
'weight' => 10,
|
1695 |
|
|
),
|
1696 |
|
|
),
|
1697 |
|
|
);
|
1698 |
|
|
field_create_instance($instance);
|
1699 |
|
|
|
1700 |
|
|
$term1 = entity_property_values_create_entity('taxonomy_term', array(
|
1701 |
|
|
'name' => $this->randomName(),
|
1702 |
|
|
'vocabulary' => 1,
|
1703 |
|
|
))->save();
|
1704 |
|
|
$term2 = entity_property_values_create_entity('taxonomy_term', array(
|
1705 |
|
|
'name' => $this->randomName(),
|
1706 |
|
|
'vocabulary' => 1,
|
1707 |
|
|
))->save();
|
1708 |
|
|
|
1709 |
|
|
// Test asserting the term reference field and using it afterwards.
|
1710 |
|
|
$rule = rule(array('taxonomy_term' => array('type' => 'taxonomy_term')));
|
1711 |
|
|
$rule->condition('entity_has_field', array('entity:select' => 'taxonomy-term', 'field' => $field_name));
|
1712 |
|
|
// Add $term2 to $term1 using the term reference field.
|
1713 |
|
|
$selector = str_replace('_', '-', 'taxonomy_term:' . $field_name);
|
1714 |
|
|
$rule->action('list_add', array('list:select' => $selector, 'item' => $term2));
|
1715 |
|
|
$rule->integrityCheck();
|
1716 |
|
|
$rule->execute($term1);
|
1717 |
|
|
|
1718 |
|
|
RulesLog::logger()->checkLog();
|
1719 |
|
|
$this->assertEqual($term1->{$field_name}[0]->getIdentifier(), $term2->getIdentifier(), 'Rule appended a term to the term reference field on a term.');
|
1720 |
|
|
|
1721 |
|
|
// Test an action set for merging term parents, which is provided as default
|
1722 |
|
|
// config.
|
1723 |
|
|
$term = entity_property_values_create_entity('taxonomy_term', array(
|
1724 |
|
|
'name' => $this->randomName(),
|
1725 |
|
|
'vocabulary' => 1,
|
1726 |
|
|
'parent' => array($term1->value()),
|
1727 |
|
|
))->save();
|
1728 |
|
|
|
1729 |
|
|
$action = rules_action('component_rules_retrieve_term_parents');
|
1730 |
|
|
list($parents) = $action->execute(array($term->getIdentifier()));
|
1731 |
|
|
$this->assertTrue($parents[0]->tid == $term1->getIdentifier(), 'Invoked component to retrieve term parents.');
|
1732 |
|
|
RulesLog::logger()->checkLog();
|
1733 |
|
|
}
|
1734 |
|
|
|
1735 |
|
|
/**
|
1736 |
|
|
* Test integration for the node module.
|
1737 |
|
|
*/
|
1738 |
|
|
function testNodeIntegration() {
|
1739 |
|
|
$tests = array(
|
1740 |
|
|
array('node_unpublish', 'node_is_published', 'node_publish', 'status'),
|
1741 |
|
|
array('node_make_unsticky', 'node_is_sticky', 'node_make_sticky', 'sticky'),
|
1742 |
|
|
array('node_unpromote', 'node_is_promoted', 'node_promote', 'promote'),
|
1743 |
|
|
);
|
1744 |
|
|
$node = $this->drupalCreateNode(array('type' => 'page', 'status' => 1, 'sticky' => 1, 'promote' => 1));
|
1745 |
|
|
|
1746 |
|
|
foreach ($tests as $info) {
|
1747 |
|
|
list($action1, $condition, $action2, $property) = $info;
|
1748 |
|
|
rules_action($action1)->execute($node);
|
1749 |
|
|
|
1750 |
|
|
$node = node_load($node->nid, NULL, TRUE);
|
1751 |
|
|
$this->assertFalse($node->$property, 'Action has permanently disabled node '. $property);
|
1752 |
|
|
$return = rules_condition($condition)->execute($node);
|
1753 |
|
|
$this->assertFalse($return, 'Condition determines node '. $property . ' is disabled.');
|
1754 |
|
|
|
1755 |
|
|
rules_action($action2)->execute($node);
|
1756 |
|
|
$node = node_load($node->nid, NULL, TRUE);
|
1757 |
|
|
$this->assertTrue($node->$property, 'Action has permanently enabled node '. $property);
|
1758 |
|
|
$return = rules_condition($condition)->execute($node);
|
1759 |
|
|
$this->assertTrue($return, 'Condition determines node '. $property . ' is enabled.');
|
1760 |
|
|
}
|
1761 |
|
|
|
1762 |
|
|
$return = rules_condition('node_is_of_type', array('type' => array('page', 'article')))->execute($node);
|
1763 |
|
|
$this->assertTrue($return, 'Condition determines node is of type page.');
|
1764 |
|
|
$return = rules_condition('node_is_of_type', array('type' => array('article')))->execute($node);
|
1765 |
|
|
$this->assertFalse($return, 'Condition determines node is not of type article.');
|
1766 |
|
|
|
1767 |
|
|
|
1768 |
|
|
// Test auto saving of a new node after it has been inserted into the DB.
|
1769 |
|
|
$rule = rules_reaction_rule();
|
1770 |
|
|
$rand = $this->randomName();
|
1771 |
|
|
$rule->event('node_insert')
|
1772 |
|
|
->action('data_set', array('data:select' => 'node:title', 'value' => $rand));
|
1773 |
|
|
$rule->save('test');
|
1774 |
|
|
$node = $this->drupalCreateNode();
|
1775 |
|
|
$node = node_load($node->nid);
|
1776 |
|
|
$this->assertEqual($node->title, $rand, 'Node title is correct.');
|
1777 |
|
|
RulesLog::logger()->checkLog();
|
1778 |
|
|
}
|
1779 |
|
|
|
1780 |
|
|
/**
|
1781 |
|
|
* Test integration for the user module.
|
1782 |
|
|
*/
|
1783 |
|
|
function testUserIntegration() {
|
1784 |
|
|
$rid = $this->drupalCreateRole(array('administer nodes'), 'foo');
|
1785 |
|
|
$user = $this->drupalCreateUser();
|
1786 |
|
|
|
1787 |
|
|
// Test assigning a role with the list_add action.
|
1788 |
|
|
$rule = rule(array('user' => array('type' => 'user')));
|
1789 |
|
|
$rule->action('list_add', array('list:select' => 'user:roles', 'item' => $rid));
|
1790 |
|
|
$rule->execute($user);
|
1791 |
|
|
$this->assertTrue(isset($user->roles[$rid]), 'Role assigned to user.');
|
1792 |
|
|
|
1793 |
|
|
// Test removing a role with the list_remove action.
|
1794 |
|
|
$rule = rule(array('user' => array('type' => 'user')));
|
1795 |
|
|
$rule->action('list_remove', array('list:select' => 'user:roles', 'item' => $rid));
|
1796 |
|
|
$rule->execute($user);
|
1797 |
|
|
$this->assertTrue(!isset($user->roles[$rid]), 'Role removed from user.');
|
1798 |
|
|
|
1799 |
|
|
// Test assigning a role with user_add_role action.
|
1800 |
|
|
$rule = rule(array('user' => array('type' => 'user')));
|
1801 |
|
|
$rule->action('user_add_role', array('account:select' => 'user', 'roles' => array($rid)));
|
1802 |
|
|
$rule->execute($user);
|
1803 |
|
|
|
1804 |
|
|
$user = user_load($user->uid, TRUE);
|
1805 |
|
|
$result = rules_condition('user_has_role', array('roles' => array($rid)))->execute($user);
|
1806 |
|
|
$this->assertTrue($result, 'Role assigned to user.');
|
1807 |
|
|
|
1808 |
|
|
// Test removing a role with the user_remove_role action.
|
1809 |
|
|
$rule = rule(array('user' => array('type' => 'user')));
|
1810 |
|
|
$rule->action('user_remove_role', array('account:select' => 'user', 'roles' => array($rid)));
|
1811 |
|
|
$rule->execute($user);
|
1812 |
|
|
|
1813 |
|
|
$user = user_load($user->uid, TRUE);
|
1814 |
|
|
$result = rules_condition('user_has_role', array('roles' => array($rid)))->execute($user);
|
1815 |
|
|
$this->assertFalse($result, 'Role removed from user.');
|
1816 |
|
|
|
1817 |
|
|
// Test user blocking.
|
1818 |
|
|
rules_action('user_block')->execute($user);
|
1819 |
|
|
$user = user_load($user->uid, TRUE);
|
1820 |
|
|
$this->assertTrue(rules_condition('user_is_blocked')->execute($user), 'User has been blocked.');
|
1821 |
|
|
|
1822 |
|
|
rules_action('user_unblock')->execute($user);
|
1823 |
|
|
$user = user_load($user->uid, TRUE);
|
1824 |
|
|
$this->assertFalse(rules_condition('user_is_blocked')->execute($user), 'User has been unblocked.');
|
1825 |
|
|
|
1826 |
|
|
RulesLog::logger()->checkLog();
|
1827 |
|
|
}
|
1828 |
|
|
|
1829 |
|
|
/**
|
1830 |
|
|
* Test integration for the php module.
|
1831 |
|
|
*/
|
1832 |
|
|
function testPHPIntegration() {
|
1833 |
|
|
$node = $this->drupalCreateNode(array('title' => 'foo'));
|
1834 |
|
|
$rule = rule(array('var_name' => array('type' => 'node')));
|
1835 |
|
|
$rule->condition('php_eval', array('code' => 'return TRUE;'))
|
1836 |
|
|
->action('php_eval', array('code' => 'drupal_set_message("Executed-" . $var_name->title);'))
|
1837 |
|
|
->action('drupal_message', array('message' => 'Title: <?php echo $var_name->title; ?> Token: [var_name:title]'));
|
1838 |
|
|
|
1839 |
|
|
$rule->execute($node);
|
1840 |
|
|
$rule->access();
|
1841 |
|
|
RulesLog::logger()->checkLog();
|
1842 |
|
|
$msg = drupal_get_messages();
|
1843 |
|
|
$this->assertEqual(array_pop($msg['status']), "Title: foo Token: foo", 'PHP input evaluation has been applied.');
|
1844 |
|
|
$this->assertEqual(array_pop($msg['status']), "Executed-foo", 'PHP code condition and action have been evaluated.');
|
1845 |
|
|
|
1846 |
|
|
// Test PHP data processor
|
1847 |
|
|
$rule = rule(array('var_name' => array('type' => 'node')));
|
1848 |
|
|
$rule->action('drupal_message', array(
|
1849 |
|
|
'message:select' => 'var_name:title',
|
1850 |
|
|
'message:process' => array(
|
1851 |
|
|
'php' => array('code' => 'return "Title: $value";')
|
1852 |
|
|
),
|
1853 |
|
|
));
|
1854 |
|
|
$rule->execute($node);
|
1855 |
|
|
$rule->access();
|
1856 |
|
|
RulesLog::logger()->checkLog();
|
1857 |
|
|
$msg = drupal_get_messages();
|
1858 |
|
|
$this->assertEqual(array_pop($msg['status']), "Title: foo", 'PHP data processor has been applied.');
|
1859 |
|
|
}
|
1860 |
|
|
|
1861 |
|
|
/**
|
1862 |
|
|
* Test the "rules_core" integration.
|
1863 |
|
|
*/
|
1864 |
|
|
function testRulesCoreIntegration() {
|
1865 |
|
|
// Make sure the date input evaluator evaluates properly using strtotime().
|
1866 |
|
|
$node = $this->drupalCreateNode(array('title' => 'foo'));
|
1867 |
|
|
$rule = rule(array('node' => array('type' => 'node')));
|
1868 |
|
|
$rule->action('data_set', array('data:select' => 'node:created', 'value' => '+1 day'));
|
1869 |
|
|
|
1870 |
|
|
$rule->execute($node);
|
1871 |
|
|
RulesLog::logger()->checkLog();
|
1872 |
|
|
$node = node_load($node->nid, NULL, TRUE);
|
1873 |
|
|
$now = RulesDateInputEvaluator::gmstrtotime('now');
|
1874 |
|
|
// Tolerate a difference of a second.
|
1875 |
|
|
$this->assertTrue(abs($node->created - $now - 86400) <= 1, 'Date input has been evaluated.');
|
1876 |
|
|
|
1877 |
|
|
// Test using a numeric offset.
|
1878 |
|
|
$rule = rule(array('number' => array('type' => 'decimal')), array('number'));
|
1879 |
|
|
$rule->action('data_set', array(
|
1880 |
|
|
'data:select' => 'number',
|
1881 |
|
|
'value:select' => 'number',
|
1882 |
|
|
'value:process' => array(
|
1883 |
|
|
'num_offset' => array('value' => 1),
|
1884 |
|
|
),
|
1885 |
|
|
));
|
1886 |
|
|
$rule->integrityCheck();
|
1887 |
|
|
list($result) = $rule->execute(10);
|
1888 |
|
|
$this->assertTrue($result == 11, 'Numeric offset has been applied');
|
1889 |
|
|
|
1890 |
|
|
// Test using a date offset.
|
1891 |
|
|
$set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
|
1892 |
|
|
$set->action('data_set', array(
|
1893 |
|
|
'data:select' => 'date',
|
1894 |
|
|
'value:select' => 'date',
|
1895 |
|
|
'value:process' => array(
|
1896 |
|
|
'date_offset' => array('value' => 1000),
|
1897 |
|
|
),
|
1898 |
|
|
));
|
1899 |
|
|
$date = date_create("14 Mar 1984 10:19:23 +01:00")->format('U');
|
1900 |
|
|
list($result) = $set->execute($date);
|
1901 |
|
|
$this->assertEqual($result, $date + 1000, 'Date offset in seconds has been added.');
|
1902 |
|
|
|
1903 |
|
|
// Test using a negative offset of 2 months.
|
1904 |
|
|
$set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
|
1905 |
|
|
$set->action('data_set', array(
|
1906 |
|
|
'data:select' => 'date',
|
1907 |
|
|
'value:select' => 'date',
|
1908 |
|
|
'value:process' => array(
|
1909 |
|
|
'date_offset' => array('value' => - 86400 * 30 * 2),
|
1910 |
|
|
),
|
1911 |
|
|
));
|
1912 |
|
|
$date = date_create("14 Mar 1984 10:19:23 +01:00")->format('U');
|
1913 |
|
|
list($result) = $set->execute($date);
|
1914 |
|
|
$this->assertEqual($result, date_create("14 Jan 1984 10:19:23 +01:00")->format('U'), 'Date offset of -2 months has been added.');
|
1915 |
|
|
|
1916 |
|
|
// Test using a positive offset of 1 year 6 months and 30 minutes.
|
1917 |
|
|
$set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
|
1918 |
|
|
$set->action('data_set', array(
|
1919 |
|
|
'data:select' => 'date',
|
1920 |
|
|
'value:select' => 'date',
|
1921 |
|
|
'value:process' => array(
|
1922 |
|
|
'date_offset' => array('value' => 86400 * 30 * 18 + 30 * 60),
|
1923 |
|
|
),
|
1924 |
|
|
));
|
1925 |
|
|
$date = date_create("14 Mar 1984 10:19:23 +01:00")->format('U');
|
1926 |
|
|
list($result) = $set->execute($date);
|
1927 |
|
|
$this->assertEqual($result, date_create("14 Sep 1985 10:49:23 +01:00")->format('U'), 'Date offset of 1 year 6 months and 30 minutes has been added.');
|
1928 |
|
|
|
1929 |
|
|
RulesLog::logger()->checkLog();
|
1930 |
|
|
}
|
1931 |
|
|
|
1932 |
|
|
/**
|
1933 |
|
|
* Test site/system integration.
|
1934 |
|
|
*/
|
1935 |
|
|
function testSystemIntegration() {
|
1936 |
|
|
// Test using the 'site' variable.
|
1937 |
|
|
$condition = rules_condition('data_is', array('data:select' => 'site:current-user:name', 'value' => $GLOBALS['user']->name));
|
1938 |
|
|
$this->assertTrue($condition->execute(), 'Retrieved the current user\'s name.');
|
1939 |
|
|
// Another test using a token replacement.
|
1940 |
|
|
$condition = rules_condition('data_is', array('data:select' => 'site:current-user:name', 'value' => '[site:current-user:name]'));
|
1941 |
|
|
$this->assertTrue($condition->execute(), 'Replaced the token for the current user\'s name.');
|
1942 |
|
|
|
1943 |
|
|
// Test breadcrumbs and drupal set message.
|
1944 |
|
|
$rule = rules_reaction_rule();
|
1945 |
|
|
$rule->event('init')
|
1946 |
|
|
->action('breadcrumb_set', array('titles' => array('foo'), 'paths' => array('bar')))
|
1947 |
|
|
->action('drupal_message', array('message' => 'A message.'));
|
1948 |
|
|
$rule->save('test');
|
1949 |
|
|
|
1950 |
|
|
$this->drupalGet('node');
|
1951 |
|
|
$this->assertLink('foo', 0, 'Breadcrumb has been set.');
|
1952 |
|
|
$this->assertText('A message.', 'Drupal message has been shown.');
|
1953 |
|
|
|
1954 |
|
|
// Test the page redirect.
|
1955 |
|
|
$node = $this->drupalCreateNode();
|
1956 |
|
|
$rule = rules_reaction_rule();
|
1957 |
|
|
$rule->event('node_view')
|
1958 |
|
|
->action('redirect', array('url' => 'user'));
|
1959 |
|
|
$rule->save('test2');
|
1960 |
|
|
|
1961 |
|
|
$this->drupalGet('node/' . $node->nid);
|
1962 |
|
|
$this->assertEqual($this->getUrl(), url('user', array('absolute' => TRUE)), 'Redirect has been issued.');
|
1963 |
|
|
|
1964 |
|
|
// Also test using a url including a fragment.
|
1965 |
|
|
$actions = $rule->actions();
|
1966 |
|
|
$actions[0]->settings['url'] = 'user#fragment';
|
1967 |
|
|
$rule->save();
|
1968 |
|
|
|
1969 |
|
|
$this->drupalGet('node/' . $node->nid);
|
1970 |
|
|
$this->assertEqual($this->getUrl(), url('user', array('absolute' => TRUE, 'fragment' => 'fragment')), 'Redirect has been issued.');
|
1971 |
|
|
|
1972 |
|
|
|
1973 |
|
|
// Test sending mail.
|
1974 |
|
|
$settings = array('to' => 'mail@example.com', 'subject' => 'subject', 'message' => 'hello.');
|
1975 |
|
|
rules_action('mail', $settings)->execute();
|
1976 |
|
|
$this->assertMail('to', 'mail@example.com', 'Mail has been sent.');
|
1977 |
|
|
$this->assertMail('from', variable_get('site_mail', ini_get('sendmail_from')), 'Default from address has been used');
|
1978 |
|
|
|
1979 |
|
|
rules_action('mail', $settings + array('from' => 'sender@example.com'))->execute();
|
1980 |
|
|
$this->assertMail('from', 'sender@example.com', 'Specified from address has been used');
|
1981 |
|
|
|
1982 |
|
|
// Test sending mail to all users of a role. First make sure there is a
|
1983 |
|
|
// custom role and a user for it.
|
1984 |
|
|
$user = $this->drupalCreateUser(array('administer nodes'));
|
1985 |
|
|
$roles = $user->roles;
|
1986 |
|
|
// Remove the authenticate role so we only use the new role created by
|
1987 |
|
|
// drupalCreateUser().
|
1988 |
|
|
unset($roles[DRUPAL_AUTHENTICATED_RID]);
|
1989 |
|
|
rules_action('mail_to_users_of_role', $settings + array('roles' => array_keys($roles)))->execute();
|
1990 |
|
|
$this->assertMail('to', $user->mail, 'Mail to users of a role has been sent.');
|
1991 |
|
|
|
1992 |
|
|
// Test reacting on new log entries and make sure the log entry is usable.
|
1993 |
|
|
$rule = rules_reaction_rule();
|
1994 |
|
|
$rule->event('watchdog');
|
1995 |
|
|
$rule->action('drupal_message', array('message:select' => 'log_entry:message'));
|
1996 |
|
|
$rule->integrityCheck()->save('test_watchdog');
|
1997 |
|
|
|
1998 |
|
|
watchdog('php', 'test %message', array('%message' => 'message'));
|
1999 |
|
|
$msg = drupal_get_messages();
|
2000 |
|
|
$this->assertEqual(array_pop($msg['status']), t('test %message', array('%message' => 'message')), 'Watchdog event occurred and log entry properties can be used.');
|
2001 |
|
|
}
|
2002 |
|
|
|
2003 |
|
|
/**
|
2004 |
|
|
* Tests the path module integration.
|
2005 |
|
|
*/
|
2006 |
|
|
function testPathIntegration() {
|
2007 |
|
|
rules_action('path_alias')->execute('foo', 'bar');
|
2008 |
|
|
$path = path_load('foo');
|
2009 |
|
|
$this->assertTrue($path['alias'] == 'bar', 'URL alias has been created.');
|
2010 |
|
|
|
2011 |
|
|
$alias_exists = rules_condition('path_alias_exists', array('alias' => 'bar'))->execute();
|
2012 |
|
|
$this->assertTrue($alias_exists, 'Created URL alias exists.');
|
2013 |
|
|
|
2014 |
|
|
$has_alias = rules_condition('path_has_alias', array('source' => 'foo'))->execute();
|
2015 |
|
|
$this->assertTrue($has_alias, 'System path has an alias.');
|
2016 |
|
|
|
2017 |
|
|
// Test node alias action.
|
2018 |
|
|
$node = $this->drupalCreateNode();
|
2019 |
|
|
rules_action('node_path_alias')->execute($node, 'test');
|
2020 |
|
|
$path = path_load("node/$node->nid");
|
2021 |
|
|
$this->assertTrue($path['alias'] == 'test', 'Node URL alias has been created.');
|
2022 |
|
|
|
2023 |
|
|
// Test term alias action.
|
2024 |
|
|
$term = entity_property_values_create_entity('taxonomy_term', array(
|
2025 |
|
|
'name' => $this->randomName(),
|
2026 |
|
|
'vocabulary' => 1,
|
2027 |
|
|
))->value();
|
2028 |
|
|
rules_action('taxonomy_term_path_alias')->execute($term, 'term-test');
|
2029 |
|
|
$path = path_load("taxonomy/term/$term->tid");
|
2030 |
|
|
$this->assertTrue($path['alias'] == 'term-test', 'Term URL alias has been created.');
|
2031 |
|
|
|
2032 |
|
|
RulesLog::logger()->checkLog();
|
2033 |
|
|
}
|
2034 |
|
|
}
|
2035 |
|
|
|
2036 |
|
|
/**
|
2037 |
|
|
* Test event dispatcher functionality.
|
2038 |
|
|
*/
|
2039 |
|
|
class RulesEventDispatcherTestCase extends DrupalWebTestCase {
|
2040 |
|
|
|
2041 |
|
|
static function getInfo() {
|
2042 |
|
|
return array(
|
2043 |
|
|
'name' => 'Rules event dispatchers',
|
2044 |
|
|
'description' => 'Tests event dispatcher functionality.',
|
2045 |
|
|
'group' => 'Rules',
|
2046 |
|
|
);
|
2047 |
|
|
}
|
2048 |
|
|
|
2049 |
|
|
function setUp() {
|
2050 |
|
|
parent::setUp('rules', 'rules_test');
|
2051 |
|
|
}
|
2052 |
|
|
|
2053 |
|
|
/**
|
2054 |
|
|
* Tests start and stop functionality.
|
2055 |
|
|
*/
|
2056 |
|
|
public function testStartAndStop() {
|
2057 |
|
|
$handler = rules_get_event_handler('rules_test_event');
|
2058 |
|
|
$rule = rules_reaction_rule();
|
2059 |
|
|
$rule->event('rules_test_event');
|
2060 |
|
|
|
2061 |
|
|
// The handler should not yet be watching.
|
2062 |
|
|
$this->assertFalse($handler->isWatching());
|
2063 |
|
|
|
2064 |
|
|
// Once saved, the event cache rebuild should start the watcher.
|
2065 |
|
|
$rule->save();
|
2066 |
|
|
RulesEventSet::rebuildEventCache();
|
2067 |
|
|
$this->assertTrue($handler->isWatching());
|
2068 |
|
|
|
2069 |
|
|
// Deleting should stop the watcher.
|
2070 |
|
|
$rule->delete();
|
2071 |
|
|
$this->assertFalse($handler->isWatching());
|
2072 |
|
|
}
|
2073 |
|
|
|
2074 |
|
|
/**
|
2075 |
|
|
* Tests start and stop functionality when used with multiple events.
|
2076 |
|
|
*/
|
2077 |
|
|
public function testStartAndStopMultiple() {
|
2078 |
|
|
$handler = rules_get_event_handler('rules_test_event');
|
2079 |
|
|
|
2080 |
|
|
// Initially, the task handler should not be watching.
|
2081 |
|
|
$this->assertFalse($handler->isWatching());
|
2082 |
|
|
|
2083 |
|
|
// Set up five rules that all use the same event.
|
2084 |
|
|
$rules = array();
|
2085 |
|
|
foreach (array(1, 2, 3, 4, 5) as $key) {
|
2086 |
|
|
$rules[$key] = rules_reaction_rule();
|
2087 |
|
|
$rules[$key]->event('rules_test_event');
|
2088 |
|
|
$rules[$key]->save();
|
2089 |
|
|
}
|
2090 |
|
|
|
2091 |
|
|
// Once saved, the event cache rebuild should start the watcher.
|
2092 |
|
|
RulesEventSet::rebuildEventCache();
|
2093 |
|
|
$this->assertTrue($handler->isWatching());
|
2094 |
|
|
|
2095 |
|
|
// It should continue watching until all events are deleted.
|
2096 |
|
|
foreach ($rules as $key => $rule) {
|
2097 |
|
|
$rule->delete();
|
2098 |
|
|
$this->assertEqual($key !== 5, $handler->isWatching());
|
2099 |
|
|
}
|
2100 |
|
|
}
|
2101 |
|
|
} |