Merge lp:~jody-zivtech/uc/trunk into lp:uc

Proposed by Jody Hamilton
Status: Needs review
Proposed branch: lp:~jody-zivtech/uc/trunk
Merge into: lp:uc
Diff against target: 2977 lines (+2899/-1)
12 files modified
includes/ubercore.admin.inc (+17/-0)
modules/uc_ca/includes/uc_ca.admin.inc (+1027/-0)
modules/uc_ca/includes/uc_ca.ca.inc (+512/-0)
modules/uc_ca/theme/uc_ca.css (+21/-0)
modules/uc_ca/theme/uc_ca.js (+19/-0)
modules/uc_ca/uc_ca.info (+10/-0)
modules/uc_ca/uc_ca.install (+110/-0)
modules/uc_ca/uc_ca.module (+1082/-0)
modules/uc_ca/uc_ca_test/uc_ca_test.info (+7/-0)
modules/uc_ca/uc_ca_test/uc_ca_test.module (+61/-0)
ubercore.info (+3/-1)
ubercore.module (+30/-0)
To merge this branch: bzr merge lp:~jody-zivtech/uc/trunk
Reviewer Review Type Date Requested Status
Ubercore Pending
Review via email: mp+15131@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jody Hamilton (jody-zivtech) wrote :

Unmerged revisions

5. By Jody Hamilton <email address hidden>

d.o. #615306, more changes to function names, updating the non-admin ca functions, and fixing the dsm action form to use regular drupal input formats

4. By Jody Hamilton <email address hidden>

