/[drupal]/contributions/modules/connect/connect.module
ViewVC logotype

Contents of /contributions/modules/connect/connect.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.7 - (show annotations) (download) (as text)
Wed Apr 8 14:36:54 2009 UTC (7 months, 3 weeks ago) by stevem
Branch: MAIN
CVS Tags: DRUPAL-5--2-0-BETA1, HEAD
Changes since 1.6: +24 -27 lines
File MIME type: text/x-php
rough in double-opt-in function
1 <?php
2 // $Id: connect.module,v 1.6 2009/04/03 19:09:52 stevem Exp $
3
4 /*
5 * connect.module
6 *
7 * steve@openconcept.ca
8 *
9 * This module uses a couple of GPL-licensed icons from the
10 * Silk icon set 1.3: http://www.famfamfam.com/lab/icons/silk/
11 *
12 */
13
14
15 /**
16 * Implementation of hook_help
17 */
18 function connect_help($section = 'admin/help#connect') {
19 $output = '';
20 switch ($section) {
21 case 'admin/modules#description':
22 $output = t("Allows the creation of online actions, petitions, event registration ... anything where you want to associate two kinds of data in a one-to-many way.");
23 break;
24 case 'admin/help#connect':
25 $output = file_get_contents(drupal_get_path('module', 'connect') .'/connect_help.html');
26 break;
27 }
28 return $output;
29 }
30
31
32 /**
33 * Implementation of hook_perm
34 */
35 function connect_perm() {
36 return array('manage connect', 'create connect campaign', 'access connect');
37 }
38
39
40 /*
41 * Implementation of hook_cron
42 */
43 function connect_cron() {
44 // clear out expired cached data
45 require_once(drupal_get_path('module', 'connect') .'/connect_lookup.php');
46 $cache_names = _connect_get_cache_names();
47 foreach ($cache_names as $name => $title) {
48 $lifetime = variable_get("connect_cache_$name", '0 s');
49 list($num, $unit) = sscanf($lifetime, '%d %s');
50 if ($num == 0) {
51 continue;
52 }
53 switch ($unit) {
54 case 'm' :
55 $seconds = 60;
56 break;
57 case 'h' :
58 $seconds = 60*60;
59 break;
60 case 'd' :
61 $seconds = 60*60*24;
62 break;
63 default :
64 continue;
65 break;
66 }
67 $sql = "DELETE FROM {connect_cache} WHERE type='%s' AND created < %d;";
68 $result = db_query($sql, $name, time() - $num * $seconds);
69 }
70 }
71
72
73 /**
74 * Implementation of hook_menu
75 */
76 function connect_menu($may_cache) {
77 require_once('connect_actions.php');
78
79 $items = array();
80
81 if ($may_cache) {
82 // administer module settings
83 $items[] = array(
84 'path' => 'admin/settings/connect',
85 'title' => t('Connect'),
86 'description' => t("Configure your parent and participant node types."),
87 'callback' => 'connect_admin_overview_page',
88 'access' => user_access('manage connect'),
89 );
90
91 // register callbacks for connect_action functions
92 $empty = array();
93 $all_actions = connect_get_actions();
94 foreach ($all_actions as $action) {
95 $action_menu = call_user_func($action, $empty, $empty, 'menu');
96 if (!empty($action_menu)) {
97 $items = array_merge($items, $action_menu);
98 }
99 }
100 }
101
102 //TODO move the non-cached menu callbacks to the action functions too
103 else {
104 if (arg(0) == 'node' && is_numeric(arg(1))) {
105 $node = node_load(arg(1));
106 if ( !connect_is_parent_node($node) ) {
107 return;
108 }
109
110 $can_admin = (($user->id == $node->uid && user_access('create connect campaign')) || user_access('manage connect'));
111
112 // cache report ?
113 $child = array();
114 $cache_required = FALSE;
115 $actions = connect_get_actions($node->nid);
116 foreach ($actions as $action) {
117 $desc = call_user_func($action, $node, $child, 'describe', 'parent');
118 if (isset($desc['cache']) && $desc['cache'] == TRUE) {
119 $items[] = array(
120 'path' => 'node/'. arg(1) .'/connect/status',
121 'title' => t('Cache report'),
122 'callback' => 'connect_node_list_page',
123 'access' => $can_admin,
124 'type' => MENU_LOCAL_TASK,
125 'weight' => 10,
126 );
127 break;
128 }
129 }
130
131 // re-send emails
132 if (in_array('connect_action_send_email', $actions)) {
133 $items[] = array(
134 'path' => 'node/'. arg(1) .'/connect/resend_failed_mail',
135 'title' => t('Re-send emails'),
136 'callback' => '_connect_action_send_email_resend_failed',
137 'access' => $can_admin,
138 'type' => MENU_LOCAL_TASK,
139 'weight' => 10
140 );
141 }
142
143 // select functions
144 $items[] = array(
145 'path' => 'node/'. arg(1) .'/connect/admin/functions',
146 'title' => t('Functions'),
147 'callback' => 'connect_node_config_functions',
148 'access' => $can_admin,
149 'type' => MENU_LOCAL_TASK,
150 'weight' => 5
151 );
152
153 // configure functions
154 $items[] = array(
155 'path' => 'node/'. arg(1) .'/connect/admin/settings',
156 'title' => t('Settings'),
157 'callback' => 'connect_node_config_settings',
158 'access' => $can_admin,
159 'type' => MENU_LOCAL_TASK,
160 'weight' => 6
161 );
162 }
163
164 //embedded function
165 $items[] = array(
166 'path' => 'connect/embed/'. arg(2),
167 'title' => t('Connect Embed'),
168 'access' => TRUE,
169 'callback' => '_connect_embed',
170 'callback arguments' => array(arg(2)),
171 'type' => MENU_CALLBACK,
172 );
173 }
174 return $items;
175 }
176
177
178 /**
179 * Implementation of hook_block
180 */
181 function connect_block($op = 'list', $delta = 0, $edit = array()) {
182 require_once('connect_blocks.php');
183 switch ($op) {
184 case 'list':
185 return connect_block_list();
186 break;
187
188 case 'view':
189 return connect_block_view($delta);
190 break;
191 }
192 }
193
194
195 /**
196 * Implementation of hook_nodeapi
197 */
198 function connect_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
199 $is_parent = connect_is_parent_node($node);
200 $is_child = $is_parent ? FALSE : connect_is_participant_node($node);
201 if (!$is_parent && !$is_child) {
202 return;
203 }
204 require_once('connect_actions.php');
205
206 // prevent PHP4 from throwing a fit
207 if (PHP_VERSION < 5) error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
208
209 // child nodes
210 if ($is_child) {
211 // prevent looping
212 $lock = "connect_child_lock_$op";
213 if (!isset($_SESSION[$lock])) {
214 $_SESSION[$lock] = TRUE;
215 }
216 else {
217 return $_SESSION[$lock];
218 }
219
220 // call hooks
221 $parent =& _connect_parent_node();
222 if (!$parent) {
223 $temp = node_load(connect_get_parent($node));
224 $parent =& _connect_parent_node($temp);
225 }
226 connect_call_hooks($parent, $node, $op, 'child');
227
228 // release lock
229 unset($_SESSION[$lock]);
230 return;
231 }
232
233 // don't cache me
234 global $conf;
235 $conf['cache'] = FALSE;
236
237 // parent nodes
238 switch ($op) {
239 case 'delete' :
240 // TODO: delete all child nodes too?
241 $sql = "DELETE FROM {connect_data} WHERE pid=%d;";
242 db_query($sql, $node->nid);
243 break;
244
245 case 'view':
246 if ($teaser == TRUE || !user_access('access connect') || !_connect_captcha_test()) {
247 break;
248 }
249 drupal_add_css(drupal_get_path('module', 'connect') .'/connect.css');
250 $parent =& _connect_parent_node($node); // save a reference to this node
251 $child = NULL;
252
253 // allow actions to add display items to the node
254 $weight = 5; // arbitrary
255 $additions = connect_call_hooks($parent, $child, 'display', 'parent');
256 foreach ($additions as $function => $add) {
257 $node->content[$function] = array(
258 '#value' => t($add['data']),
259 '#weight' => $weight++,
260 );
261 }
262
263 // display messages, forms, etc. depending on status
264 $show_form = TRUE;
265 $status = connect_call_hooks($parent, $child, 'status', 'parent');
266 foreach ($status as $add) {
267 if ( isset($add['show_form']) && $add['show_form'] === FALSE ) {
268 $show_form = FALSE;
269 }
270 if ( isset($add['status']) && !empty($add['status']) ) {
271 drupal_set_message( t($add['status']) );
272 }
273 }
274
275 // add form
276 if ($show_form) {
277 $form = drupal_get_form('connect_form_page');
278 $node->content['connect_form_page'] = array(
279 '#value' => $form,
280 '#weight' => 20,
281 );
282 }
283 break;
284 }
285 }
286
287 /*
288 * Implementation of hook_user
289 *
290 */
291 function connect_user($op, &$edit, &$account, $category = NULL) {
292 switch ($op) {
293 //clear session info on login/out
294 case 'logout':
295 case 'login':
296 foreach ($_SESSION as $key => $value) {
297 if (strpos($key, 'connect_') !== FALSE) unset($_SESSION[$key]);
298 }
299 break;
300 }
301 }
302
303
304 /**
305 * Menu callback: cache report
306 */
307 function connect_node_list_page() {
308 $nid = arg(1);
309 $parent = node_load($nid);
310 _connect_parent_node($parent); // cache the parent
311 return drupal_get_form('connect_campaign_cache_report_form');
312 }
313
314
315 /**
316 * Menu callback: displays function selection page
317 */
318 function connect_node_config_functions() {
319 require_once('connect_admin.php');
320 drupal_set_title(t('Select functions'));
321 return drupal_get_form('connect_node_functions_form');
322 }
323
324 /**
325 * Menu callback: displays function configuration page
326 */
327 function connect_node_config_settings() {
328 require_once('connect_admin.php');
329 drupal_set_title(t('Function settings'));
330 return drupal_get_form('connect_node_settings_form');
331 }
332
333 /**
334 * Menu callback; displays the connect administration page.
335 */
336 function connect_admin_overview_page() {
337 require_once('connect_admin.php');
338 drupal_set_title(t('Connect settings'));
339 return drupal_get_form('connect_admin_form');
340 }
341
342
343 /**** FORM: participate ****/
344
345 function connect_forms() {
346 $forms['connect_form_page'] = array(
347 'callback' => 'connect_form',
348 'callback arguments' => array('page'),
349 );
350 $forms['connect_form_block'] = array(
351 'callback' => 'connect_form',
352 'callback arguments' => array('block'),
353 );
354 return $forms;
355 }
356
357 function connect_form_page_validate($form_id, &$form_values) {
358 connect_form_validate($form_id, &$form_values);
359 }
360
361 function connect_form_page_submit($form_id, &$form_values) {
362 connect_form_submit($form_id, &$form_values);
363 }
364
365 function connect_form_block_validate($form_id, &$form_values) {
366 connect_form_validate($form_id, &$form_values);
367 }
368
369 function connect_form_block_submit($form_id, &$form_values) {
370 connect_form_submit($form_id, &$form_values);
371 }
372
373
374 /** added argument for block view **/
375
376 function connect_form($type = 'page') {
377 $parent =& _connect_parent_node();
378 if (!$parent || !isset($parent->nid)) return;
379
380 $participant_type = connect_node_options($parent->nid, 'participant_type');
381 if (!$participant_type) return;
382
383 // generate form
384 $participant->nid = NULL;
385 $participant->type = $participant_type;
386 $form = content_form($participant);
387
388 // remove some fields (hidden, computed)
389 $cck_type = content_types($participant_type);
390 foreach ($cck_type["fields"] as $field) {
391 if ($field['display_settings']["teaser"]["format"] == "hidden" || $field['display_settings']["full"]["format"] == "hidden" || $field["type"] == "computed") {
392 unset($form[ $field['field_name'] ]);
393 }
394 }
395
396 // allow action functions to alter the form
397 $temp = connect_call_hooks($parent, $form, 'form_alter', 'child');
398 if ($temp) $form = $temp;
399
400 // add call to action
401 $form['call_to_action'] = array(
402 '#prefix' => '<div id="connect-form-title">',
403 '#value' => t(connect_node_options($parent->nid, 'call_to_action')),
404 '#suffix' => '</div>',
405 '#weight' => -10,
406 );
407
408 // pass the participant type along so that a custom form can be generated in the template, if desired
409 $form['participant_type'] = array(
410 '#type' => 'hidden',
411 '#value' => $participant_type,
412 );
413
414 $form['submit'] = array(
415 '#type' => 'submit',
416 '#value' => t('Submit'),
417 '#weight' => 15,
418 );
419
420 return $form;
421 }
422
423
424 function connect_form_validate($form_id, &$form_values) {
425 // call connect functions validation
426 $parent =& _connect_parent_node();
427 $results = connect_call_hooks($parent, $form_values, 'validate', 'child');
428 if (!empty($results)) {
429 foreach ($results as $result) {
430 if ($result['value'] == FALSE) form_set_error('', $result['message']);
431 }
432 }
433
434 // call cck fields validation
435 $return = array();
436 $nothing = array();
437 $cck_type = content_types($form_values['participant_type']);
438 $cck_fields = $cck_type['fields'];
439 $widget_types = _content_widget_types();
440 foreach ($form_values as $form_field => $field_value) {
441 $module = $widget_types[$cck_fields[$form_field]['widget']['type']]['module'];
442 $function = $module .'_widget';
443 if (function_exists($function)) {
444 $result = $function('validate', $nothing, $cck_fields[$form_field], $form_values[$form_field]);
445 }
446 }
447 }
448
449 function connect_form_submit($form_id, &$form_values) {
450
451 // build CCK node
452 foreach ($form_values as $key => $value) {
453 // simple form elements
454 if (isset($value[0])) {
455 $node_cck->$key = array($value[0]);
456 }
457 // single-value elements
458 elseif (isset($value['key'])) {
459 $node_cck->$key = array(array('value' => $value['key']));
460 }
461 // multi-value elements
462 elseif (isset($value['keys'])) {
463 // select lists
464 if (is_array($value['keys'])) {
465 foreach ($value['keys'] as $item_key => $item_value) {
466 if ($item_key === $item_value) {
467 $temp_array[] = array('value' => $item_value);
468 }
469 }
470 $node_cck->$key = $temp_array;
471 }
472 // checkboxes
473 else {
474 $node_cck->$key = array(array('value' => $value['keys']));
475 }
476 }
477 }
478
479 $parent =& _connect_parent_node();
480 $node_cck->nid = NULL;
481 $node_cck->type = connect_node_options($parent->nid, 'participant_type');
482 $node_cck->title = 'participant in \''. $parent->title .'\'';
483 node_save($node_cck);
484
485 // allow actions to change redirected page
486 $redirect = connect_call_hooks($parent, $node_cck, 'redirect', 'parent');
487 if (!empty($redirect)) {
488 // just grab first one ... yech, but necessary
489 $keys = array_keys($redirect);
490 $target = $redirect[$keys[0]];
491 }
492 else {
493 $target = 'node/'. $parent->nid;
494 }
495
496 return $target;
497 }
498
499
500 /********************************
501 * UTILITY FUNCTIONS
502 ********************************/
503
504 // is this node a participant node?
505 function connect_is_participant_node(&$node) {
506 $participant_nodes = variable_get('connect_participant_nodes', array());
507 $return = in_array($node->type, $participant_nodes);
508 if ( $return ) {
509 $sql = 'SELECT pid FROM {connect_data} WHERE nid = %d';
510 $parent = db_result(db_query($sql, $node->nid));
511 if ($parent) $node->parent_id[0]['value'] = $parent;
512 }
513 return $return;
514 }
515
516
517 // is this node type a parent node type?
518 function connect_is_parent_node(&$node) {
519 $parent_nodes = variable_get('connect_parent_nodes', array());
520 return (in_array($node->type, $parent_nodes));
521 }
522
523
524 // test for valid canadian postal code
525 function connect_is_postalcode($code) {
526 $regexp = '/^[a-zA-Z][0-9][a-zA-Z]\s*[0-9][a-zA-Z][0-9]$/';
527 return preg_match($regexp, $code);
528 }
529
530
531 // get db info for CCK field
532 function _connect_get_cck_db_info($type) {
533 $field = content_fields($type);
534 $db_info = content_database_info($field);
535 return $db_info;
536 }
537
538
539 // gets form value key(s) for cck field
540 function connect_get_field_keys($field) {
541 if ($field == 'title' || $field == 'body' ) {
542 return array('value');
543 }
544 $test_db = _connect_get_cck_db_info($field);
545 if (isset($test_db['columns'])) {
546 return array_keys($test_db['columns']);
547 }
548 else {
549 return array();
550 }
551 }
552
553
554 // counts the participants for a parent
555 function connect_participant_count(&$parent) {
556 $sql = "SELECT count(*) FROM {connect_data} WHERE pid = %d";
557 $count = db_result(db_query($sql, $parent->nid));
558 return $count;
559 }
560
561
562 // what type of participant does this parent use?
563 /*
564 function connect_get_participant_type($parent_nid) {
565 $type = connect_node_options( $parent_nid, 'participant_type' );
566 return $type;
567 }
568 */
569
570
571 // return an array of participant node types suitable for use in forms
572 function connect_participant_types_options() {
573 $array = array();
574 $values = variable_get('connect_participant_nodes', array() );
575 foreach ($values as $value) {
576 $array[$value] = $value;
577 }
578 return $array;
579 }
580
581
582 /**
583 * Returns an array of connect_action_* functions
584 * if an nid is passed in, returns the declared functions for that parent
585 * otherwise, it returns all the available actions
586 */
587 function connect_get_actions( $parent_id = FALSE ) {
588 require_once('connect_actions.php');
589 static $functions = array();
590 $key = $parent_id ? (int) $parent_id : 'all';
591 if (!isset($functions[$key])) {
592 if ($key == 'all') {
593 $allfns = get_defined_functions();
594 $array = $allfns['user'];
595 foreach ($array as $fid => $fname) {
596 if (strpos($fname, 'connect_action_') !== 0) {
597 unset($array[$fid]);
598 }
599 }
600 asort($array);
601 }
602 else {
603 $array = connect_node_options($key, 'connect_actions');
604 if (empty($array)) {
605 $array = array('connect_action_basic');
606 }
607 }
608 $functions[$key] = $array;
609 }
610 return $functions[$key];
611 }
612
613
614 /**
615 * returns an array of arrays mapping action variables onto CCK fields
616 * array( 'parent' => array( action => cck, ... ), 'child' => array( action => cck, ... ) )
617 */
618 function connect_get_map( $parent_id = 0 ) {
619 static $maps = array();
620 if ( !isset($maps[$parent_id]) ) {
621 $maps[$parent_id] = connect_node_options( $parent_id, 'connect_map' );
622 }
623 return $maps[$parent_id];
624 }
625
626
627 /**
628 * determines all the required variables for a given campaign node
629 * @ return
630 * array( 'parent' => array(), 'child' => array() )
631 */
632 function connect_get_required_vars(&$node, &$child) {
633 if (empty($node)) return;
634
635 $parent = array();
636 $variables = array();
637 $vars_done = array();
638
639 $function_vars = connect_call_hooks($node, $child, 'requires', 'parent');
640 foreach ($function_vars as $fn_name => $fn_requires) {
641 foreach (array('parent', 'child') as $target ) {
642 if (isset($fn_requires[$target])) {
643 $$target = array_merge( $$target, $fn_requires[$target] );
644 }
645 }
646 if (isset($fn_requires['variables'])) {
647 foreach ($fn_requires['variables'] as $name => $item) {
648 if (!isset($vars_done[$name])) {
649 $vars_done[$name] = TRUE;
650 $variables[$fn_name][$name] = $item;
651 }
652 }
653 }
654 }
655 return array(
656 'parent' => $parent,
657 'child' => $child,
658 'variables' => $variables,
659 );
660 }
661
662
663 /**
664 * determine the content fields in a node type and
665 * return as an array for use in admin forms
666 */
667 function connect_get_node_fields( $typename ) {
668 $options = array( 0 => '', 'title' => 'Title' );
669 // body?
670 $type = node_get_types('type', $typename);
671 if ($type->has_body) $options['body'] = 'Body';
672 $cck_type = content_types($typename);
673 $fields = $cck_type["fields"];
674 if (!empty($fields)) {
675 foreach ($fields as $field) {
676 $options[$field['field_name']] = $field['widget']['label'];
677 }
678 }
679 //asort($options);
680 return $options;
681 }
682
683
684 /**
685 * returns or sets the required value from $target = child | parent
686 if $value is set, this is a setter, otherwise, it's a getter
687 */
688 function connect_value($variable = '', &$parent, &$child, $target = '', $value=FALSE) {
689 $return = '';
690 if (!empty($variable) && isset($parent->nid) && ($target == 'child' || $target == 'parent')) {
691 $map = connect_get_map($parent->nid);
692 $var = $map[$variable];
693 $path = _connect_get_field_path($$target, $var);
694 if (!$path) return FALSE;
695
696 if (!$value) {
697 $exp = '$return = stripslashes($'. $target . $path .');';
698 eval($exp);
699 }
700 else {
701 $exp = '$'. $target . $path .' = "'. addslashes(trim($value)) .'";';
702 eval($exp);
703 }
704 }
705 return $return;
706 }
707
708
709 /**
710 * Returns the correct object or array path to retrieve a node field value
711 */
712 function _connect_get_field_path( $target = FALSE, $var = FALSE) {
713 if (!$target || !$var) return FALSE;
714
715 $path = '';
716 // standard fields
717 if ($var == 'body' || $var == 'title') {
718 if (is_object($target)) {
719 $path = "->$var";
720 }
721 else {
722 $path = "['$var'][0]['value']";
723 }
724
725 // cck fields
726 }
727 elseif (!empty($var)) {
728 $key = connect_get_field_keys($var);
729 if (is_object($target)) {
730 $var = "->$var";
731 }
732 else {
733 $var = "['$var']";
734 }
735 $path = $var .'[0][\''. $key[0] .'\']';
736 }
737 return $path;
738 }
739
740
741 /**
742 * Returns the admin options for a parent node
743 * @param $nid: the node id of the parent
744 * @param $field: the name of the option to retrieve | NULL to retrieve all
745 * @param $value: if provided, sets $field to $value
746 *
747 */
748 function connect_node_options($nid = 0, $field = NULL, $value = NULL) {
749 if (empty($nid)) return FALSE;
750
751 // cache this info
752 static $options = array();
753 if (!isset($options[$nid])) {
754 $temp = variable_get('connect_'. $nid .'_options', FALSE);
755 $options[$nid] = $temp ? unserialize($temp) : array();
756 }
757
758 // setter
759 if ($value !== NULL && $field !== NULL) {
760 $options[$nid][$field] = $value;
761 variable_set( 'connect_'. $nid .'_options', serialize($options[$nid]) );
762 }
763
764 // getter
765 if ($field !== NULL) {
766 $return = isset($options[$nid][$field]) ? $options[$nid][$field]: '';
767 }
768 else {
769 $return = $options[$nid];
770 }
771
772 return $return;
773 }
774
775
776 /**
777 * Iterates through connect_action_* functions and calls them for the current operation and target
778 */
779 function connect_call_hooks(&$parent, &$child, $op = '', $target = 'child') {
780 $debug_hooks = FALSE; // use this to turn on a display of what hooks are called in what order
781 $return = array();
782
783 // parent always required, child only if acting on child, operation always required
784 if (!$op || !$parent || ($target=='child' && !$child)) {
785 return $return;
786 }
787
788 $parent = empty($parent) ? array() : $parent;
789 $child = empty($child) ? array() : $child;
790 $debug_parent = empty($parent) ? 'empty' : gettype($parent);
791 $debug_child = empty($child) ? 'empty' : gettype($child);
792
793 if ( $op != 'describe' ) {
794 $actions = connect_get_actions($parent->nid);
795 }
796 else {
797 $actions = connect_get_actions();
798 }
799
800 if ($debug_hooks) drupal_set_message("$op ($target | $debug_parent | $debug_child)");
801
802 foreach ($actions as $action) {
803 if ($debug_hooks) drupal_set_message("$action 1");
804
805 // make sure we have the required vars before calling the function
806 // skip check for purely informational $op values
807 $skip_check = array('describe','requires', 'admin-validate');
808 if (!in_array($op, $skip_check)) {
809 if (_connect_hook_check_requirements($parent, $child, $action, $target) !== TRUE){
810 continue;
811 }
812 }
813
814 if ($debug_hooks) drupal_set_message("$action 2");
815
816 // call the hook
817 $action_return = call_user_func($action, &$parent, &$child, $op, $target);
818 if ($action_return) {
819 $return[$action] = $action_return;
820 }
821 }
822 return $return;
823 }
824
825
826 /*
827 * tests to see that the required settings are present for a given action
828 */
829 function _connect_hook_check_requirements(&$parent, &$child, $action, $target) {
830 static $requirements_ok = array();
831 if (!isset($requirements_ok[$action])) {
832 $required = call_user_func($action, $parent, $child, 'requires', $target);
833 if (empty($required)) return TRUE; // no settings = no requirements, but let's not cache that
834
835 // default to true, test for exceptions
836 $requirements_ok[$action] = '';
837
838 // parent + child fields, all required
839 $map = connect_get_map($parent->nid);
840 $map_keys = array_keys($map);
841 foreach (array('parent', 'child') as $type) {
842 if (isset($required[$type])) {
843 foreach ($required[$type] as $name => $desc) {
844 if (!in_array($name, $map_keys) || empty($map[$name])) {
845 $requirements_ok[$action] .= "'$desc' (required field in $type node)<br />\n";
846 }
847 }
848 }
849 }
850
851 // settings, might be optional
852 if (isset($required['variables'])) {
853 foreach ($required['variables'] as $name => $desc) {
854 $value = connect_node_options($parent->nid, $name);
855 if ($desc['#required'] == TRUE && empty($value)) {
856 $requirements_ok[$action] .= $desc['#title'] . "<br />\n";
857 }
858 }
859 }
860
861 // return TRUE or a message
862 if (empty($requirements_ok[$action])) $requirements_ok[$action] = TRUE;
863 }
864 return $requirements_ok[$action];
865 }
866
867 /*
868 * determine parent nid for child node
869 */
870 function connect_get_parent(&$child) {
871 if (empty($child->nid)) return 0;
872
873 static $parents = array();
874 if (!isset( $parents[$child->nid])) {
875 $sql = "SELECT pid FROM {connect_data} WHERE nid = %d;";
876 $parents[$child->nid]= db_result(db_query($sql, $child->nid));
877 }
878 $child->connect_parent_id = $parents[$child->nid];
879 return $parents[$child->nid];
880 }
881
882
883 /*
884 * determines if connect_form captcha is required, and if so, if it's enabled
885 */
886 function _connect_captcha_test($form_id = 'connect_form_page') {
887 $captcha_required = variable_get('connect_captcha_required', 'yes');
888 if ($captcha_required == 'yes') {
889 $captcha = db_result(db_query("SELECT type FROM {captcha_points} WHERE form_id = '%s'", $form_id));
890 if (!$captcha) {
891 if (!user_access('skip CAPTCHA')) {
892 watchdog('debug', 'connect form cannot be displayed: no CAPTCHA defined');
893 return FALSE;
894 }
895 else {
896 drupal_set_message('Warning: CAPTCHA has not been configured for connect forms yet.');
897 }
898 }
899 }
900 return TRUE;
901 }
902
903
904 /*
905 * use this wherever it's necessary to load the parent
906 * enforces a singleton pattern to solve reference problems in PHP4
907 *
908 * made it an array to fix problems with displaying the form in a block
909 * - it is called twice when a block is visible and the current node is also a campaign
910 */
911 function &_connect_parent_node($node = NULL) {
912 static $parent_node = array( 0 => NULL );
913 if ($node == NULL && arg(0) == 'node') {
914 $nid = arg(1);
915 }
916 else {
917 $nid = isset($node->nid) ? $node->nid : 0;
918 }
919 if (!isset($parent_node[$nid])) {
920 $parent_node[$nid] = $node;
921 }
922 return $parent_node[$nid];
923 }
924
925
926 /*
927 * returns cck fields in array for use in forms
928 */
929 function _connect_get_child_fields($child_type = NULL) {
930 $cck_options = array();
931 if ($child_type) {
932 $cck_info = _content_type_info();
933 $cck_vars = $cck_info['content types'][$child_type]['fields'];
934 foreach ($cck_vars as $name => $field) {
935 if ($field['display_settings']['teaser']['format'] != 'hidden' && $field['display_settings']['full']['format'] != 'hidden') {
936 $cck_options[$name] = $field['widget']['label'];
937 }
938 }
939 $cck_options[''] = '';
940 asort($cck_options);
941 }
942 return $cck_options;
943 }
944
945
946 /*
947 * accept a range of values that mean 'yes'/true
948 */
949 function _connect_positive_value($in) {
950 $positive_strings = array('y', 'yes', 'true', 'ok', '1');
951 if (in_array(strtolower($in), $positive_strings) || $in === TRUE) {
952 return TRUE;
953 }
954 else {
955 return FALSE;
956 }
957 //if (empty($in) || strtolower($in) == 'no' || strtolower($in) == 'n') {
958 // return FALSE;
959 //}
960 //else {
961 // return TRUE;
962 //}
963 }
964
965
966 /*
967 * menu callback: display the participants
968 */
969 function _connect_action_display_participants($parent_nid = NULL) {
970 $output = FALSE;
971 if (is_numeric($parent_nid)) {
972 $parent = node_load($parent_nid);
973 drupal_set_title('"'. $parent->title .'" '. connect_node_options($parent->nid, 'participant_title'));
974
975 // determine display field names
976 $header = array();
977 $display_fields = variable_get('connect_'. $parent->nid .'_options', FALSE);
978 if (!$display_fields) return;
979
980 $display_fields = unserialize($display_fields);
981 foreach ($display_fields as $key => $value) {
982 if (empty($value) || strpos($key, 'display_participants_fields_') === FALSE) {
983 unset($display_fields[$key]);
984 }
985 }
986
987 // set header
988 $child_type = connect_node_options($parent->nid, 'participant_type');
989 $cck_options = _connect_get_child_fields($child_type);
990 foreach ($display_fields as $key => $value) {
991 $header[] = $cck_options[$value];
992 }
993
994 // double opt-in?
995 $double_opt_in = (in_array('connect_action_double_optin', connect_get_actions($parent->nid)));
996
997 // walk through participants
998 $pager = connect_node_options($parent->nid, 'display_participants_pager');
999 if (!$pager) $pager = 25;
1000 $data = array();
1001 $sql = "SELECT nid FROM {connect_data} WHERE pid = %d";
1002 $result = pager_query($sql, $pager, 0, NULL, $parent->nid);
1003 while ($row = db_fetch_object($result)) {
1004 $this_item = array();
1005 $child = node_load($row->nid);
1006 $display = connect_value('display_participants_displayme', $parent, $child, 'child');
1007
1008 // double opt-in?
1009 if ($double_opt_in) {
1010 $opt_in = connect_value('double_optin_token', $parent, $child, 'child');
1011 if (!_connect_positive_value($opt_in)) continue;
1012 }
1013
1014 if (_connect_positive_value($display)) {
1015 foreach ($display_fields as $key => $value) {
1016 $path = _connect_get_field_path($child, $value);
1017 $exp = '$this_item[] = $child'. $path .';';
1018 eval($exp);
1019 }
1020 $data[] = $this_item;
1021 }
1022 }
1023 if (empty($data)) return theme_page('<p>no participants</p>');
1024 }
1025
1026 $link = theme('connect_action_display_participants_return', $parent->title, $parent->nid);
1027 $output = theme_table($header, $data) . $link;
1028 $output .= theme('pager', NULL, $pager);
1029 return $output;
1030 }
1031
1032 /* themeable function */
1033 function theme_connect_action_display_participants_return($parent_title, $parent_nid) {
1034 $link = l(t('Return to ') . $parent_title, 'node/'. $parent_nid);
1035 return "<div id='connect-returnto-link'>&raquo; ". $link .'</div>';
1036 }
1037
1038
1039
1040 /** report for admins **/
1041
1042 function connect_campaign_cache_report_form() {
1043 $parent =& _connect_parent_node();
1044 $form = array();
1045
1046 // intro
1047 $form['report_intro'] = array(
1048 '#value' => 'This form allows you to create reports based on when participant data was cached by connect. If you keep persistent caches, this offers a rough measurement of the number of participants who are new to your system. Note that this assumes you are using a connect feature that uses caching (such as email target lookups) and that caching has been turned on.',
1049 );
1050
1051 // date
1052 $form['report_date'] = array(
1053 '#type' => 'date',
1054 '#title' => 'Date',
1055 );
1056
1057 // operator
1058 $form['report_operator'] = array(
1059 '#type' => 'radios',
1060 '#title' => 'Date range',
1061 '#options' => array('Before this date', 'On or after this date'),
1062 );
1063
1064 // type of report
1065 $form['report_type'] = array(
1066 '#type' => 'radios',
1067 '#title' => 'Type of report',
1068 '#options' => array('Numeric report', 'CSV'),
1069 );
1070
1071 // cache to test
1072 require_once(drupal_get_path('module', 'connect') .'/connect_lookup.php');
1073 $cache_names = _connect_get_cache_names();
1074 $form['report_cache'] = array(
1075 '#type' => 'radios',
1076 '#title' => 'Cache to query',
1077 '#description' => 'Connect maintains a number of caches, each of which can be set to expire independently. Please select the cache that will be the basis of your query.',
1078 '#options' => $cache_names,
1079 );
1080
1081 // relevant participant field
1082 $child_type = connect_node_options($parent->nid, 'participant_type');
1083 $cck_options = _connect_get_child_fields($child_type);
1084 unset($cck_options['']);
1085 $form['report_field'] = array(
1086 '#type' => 'radios',
1087 '#title' => 'Cached participant field',
1088 '#description' => 'Please select the participant item that is cached as the "source" of the lookup (i.e., if the chosen cache stores the results of "Postal code to riding" lookups, select the participant field holding the postal code).',
1089 '#options' => $cck_options,
1090 );
1091
1092 $form['submit'] = array(
1093 '#type' => 'submit',
1094 '#value' => t('Submit'),
1095 );
1096
1097 return $form;
1098 }
1099
1100
1101 function connect_campaign_cache_report_form_submit($form_id, $form_values) {
1102 $output = '';
1103 $parent =& _connect_parent_node();
1104
1105 // date
1106 $date = sprintf('%04d%02d%02d 00:00:00', $form_values['report_date']['year'], $form_values['report_date']['month'], $form_values['report_date']['day']);
1107 $date = strtotime($date);
1108
1109 // participant field
1110 $cck_db = _connect_get_cck_db_info($form_values['report_field']);
1111 $cck_table = $cck_db['table'];
1112 $cck_column = $cck_db['columns']['value']['column'];
1113
1114 // date operator
1115 $modal = ($form_values['report_operator'] == 0) ? '<': '>=';
1116
1117 // CSV reports: set header
1118 if ($form_values['report_type'] > 0) {
1119 $child_type = connect_node_options($parent->nid, 'participant_type');
1120 $child_fields = _connect_get_child_fields($child_type);
1121 unset($child_fields['']);
1122 $output = join(',', array_values($child_fields)) ."\n";
1123 }
1124
1125 // loop through all children
1126 $path = FALSE;
1127 $sql = "SELECT d.nid from {connect_data} d WHERE d.pid=%d;";
1128 $result = db_query($sql, $parent->nid);
1129 while ($row = db_fetch_object($result)) {
1130 $child = node_load($row->nid);
1131 if (!$path) {
1132 $path = _connect_get_field_path($child, $form_values['report_field']);
1133 }
1134 eval('$value = strtoupper($child'. $path .');');
1135 $value = preg_replace('/[^0-9A-Z]/', '', $value);
1136
1137 // match value, date parameters
1138 $sql = "SELECT count(*) as total FROM {connect_cache} WHERE type='%s' AND source='%s' AND created $modal %d;";
1139 $count = db_query($sql, $form_values['report_cache'], $value, $date);
1140 $number = db_result($count);
1141 if ($number > 0) {
1142 $cached_total++;
1143 if ($form_values['report_type'] > 0) {
1144 foreach ($child_fields as $field => $title) {
1145 $path = _connect_get_field_path($child, $field);
1146 eval('$item = $child'. $path .';');
1147 $line[] = '"'. str_replace('"', '""', $item) .'"';
1148 }
1149 if (!empty($line)) {
1150 $output .= join(',', $line) ."\n";
1151 $line = '';
1152 }
1153 }
1154 }
1155 }
1156
1157 // display numeric report
1158 if ($form_values['report_type'] == 0) {
1159 $total = connect_participant_count($parent);
1160 drupal_set_message("total participants: $total");
1161 drupal_set_message("cached since ". date('d M Y', $date) .": $cached_total");
1162 return;