Ubercore D7 Conditional actions upgrade: Upgrade for the Conditional Actions UI to D7 and coding standards

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'includes/ubercore.admin.inc'
2--- includes/ubercore.admin.inc 1970-01-01 00:00:00 +0000
3+++ includes/ubercore.admin.inc 2009-11-22 17:00:24 +0000
4@@ -0,0 +1,17 @@
5+<?php
6+// $Id$
7+
8+/**
9+ * @file
10+ * General administrative pages for Ubercore.
11+ */
12+
13+/**
14+ * Menu callback. Main settings page for the store.
15+ */
16+function ubercore_admin() {
17+ $output = 'Placeholder for the main admin page.';
18+ $output .= '<br />';
19+ $output .= l('Conditional Actions Admin', 'admin/store/ca');
20+ return $output;
21+}
22\ No newline at end of file
23
24=== added directory 'modules/uc_ca'
25=== added directory 'modules/uc_ca/includes'
26=== added file 'modules/uc_ca/includes/uc_ca.admin.inc'
27--- modules/uc_ca/includes/uc_ca.admin.inc 1970-01-01 00:00:00 +0000
28+++ modules/uc_ca/includes/uc_ca.admin.inc 2009-11-22 17:00:24 +0000
29@@ -0,0 +1,1027 @@
30+<?php
31+// $Id$
32+
33+/**
34+ * @file
35+ * Administrative pages for Conditional Actions.
36+ */
37+
38+/**
39+ * Menu callback.
40+ * Display the general Conditional Actions administration page.
41+ * This page lists the defined predicates.
42+ */
43+function uc_ca_admin($groupby = 'trigger') {
44+ drupal_add_css(drupal_get_path('module', 'uc_ca') .'/theme/uc_ca.css');
45+
46+ $output = '';
47+
48+ // Load all the module-defined predicates into a temporary array.
49+ $temp = module_invoke_all('uc_ca_predicate');
50+
51+ // Assign a default weight if need be.
52+ foreach ($temp as $key => $value) {
53+ $temp[$key]['#pid'] = $key;
54+ if (!isset($value['#weight'])) {
55+ $temp[$key]['#weight'] = 0;
56+ }
57+ }
58+
59+ // Load and loop through all the database stored predicates.
60+ $result = db_query("SELECT * FROM {uc_ca_predicates}", array(), array('fetch' => PDO::FETCH_ASSOC));
61+ foreach ($result as $predicate) {
62+ // Prepare the database data into a predicate.
63+ $predicate = uc_ca_db_predicate_prepare($predicate);
64+ // Overrides the module-defined predicate if it's been modified by a user.
65+ $temp[$predicate['#pid']] = $predicate;
66+ }
67+
68+ usort($temp, 'uc_ca_weight_sort');
69+
70+ // Copy the temporary array of predicates into a keyed array.
71+ $predicates = array();
72+ foreach ($temp as $predicate) {
73+ $predicates[$predicate['#pid']] = $predicate;
74+ }
75+
76+ // Load up the trigger data so we can display them by title.
77+ $triggers = module_invoke_all('uc_ca_trigger');
78+
79+ // Build the header for the predicate tables based on the grouping type.
80+ if ($groupby == 'trigger') {
81+ $header = array(
82+ array('data' => t('Title'), 'class' => 'col-title'),
83+ t('Class'),
84+ t('Status'),
85+ t('Weight'),
86+ t('Operations'),
87+ );
88+
89+ $table_class = 'ca-predicate-trigger';
90+ $table_label = '<strong>' . t('Trigger') . ':</strong> ';
91+ }
92+ elseif ($groupby == 'class') {
93+ $header = array(
94+ array('data' => t('Title'), 'class' => 'col-title'),
95+ t('Trigger'),
96+ t('Status'),
97+ t('Weight'),
98+ t('Operations'),
99+ );
100+
101+ $table_class = 'ca-predicate-class';
102+ $table_label = '<strong>' . t('Class') . ':</strong> ';
103+ }
104+
105+ $tables = array();
106+
107+ foreach ($predicates as $key => $value) {
108+ // Build the basic operation links for each predicate.
109+ $ops = array(
110+ l(t('edit'), 'admin/store/ca/' . $key . '/edit'),
111+ );
112+
113+ // Add the reset link if a module defined predicate has been modified.
114+ if (!is_numeric($key) && isset($value['#modified'])) {
115+ $ops[] = l(t('reset'), 'admin/store/ca/'. $key .'/reset');
116+ }
117+
118+ // Add a delete link if displaying a custom predicate.
119+ if (is_numeric($key)) {
120+ $ops[] = l(t('delete'), 'admin/store/ca/'. $key .'/delete');
121+ }
122+
123+ // Add the predicate's row to the table based on the grouping type.
124+ if ($groupby == 'trigger') {
125+ $tables[$triggers[$value['#trigger']]['#title']]['rows'][] = array(
126+ check_plain($value['#title']),
127+ strpos($value['#class'], 'custom') === 0 ? check_plain(substr($value['#class'], 7)) : $value['#class'],
128+ $value['#status'] == 0 ? t('Disabled') : t('Enabled'),
129+ array('data' => $value['#weight'], 'class' => 'ca-predicate-table-weight'),
130+ array('data' => implode(' ', $ops), 'class' => 'ca-predicate-table-ops'),
131+ );
132+ }
133+ elseif ($groupby == 'class') {
134+ $class = strpos($value['#class'], 'custom') === 0 ? check_plain(substr($value['#class'], 7)) : $value['#class'];
135+
136+ $tables[$class]['rows'][] = array(
137+ check_plain($value['#title']),
138+ check_plain($triggers[$value['#trigger']]['#title']),
139+ $value['#status'] == 0 ? t('Disabled') : t('Enabled'),
140+ array('data' => $value['#weight'], 'class' => 'ca-predicate-table-weight'),
141+ array('data' => implode(' ', $ops), 'class' => 'ca-predicate-table-ops'),
142+ );
143+ }
144+ }
145+
146+ // Put the tables in alphabetical order.
147+ ksort($tables);
148+
149+ // Add the tables for each trigger to the output.
150+ foreach ($tables as $key => $value) {
151+ // Accommodate empty class names.
152+ if (empty($key)) {
153+ $key = t('- None -');
154+ }
155+
156+ $output .= theme('table', array(
157+ 'header' => $header,
158+ 'rows' => $value['rows'],
159+ 'attributes' => array(
160+ 'class' => array($table_class),
161+ ),
162+ 'caption' => $table_label . check_plain($key),
163+ ));
164+ }
165+
166+ $output .= l(t('Add a predicate'), 'admin/store/ca/add');
167+
168+ return $output;
169+}
170+
171+/**
172+ * Form to allow the creation and editing of conditional action predicates.
173+ */
174+function uc_ca_predicate_meta_form($form_state, &$form_state, $predicate = array()) {
175+ // Load the predicate if an ID is passed in.
176+ if (isset($predicate['#title'])) {
177+ drupal_set_title(check_plain($predicate['#title']));
178+ }
179+ $pid = isset($predicate['#pid']) ? check_plain($predicate['#pid']) : 0;
180+
181+ $form['predicate_pid'] = array(
182+ '#type' => 'value',
183+ '#value' => $pid,
184+ );
185+
186+ $form['predicate_title'] = array(
187+ '#type' => 'textfield',
188+ '#title' => t('Title'),
189+ '#description' => t('Enter a title used for display on the overview tables.'),
190+ '#default_value' => $pid ? check_plain($predicate['#title']) : '',
191+ '#required' => TRUE,
192+ );
193+
194+ // Create an array of triggers for the select box.
195+ $triggers = module_invoke_all('uc_ca_trigger');
196+ $options = array();
197+ foreach ($triggers as $key => $value) {
198+ $options[$value['#category']][$key] = check_plain($value['#title']);
199+ }
200+
201+ $form['predicate_trigger'] = array(
202+ '#type' => 'select',
203+ '#title' => t('Trigger'),
204+ '#description' => t('Select the trigger for this predicate. This cannot be modified if the predicate has conditions or actions.'),
205+ '#options' => $options,
206+ '#default_value' => $pid ? check_plain($predicate['#trigger']) : '',
207+ '#disabled' => empty($predicate['#conditions']) && empty($predicate['#actions']) ? FALSE : TRUE,
208+ '#required' => TRUE,
209+ );
210+
211+ $form['predicate_description'] = array(
212+ '#type' => 'textarea',
213+ '#title' => t('Description'),
214+ '#description' => t('Enter a description that summarizes the use and intent of the predicate.'),
215+ '#default_value' => $pid ? check_plain($predicate['#description']) : '',
216+ );
217+
218+ // Accommodate the mandatory custom prefix for predicate classes.
219+ $class = isset($predicate['#class']) ? $predicate['#class'] : '';
220+ if (strpos($class, 'custom') === 0) {
221+ $class = ltrim(substr($class, 6), ':');
222+ }
223+
224+ if (is_numeric($pid)) {
225+ $form['predicate_class'] = array(
226+ '#type' => 'textfield',
227+ '#title' => t('Class'),
228+ '#description' => t('Classes let you categorize your predicates based on the type of functionality they provide.'),
229+ '#field_prefix' => 'custom:',
230+ '#default_value' => check_plain($class),
231+ );
232+ }
233+ else {
234+ $form['predicate_class'] = array(
235+ '#type' => 'value',
236+ '#value' => $class,
237+ );
238+ }
239+
240+ $form['predicate_status'] = array(
241+ '#type' => 'radios',
242+ '#title' => t('Status'),
243+ '#description' => t('Disabled predicates will not be processed when their trigger is pulled.'),
244+ '#options' => array(
245+ 1 => t('Enabled'),
246+ 0 => t('Disabled'),
247+ ),
248+ '#default_value' => $pid ? $predicate['#status'] : 1,
249+ );
250+
251+ $form['predicate_weight'] = array(
252+ '#type' => 'weight',
253+ '#title' => t('Weight'),
254+ '#description' => t('Predicates will be sorted by weight and processed sequentially.'),
255+ '#default_value' => ($pid && isset($predicate['#weight'])) ? check_plain($predicate['#weight']) : 0,
256+ );
257+
258+ $form['submit'] = array(
259+ '#type' => 'submit',
260+ '#value' => t('Save predicate'),
261+ '#suffix' => l(t('Cancel'), 'admin/store/ca'),
262+ );
263+
264+ return $form;
265+}
266+
267+/**
268+ * Submit handler for predicate creation and editing form.
269+ * @see uc_ca_predicate_meta_form()
270+ */
271+function uc_ca_predicate_meta_form_submit($form, &$form_state) {
272+
273+ $form_state['redirect'] = 'admin/store/ca';
274+ $predicate = array();
275+
276+ // Load the original predicate.
277+ if ($form_state['values']['predicate_pid']) {
278+ $predicate = uc_ca_predicate_load($form_state['values']['predicate_pid']);
279+ }
280+
281+ // Setup a list of fields to check for and apply changes.
282+ $fields = array('title', 'trigger', 'description', 'class', 'status', 'weight');
283+
284+ // Compare the values from the form submission with what is already set.
285+ $save = FALSE;
286+ foreach ($fields as $field) {
287+ if (!isset($predicate['#' . $field]) || $form_state['values']['predicate_' . $field] != $predicate['#' . $field]) {
288+ $predicate['#' . $field] = $form_state['values']['predicate_' . $field];
289+ $save = TRUE;
290+ }
291+ }
292+
293+ // Add a new predicate.
294+ if (!isset($predicate['#pid'])) {
295+ uc_ca_predicate_save($predicate);
296+ drupal_set_message(t('The new predicate was added.'));
297+ // For new predicates, redirect to the conditions tab.
298+ $form_state['redirect'] = 'admin/store/ca/' . $predicate['#pid'] .'/edit/conditions';
299+ }
300+
301+ elseif ($save) {
302+ uc_ca_predicate_save($predicate);
303+ drupal_set_message(t('The predicate meta-data was saved.'));
304+ }
305+}
306+
307+/**
308+ * Build a form for adding and editing conditions on a predicate.
309+ *
310+ * @ingroup forms
311+ * @see
312+ * uc_ca_conditions_form_add_condition_group_submit()
313+ * uc_ca_conditions_form_remove_condition_group_submit()
314+ * uc_ca_conditions_form_add_condition_submit()
315+ * uc_ca_conditions_form_remove_condition_submit()
316+ * uc_ca_conditions_form_save_changes_submit()
317+ */
318+function uc_ca_conditions_form($form, &$form_state, $predicate = array()) {
319+
320+ // Return an empty form if the predicate does not exist.
321+ if (empty($predicate)) {
322+ return array();
323+ }
324+
325+ // Include the JS for conditional actions forms.
326+ drupal_add_js(drupal_get_path('module', 'uc_ca') .'/theme/ca.js');
327+
328+ // Display the title.
329+ drupal_set_title($predicate['#title']);
330+
331+ // Initialize the conditions available for this predicate.
332+ uc_ca_trigger_conditions_load($predicate['#trigger']);
333+
334+ // Add the predicate ID to the form array.
335+ $form['pid'] = array(
336+ '#type' => 'value',
337+ '#value' => $predicate['#pid'],
338+ );
339+
340+ // If the predicate conditions array isn't already a container of containers,
341+ // make it so. i.e. group => array('group' => array('cond', 'cond'))
342+ if (empty($predicate['#conditions'])) {
343+ $predicate['#conditions'] = uc_ca_new_conditions();
344+ }
345+ else {
346+ $key = array_shift(array_keys($predicate['#conditions']['#conditions']));
347+
348+ if (empty($predicate['#conditions']['#conditions'][$key]['#operator'])) {
349+ $predicate['#conditions'] = array(
350+ '#operator' => 'AND',
351+ '#conditions' => array(
352+ $predicate['#conditions'],
353+ ),
354+ );
355+ }
356+ }
357+
358+ // Recurse through the predicate's conditions to build the form.
359+ $form['conditions'] = _uc_ca_conditions_form_tree($predicate['#conditions'], $predicate['#trigger']);
360+ $form['conditions'] = $form['conditions'][0];
361+
362+ // Always remove the top level condition group controls.
363+ unset($form['conditions']['condition']);
364+ unset($form['conditions']['add_condition']);
365+ unset($form['conditions']['remove_condition_group']);
366+
367+ // If there are more than one top level condition groups...
368+ if (count($form['conditions']['conditions']) > 1) {
369+ // Update the top level fieldset.
370+ $form['conditions']['#title'] = t('Condition groups');
371+ $form['conditions']['#collapsible'] = FALSE;
372+
373+ // Adjust the titles of the operator options to be more explicit.
374+ $form['conditions']['operator']['#options'] = array(
375+ 'AND' => t('AND. If all of these condition groups are TRUE.'),
376+ 'OR' => t('OR. If any of these condition groups are TRUE.'),
377+ );
378+ }
379+ else {
380+ // Otherwise remove the top level fieldset and operator.
381+ unset($form['conditions']['#type']);
382+ unset($form['conditions']['operator']);
383+
384+ // Adjust the second level conditions group to not collapse.
385+ $form['conditions']['conditions'][0]['#collapsible'] = FALSE;
386+ }
387+
388+ // Set the conditions array to a tree so the submitted form values mirror the
389+ // structure of a predicate's #conditions array.
390+ $form['conditions']['#tree'] = TRUE;
391+
392+ $form['add_condition_group'] = array(
393+ '#type' => 'submit',
394+ '#value' => t('Add condition group'),
395+ '#submit' => array('uc_ca_conditions_form_add_condition_group_submit'),
396+ );
397+
398+ $form['save'] = array(
399+ '#type' => 'submit',
400+ '#value' => t('Save changes'),
401+ '#submit' => array('uc_ca_conditions_form_save_changes_submit'),
402+ );
403+
404+ uc_ca_filter_configure_form($form);
405+
406+ return $form;
407+}
408+
409+/**
410+ * Submit handler for button to add a condition group.
411+ *
412+ * @see uc_ca_conditions_form()
413+ */
414+function uc_ca_conditions_form_add_condition_group_submit($form, &$form_state) {
415+ // Save the existing conditions to preserve any changes.
416+ uc_ca_conditions_form_update_conditions($form_state['values']['pid'], $form_state['values']['conditions']);
417+
418+ // Add the condition group to the predicate.
419+ uc_ca_condition_group_add($form_state['values']['pid']);
420+
421+ // Display a confirmation message.
422+ drupal_set_message(t('Condition group added.'));
423+}
424+
425+/**
426+ * Submit handler for button to remove a condition group.
427+ *
428+ * @see uc_ca_conditions_form()
429+ */
430+function uc_ca_conditions_form_remove_condition_group_submit($form, &$form_state) {
431+ $group_key = FALSE;
432+
433+ // Loop through the array keys of the conditions group.
434+ foreach (array_keys($form['conditions']['conditions']) as $key => $value) {
435+ // If we find the key we're looking for...
436+ if (is_numeric($value) && $value == $form_state['clicked_button']['#array_parents'][2]) {
437+ // Store its position as the new group key once the conditions are saved.
438+ // This is necessary because the save function will truncate any holes so
439+ // the keys are in numerical order starting with 0.
440+ $group_key = $key;
441+ }
442+ }
443+
444+ // Save the existing conditions to preserve any changes.
445+ uc_ca_conditions_form_update_conditions($form_state['values']['pid'], $form_state['values']['conditions']);
446+
447+ // Fail if we didn't find a new group key for some reason.
448+ if ($group_key === FALSE) {
449+ drupal_set_message(t('An error occurred when trying to add the condition. Please try again.'), 'error');
450+ }
451+ else {
452+ // Remove the condition group from the predicate.
453+ uc_ca_condition_group_remove($form_state['values']['pid'], $group_key);
454+
455+ // Display a confirmation message.
456+ drupal_set_message(t('Condition group removed.'));
457+ }
458+}
459+
460+/**
461+ * Save changes submit handler for the conditions form.
462+ *
463+ * @see uc_ca_conditions_form()
464+ */
465+function uc_ca_conditions_form_save_changes_submit($form, &$form_state) {
466+ // Save the existing conditions to preserve any changes.
467+ uc_ca_conditions_form_update_conditions($form_state['values']['pid'], $form_state['values']['conditions']);
468+
469+ drupal_set_message(t('Conditions saved.'));
470+}
471+
472+/**
473+ * Update a predicate's conditions based on the values from a conditions form.
474+ *
475+ * @param $pid
476+ * The ID of the predicate whose conditions should be updated.
477+ * @param $data
478+ * The conditions values from the form state that should be used to update the
479+ * predicate; normally $form_state['values']['conditions'].
480+ * @return
481+ * An array representing the full, updated predicate.
482+ */
483+function uc_ca_conditions_form_update_conditions($pid, $data) {
484+ // Build the new conditions array from scratch.
485+ $conditions = uc_ca_new_conditions();
486+
487+ // Override the top level operator if need be.
488+ if (isset($data['operator'])) {
489+ $conditions['#operator'] = $data['operator'];
490+ }
491+
492+ // Use a variable to track the group array key so we can "reset" the keys.
493+ $group = 0;
494+
495+ // Loop through each second level condition group.
496+ foreach ($data['conditions'] as $key => $value) {
497+ // Save the operator setting.
498+ $conditions['#conditions'][$group]['#operator'] = $value['operator'];
499+ $conditions['#conditions'][$group]['#conditions'] = array();
500+
501+ // If conditions exist...
502+ if (isset($value['conditions']) && count($value['conditions']) > 0) {
503+ // Use a variable to track the condition array key as for groups.
504+ $condition = 0;
505+
506+ // Loop through the conditions in this group.
507+ foreach ($value['conditions'] as $cond_key => $cond_value) {
508+ // Save the condition's settings.
509+ $conditions['#conditions'][$group]['#conditions'][$condition] = array(
510+ '#name' => $cond_value['name'],
511+ '#title' => $cond_value['title'],
512+ '#argument_map' => isset($cond_value['argument_map']) ? $cond_value['argument_map'] : array(),
513+ '#settings' => $cond_value['settings'],
514+ );
515+
516+ $condition++;
517+ }
518+ }
519+
520+ $group++;
521+ }
522+
523+ // Load the predicate as it is now.
524+ $predicate = uc_ca_predicate_load($pid);
525+
526+ // Update the actions and save the result.
527+ $predicate['#conditions'] = $conditions;
528+ uc_ca_predicate_save($predicate);
529+
530+ return $predicate;
531+}
532+
533+/**
534+ * Recursively add logical groups to the conditions form as fieldsets.
535+ *
536+ * @param $condition
537+ * An array consisting of an #operator and another array of #conditions, or
538+ * a condition array with data pertaining to its callback function.
539+ * @param $trigger
540+ * The trigger name and data concerning the arguments that are passed in to
541+ * the condition.
542+ * @return
543+ * A form array representing the tree of conditions.
544+ */
545+function _uc_ca_conditions_form_tree($condition, $trigger) {
546+ // We need an index so condition group fieldsets have unique keys.
547+ static $index = 0;
548+ $form = array();
549+
550+ // If we're dealing with a conditions group and not a single condition.
551+ if (isset($condition['#operator']) && is_array($condition['#conditions'])) {
552+ $level = $index;
553+
554+ $form[$level] = array(
555+ '#type' => 'fieldset',
556+ '#title' => t('Conditions group'),
557+ '#collapsible' => TRUE,
558+ );
559+
560+ // Add an operator radio select for the group.
561+ $form[$level]['operator'] = array(
562+ '#type' => 'radios',
563+ '#title' => t('Operator'),
564+ '#options' => array(
565+ 'AND' => t('AND. If all of these conditions are TRUE.'),
566+ 'OR' => t('OR. If any of these conditions are TRUE.'),
567+ ),
568+ '#default_value' => $condition['#operator'],
569+ );
570+
571+ $form[$level]['conditions'] = array();
572+
573+ // For each condition in #conditions, add a condition fieldset to the form.
574+ foreach ($condition['#conditions'] as $sub_condition) {
575+ $result = _uc_ca_conditions_form_tree($sub_condition, $trigger);
576+
577+ if (is_array($result)) {
578+ $form[$level]['conditions'] = array_merge($form[$level]['conditions'], $result);
579+ }
580+ }
581+
582+ $form[$level]['condition'] = array(
583+ '#type' => 'select',
584+ '#title' => t('Available conditions'),
585+ '#options' => uc_ca_trigger_conditions_load(),
586+ );
587+ $form[$level]['add_condition'] = array(
588+ '#type' => 'submit',
589+ '#value' => t('Add condition'),
590+ '#submit' => array('uc_ca_conditions_form_add_condition_submit'),
591+ '#name' => 'add_condition_' . $level,
592+ );
593+ $form[$level]['remove_condition_group'] = array(
594+ '#type' => 'submit',
595+ '#value' => t('Remove group'),
596+ '#submit' => array('uc_ca_conditions_form_remove_condition_group_submit'),
597+ '#attributes' => array('class' => array('ca-remove-confirm')),
598+ '#name' => 'remove_condition_group_' . $level,
599+ );
600+ $index++;
601+
602+ return $form;
603+ }
604+ elseif (!empty($condition)) {
605+ // Load the data for the conditions as defined by modules.
606+ $condition_data = uc_ca_condition_load();
607+
608+ // Otherwise add a fieldset for the individual condition.
609+ $form[0] = array(
610+ '#type' => 'fieldset',
611+ '#title' => t('Condition: @title', array('@title' => $condition_data[$condition['#name']]['#title'])),
612+ '#description' => t('@description', array('@description' => $condition_data[$condition['#name']]['#description'])),
613+ '#collapsible' => TRUE,
614+ '#collapsed' => isset($condition['#expanded']) ? FALSE : TRUE,
615+ );
616+
617+ // Add form elements to the fieldset to adjust the condition's settings.
618+ $form[0] += _uc_ca_conditions_form_condition($condition, $trigger);
619+
620+ return $form;
621+ }
622+}
623+
624+/**
625+ * Add a single condition's fieldset to the conditions form.
626+ *
627+ * @param $condition
628+ * The condition data array.
629+ * @param $trigger
630+ * The trigger name and data concerning the arguments that are passed in to
631+ * the condition.
632+ * @return
633+ * A form array representing the condition.
634+ */
635+function _uc_ca_conditions_form_condition($condition, $trigger) {
636+ static $identifier = 0;
637+
638+ // Load the data for the conditions as defined by modules.
639+ $condition_data = uc_ca_condition_load();
640+
641+ // Condition name is a hard reference to the condition and so not editable.
642+ $form['name'] = array(
643+ '#type' => 'value',
644+ '#value' => $condition['#name'],
645+ );
646+
647+ // The title for this particular instance of this condition.
648+ $form['title'] = array(
649+ '#type' => 'textfield',
650+ '#title' => t('Title'),
651+ '#default_value' => $condition['#title'],
652+ );
653+
654+ $form['argument_map'] = array(
655+ '#type' => 'fieldset',
656+ '#title' => t('Arguments'),
657+ '#description' => t('Some triggers pass in multiple options for arguments related to a particular condition, so you must specify which options to use in the fields below.'),
658+ '#collapsible' => TRUE,
659+ );
660+
661+ // Setup a variable to decide if we can collapse the arguments fieldset.
662+ $collapsed = TRUE;
663+
664+ // Cast to an array to accommodate conditions that need no arguments.
665+ foreach ((array) $condition_data[$condition['#name']]['#arguments'] as $key => $value) {
666+ // Load the available arguments for each entity as received by the
667+ // trigger when it was pulled.
668+ $options = uc_ca_trigger_arguments_load($trigger, $value['#entity']);
669+
670+ // If we have more than one option for any argument, do not collapse.
671+ if (count($options) > 1) {
672+ $collapsed = FALSE;
673+ }
674+
675+ $form['argument_map'][$key] = array(
676+ '#type' => 'select',
677+ '#title' => check_plain($value['#title']),
678+ '#options' => $options,
679+ '#default_value' => $condition['#argument_map'][$key],
680+ );
681+ }
682+
683+ $form['argument_map']['#collapsed'] = $collapsed;
684+
685+ // Whether or not to negate the result of this condition.
686+ $form['settings']['negate'] = array(
687+ '#type' => 'checkbox',
688+ '#title' => t('Negate this condition.'),
689+ '#description' => t('Return FALSE if the condition is TRUE and vice versa.'),
690+ '#default_value' => isset($condition['#settings']['negate']) ? $condition['#settings']['negate'] : '',
691+ );
692+
693+ // Get the callback for the condition we're displaying.
694+ $callback = $condition_data[$condition['#name']]['#callback'] . '_form';
695+
696+ if (function_exists($callback)) {
697+ // Load the trigger data.
698+ $trigger_data = uc_ca_trigger_load($trigger);
699+
700+ // Add the condition's form elements to the fieldset.
701+ $form['settings'] += $callback(array(), $condition['#settings'], $trigger_data['#arguments']);
702+ }
703+
704+ $form['remove'] = array(
705+ '#type' => 'submit',
706+ '#value' => t('Remove this condition'),
707+ '#submit' => array('uc_ca_conditions_form_remove_condition_submit'),
708+ '#attributes' => array('class' => array('ca-remove-confirm')),
709+ '#name' => 'remove_condition_group_' . $identifier++,
710+ );
711+
712+ return $form;
713+}
714+
715+/**
716+ * Submit handler for button to add a condition to a condition group.
717+ *
718+ * @see uc_ca_conditions_form()
719+ */
720+function uc_ca_conditions_form_add_condition_submit($form, &$form_state) {
721+ $group_key = FALSE;
722+
723+ // Loop through the array keys of the conditions group.
724+ foreach (array_keys($form['conditions']['conditions']) as $key => $value) {
725+ // If we find the key we're looking for...
726+ if (is_numeric($value) && $value == $form_state['clicked_button']['#array_parents'][2]) {
727+ // Store its position as the new group key once the conditions are saved.
728+ // This is necessary because the save function will truncate any holes so
729+ // the keys are in numerical order starting with 0.
730+ $group_key = $key;
731+ }
732+ }
733+
734+ // Save the existing conditions to preserve any changes.
735+ uc_ca_conditions_form_update_conditions($form_state['values']['pid'], $form_state['values']['conditions']);
736+
737+ // Fail if we didn't find a new group key for some reason.
738+ if ($group_key === FALSE) {
739+ drupal_set_message(t('An error occurred when trying to add the condition. Please try again.'), 'error');
740+ }
741+ else {
742+ // Otherwise add the condition to the specified group.
743+ $name = $form_state['values']['conditions']['conditions'][$group_key]['condition'];
744+
745+ uc_ca_condition_add($form_state['values']['pid'], $name, $group_key);
746+
747+ $condition = uc_ca_condition_load($name);
748+
749+ drupal_set_message(t('%title condition added.', array('%title' => $condition['#title'])));
750+ }
751+}
752+
753+/**
754+ * Submit handler for button to remove a condition from a condition group.
755+ *
756+ * @see uc_ca_conditions_form()
757+ */
758+function uc_ca_conditions_form_remove_condition_submit($form, &$form_state) {
759+ $group_key = FALSE;
760+ $cond_key = FALSE;
761+
762+ // Loop through the array keys of the conditions group.
763+ foreach (array_keys($form['conditions']['conditions']) as $key => $value) {
764+ // If we find the key we're looking for...
765+ if (is_numeric($value) && $value == $form_state['clicked_button']['#array_parents'][2]) {
766+ // Store its position as the new group key once the conditions are saved.
767+ // This is necessary because the save function will truncate any holes so
768+ // the keys are in numerical order starting with 0.
769+ $group_key = $key;
770+ }
771+ }
772+
773+ // Loop through the array keys of the conditions in the group.
774+ foreach (array_keys($form['conditions']['conditions'][$form_state['clicked_button']['#array_parents'][2]]['conditions']) as $key => $value) {
775+ // If we find the key we're looking for...
776+ if (is_numeric($value) && $value == $form_state['clicked_button']['#array_parents'][4]) {
777+ // Store its position as the new condition key once the conditions are
778+ // saved. This is necessary because the save function will truncate any
779+ // holes so the keys are in numerical order starting with 0.
780+ $cond_key = $key;
781+ }
782+ }
783+
784+ // Save the existing conditions to preserve any changes.
785+ uc_ca_conditions_form_update_conditions($form_state['values']['pid'], $form_state['values']['conditions']);
786+
787+ // Fail if we didn't find a new group key for some reason.
788+ if ($group_key === FALSE || $cond_key === FALSE) {
789+ drupal_set_message(t('An error occurred when trying to remove the condition. Please try again.'), 'error');
790+ }
791+ else {
792+ // Otherwise remove the condition from the specified group.
793+ uc_ca_condition_remove($form_state['values']['pid'], $group_key, $cond_key);
794+
795+ drupal_set_message(t('Condition removed.'));
796+ }
797+}
798+
799+/**
800+ * Form to delete or reset a predicate.
801+ */
802+function uc_ca_predicate_delete_form($form, &$form_state, $predicate) {
803+
804+ $form['predicate_pid'] = array(
805+ '#type' => 'value',
806+ '#value' => $predicate['#pid'],
807+ );
808+
809+ $form['predicate_title'] = array(
810+ '#type' => 'value',
811+ '#value' => $predicate['#title'],
812+ );
813+
814+ $description = '<p><strong>' . check_plain($predicate['#title']) . '</strong><br />'
815+ . check_plain($predicate['#description']) . '</p><p>'
816+ . t('This action cannot be undone.') .'</p>';
817+
818+ return confirm_form($form, t('Are you sure you want to !op this predicate?', array('!op' => is_numeric($predicate['#pid']) ? t('delete') : t('reset'))), 'admin/store/ca', $description);
819+}
820+
821+/**
822+ * Submit handler for predicate delete form.
823+ * @see uc_ca_predicate_delete_form()
824+ */
825+function uc_ca_predicate_delete_form_submit($form, &$form_state) {
826+ uc_ca_predicate_delete($form_state['values']['predicate_pid']);
827+
828+ drupal_set_message(t('Predicate %title !op.', array('%title' => $form_state['values']['predicate_title'], '!op' => is_numeric($form_state['values']['predicate_pid']) ? t('deleted') : t('reset'))));
829+
830+ $form_state['redirect'] = 'admin/store/ca';
831+}
832+
833+/**
834+ * Build a form for adding and editing actions on a predicate.
835+ *
836+ * @param $predicate
837+ * The loaded predicate array whose actions are being modified.
838+ * @return
839+ * The form array for the actions form.
840+ *
841+ * @ingroup forms
842+ * @see
843+ * uc_ca_actions_form_add_action_submit()
844+ * uc_ca_actions_form_add_action_submit()
845+ * uc_ca_actions_form_save_changes_submit()
846+ */
847+function uc_ca_actions_form($form, &$form_state, $predicate) {
848+
849+ // Include the JS for conditional actions forms.
850+ drupal_add_js(drupal_get_path('module', 'uc_ca') .'/theme/uc_ca.js');
851+
852+ // Display the title.
853+ drupal_set_title($predicate['#title']);
854+
855+ // Load up any actions that could possibly be on this predicate.
856+ $action_data = uc_ca_action_load();
857+
858+ $form['pid'] = array(
859+ '#type' => 'value',
860+ '#value' => $predicate['#pid'],
861+ );
862+
863+ $form['actions'] = array(
864+ '#type' => 'fieldset',
865+ '#title' => t('Actions'),
866+ '#description' => t('These actions will be performed in order when this predicate passes the conditions evaluation.'),
867+ '#tree' => TRUE,
868+ );
869+
870+ // Loop through the actions and add them to the form if valid.
871+ $i = 0;
872+
873+ foreach ($predicate['#actions'] as $key => $action) {
874+ // Add it to the form if the action's callback exists.
875+ $callback = isset($action_data[$action['#name']]['#callback']) ? $action_data[$action['#name']]['#callback'] : '';
876+
877+ if (function_exists($callback)) {
878+ $form['actions'][$i] = array(
879+ '#type' => 'fieldset',
880+ '#title' => t('Action: @title', array('@title' => $action_data[$action['#name']]['#title'])),
881+ '#description' => isset($action_data[$action['#name']]['#description']) ? $action_data[$action['#name']]['#description'] : '',
882+ '#collapsible' => TRUE,
883+ '#collapsed' => isset($_SESSION['ca_action_focus']) && $i == $_SESSION['ca_action_focus'] ? FALSE : TRUE,
884+ );
885+
886+ $form['actions'][$i]['name'] = array(
887+ '#type' => 'value',
888+ '#value' => $action['#name'],
889+ );
890+
891+ $form['actions'][$i]['title'] = array(
892+ '#type' => 'textfield',
893+ '#title' => t('Title'),
894+ '#default_value' => $action['#title'],
895+ );
896+
897+ $form['actions'][$i]['argument_map'] = array(
898+ '#type' => 'fieldset',
899+ '#title' => t('Arguments'),
900+ '#description' => t('Some triggers pass in multiple options for arguments related to a particular action, so you must specify which options to use in the fields below.'),
901+ '#collapsible' => TRUE,
902+ );
903+
904+ // Setup a variable to decide if we can collapse the arguments fieldset.
905+ $collapsed = TRUE;
906+
907+ foreach ($action_data[$action['#name']]['#arguments'] as $key => $value) {
908+ // Load the available arguments for each entity as received by the
909+ // trigger when it was pulled.
910+ $options = uc_ca_trigger_arguments_load($predicate['#trigger'], $value['#entity']);
911+
912+ // If we have more than one option for any argument, do not collapse.
913+ if (count($options) > 1) {
914+ $collapsed = FALSE;
915+ }
916+
917+ $form['actions'][$i]['argument_map'][$key] = array(
918+ '#type' => 'select',
919+ '#title' => check_plain($value['#title']),
920+ '#options' => $options,
921+ '#default_value' => isset($action['#argument_map']) ? $action['#argument_map'][$key] : '',
922+ );
923+ }
924+
925+ $form['actions'][$i]['argument_map']['#collapsed'] = $collapsed;
926+
927+ // Add the action's form elements if any exist.
928+ $callback .= '_form';
929+
930+ if (function_exists($callback)) {
931+ // Load the trigger data.
932+ $trigger_data = uc_ca_trigger_load($predicate['#trigger']);
933+
934+ $form['actions'][$i]['settings'] = $callback(array(), isset($action['#settings']) ? $action['#settings'] : array(), $trigger_data['#arguments']);
935+ }
936+
937+ $form['actions'][$i]['remove'] = array(
938+ '#type' => 'submit',
939+ '#value' => t('Remove this action'),
940+ '#name' => 'ca_remove_action_'. $i,
941+ '#submit' => array('uc_ca_actions_form_remove_action_submit'),
942+ '#attributes' => array('class' => array('ca-remove-confirm')),
943+ );
944+
945+ $i++;
946+ }
947+ }
948+
949+ // Unset the session variable used to focus on the new action.
950+ unset($_SESSION['ca_action_focus']);
951+
952+ $form['actions']['add_action'] = array(
953+ '#type' => 'select',
954+ '#title' => t('Available actions'),
955+ '#options' => uc_ca_trigger_actions_load($predicate['#trigger']),
956+ );
957+
958+ $form['actions']['add'] = array(
959+ '#type' => 'submit',
960+ '#value' => t('Add action'),
961+ '#submit' => array('uc_ca_actions_form_add_action_submit'),
962+ );
963+
964+ $form['submit'] = array(
965+ '#type' => 'submit',
966+ '#value' => t('Save changes'),
967+ '#submit' => array('uc_ca_actions_form_save_changes_submit'),
968+ );
969+
970+ uc_ca_filter_configure_form($form);
971+
972+ return $form;
973+}
974+
975+/**
976+ * Remove action submit handler.
977+ *
978+ * @see uc_ca_actions_form()
979+ */
980+function uc_ca_actions_form_remove_action_submit($form, &$form_state) {
981+ // Save the existing actions to preserve any changes.
982+ uc_ca_actions_form_update_actions($form_state['values']['pid'], $form_state['values']['actions']);
983+
984+ // Remove the appropriate action from the predicate and save it.
985+ uc_ca_action_remove($form_state['values']['pid'], $form_state['clicked_button']['#parents'][1]);
986+
987+ drupal_set_message(t('Action removed.'));
988+}
989+
990+/**
991+ * Add action submit handler.
992+ *
993+ * @see uc_ca_actions_form()
994+ */
995+function uc_ca_actions_form_add_action_submit($form, &$form_state) {
996+ // Save the existing actions to preserve any changes.
997+ $predicate = uc_ca_actions_form_update_actions($form_state['values']['pid'], $form_state['values']['actions']);
998+
999+ // Store the presumed key of the new action.
1000+ $_SESSION['ca_action_focus'] = count($predicate['#actions']);
1001+
1002+ // Add the specified action to the predicate.
1003+ uc_ca_action_add($form_state['values']['pid'], $form_state['values']['actions']['add_action']);
1004+
1005+ drupal_set_message(t('Action added.'));
1006+}
1007+
1008+/**
1009+ * Save changes submit handler for the actions form.
1010+ *
1011+ * @see uc_ca_actions_form()
1012+ */
1013+function uc_ca_actions_form_save_changes_submit($form, &$form_state) {
1014+ // Save the existing actions to preserve any changes.
1015+ uc_ca_actions_form_update_actions($form_state['values']['pid'], $form_state['values']['actions']);
1016+
1017+ drupal_set_message(t('Actions saved.'));
1018+}
1019+
1020+/**
1021+ * Updates a predicate's actions based on the values from an actions form.
1022+ *
1023+ * @param $pid
1024+ * The ID of the predicate whose actions should be updated.
1025+ * @param $data
1026+ * The actions values from the form state that should be used to update the
1027+ * predicate; normally $form_state['values']['actions'].
1028+ * @return
1029+ * An array representing the full, updated predicate.
1030+ */
1031+function uc_ca_actions_form_update_actions($pid, $data) {
1032+ $actions = array();
1033+
1034+ // Unset top level components we don't want to get in the way.
1035+ unset($data['add_action'], $data['add']);
1036+
1037+ // Loop through the actions from the form and add them to our temporary array.
1038+ foreach ((array) $data as $key => $value) {
1039+ $actions[] = array(
1040+ '#name' => $value['name'],
1041+ '#title' => $value['title'],
1042+ '#argument_map' => isset($value['argument_map']) ? $value['argument_map'] : array(),
1043+ '#settings' => empty($value['settings']) ? array() : $value['settings'],
1044+ );
1045+ }
1046+
1047+ // Load the predicate as it is now.
1048+ $predicate = uc_ca_predicate_load($pid);
1049+
1050+ // Update the actions and save the result.
1051+ $predicate['#actions'] = $actions;
1052+ uc_ca_predicate_save($predicate);
1053+
1054+ return $predicate;
1055+}
1056+
1057
1058=== added file 'modules/uc_ca/includes/uc_ca.ca.inc'
1059--- modules/uc_ca/includes/uc_ca.ca.inc 1970-01-01 00:00:00 +0000
1060+++ modules/uc_ca/includes/uc_ca.ca.inc 2009-11-22 17:00:24 +0000
1061@@ -0,0 +1,512 @@
1062+<?php
1063+// $Id$
1064+
1065+/**
1066+ * @file
1067+ * This file includes some generic conditions and actions.
1068+ */
1069+
1070+/*******************************************************************************
1071+ * Conditional Actions Hooks
1072+ ******************************************************************************/
1073+
1074+/**
1075+ * Implement hook_uc_ca_entity().
1076+ *
1077+ * Demonstrates defining a data object that may be passed in to uc_ca_trigger_pull
1078+ * and mapped to a predicate's arguments.
1079+ */
1080+function uc_ca_uc_ca_entity() {
1081+ $entities = array();
1082+
1083+ $entities['user'] = array(
1084+ '#title' => t('Drupal user'),
1085+ '#type' => 'object',
1086+ '#load' => 'user_load',
1087+ '#save' => 'user_save',
1088+ );
1089+ $entities['node'] = array(
1090+ '#title' => t('Node'),
1091+ '#type' => 'object',
1092+ '#load' => 'node_load',
1093+ '#save' => 'node_save',
1094+ );
1095+ $entities['arguments'] = array(
1096+ '#title' => t('Trigger arguments'),
1097+ '#type' => 'array',
1098+ );
1099+
1100+ return $entities;
1101+}
1102+
1103+/**
1104+ * Implement hook_uc_ca_condition().
1105+ */
1106+function uc_ca_uc_ca_condition() {
1107+ $conditions = array();
1108+
1109+ $conditions['uc_ca_condition_date'] = array(
1110+ '#title' => t('Check the current date'),
1111+ '#description' => t('Used to determine if the action should be performed on the current date.'),
1112+ '#category' => t('Drupal'),
1113+ '#callback' => 'uc_ca_condition_date',
1114+ '#arguments' => array(),
1115+ );
1116+
1117+ $conditions['node_field_comparison'] = array(
1118+ '#title' => t('Compare a node field value'),
1119+ '#description' => t('Returns TRUE if the node field selected below compares to the value entered as specified by the operator.'),
1120+ '#category' => t('Node'),
1121+ '#callback' => 'uc_ca_condition_node_field_comparison',
1122+ '#arguments' => array(
1123+ 'node' => array('#entity' => 'node'),
1124+ ),
1125+ );
1126+
1127+ $conditions['uc_ca_condition_custom_php'] = array(
1128+ '#title' => t('Execute custom PHP code'),
1129+ '#description' => t('Returns whatever your custom PHP code returns.'),
1130+ '#category' => t('System'),
1131+ '#callback' => 'uc_ca_condition_custom_php',
1132+ '#arguments' => array(
1133+ 'arguments' => array('#entity' => 'arguments', '#title' => t('Arguments')),
1134+ ),
1135+ );
1136+
1137+ $conditions['uc_ca_condition_user_roles'] = array(
1138+ '#title' => t("Check the user's roles"),
1139+ '#category' => t('User'),
1140+ '#description' => t('Returns TRUE if the user roles match your settings.'),
1141+ '#callback' => 'uc_ca_condition_user_roles',
1142+ '#arguments' => array(
1143+ 'account' => array('#entity' => 'user', '#title' => t('User')),
1144+ ),
1145+ );
1146+
1147+ return $conditions;
1148+}
1149+
1150+/**
1151+ * Implement hook_uc_ca_action().
1152+ *
1153+ * Demonstrates defining an action for predicates to use; primarily specifies a
1154+ * callback function to perform the action and an array that specifies arguments
1155+ * and their data types.
1156+ */
1157+function uc_ca_uc_ca_action() {
1158+ $actions['uc_ca_drupal_set_message'] = array(
1159+ '#title' => t('Display a message to the user'),
1160+ '#category' => t('Drupal'),
1161+ '#callback' => 'uc_ca_action_drupal_set_message',
1162+ '#arguments' => array(),
1163+ );
1164+
1165+ $actions['uc_ca_action_custom_php'] = array(
1166+ '#title' => t('Execute custom PHP code'),
1167+ '#category' => t('System'),
1168+ '#callback' => 'uc_ca_action_custom_php',
1169+ '#arguments' => array(
1170+ 'arguments' => array('#entity' => 'arguments', '#title' => t('Arguments')),
1171+ ),
1172+ );
1173+
1174+ return $actions;
1175+}
1176+
1177+/**
1178+ * Check that the current time is within a specified range.
1179+ *
1180+ * @see uc_ca_condition_date_form()
1181+ */
1182+function uc_ca_condition_date($settings) {
1183+ $set_date_start = gmmktime(0, 0, 0, $settings['date']['month'], $settings['date']['day'], $settings['date']['year']);
1184+ $set_date_end = gmmktime(23, 59, 59, $settings['date']['month'], $settings['date']['day'], $settings['date']['year']);
1185+
1186+ $curr_time = time();
1187+
1188+ switch ($settings['operator']) {
1189+ case 'before':
1190+ return $curr_time < $set_date_start;
1191+ case 'only':
1192+ return ($set_date_start < $curr_time) && ($curr_time < $set_date_end);
1193+ case 'after':
1194+ return $set_date_end < $curr_time;
1195+ default:
1196+ return FALSE;
1197+ }
1198+}
1199+
1200+/**
1201+ * @see uc_ca_condition_date()
1202+ */
1203+function uc_ca_condition_date_form($form_state, $settings = array()) {
1204+ $form = array();
1205+
1206+ $form['operator'] = array(
1207+ '#type' => 'radios',
1208+ '#title' => t('Comparison'),
1209+ '#options' => array(
1210+ 'before' => t('Before'),
1211+ 'only' => t('Only'),
1212+ 'after' => t('After'),
1213+ ),
1214+ '#default_value' => isset($settings['operator']) ? $settings['operator'] : '',
1215+ '#description' => t('Example: "The current date is before the date below."'),
1216+ );
1217+
1218+ $form['date'] = array(
1219+ '#type' => 'date',
1220+ '#title' => t('Date'),
1221+ '#default_value' => isset($settings['date']) ? $settings['date'] : '',
1222+ '#description' => t('When the predicate is evaluated, the current date is compared to this date.'),
1223+ );
1224+
1225+ return $form;
1226+}
1227+
1228+/**
1229+ * Compare a node field to a specified value using an operator list.
1230+ *
1231+ * @see uc_ca_condition_node_field_comparison_form()
1232+ */
1233+function uc_ca_condition_node_field_comparison($node, $settings) {
1234+ // Convert the node to an array.
1235+ $node_array = (array) $node;
1236+
1237+ // Store the value of the field we're comparing.
1238+ $field_value = $node_array[$settings['field']];
1239+
1240+ // Return the result based on the operator.
1241+ switch ($settings['operator']) {
1242+ case 'equal':
1243+ if ($field_value == $settings['value']) {
1244+ return TRUE;
1245+ }
1246+ break;
1247+
1248+ case 'not':
1249+ if ($field_value != $settings['value']) {
1250+ return TRUE;
1251+ }
1252+ break;
1253+
1254+ case 'greater':
1255+ if ($field_value > $settings['value']) {
1256+ return TRUE;
1257+ }
1258+ break;
1259+
1260+ case 'less':
1261+ if ($field_value < $settings['value']) {
1262+ return TRUE;
1263+ }
1264+ break;
1265+
1266+ case 'greater_equal':
1267+ if ($field_value >= $settings['value']) {
1268+ return TRUE;
1269+ }
1270+ break;
1271+
1272+ case 'less_equal':
1273+ if ($field_value <= $settings['value']) {
1274+ return TRUE;
1275+ }
1276+ break;
1277+
1278+ case 'begins':
1279+ if (strpos($field_value, $settings['value']) === 0) {
1280+ return TRUE;
1281+ }
1282+ break;
1283+
1284+ case 'ends':
1285+ if (substr($field_value, -1 * strlen($settings['value'])) === $settings['value']) {
1286+ return TRUE;
1287+ }
1288+ break;
1289+
1290+ case 'contains':
1291+ if (strpos($field_value, $settings['value']) !== FALSE) {
1292+ return TRUE;
1293+ }
1294+ break;
1295+
1296+ case 'yes':
1297+ if ($field_value) {
1298+ return TRUE;
1299+ }
1300+ break;
1301+
1302+ case 'no':
1303+ if (!$field_value) {
1304+ return TRUE;
1305+ }
1306+ break;
1307+ }
1308+
1309+ return FALSE;
1310+}
1311+
1312+/**
1313+ * @see uc_ca_condition_node_field_comparision()
1314+ */
1315+function uc_ca_condition_node_field_comparison_form($form_state, $settings = array()) {
1316+ $form = array();
1317+
1318+ // Define the fields this works for.
1319+ $options = array(
1320+ t('Core node fields') => array(
1321+ 'nid' => t('Node ID'),
1322+ 'vid' => t('Node revision ID'),
1323+ 'type' => t('Node type'),
1324+ 'title' => t('Title'),
1325+ 'uid' => t("Author's user ID"),
1326+ 'status' => t('Node is published?'),
1327+ 'promote' => t('Node is promoted?'),
1328+ 'sticky' => t('Node is sticky?'),
1329+ ),
1330+ );
1331+
1332+ $form['field'] = array(
1333+ '#type' => 'select',
1334+ '#title' => t('Node field'),
1335+ '#options' => $options,
1336+ '#default_value' => $settings['field'],
1337+ );
1338+
1339+ // Define the operators for the fields.
1340+ $options = array(
1341+ t('Simple comparison') => array(
1342+ 'equal' => t('is equal to'),
1343+ 'not' => t('is not equal to'),
1344+ 'greater' => t('is greater than'),
1345+ 'less' => t('is less than'),
1346+ 'greater_equal' => t('is greater than or equal to'),
1347+ 'less_equal' => t('is less than or equal to'),
1348+ ),
1349+ t('Text matching') => array(
1350+ 'begins' => t('begins with'),
1351+ 'contains' => t('contains'),
1352+ 'ends' => t('ends with'),
1353+ ),
1354+ t('Yes/No') => array(
1355+ 'yes' => t('yes'),
1356+ 'no' => t('no'),
1357+ ),
1358+ );
1359+
1360+ $form['operator'] = array(
1361+ '#type' => 'select',
1362+ '#title' => t('Operator'),
1363+ '#description' => t('Please note that not every operator makes sense for every field.'),
1364+ '#options' => $options,
1365+ '#default_value' => $settings['operator'],
1366+ );
1367+
1368+ $form['value'] = array(
1369+ '#type' => 'textfield',
1370+ '#title' => t('Comparison value'),
1371+ '#description' => t('You do not need to specify a value if your operator is in the Yes/No category.'),
1372+ '#default_value' => $settings['value'],
1373+ );
1374+
1375+ return $form;
1376+}
1377+
1378+/**
1379+ * Display a message to the user.
1380+ *
1381+ * @see uc_ca_action_drupal_set_message_form()
1382+ */
1383+function uc_ca_action_drupal_set_message($settings) {
1384+ // Filter the text using the default format.
1385+ $message = check_markup($settings['message_text'], $settings['message_text_format'], FALSE);
1386+
1387+ // Return if there's nothing to display.
1388+ if (empty($message) || empty($settings['message_text'])) {
1389+ return;
1390+ }
1391+
1392+ // Make sure we have a valid message type.
1393+ if ($settings['message_type'] == 'error') {
1394+ $type = $settings['message_type'];
1395+ }
1396+ else {
1397+ $type = 'status';
1398+ }
1399+
1400+ // Output the message using the Drupal message API.
1401+ drupal_set_message($message, $type);
1402+}
1403+
1404+/**
1405+ * @see uc_ca_action_drupal_set_message()
1406+ */
1407+function uc_ca_action_drupal_set_message_form($form_state, $settings = array()) {
1408+ $form = array();
1409+
1410+ $form['message_text'] = array(
1411+ '#type' => 'textarea',
1412+ '#title' => t('Message text'),
1413+ '#default_value' => isset($settings['message_text']) ? $settings['message_text'] : '',
1414+ '#text_format' => isset($settings['message_text_format']) ? $settings['message_text_format'] : filter_default_format(),
1415+ );
1416+
1417+ $form['message_type'] = array(
1418+ '#type' => 'radios',
1419+ '#title' => t('Message type'),
1420+ '#options' => array(
1421+ 'status' => t('Status'),
1422+ 'error' => t('Error'),
1423+ ),
1424+ '#default_value' => isset($settings['message_type']) ? $settings['message_type'] : 'status',
1425+ );
1426+
1427+ return $form;
1428+}
1429+
1430+/**
1431+ * Evaluate a custom PHP condition.
1432+ *
1433+ * @see uc_ca_condition_custom_php_form()
1434+ */
1435+function uc_ca_condition_custom_php($arguments, $settings) {
1436+ return _uc_ca_custom_php_eval($settings['php'], $arguments);
1437+}
1438+
1439+/**
1440+ * @see uc_ca_condition_custom_php()
1441+ */
1442+function uc_ca_condition_custom_php_form($form_state, $settings = array(), $arguments = array()) {
1443+ $form['variables'] = _uc_ca_custom_php_variables_table($arguments);
1444+
1445+ $form['php'] = array(
1446+ '#type' => 'textarea',
1447+ '#title' => t('Custom PHP'),
1448+ '#description' => t('Enter the custom PHP to be evaluated when this condition is executed. Should not include &lt;?php ?> delimiters.'),
1449+ '#default_value' => isset($settings['php']) ? $settings['php'] : '',
1450+ );
1451+
1452+ return $form;
1453+}
1454+
1455+/**
1456+ * Check a user's roles.
1457+ *
1458+ * @see uc_ca_condition_user_roles_form()
1459+ */
1460+function uc_ca_condition_user_roles($account, $settings) {
1461+ $settings['roles'] = array_filter($settings['roles']);
1462+
1463+ if ($settings['operator'] == 'AND') {
1464+ foreach ($settings['roles'] as $key) {
1465+ if (!isset($account->roles[$key])) {
1466+ return FALSE;
1467+ }
1468+ }
1469+ return TRUE;
1470+ }
1471+ else {
1472+ foreach ($settings['roles'] as $key) {
1473+ if (isset($account->roles[$key])) {
1474+ return TRUE;
1475+ }
1476+ }
1477+ return FALSE;
1478+ }
1479+}
1480+
1481+/**
1482+ * @see uc_ca_condition_user_roles()
1483+ */
1484+function uc_ca_condition_user_roles_form($form_state, $settings = array()) {
1485+ $form['operator'] = array(
1486+ '#type' => 'radios',
1487+ '#title' => t('Operator'),
1488+ '#description' => t('If you specify <em>AND</em> and want to check a custom role, remember to specify <em>authenticated user</em> where applicable.'),
1489+ '#options' => array(
1490+ 'OR' => t('OR: If the user has any of these roles.'),
1491+ 'AND' => t('AND: If the user has all of these roles.'),
1492+ ),
1493+ '#default_value' => isset($settings['operator']) ? $settings['operator'] : 'OR',
1494+ );
1495+ $form['roles'] = array(
1496+ '#type' => 'checkboxes',
1497+ '#title' => t('Roles'),
1498+ '#options' => user_roles(),
1499+ '#default_value' => isset($settings['roles']) ? $settings['roles'] : array(),
1500+ );
1501+
1502+ return $form;
1503+}
1504+
1505+/**
1506+ * Perform a custom PHP action.
1507+ *
1508+ * @see uc_ca_action_custom_php_form()
1509+ */
1510+function uc_ca_action_custom_php($arguments, $settings) {
1511+ _uc_ca_custom_php_eval($settings['php'], $arguments);
1512+}
1513+
1514+/**
1515+ * @see uc_ca_action_custom_php()
1516+ */
1517+function uc_ca_action_custom_php_form($form_state, $settings = array(), $arguments = array()) {
1518+ $form['variables'] = _uc_ca_custom_php_variables_table($arguments);
1519+
1520+ $form['php'] = array(
1521+ '#type' => 'textarea',
1522+ '#title' => t('Custom PHP'),
1523+ '#description' => t('Enter the custom PHP to be evaluated when this action is executed. Should not include &lt;?php ?> delimiters.'),
1524+ '#default_value' => isset($settings['php']) ? $settings['php'] : '',
1525+ );
1526+
1527+ return $form;
1528+}
1529+
1530+/**
1531+ * Return a fieldset used to display available variables for custom PHP
1532+ * conditions and actions.
1533+ */
1534+function _uc_ca_custom_php_variables_table($arguments) {
1535+ $rows = array();
1536+
1537+ $header = array(t('Variable'), t('Type'), t('Description'));
1538+
1539+ // Translate the arguments into descriptive rows.
1540+ foreach ($arguments as $key => $value) {
1541+ $rows[] = array(check_plain('$'. $key), check_plain($value['#entity']), $value['#title']);
1542+ }
1543+
1544+ if (empty($rows)) {
1545+ $rows[] = array(array('data' => t('No variables available.'), 'colspan' => 3));
1546+ }
1547+
1548+ $fieldset = array(
1549+ '#type' => 'fieldset',
1550+ '#title' => t('Available PHP variables'),
1551+ '#description' => t('You may use these variables in your custom PHP.')
1552+ . theme('table', array('header' => $header, 'rows' => $rows)),
1553+ '#collapsible' => TRUE,
1554+ );
1555+
1556+ return $fieldset;
1557+}
1558+
1559+/**
1560+ * Evaluate a custom PHP condition or action with trigger arguments available
1561+ * as variables.
1562+ */
1563+function _uc_ca_custom_php_eval($php, $arguments) {
1564+ $argument_data = array();
1565+
1566+ // Convert the arguments to an array of data that we can extract.
1567+ foreach ($arguments as $key => $value) {
1568+ $argument_data[$key] = $value['#data'];
1569+ }
1570+
1571+ extract($argument_data);
1572+ return eval($php);
1573+}
1574
1575=== added directory 'modules/uc_ca/theme'
1576=== added file 'modules/uc_ca/theme/uc_ca.css'
1577--- modules/uc_ca/theme/uc_ca.css 1970-01-01 00:00:00 +0000
1578+++ modules/uc_ca/theme/uc_ca.css 2009-11-22 17:00:24 +0000
1579@@ -0,0 +1,21 @@
1580+/* $Id$ */
1581+
1582+table.ca-predicate-trigger .col-title {
1583+ width: 70%;
1584+}
1585+
1586+table.ca-predicate-class .col-title {
1587+ width: 40%;
1588+}
1589+
1590+.ca-predicate-trigger tr, .ca-predicate-class tr {
1591+ vertical-align: top;
1592+}
1593+
1594+.ca-predicate-table-weight {
1595+ text-align: center;
1596+}
1597+
1598+.ca-predicate-table-ops {
1599+ white-space: nowrap;
1600+}
1601
1602=== added file 'modules/uc_ca/theme/uc_ca.js'
1603--- modules/uc_ca/theme/uc_ca.js 1970-01-01 00:00:00 +0000
1604+++ modules/uc_ca/theme/uc_ca.js 2009-11-22 17:00:24 +0000
1605@@ -0,0 +1,19 @@
1606+// $Id$
1607+
1608+/**
1609+ * @file
1610+ * Adds some helper JS to the conditional actions forms.
1611+ */
1612+(function ($) {
1613+
1614+/**
1615+ * Add confirmation prompts to remove buttons.
1616+ */
1617+Drupal.behaviors.caRemoveConfirm = function(context) {
1618+ $('.ca-remove-confirm:not(.caRemoveConfirm-processed)', context).addClass('caRemoveConfirm-processed').click(function() {
1619+ return confirm(Drupal.t('Are you sure you want to remove this item?'));
1620+ });
1621+}
1622+
1623+})(jQuery);
1624+
1625
1626=== added file 'modules/uc_ca/uc_ca.info'
1627--- modules/uc_ca/uc_ca.info 1970-01-01 00:00:00 +0000
1628+++ modules/uc_ca/uc_ca.info 2009-11-22 17:00:24 +0000
1629@@ -0,0 +1,10 @@
1630+; $Id$
1631+name = Conditional Actions
1632+description = Ubercore Decision Engine
1633+package = Ubercore
1634+files[] = uc_ca.module
1635+files[] = uc_ca.install
1636+files[] = includes/uc_ca.admin.inc
1637+files[] = includes/uc_ca.ca.inc
1638+core = 7.x
1639+configure = admin/store/ca
1640\ No newline at end of file
1641
1642=== added file 'modules/uc_ca/uc_ca.install'
1643--- modules/uc_ca/uc_ca.install 1970-01-01 00:00:00 +0000
1644+++ modules/uc_ca/uc_ca.install 2009-11-22 17:00:24 +0000
1645@@ -0,0 +1,110 @@
1646+<?php
1647+// $Id$
1648+
1649+/**
1650+ * @file
1651+ * Install hooks for uc_ca.module.
1652+ */
1653+
1654+/**
1655+ * Implement hook_schema().
1656+ */
1657+function uc_ca_schema() {
1658+ $schema = array();
1659+
1660+ $schema['uc_ca_predicates'] = array(
1661+ 'fields' => array(
1662+ 'pid' => array(
1663+ 'type' => 'varchar',
1664+ 'length' => 255,
1665+ 'not null' => TRUE,
1666+ 'default' => '',
1667+ ),
1668+ 'title' => array(
1669+ 'type' => 'varchar',
1670+ 'length' => 255,
1671+ 'not null' => TRUE,
1672+ 'default' => '',
1673+ ),
1674+ 'description' => array(
1675+ 'type' => 'text',
1676+ ),
1677+ 'class' => array(
1678+ 'type' => 'varchar',
1679+ 'length' => 255,
1680+ 'not null' => TRUE,
1681+ 'default' => '',
1682+ ),
1683+ 'status' => array(
1684+ 'type' => 'int',
1685+ 'size' => 'tiny',
1686+ 'unsigned' => TRUE,
1687+ 'not null' => TRUE,
1688+ 'default' => 0,
1689+ ),
1690+ 'weight' => array(
1691+ 'type' => 'int',
1692+ 'size' => 'small',
1693+ 'not null' => TRUE,
1694+ 'default' => 0,
1695+ ),
1696+ 'ca_trigger' => array(
1697+ 'type' => 'varchar',
1698+ 'length' => 255,
1699+ 'not null' => TRUE,
1700+ 'default' => '',
1701+ ),
1702+ 'conditions' => array(
1703+ 'type' => 'text',
1704+ 'size' => 'big',
1705+ 'serialize' => TRUE,
1706+ ),
1707+ 'actions' => array(
1708+ 'type' => 'text',
1709+ 'size' => 'big',
1710+ 'serialize' => TRUE,
1711+ ),
1712+ 'created' => array(
1713+ 'type' => 'int',
1714+ 'not null' => TRUE,
1715+ 'default' => 0,
1716+ ),
1717+ 'modified' => array(
1718+ 'type' => 'int',
1719+ 'not null' => TRUE,
1720+ 'default' => 0,
1721+ ),
1722+ ),
1723+ 'indexes' => array(
1724+ 'uc_ca_predicates_class' => array('class'),
1725+ 'uc_ca_predicates_status' => array('status'),
1726+ 'uc_ca_predicates_ca_trigger' => array('ca_trigger'),
1727+ ),
1728+ 'primary key' => array('pid'),
1729+ );
1730+
1731+ return $schema;
1732+}
1733+
1734+/**
1735+ * Implement hook_install().
1736+ */
1737+function uc_ca_install() {
1738+ drupal_install_schema('uc_ca');
1739+}
1740+
1741+/**
1742+ * Implement hook_uninstall().
1743+ */
1744+function uc_ca_uninstall() {
1745+ drupal_uninstall_schema('uc_ca');
1746+}
1747+
1748+/**
1749+ * Updates for the D7 version.
1750+ */
1751+function uc_ca_update_7000() {
1752+ db_rename_table('ca_predicates', 'uc_ca_predicates');
1753+ db_drop_field('uc_ca_predicates', 'uid');
1754+ variable_del('ca_predicates_pid');
1755+}
1756
1757=== added file 'modules/uc_ca/uc_ca.module'
1758--- modules/uc_ca/uc_ca.module 1970-01-01 00:00:00 +0000
1759+++ modules/uc_ca/uc_ca.module 2009-11-22 17:00:24 +0000
1760@@ -0,0 +1,1082 @@
1761+<?php
1762+// $Id$
1763+
1764+/**
1765+ * @file
1766+ * Ubercore decision engine.
1767+ */
1768+
1769+require_once('includes/uc_ca.ca.inc');
1770+
1771+/**
1772+ * Implement hook_permission().
1773+ */
1774+function uc_ca_permission() {
1775+ $permissions = array(
1776+ 'administer conditional actions' => array(
1777+ 'title' => t('Administer Ubercore Conditional Actions'),
1778+ 'description' => t('Manage the Ubercore decision engine.'),
1779+ ),
1780+ );
1781+ return $permissions;
1782+}
1783+
1784+/**
1785+ * Implement hook_menu().
1786+ */
1787+function uc_ca_menu() {
1788+ $items = array();
1789+
1790+ $items['admin/store/ca'] = array(
1791+ 'title' => 'Conditional Actions',
1792+ 'description' => 'Administer the predicates setup to automate your store.',
1793+ 'page callback' => 'uc_ca_admin',
1794+ 'access arguments' => array('administer conditional actions'),
1795+ 'file' => 'includes/uc_ca.admin.inc',
1796+ );
1797+ $items['admin/store/ca/overview'] = array(
1798+ 'title' => 'Overview',
1799+ 'type' => MENU_DEFAULT_LOCAL_TASK,
1800+ 'weight' => 0,
1801+ );
1802+ $items['admin/store/ca/overview/trigger'] = array(
1803+ 'title' => 'By trigger',
1804+ 'description' => 'Administer the predicates setup to automate your store.',
1805+ 'type' => MENU_DEFAULT_LOCAL_TASK,
1806+ 'weight' => 0,
1807+ );
1808+ $items['admin/store/ca/overview/class'] = array(
1809+ 'title' => 'By class',
1810+ 'description' => 'Administer the predicates setup to automate your store.',
1811+ 'page callback' => 'uc_ca_admin',
1812+ 'page arguments' => array('class'),
1813+ 'access arguments' => array('administer conditional actions'),
1814+ 'type' => MENU_LOCAL_TASK,
1815+ 'file' => 'includes/uc_ca.admin.inc',
1816+ 'weight' => 5,
1817+ );
1818+ $items['admin/store/ca/add'] = array(
1819+ 'title' => 'Add a predicate',
1820+ 'description' => 'Allows an administrator to create a new predicate.',
1821+ 'page callback' => 'drupal_get_form',
1822+ 'page arguments' => array('uc_ca_predicate_meta_form'),
1823+ 'access arguments' => array('administer conditional actions'),
1824+ 'type' => MENU_LOCAL_TASK,
1825+ 'file' => 'includes/uc_ca.admin.inc',
1826+ 'weight' => 5,
1827+ );
1828+ $items['admin/store/ca/%uc_ca_predicate/edit'] = array(
1829+ 'title' => 'Edit predicate',
1830+ 'description' => "Edit a predicate's meta data, conditions, and actions.",
1831+ 'page callback' => 'drupal_get_form',
1832+ 'page arguments' => array('uc_ca_predicate_meta_form', 3),
1833+ 'access arguments' => array('administer conditional actions'),
1834+ 'file' => 'includes/uc_ca.admin.inc',
1835+ 'type' => MENU_CALLBACK,
1836+ );
1837+ $items['admin/store/ca/%uc_ca_predicate/edit/meta'] = array(
1838+ 'title' => 'Meta data',
1839+ 'description' => 'Edit the meta data for a predicate like title, trigger, etc.',
1840+ 'page callback' => 'drupal_get_form',
1841+ 'page arguments' => array('uc_ca_predicate_meta_form', 3),
1842+ 'access arguments' => array('administer conditional actions'),
1843+ 'file' => 'includes/uc_ca.admin.inc',
1844+ 'type' => MENU_DEFAULT_LOCAL_TASK,
1845+ 'weight' => -10,
1846+ );
1847+ $items['admin/store/ca/%uc_ca_predicate/edit/conditions'] = array(
1848+ 'title' => 'Conditions',
1849+ 'description' => 'Edit the conditions for a predicate.',
1850+ 'page callback' => 'drupal_get_form',
1851+ 'page arguments' => array('uc_ca_conditions_form', 3),
1852+ 'access arguments' => array('administer conditional actions'),
1853+ 'file' => 'includes/uc_ca.admin.inc',
1854+ 'type' => MENU_LOCAL_TASK,
1855+ 'weight' => -5,
1856+ );
1857+ $items['admin/store/ca/%uc_ca_predicate/edit/actions'] = array(
1858+ 'title' => 'Actions',
1859+ 'description' => 'Edit the actions for a predicate.',
1860+ 'page callback' => 'drupal_get_form',
1861+ 'page arguments' => array('uc_ca_actions_form', 3),
1862+ 'access arguments' => array('administer conditional actions'),
1863+ 'file' => 'includes/uc_ca.admin.inc',
1864+ 'type' => MENU_LOCAL_TASK,
1865+ 'weight' => 0,
1866+ );
1867+ $items['admin/store/ca/%uc_ca_predicate/reset'] = array(
1868+ 'title' => 'Reset a predicate',
1869+ 'page callback' => 'drupal_get_form',
1870+ 'page arguments' => array('uc_ca_predicate_delete_form', 3),
1871+ 'access arguments' => array('administer conditional actions'),
1872+ 'file' => 'includes/uc_ca.admin.inc',
1873+ 'type' => MENU_CALLBACK,
1874+ );
1875+ $items['admin/store/ca/%uc_ca_predicate/delete'] = array(
1876+ 'title' => 'Delete a predicate',
1877+ 'page callback' => 'drupal_get_form',
1878+ 'page arguments' => array('uc_ca_predicate_delete_form', 3),
1879+ 'access arguments' => array('administer conditional actions'),
1880+ 'file' => 'includes/uc_ca.admin.inc',
1881+ 'type' => MENU_CALLBACK,
1882+ );
1883+
1884+ return $items;
1885+}
1886+
1887+/**
1888+ * Prepare predicate data from the database into a full predicate array.
1889+ *
1890+ * @param $data
1891+ * An array of data representing a row in the predicates table.
1892+ * @return
1893+ * A predicate array.
1894+ */
1895+function uc_ca_db_predicate_prepare($data) {
1896+ $predicate = array();
1897+
1898+ foreach ($data as $key => $value) {
1899+ switch ($key) {
1900+ // Condition and action data needs to be unserialized.
1901+ case 'conditions':
1902+ case 'actions':
1903+ $predicate['#'. $key] = unserialize($value);
1904+ break;
1905+ case 'ca_trigger':
1906+ $predicate['#trigger'] = $value;
1907+ break;
1908+ default:
1909+ $predicate['#'. $key] = $value;
1910+ break;
1911+ }
1912+ }
1913+
1914+ return $predicate;
1915+}
1916+
1917+/**
1918+ * Compare two conditional action arrays to sort them by #weight.
1919+ */
1920+function uc_ca_weight_sort($a, $b) {
1921+ if ($a['#weight'] == $b['#weight']) {
1922+ return 0;
1923+ }
1924+
1925+ return ($a['#weight'] > $b['#weight']) ? 1 : -1;
1926+}
1927+
1928+/**
1929+ * Load a predicate by its ID.
1930+ *
1931+ * @param $pid
1932+ * The ID of the predicate to load.
1933+ * @return
1934+ * A fully loaded predicate array.
1935+ */
1936+function uc_ca_predicate_load($pid) {
1937+ $predicate = array();
1938+
1939+ // First attempt to load the predicate from the database.
1940+ $predicate = db_query("SELECT * FROM {uc_ca_predicates} WHERE pid = :pid", array(':pid' => $pid))->fetchAssoc();
1941+
1942+ if ($predicate) {
1943+ $predicate = uc_ca_db_predicate_prepare($predicate);
1944+ }
1945+ else {
1946+ // Otherwise look for it in the module defined predicates.
1947+ $predicates = module_invoke_all('uc_ca_predicate');
1948+
1949+ if (!empty($predicates[$pid])) {
1950+ $predicate = $predicates[$pid];
1951+ }
1952+ }
1953+
1954+ // Add the pid to the predicate so it can be resaved later.
1955+ if (!empty($predicate)) {
1956+ $predicate['#pid'] = $pid;
1957+ return $predicate;
1958+ }
1959+ else {
1960+ return FALSE;
1961+ }
1962+}
1963+
1964+/**
1965+ * Save a predicate array to the database.
1966+ *
1967+ * @param $predicate
1968+ * A fully loaded predicate array.
1969+ */
1970+function uc_ca_predicate_save(&$predicate) {
1971+ $count = 0;
1972+ if (!isset($predicate['#pid']) || !$predicate['#pid']) { // A new predicate is being saved.
1973+ // Give the new predicate a numeric pid higher than the last numeric pid.
1974+ $pids = db_query("SELECT pid FROM {uc_ca_predicates} ORDER BY pid DESC")->fetchCol();
1975+ $highest_pid = 0;
1976+ foreach ($pids as $pid) {
1977+ print $pid;
1978+ if (!$highest_pid) {
1979+ if (is_numeric($pid) && $pid > 0) {
1980+ $highest_pid = $pid;
1981+ print $highest_pid;
1982+ }
1983+ }
1984+ }
1985+ print $highest_pid;
1986+ $predicate['#pid'] = $highest_pid + 1;
1987+ }
1988+ else {
1989+ // Check to see if the predicate has been previously saved to the database.
1990+ $count = db_query("SELECT COUNT(*) FROM {uc_ca_predicates} WHERE pid = :pid", array(':pid' => $predicate['#pid']))->fetchField();
1991+ }
1992+
1993+ $fields = array(
1994+ 'title' => $predicate['#title'],
1995+ 'description' => $predicate['#description'],
1996+ 'class' => $predicate['#class'],
1997+ 'status' => $predicate['#status'],
1998+ 'weight' => $predicate['#weight'],
1999+ 'ca_trigger' => $predicate['#trigger'],
2000+ 'conditions' => isset($predicate['#conditions']) ? serialize($predicate['#conditions']) : serialize(array()),
2001+ 'actions' => isset($predicate['#actions']) ? serialize($predicate['#actions']) : serialize(array()),
2002+ 'modified' => time(),
2003+ );
2004+
2005+ if (!$count) { // Insert a new record.
2006+ $fields['created'] = time();
2007+ $fields['pid'] = $predicate['#pid'];
2008+ db_insert('uc_ca_predicates')
2009+ ->fields($fields)
2010+ ->execute();
2011+ }
2012+ else { // Update a record.
2013+ db_update('uc_ca_predicates')
2014+ ->fields($fields)
2015+ ->condition('pid', $predicate['#pid'])
2016+ ->execute();
2017+ }
2018+}
2019+
2020+/**
2021+ * Delete a predicate from the database.
2022+ *
2023+ * @param $pid
2024+ * The ID of the predicate to delete.
2025+ */
2026+function uc_ca_predicate_delete($pid) {
2027+ db_delete('uc_ca_predicates')
2028+ ->condition('pid', $pid)
2029+ ->execute();
2030+}
2031+
2032+/**
2033+ * Return an array of conditions available for the specified trigger.
2034+ *
2035+ * @param $trigger
2036+ * The name of a trigger to find conditions for; if left empty, the function
2037+ * returns conditions for the previously specified trigger.
2038+ * @return
2039+ * A nested array of names/titles for the conditions available for the
2040+ * trigger; in the format required by select elements in FAPI.
2041+ */
2042+function uc_ca_trigger_conditions_load($trigger = '') {
2043+ static $options = array();
2044+
2045+ if (!empty($trigger)) {
2046+ // Load the specified trigger.
2047+ $trigger = uc_ca_trigger_load($trigger);
2048+ $trigger_entities = array();
2049+
2050+ // Organize trigger arguments by entity.
2051+ foreach ($trigger['#arguments'] as $argument) {
2052+ $trigger_entities[$argument['#entity']] = $argument;
2053+ }
2054+
2055+ // Load and loop through all the conditions defined by modules.
2056+ $conditions = uc_ca_condition_load();
2057+
2058+ foreach ($conditions as $name => $condition) {
2059+ // Check through each argument needed for the condition.
2060+ // Cast to an array to accommodate conditions that need no arguments.
2061+ foreach ((array) $condition['#arguments'] as $argument) {
2062+ $entity = $argument['#entity'];
2063+ // If the condition requires an entity the trigger doesn't provide,
2064+ // then skip to the next condition.
2065+ if ($entity != 'arguments' && !isset($trigger_entities[$entity])) {
2066+ continue 2;
2067+ }
2068+ }
2069+
2070+ // Getting this far means that all of the condition's arguments have
2071+ // the same entity types as the trigger's. Add it to the options,
2072+ // and group them by category for usability.
2073+ $options[$condition['#category']][$name] = $condition['#title'];
2074+ }
2075+
2076+ // Alphabetically sort the groups and their options.
2077+ foreach ($options as $group => $conditions) {
2078+ asort($conditions);
2079+ $options[$group] = $conditions;
2080+ }
2081+ ksort($options);
2082+ }
2083+
2084+ return $options;
2085+}
2086+
2087+/**
2088+ * Return an array of actions available for the specified trigger.
2089+ *
2090+ * @param $trigger
2091+ * The name of a trigger to find actions for; if left empty, the function
2092+ * returns conditions for the previously specified trigger.
2093+ * @return
2094+ * A nested array of names/titles for the actions available for the trigger;
2095+ * in the format required by select elements in FAPI.
2096+ */
2097+function uc_ca_trigger_actions_load($trigger = '') {
2098+ static $options = array();
2099+
2100+ if (!empty($trigger)) {
2101+ // Load the specified trigger.
2102+ $trigger = uc_ca_trigger_load($trigger);
2103+ $trigger_entities = array();
2104+
2105+ // Organize trigger arguments by entity.
2106+ foreach ($trigger['#arguments'] as $argument) {
2107+ $trigger_entities[$argument['#entity']] = $argument;
2108+ }
2109+
2110+ // Load and loop through all the actions defined by modules.
2111+ $actions = uc_ca_action_load();
2112+
2113+ foreach ($actions as $name => $action) {
2114+ // Check through each argument needed for the condition.
2115+ foreach ($action['#arguments'] as $argument) {
2116+ $entity = $argument['#entity'];
2117+ // If the action requires an entity the trigger doesn't provide,
2118+ // then skip to the next action.
2119+ if ($entity != 'arguments' && !$trigger_entities[$entity]) {
2120+ continue 2;
2121+ }
2122+ }
2123+
2124+ // Getting this far means that all of the condition's arguments have
2125+ // the same entity types as the trigger's. Add it to the options,
2126+ // and group them by category for usability.
2127+ $options[$action['#category']][$name] = $action['#title'];
2128+ }
2129+
2130+ // Alphabetically sort the groups and their options.
2131+ foreach ($options as $group => $actions) {
2132+ asort($actions);
2133+ $options[$group] = $actions;
2134+ }
2135+ ksort($options);
2136+ }
2137+
2138+ return $options;
2139+}
2140+
2141+
2142+/**
2143+ * Load triggers defined in modules via hook_uc_ca_trigger().
2144+ *
2145+ * @param $trigger
2146+ * Defaults to 'all'; may instead be the name of the trigger to load.
2147+ * @return
2148+ * An array of data for the specified trigger or an array of all the trigger
2149+ * data if 'all' was specified. Returns FALSE if a specified trigger is
2150+ * non-existent.
2151+ */
2152+function uc_ca_trigger_load($trigger = 'all') {
2153+ static $triggers;
2154+
2155+ // Load the triggers defined in enabled modules to a cached variable.
2156+ if (empty($triggers)) {
2157+ $triggers = module_invoke_all('uc_ca_trigger');
2158+ }
2159+
2160+ // Return the whole array if the trigger specified is 'all'.
2161+ if ($trigger === 'all') {
2162+ return $triggers;
2163+ }
2164+
2165+ // If the specified trigger is non-existent, return FALSE.
2166+ if (!isset($triggers[$trigger])) {
2167+ return FALSE;
2168+ }
2169+
2170+ return $triggers[$trigger];
2171+}
2172+
2173+/**
2174+ * Return a trigger's arguments formatted for select element options.
2175+ *
2176+ * @param $trigger
2177+ * The name of the trigger to load arguments for.
2178+ * @param $entity
2179+ * The name of the entity type we must restrict our returned arguments to.
2180+ * @return
2181+ * An array of arguments in FAPI options format.
2182+ */
2183+function uc_ca_trigger_arguments_load($trigger, $entity) {
2184+ static $arguments = array();
2185+
2186+ // Load the arguments if we can't find a cached version.
2187+ if (!isset($arguments[$trigger][$entity])) {
2188+ $arguments[$trigger][$entity] = array();
2189+
2190+ // Handle the arguments entity specially, as it should never be specified
2191+ // in a trigger.
2192+ if ($entity == 'arguments') {
2193+ $arguments[$trigger][$entity]['arguments'] = t('Arguments');
2194+ }
2195+ else {
2196+ // Load the trigger data.
2197+ $trigger_data = uc_ca_trigger_load($trigger);
2198+
2199+ // Add the trigger's arguments to the options array if the entity matches.
2200+ foreach ((array) $trigger_data['#arguments'] as $key => $value) {
2201+ if ($value['#entity'] == $entity) {
2202+ $arguments[$trigger][$entity][$key] = $value['#title'];
2203+ }
2204+ }
2205+ }
2206+ }
2207+
2208+ return $arguments[$trigger][$entity];
2209+}
2210+
2211+
2212+/**
2213+ * Load conditions defined in modules via hook_uc_ca_condition().
2214+ *
2215+ * @param $condition
2216+ * Defaults to 'all'; may instead be the name of the condition to load.
2217+ * @return
2218+ * An array of data for the specified condition or an array of all the
2219+ * condition data if 'all' was specified. Returns FALSE if a specified
2220+ * condition is non-existent.
2221+ */
2222+function uc_ca_condition_load($condition = 'all') {
2223+ static $conditions;
2224+
2225+ // Load the conditions defined in enabled modules to a cached variable.
2226+ if (empty($conditions)) {
2227+ $conditions = module_invoke_all('uc_ca_condition');
2228+ }
2229+
2230+ // Return the whole array if the trigger specified is 'all'.
2231+ if ($condition === 'all') {
2232+ return $conditions;
2233+ }
2234+
2235+ // If the specified trigger is non-existent, return FALSE.
2236+ if (!isset($conditions[$condition])) {
2237+ return FALSE;
2238+ }
2239+
2240+ return $conditions[$condition];
2241+}
2242+
2243+/**
2244+ * Add a new condition to a predicate.
2245+ *
2246+ * @param $pid
2247+ * The ID of the predicate to add the condition to.
2248+ * @param $name
2249+ * The name of the condition to add.
2250+ * @param $group_key
2251+ * The key of the condition group we're adding the condition to.
2252+ * @param $mark_expanded
2253+ * If set to TRUE marks the condition so that it will be expanded next time it
2254+ * displays in the UI so the user can adjust its settings.
2255+ * @return
2256+ * An array representing the full, updated predicate.
2257+ */
2258+function uc_ca_condition_add($pid, $name, $group_key, $mark_expanded = TRUE) {
2259+ // Load the predicate.
2260+ $predicate = uc_ca_predicate_load($pid);
2261+
2262+ // Load the condition we want to add to the predicate.
2263+ $data = uc_ca_condition_load($name);
2264+
2265+ // If the condition exists...
2266+ if ($data) {
2267+ // Build the condition array.
2268+ $condition = array(
2269+ '#name' => $name,
2270+ '#title' => $data['#title'],
2271+ '#argument_map' => array(),
2272+ '#settings' => array(),
2273+ );
2274+
2275+ // Mark it for expansion in the form if specified.
2276+ if ($mark_expanded) {
2277+ $condition['#expanded'] = TRUE;
2278+ }
2279+
2280+ // Add the condition to the predicate.
2281+ $predicate['#conditions']['#conditions'][$group_key]['#conditions'][] = $condition;
2282+ }
2283+
2284+ uc_ca_predicate_save($predicate);
2285+
2286+ return $predicate;
2287+}
2288+
2289+/**
2290+ * Remove a condition from a predicate.
2291+ *
2292+ * @param $pid
2293+ * The ID of the predicate to remove the condition group from.
2294+ * @param $group_key
2295+ * The key of the condition group the condition is in.
2296+ * @param $cond_key
2297+ * The key of the condition to remove.
2298+ * @return
2299+ * An array representing the full, updated predicate.
2300+ */
2301+function uc_ca_condition_remove($pid, $group_key, $cond_key) {
2302+ // Load the predicate as it is now.
2303+ $predicate = uc_ca_predicate_load($pid);
2304+
2305+ // Update, save, and return the predicate.
2306+ unset($predicate['#conditions']['#conditions'][$group_key]['#conditions'][$cond_key]);
2307+ uc_ca_predicate_save($predicate);
2308+
2309+ return $predicate;
2310+}
2311+
2312+
2313+/**
2314+ * Return a default conditions array for use on new predicates or in the UI.
2315+ */
2316+function uc_ca_new_conditions() {
2317+ return array(
2318+ '#operator' => 'AND',
2319+ '#conditions' => array(
2320+ array(
2321+ '#operator' => 'AND',
2322+ '#conditions' => array(),
2323+ ),
2324+ ),
2325+ );
2326+}
2327+
2328+/**
2329+ * Add a new condition group to a predicate.
2330+ *
2331+ * @param $pid
2332+ * The ID of the predicate to add the condition group to.
2333+ * @return
2334+ * An array representing the full, updated predicate.
2335+ */
2336+function uc_ca_condition_group_add($pid) {
2337+ // Load the predicate.
2338+ $predicate = uc_ca_predicate_load($pid);
2339+
2340+ // Add the condition group to the conditions array in the appropriate place.
2341+ if (empty($predicate['#conditions'])) {
2342+ $predicate['#conditions'] = uc_ca_new_conditions();
2343+ }
2344+ else {
2345+ $predicate['#conditions']['#conditions'][] = array(
2346+ '#operator' => 'AND',
2347+ '#conditions' => array(),
2348+ );
2349+ }
2350+
2351+ // Save the changes.
2352+ uc_ca_predicate_save($predicate);
2353+
2354+ return $predicate;
2355+}
2356+
2357+/**
2358+ * Remove a condition group from a predicate.
2359+ *
2360+ * @param $pid
2361+ * The ID of the predicate to remove the condition group from.
2362+ * @param $group_key
2363+ * The key of the condition group to remove.
2364+ * @return
2365+ * An array representing the full, updated predicate.
2366+ */
2367+function uc_ca_condition_group_remove($pid, $group_key) {
2368+ // Load the predicate as it is now.
2369+ $predicate = uc_ca_predicate_load($pid);
2370+
2371+ // Update, save, and return the predicate.
2372+ unset($predicate['#conditions']['#conditions'][$group_key]);
2373+ uc_ca_predicate_save($predicate);
2374+
2375+ return $predicate;
2376+}
2377+
2378+/**
2379+ * Configure the #parents and #id field of any child filter_forms.
2380+ *
2381+ * Unfortunately, FAPI doesn't give us any way to know we have a filter
2382+ * element, so we require all filters to have an #is_format element.
2383+ */
2384+function uc_ca_filter_configure_form(&$form) {
2385+ // Grab all the filters, and search for the formats.
2386+ $formats = filter_formats();
2387+ _uc_ca_filter_configure_form_recur($form, $formats, array());
2388+}
2389+
2390+/**
2391+ * Recursive filter configuration.
2392+ */
2393+function _uc_ca_filter_configure_form_recur(&$form, &$formats, $parents) {
2394+ // Look for our special element, then search the children.
2395+ if (isset($form['#is_format']) ? $form['#is_format'] : FALSE) {
2396+ foreach (element_children($form) as $key) {
2397+ // Found a filter.
2398+ if (array_key_exists($key, $formats)) {
2399+ // Configure the parents and ID so FAPI is happy.
2400+ $form[$key] = array(
2401+ '#parents' => $parents,
2402+ '#id' => form_clean_id('edit-'. implode('-', array_merge($parents, array($key)))),
2403+ ) + $form[$key];
2404+
2405+ }
2406+ }
2407+ }
2408+ else {
2409+ // Keep looking.
2410+ foreach (element_children($form) as $key) {
2411+ _uc_ca_filter_configure_form_recur($form[$key], $formats, array_merge($parents, array($key)));
2412+ }
2413+ }
2414+}
2415+
2416+/**
2417+ * Load actions defined in modules via hook_uc_ca_action().
2418+ *
2419+ * @param $action
2420+ * Defaults to 'all'; may instead be the name of the action to load.
2421+ * @return
2422+ * An array of data for the specified action or an array of all the action
2423+ * data if 'all' was specified. Returns FALSE if a specified action is
2424+ * non-existent.
2425+ */
2426+function uc_ca_action_load($action = 'all') {
2427+ static $actions;
2428+
2429+ // Load the actions defined in enabled modules to a cached variable.
2430+ if (empty($actions)) {
2431+ $actions = module_invoke_all('uc_ca_action');
2432+ }
2433+
2434+ // Return the whole array if the trigger specified is 'all'.
2435+ if ($action === 'all') {
2436+ return $actions;
2437+ }
2438+
2439+ // If the specified trigger is non-existent, return FALSE.
2440+ if (!isset($actions[$action])) {
2441+ return FALSE;
2442+ }
2443+
2444+ return $actions[$action];
2445+}
2446+
2447+/**
2448+ * Add a new action to a predicate.
2449+ *
2450+ * @param $pid
2451+ * The ID of the predicate to add the action to.
2452+ * @param $name
2453+ * The name of the action to add.
2454+ * @return
2455+ * An array representing the full, updated predicate.
2456+ */
2457+function uc_ca_action_add($pid, $name) {
2458+ // Load the predicate as it is now.
2459+ $predicate = uc_ca_predicate_load($pid);
2460+
2461+ // Load the action.
2462+ $action = uc_ca_action_load($name);
2463+
2464+ // Abort if it did not exist.
2465+ if (empty($action)) {
2466+ return $predicate;
2467+ }
2468+
2469+ // Add the name to the action array so it's saved in the predicate.
2470+ $action['#name'] = $name;
2471+
2472+ // Add the action to the predicate's actions array.
2473+ $predicate['#actions'][] = $action;
2474+
2475+ // Save and return the updated predicate.
2476+ uc_ca_predicate_save($predicate);
2477+
2478+ return $predicate;
2479+}
2480+
2481+
2482+/**
2483+ * Remove an action from a predicate.
2484+ *
2485+ * @param $pid
2486+ * The ID of the predicate to remove the action from.
2487+ * @param $index
2488+ * The index of the action to remove.
2489+ * @return
2490+ * An array representing the full, updated predicate.
2491+ */
2492+function uc_ca_action_remove($pid, $index) {
2493+ $actions = array();
2494+
2495+ // Load the predicate as it is now.
2496+ $predicate = uc_ca_predicate_load($pid);
2497+
2498+ // Build a new actions array, leaving out the one marked for removal.
2499+ foreach ($predicate['#actions'] as $key => $action) {
2500+ if ($key != $index) {
2501+ $actions[] = $action;
2502+ }
2503+ }
2504+
2505+ // Update, save, and return the predicate.
2506+ $predicate['#actions'] = $actions;
2507+ uc_ca_predicate_save($predicate);
2508+
2509+ return $predicate;
2510+}
2511+
2512+/**
2513+ * Evaluate a predicate's conditions.
2514+ *
2515+ * @param $predicate
2516+ * A fully loaded predicate array.
2517+ * @param $arguments
2518+ * The array of parsed arguments for the trigger.
2519+ * @return
2520+ * TRUE or FALSE indicating the success or failure of the evaluation.
2521+ */
2522+function _uc_ca_conditions_evaluate($predicate, $arguments) {
2523+ // Automatically pass if there are no conditions.
2524+ if (count($predicate['#conditions']) == 0) {
2525+ return TRUE;
2526+ }
2527+
2528+ // Load the data for the conditions as defined by modules.
2529+ $condition_data = module_invoke_all('uc_ca_condition');
2530+
2531+ // Recurse through the predicate's conditions for evaluation.
2532+ $result = _uc_ca_conditions_evaluate_tree($predicate['#conditions'], $arguments, $condition_data);
2533+
2534+ if (is_null($result)) {
2535+ $result = FALSE;
2536+ }
2537+
2538+ return $result;
2539+}
2540+
2541+/**
2542+ * Recursively evaluate conditions to accommodate nested logical groups.
2543+ */
2544+function _uc_ca_conditions_evaluate_tree($condition, $arguments, $condition_data) {
2545+ if (isset($condition['#operator']) && is_array($condition['#conditions'])) {
2546+ // Default to TRUE for cases of empty conditions arrays.
2547+ $result = TRUE;
2548+
2549+ foreach ($condition['#conditions'] as $sub_condition) {
2550+ $result = _uc_ca_conditions_evaluate_tree($sub_condition, $arguments, $condition_data);
2551+
2552+ // Invalid conditions return NULL. Skip it and go to the next one.
2553+ if (is_null($result)) {
2554+ continue;
2555+ }
2556+ // Save the processors! Apply Boolean shortcutting if we can.
2557+ if ($condition['#operator'] == 'OR' && $result) {
2558+ return TRUE;
2559+ }
2560+ elseif ($condition['#operator'] == 'AND' && !$result) {
2561+ return FALSE;
2562+ }
2563+ }
2564+ return $result;
2565+ }
2566+ else {
2567+ return _uc_ca_condition_evaluate($condition, $arguments, $condition_data);
2568+ }
2569+}
2570+
2571+/**
2572+ * Evaluate a single condition.
2573+ */
2574+function _uc_ca_condition_evaluate($condition, $arguments, $condition_data) {
2575+ $args = array();
2576+
2577+ // Make sure the condition tree is sane.
2578+ if (!is_array($condition_data[$condition['#name']])) {
2579+ return NULL;
2580+ }
2581+
2582+ // Get the callback function for the current condition.
2583+ $callback = $condition_data[$condition['#name']]['#callback'];
2584+
2585+ // Skip this condition if the function does not exist.
2586+ if (!function_exists($callback)) {
2587+ return NULL;
2588+ }
2589+
2590+ // Loop through the expected arguments for a condition.
2591+ // Cast to an array to accommodate conditions that need no arguments.
2592+ foreach ((array) $condition_data[$condition['#name']]['#arguments'] as $key => $value) {
2593+ // Using the argument map for the condition on this predicate, fetch the
2594+ // argument that was passed to the trigger that matches to the argument
2595+ // needed to evaluate this condition.
2596+ if (isset($condition['#argument_map'][$key])) {
2597+ if ($value['#entity'] == 'arguments') {
2598+ $args[] = $arguments;
2599+ }
2600+ else {
2601+ $args[] = $arguments[$condition['#argument_map'][$key]]['#data'];
2602+ }
2603+ }
2604+ else {
2605+ // Skip this condition of the predicate didn't map the arguments needed.
2606+ return NULL;
2607+ }
2608+ }
2609+
2610+ // Add the condition settings to the argument list.
2611+ $args[] = $condition['#settings'];
2612+
2613+ // Call the condition's function with the appropriate arguments.
2614+ $result = call_user_func_array($callback, $args);
2615+
2616+ // If the negate operator is TRUE, then switch the result!
2617+ if ($condition['#settings']['negate']) {
2618+ $result = !$result;
2619+ }
2620+
2621+ return $result;
2622+}
2623+
2624+/**
2625+ * Perform a predicate's actions in order, preserving changes to the arguments.
2626+ *
2627+ * @param $predicate
2628+ * A fully loaded predicate array.
2629+ * @param $arguments
2630+ * The array of parsed arguments for the trigger.
2631+ * @return
2632+ * An array of results, if any, that were returned by the actions.
2633+ */
2634+function uc_ca_actions_perform($predicate, $arguments) {
2635+ // Exit now if we don't have any actions.
2636+ if (count($predicate['#actions']) == 0) {
2637+ return;
2638+ }
2639+
2640+ $results = array();
2641+
2642+ // Load the data for the actions as defined by modules.
2643+ $action_data = module_invoke_all('uc_ca_action');
2644+
2645+ foreach ($predicate['#actions'] as $i => $action) {
2646+ $args = array();
2647+
2648+ // Get the callback function for the current action.
2649+ $callback = $action_data[$action['#name']]['#callback'];
2650+
2651+ // Do not perform the action if the function does not exist.
2652+ if (!function_exists($callback)) {
2653+ continue;
2654+ }
2655+
2656+ // Loop through the expected arguments for an action.
2657+ foreach ($action_data[$action['#name']]['#arguments'] as $key => $value) {
2658+ // Using the argument map for the action on this predicate, fetch the
2659+ // argument that was passed to the trigger that matches to the argument
2660+ // needed to perform this action.
2661+ if (isset($action['#argument_map'][$key])) {
2662+ // Adding the arguments as references so action functions can update the
2663+ // arguments here when they make changes to the argument data.
2664+ if ($value['#entity'] == 'arguments') {
2665+ $args[] = &$arguments;
2666+ }
2667+ else {
2668+ $args[] = &$arguments[$action['#argument_map'][$key]]['#data'];
2669+ }
2670+ }
2671+ else {
2672+ // Skip this action of the predicate didn't map the arguments needed.
2673+ continue 2;
2674+ }
2675+ }
2676+
2677+ // Add the condition settings to the argument list.
2678+ $args[] = is_array($action['#settings']) ? $action['#settings'] : array();
2679+
2680+ // Call the action's function with the appropriate arguments.
2681+ $results[$i] = call_user_func_array($callback, $args);
2682+ }
2683+
2684+ return $results;
2685+}
2686+
2687+/**
2688+ * Load predicates based on the specified parameters.
2689+ *
2690+ * @param $trigger
2691+ * The name of the trigger for which to search when loading the predicates.
2692+ * @param $all
2693+ * FALSE by default, specifies whether we want to load all possible predicates
2694+ * or only those that are active (status > 0).
2695+ * @return
2696+ * An array of predicates.
2697+ */
2698+function uc_ca_trigger_predicates_load($trigger, $all = FALSE) {
2699+ // Load all the module defined predicates.
2700+ $predicates = module_invoke_all('uc_ca_predicate');
2701+
2702+ // Loop through the module defined predicates to prepare the data - unsets
2703+ // inactive predicates if $all == FALSE and adds a default weight if need be.
2704+ foreach ($predicates as $key => $value) {
2705+ // Unset the predicate if it doesn't use the specified trigger.
2706+ if ($value['#trigger'] != $trigger) {
2707+ unset($predicates[$key]);
2708+ continue;
2709+ }
2710+
2711+ if (!$all && $value['#status'] <= 0) {
2712+ unset($predicates[$key]);
2713+ }
2714+ elseif (!isset($value['#weight'])) {
2715+ $predicates[$key]['#weight'] = 0;
2716+ }
2717+ }
2718+
2719+ // Load and loop through the predicates from the database for this trigger.
2720+ $result = db_query("SELECT * FROM {uc_ca_predicates} WHERE ca_trigger = :trigger", array(':trigger' => $trigger), array('fetch' => PDO::FETCH_ASSOC));
2721+ foreach ($result as $data) {
2722+ // Module defined predicates have string IDs. When a user modifies one of
2723+ // these, we unset the module defined predicate and reconsider adding it in.
2724+ if (!is_numeric($data['pid'])) {
2725+ unset($predicates[$data['pid']]);
2726+ }
2727+
2728+ // Add predicates from the database to our return array if $all == TRUE or
2729+ // if the predicate is active.
2730+ if ($all || $data['status'] > 0) {
2731+ $predicate = uc_ca_db_predicate_prepare($data);
2732+
2733+ // Set the actions' weight if necessary and sort actions by their weight.
2734+ for ($i = 0; $i < count($predicate['#actions']); $i++) {
2735+ if (!isset($predicate['#actions'][$i]['#weight'])) {
2736+ $predicate['#actions'][$i]['#weight'] = 0;
2737+ }
2738+ }
2739+ usort($predicate['#actions'], 'uc_ca_weight_sort');
2740+
2741+ $predicates[$data['pid']] = $predicate;
2742+ }
2743+ }
2744+
2745+ uasort($predicates, 'uc_ca_weight_sort');
2746+
2747+ return $predicates;
2748+}
2749+
2750+/**
2751+ * Parse the argument array into a CA friendly array for the trigger.
2752+ *
2753+ * @param $trigger
2754+ * The name of the trigger for which we are parsing the arguments.
2755+ * @param $args
2756+ * An array of arguments to check against the expected arguments.
2757+ * @return
2758+ * The array of arguments keyed according to the trigger's argument names.
2759+ */
2760+function uc_ca_trigger_args_parse($trigger, $args) {
2761+ // Fail if we didn't receive enough arguments for this trigger.
2762+ if (count($args) < count($trigger['#arguments'])) {
2763+ return FALSE;
2764+ }
2765+
2766+ // Load all the entity information.
2767+ $entities = module_invoke_all('uc_ca_entity');
2768+
2769+ // Loop through the expected arguments.
2770+ foreach ($trigger['#arguments'] as $key => $value) {
2771+ // Grab for comparison the next argument passed to the trigger.
2772+ $arg = array_shift($args);
2773+
2774+ // Check the type and fail if it is incorrect.
2775+ if (gettype($arg) != $entities[$value['#entity']]['#type']) {
2776+ return FALSE;
2777+ }
2778+
2779+ // Add the entity to the arguments array along with its meta data.
2780+ $arguments[$key] = array(
2781+ '#entity' => $value['#entity'],
2782+ '#title' => $value['#title'],
2783+ '#data' => $arg,
2784+ );
2785+ }
2786+
2787+ return $arguments;
2788+}
2789+
2790+/**
2791+ * Pull a trigger and evaluate any predicates associated with that trigger.
2792+ *
2793+ * @param ...
2794+ * Accepts a variable number of arguments. The first should always be the
2795+ * string name of the trigger to pull with any additional arguments being
2796+ * the arguments expected by the trigger and used for evaluation.
2797+ * @return
2798+ * TRUE or FALSE indicating if at least one predicate was evaluated.
2799+ */
2800+function uc_ca_trigger_pull() {
2801+ $args = func_get_args();
2802+ $trigger = array_shift($args);
2803+
2804+ // Load the data for the specified trigger.
2805+ $trigger_data = uc_ca_trigger_load($trigger);
2806+
2807+ // Fail if the specified trigger doesn't exist.
2808+ if (!$trigger_data) {
2809+ return FALSE;
2810+ }
2811+
2812+ // Load any predicates associated with the trigger.
2813+ $predicates = uc_ca_trigger_predicates_load($trigger);
2814+
2815+ // Fail if we didn't find any predicates.
2816+ if (!$predicates || count($predicates) == 0) {
2817+ return FALSE;
2818+ }
2819+
2820+ // Prepare the arguments for evaluation.
2821+ $arguments = uc_ca_trigger_args_parse($trigger_data, $args);
2822+
2823+ // Fail if we didn't receive the right type of or enough arguments.
2824+ if (!$arguments) {
2825+ return FALSE;
2826+ }
2827+
2828+ // Loop through the predicates and evaluate them one by one.
2829+ foreach ($predicates as $pid => $predicate) {
2830+ // If all of a predicate's conditions evaluate to TRUE...
2831+ if (_uc_ca_conditions_evaluate($predicate, $arguments)) {
2832+ // Then perform its actions.
2833+ uc_ca_actions_perform($predicate, $arguments);
2834+ }
2835+ }
2836+
2837+ return TRUE;
2838+}
2839+
2840+
2841+
2842+
2843
2844=== added directory 'modules/uc_ca/uc_ca_test'
2845=== added file 'modules/uc_ca/uc_ca_test/uc_ca_test.info'
2846--- modules/uc_ca/uc_ca_test/uc_ca_test.info 1970-01-01 00:00:00 +0000
2847+++ modules/uc_ca/uc_ca_test/uc_ca_test.info 2009-11-22 17:00:24 +0000
2848@@ -0,0 +1,7 @@
2849+; $Id$
2850+name = Conditional Actions Test module
2851+description = A testing module for CA development
2852+package = Ubercore
2853+core = 7.x
2854+dependencies[] = uc_ca
2855+files[] = uc_ca_test.module
2856\ No newline at end of file
2857
2858=== added file 'modules/uc_ca/uc_ca_test/uc_ca_test.module'
2859--- modules/uc_ca/uc_ca_test/uc_ca_test.module 1970-01-01 00:00:00 +0000
2860+++ modules/uc_ca/uc_ca_test/uc_ca_test.module 2009-11-22 17:00:24 +0000
2861@@ -0,0 +1,61 @@
2862+<?php
2863+// $Id$
2864+
2865+/**
2866+ * @file
2867+ * This module is to help test the functionality of uc_ca while ubercore is in development.
2868+ */
2869+
2870+/**
2871+ * Implement of hook_uc_ca_trigger().
2872+ */
2873+function uc_ca_test_uc_ca_trigger() {
2874+ $triggers['uc_test_trigger'] = array(
2875+ '#title' => t('A test trigger happens'),
2876+ '#category' => t('Test'),
2877+ '#arguments' => array(
2878+ 'account' => array(
2879+ '#entity' => 'user',
2880+ '#title' => t('User'),
2881+ ),
2882+ ),
2883+ );
2884+
2885+ return $triggers;
2886+}
2887+
2888+/**
2889+ * Implement hook_uc_ca_condition().
2890+ */
2891+function uc_ca_test_uc_ca_condition() {
2892+ $conditions['uc_ca_test_condition_test'] = array(
2893+ '#title' => t('Check a test condition'),
2894+ '#category' => t('Test'),
2895+ '#callback' => 'uc_ca_test_check_test_condition',
2896+ '#arguments' => array('account' => array('#entity' => 'user')),
2897+ );
2898+
2899+ return $conditions;
2900+}
2901+
2902+/**
2903+ * Implement hook_uc_ca_action().
2904+ */
2905+function uc_ca_test_uc_ca_action() {
2906+ return array(
2907+ 'uc_ca_test_action_do_test' => array(
2908+ '#title' => t('Do a test action'),
2909+ '#category' => t('Test'),
2910+ '#arguments' => array(),
2911+ ),
2912+ );
2913+}
2914+
2915+function uc_ca_test_check_test_condition($account, $settings) {
2916+ return TRUE;
2917+}
2918+
2919+function uc_ca_test_action_do_test() {
2920+
2921+}
2922+
2923
2924=== modified file 'ubercore.info'
2925--- ubercore.info 2009-11-03 18:50:27 +0000
2926+++ ubercore.info 2009-11-22 17:00:24 +0000
2927@@ -1,8 +1,10 @@
2928 ; $Id$
2929 name = Ubercore
2930 description = Defines the Ubercore entities and systems and integrates them with Drupal core and contributed modules.
2931+dependencies[] = uc_ca
2932 package = Ubercore
2933 files[] = ubercore.module
2934+files[] = includes/ubercore.admin.inc
2935 core = 7.x
2936 php = 5.2
2937-
2938+configure = admin/store
2939
2940=== modified file 'ubercore.module'
2941--- ubercore.module 2009-11-03 18:37:11 +0000
2942+++ ubercore.module 2009-11-22 17:00:24 +0000
2943@@ -6,3 +6,33 @@
2944 * Defines the Ubercore entities and systems and provides integration with
2945 * Drupal core and contributed modules.
2946 */
2947+
2948+/**
2949+ * Implement hook_permission().
2950+ */
2951+function ubercore_permission() {
2952+ $permissions = array(
2953+ 'administer Ubercore' => array(
2954+ 'title' => t('Administer Ubercore store'),
2955+ 'description' => t('Manage store settings, products, orders and more.'),
2956+ ),
2957+ );
2958+ return $permissions;
2959+}
2960+
2961+/**
2962+ * Implement hook_menu().
2963+ */
2964+function ubercore_menu() {
2965+ $items = array();
2966+
2967+ $items['admin/store'] = array(
2968+ 'title' => 'Store administration',
2969+ 'description' => 'Administer store settings, products, orders and more.',
2970+ 'page callback' => 'ubercore_admin',
2971+ 'access arguments' => array('administer Ubercore'),
2972+ 'type' => MENU_NORMAL_ITEM,
2973+ 'file' => 'includes/ubercore.admin.inc',
2974+ );
2975+ return $items;
2976+}
2977\ No newline at end of file

Subscribers

People subscribed via source and target branches

to all changes: