petitionemail.php 40.4 KB
Newer Older
1 2 3 4
<?php

require_once 'petitionemail.civix.php';

5 6 7 8 9
// You can define multiple pairs of target groups to
// matching field. This constant defines how many are 
// presented in the user interface.
define('PETITIONEMAIL_ALLOWED_GROUP_FIELD_COMBINATIONS_COUNT', 3);

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/**
 * Implementation of hook_civicrm_config
 */
function petitionemail_civicrm_config(&$config) {
  _petitionemail_civix_civicrm_config($config);
}

/**
 * Implementation of hook_civicrm_xmlMenu
 *
 * @param $files array(string)
 */
function petitionemail_civicrm_xmlMenu(&$files) {
  _petitionemail_civix_civicrm_xmlMenu($files);
}

/**
 * Implementation of hook_civicrm_install
 */
function petitionemail_civicrm_install() {
  return _petitionemail_civix_civicrm_install();
}

/**
 * Implementation of hook_civicrm_uninstall
 */
function petitionemail_civicrm_uninstall() {
37
  // Clear out our variables.
38
  petitionemail_remove_profiles();
39
  petitionemail_remove_variables();
40 41 42 43 44 45 46
  return _petitionemail_civix_civicrm_uninstall();
}

/**
 * Implementation of hook_civicrm_enable
 */
function petitionemail_civicrm_enable() {
47
  // Ensure the profile id is created.
48
  petitionemail_get_matching_fields_profile_id();
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
  return _petitionemail_civix_civicrm_enable();
}

/**
 * Implementation of hook_civicrm_disable
 */
function petitionemail_civicrm_disable() {
  return _petitionemail_civix_civicrm_disable();
}

/**
 * Implementation of hook_civicrm_upgrade
 *
 * @param $op string, the type of operation being performed; 'check' or 'enqueue'
 * @param $queue CRM_Queue_Queue, (for 'enqueue') the modifiable list of pending up upgrade tasks
 *
 * @return mixed  based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending)
 *                for 'enqueue', returns void
 */
function petitionemail_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
  return _petitionemail_civix_civicrm_upgrade($op, $queue);
}

/**
 * Implementation of hook_civicrm_managed
 *
 * Generate a list of entities to create/deactivate/delete when this module
 * is installed, disabled, uninstalled.
 */
function petitionemail_civicrm_managed(&$entities) {
  return _petitionemail_civix_civicrm_managed($entities);
}
81

Jamie McClelland's avatar
Jamie McClelland committed
82 83 84
/**
 * Implemention of hook_civicrm_buildForm
 */
85 86 87 88
function petitionemail_civicrm_buildForm( $formName, &$form ) {
  if ($formName == 'CRM_Campaign_Form_Petition_Signature') {  
    $survey_id = $form->getVar('_surveyId');
    if ($survey_id) {
Jamie McClelland's avatar
Jamie McClelland committed
89 90 91 92 93 94 95 96
      $sql = "SELECT petition_id, 
                  default_message, 
                  message_field, 
                  subject 
             FROM civicrm_petition_email 
             WHERE petition_id = %1";
      $params = array( 1 => array( $survey_id, 'Integer' ) );
      $dao = CRM_Core_DAO::executeQuery( $sql, $params );
97 98
      $defaults = array();
      $dao->fetch();
99 100 101 102
      if($dao->N == 0) {
        // Not a email enabled petition
        return;
      }
103
      $message_field = $dao->message_field;
104 105
      $defaults[$message_field] = $dao->default_message;
      $form->setDefaults($defaults);
106 107 108
    }
  }

109
  if ($formName == 'CRM_Campaign_Form_Petition') {
110
    CRM_Core_Resources::singleton()->addScriptFile('cc.tadpole.petitionemail', 'petitionemail.js');
111 112
    $survey_id = $form->getVar('_surveyId');
    if ($survey_id) {
113
      // Set default values for saved petitions.
114 115 116 117 118
      $sql = "SELECT petition_id, 
                default_message, 
                message_field, 
                subject,
                recipients,
119
                location_type_id
120 121 122 123
              FROM civicrm_petition_email 
              WHERE petition_id = %1";
      $params = array( 1 => array( $survey_id, 'Integer' ) );
      $dao = CRM_Core_DAO::executeQuery( $sql, $params );
124
      $dao->fetch();
125 126 127 128 129
      if($dao->N > 0) {
        // Base table values.
        $defaults['email_petition'] = 1;
        $defaults['recipients'] = $dao->recipients;
        $defaults['default_message'] = $dao->default_message;
130
        $defaults['message_field'] = $dao->message_field;
131 132 133 134
        $defaults['subject'] = $dao->subject;
        $defaults['location_type_id'] = $dao->location_type_id;
        
        // Now get matching fields.
135 136
        $sql = "SELECT matching_field, matching_group_id FROM
          civicrm_petition_email_matching_field WHERE petition_id = %1";
137 138
        $dao = CRM_Core_DAO::executeQuery($sql, $params);
        $matching_fields = array();
139
        $i = 1;
140
        while($dao->fetch()) {
141 142 143
          $defaults['matching_field' . $i] = $dao->matching_field;
          $defaults['matching_group_id' . $i] = $dao->matching_group_id;
          $i++;
144 145
        }
        $form->setDefaults($defaults);
146
      }
147
    }
Jamie McClelland's avatar
Jamie McClelland committed
148

149
    // Now add our extra fields to the form.
150
    $form->add('checkbox', 'email_petition', ts('Send an email to a target'));
151

152 153 154
    // Get the Profiles in use by this petition so we can find out
    // if there are any potential fields for an extra message to the
    // petition target.
Jamie McClelland's avatar
Jamie McClelland committed
155
    $params = array('module' => 'CiviCampaign', 
156
                    'entity_table' => 'civicrm_survey', 
Jamie McClelland's avatar
Jamie McClelland committed
157
                    'entity_id' => $survey_id,
158
                    'rowCount' => 0);
Jamie McClelland's avatar
Jamie McClelland committed
159
    $join_results = civicrm_api3('UFJoin','get', $params);
160
    $custom_fields = array();
161
    $profile_ids = array();
162 163
    if ($join_results['is_error'] == 0) {
      foreach ($join_results['values'] as $join_value) {
164
        $profile_ids[] = $join_value['uf_group_id'];
165 166
      }
    }
Jamie McClelland's avatar
Jamie McClelland committed
167
    $custom_fields = petitionemail_get_textarea_fields($profile_ids);
168
    
169 170
    $custom_message_field_options = array();
    if(count($custom_fields) == 0) {
Jamie McClelland's avatar
Jamie McClelland committed
171 172 173
      $custom_message_field_options = array(
        '' => t('- No Text or TextArea fields defined in your profiles -')
      );
174 175 176 177 178 179 180
    }
    else {
      $custom_message_field_options = array('' => t('- Select -'));
      $custom_message_field_options = $custom_message_field_options + $custom_fields;
    }
    $choose_one = array('0' => ts('--choose one--'));
    $group_options = $choose_one + CRM_Core_PseudoConstant::group('Mailing');
Jamie McClelland's avatar
Jamie McClelland committed
181 182
    $location_options = $choose_one + 
      CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
183

184
    $field_options = petitionemail_get_matching_field_options();
185
    $field_options_count = count($field_options);
186 187 188 189 190 191 192
    if($field_options_count == 0) {
      // No matching fields!
      $field_options[''] = ts("No fields are configured");
    }
    else {
      array_unshift($field_options, ts("--Choose one--"));
    }
193 194
    $form->assign('petitionemail_matching_fields_count', $field_options_count);
    $url_params = array(
195
      'gid' => petitionemail_get_matching_fields_profile_id(),
196 197 198 199
      'action' => 'browse'
    );
    $url = CRM_Utils_System::url("civicrm/admin/uf/group/field", $url_params);
    $form->assign('petitionemail_profile_edit_link', $url);
200 201 202 203 204 205 206

    $i = 1;
    while($i <= PETITIONEMAIL_ALLOWED_GROUP_FIELD_COMBINATIONS_COUNT) {
      $form->add('select', 'matching_group_id' . $i, ts('Matching Target Group'), $group_options);
      $form->add('select', 'matching_field' . $i, ts('Matching field(s)'), $field_options); 
      $i++;
    }
207 208
    $form->add('select', 'location_type_id', ts('Email'), $location_options);
    $form->add('textarea', 'recipients', ts("Send petitions to"));
209
    $form->add('select', 'message_field', ts('Custom Message Field'),
Jamie McClelland's avatar
Jamie McClelland committed
210
      $custom_message_field_options);
211 212
    $form->add('textarea', 'default_message', ts('Default Message'), 'rows=20');
    $form->add('text', 'subject', ts('Email Subject Line'), array('size' => 70));
213 214 215
  }
}

216 217 218 219 220
/**
 * Get fields from the special petition email profile.
 *
 * Filter out un-supported fields.
 */
221
function petitionemail_get_matching_field_options() {
222 223
  $session = CRM_Core_Session::singleton();
  $ret = array();
224
  $uf_group_id = petitionemail_get_matching_fields_profile_id();
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
  $fields = CRM_Core_BAO_UFGroup::getFields($uf_group_id); 
  $allowed = petitionemail_get_allowed_matching_fields();
  if(is_array($fields)) {
    reset($fields);
    while(list($id, $value) = each($fields)) {
      $include = FALSE;
      // Check to see if it's a custom field
      if(preg_match('/^custom_/', $id)) {
        $ret[$id] = $value['title'];
        continue;
      }
      else {
        // Check to see if it's an address field
        $field_pieces = petitionemail_split_address_field($id);
        if($field_pieces) {
          if($field_pieces['location_name'] != 'Primary') {
            $session->setStatus(ts("Only primary address fields are support at this time."));
            continue;
          }
          if(array_key_exists($field_pieces['field_name'], $allowed)) {
            $ret[$id] = $value['title'];
            continue;
          }
        }
      }
      // Warn the user about a field that is not allowed
      $session->setStatus(ts("The field $id is not supported as a matching field at this time."));
    }
  }
254
  
255 256 257
  return $ret;
}

258 259 260 261 262 263
/**
 * Validate the petition form
 *
 * Ensure our values are consistent to avoid broken petitions.
 */
function petitionemail_civicrm_validateForm($formName, &$fields, &$files, &$form, &$errors) {
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
  if ($formName == 'CRM_Campaign_Form_Petition_Signature') {  
    // Do some basic sanity checking to prevent spammers
    if(empty($form->_surveyId)) {
      // Can't do much without the survey_id
      return;
    }
    $survey_id = $form->_surveyId;

    // Check to see if it's an email petition
    $sql = "SELECT message_field FROM civicrm_petition_email WHERE
      petition_id = %0";
    $dao = CRM_Core_DAO::executeQuery($sql, array(0 => array($survey_id, 'Integer')));
    $dao->fetch();
    if($dao->N == 0) {
      // Nothing to do
      return;
    }

    if(!empty($dao->message_field)) {
      $field_name = 'custom_' . $dao->message_field;
      // If we are allowing a user-supplied message field, ensure it doesn't
      // have any URLs or HTML in it.
      if(array_key_exists($field_name, $fields)) {
        if(preg_match('#https?://#i', $fields[$field_name])) {
          $errors[$field_name] = ts("To avoid spammers, you are not allowed to put web addresses in your message. Please revise your message and try again.");
        }
        // Now ensure we have no html tag
        if (preg_match('/([\<])([^\>]{1,})*([\>])/i', $fields[$field_name] )) {
          $errors[$field_name] = ts("To avoid spammers, you are not allowed to put HTML code in your message. Please revise your message and try again.");
        }
      }
    }
  }

298 299
  if($formName == 'CRM_Campaign_Form_Petition') {
    if(CRM_Utils_Array::value('email_petition', $fields)) {
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
      // Make sure we have a subject field and a default message.
      if(!CRM_Utils_Array::value('subject', $fields)) {
        $msg = ts("You must enter an email subject line.");
        $errors['subject'] = $msg;
      }
      if(!CRM_Utils_Array::value('default_message', $fields)) {
        $msg = ts("You must enter a default message.");
        $errors['default_message'] = $msg;
      }
      // For each matching_group_id, make sure we have a corresponding
      // matching field. 
      $i = 1;
      $using_dynamic_method = FALSE;
      while($i <= PETITIONEMAIL_ALLOWED_GROUP_FIELD_COMBINATIONS_COUNT) {
        $matching_group_id = CRM_Utils_Array::value('matching_group_id' . $i, $fields);
        $matching_field = CRM_Utils_Array::value('matching_field' . $i, $fields);

        if(!empty($matching_group_id) && empty($matching_field)) {
318
          $msg = ts("If you select a matching target group you must select
319 320 321 322 323 324 325 326 327 328 329 330
            a corresponding matching field.");
          $errors['matching_field' . $i] = $msg; 
        }
        if(empty($matching_group_id) && !empty($matching_field)) {
          $msg = ts("If you select a matching field you must select a 
            corresponding matching target group.");
          $errors['matching_group_id' . $i] = $msg; 
        }

        // Keep track to see if there are using the dynamic method
        if(!empty($matching_group_id)) {
          $using_dynamic_method = TRUE;
331
        }
332
        $i++;
333
      }
334 335 336 337 338 339 340
      $location_type_id = CRM_Utils_Array::value('location_type_id', $fields);
      if(empty($location_type_id) && $using_dynamic_method) {
        $msg = ts("If you are using the dynamic method to choose a target group
          and field, you must also select an email address location.");
        $errors['location_type_id'] = $msg; 
      }

341 342 343 344 345 346 347 348 349 350 351 352
      // If additional email targets have been provided, make sure they are
      // all syntactically correct.
      $recipients = CRM_Utils_Array::value('recipients', $fields);
      if(!empty($recipients)) {
        $recipient_array = explode("\n", $recipients);
        while(list(,$line) = each($recipient_array)) {
          if(FALSE === petitionemail_parse_email_line($line)) {
            $errors['recipients'] = ts("Invalid email address listed: %1.", array(1 => $line));
          }
        }
      }

353 354
      if(!$using_dynamic_method && empty($recipients)) {
        $msg = ts("You must select either one target matching group/field or list
355 356 357 358 359 360 361
          at least one address to send all petitions to.");
        $errors['recipients'] = $msg;
      }
    }
  }
}

362 363 364
/**
 * Given an array of profile ids, list all text area fields
 */
Jamie McClelland's avatar
Jamie McClelland committed
365
function petitionemail_get_textarea_fields($profile_ids) {
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
  // Now get all fields in this profile
  $custom_fields = array();
  while(list(,$uf_group_id) = each($profile_ids)) {
    $params = array('uf_group_id' => $uf_group_id, 'rowCount' => 0);
    $field_results = civicrm_api3('UFField', 'get', $params);
    if ($field_results['is_error'] == 0) {
      foreach ($field_results['values'] as $field_value) {
        $field_name = $field_value['field_name'];
        if(!preg_match('/^custom_[0-9]+/', $field_name)) {
          // We only know how to lookup field types for custom
          // fields. Skip core fields.
          continue;
        }

        $id = substr(strrchr($field_name, '_'), 1);
        // Finally, see if this is a text or textarea field.
        $params = array('id' => $id);
        $custom_results = civicrm_api3('CustomField', 'get', $params);
        if ($custom_results['is_error'] == 0) {
          $field_value = array_pop($custom_results['values']);
          $html_type = $field_value['html_type'];
          $label = $field_value['label'];
          $id = $field_value['id'];
          if($html_type == 'Text' || $html_type == 'TextArea') {
390
            $custom_fields['custom_' . $id] = $label;
391 392 393 394 395 396 397 398 399
          }
        }
      }
    }
  }
  return $custom_fields;
}


400
function petitionemail_civicrm_postProcess( $formName, &$form ) {
401 402 403
  if ($formName != 'CRM_Campaign_Form_Petition') { 
    return; 
  }
404 405
  $email_petition = CRM_Utils_Array::value('email_petition', $form->_submitValues);
  if($email_petition && $email_petition  == 1 ) {
406 407 408
    $survey_id = $form->getVar('_surveyId');
    $lastmoddate = 0;
    if (!$survey_id) {  // Ugly hack because the form doesn't return the id
Jamie McClelland's avatar
Jamie McClelland committed
409 410
      $params = array('title' =>$form->_submitValues['title']);
      $surveys = civicrm_api3("Survey", "get", $params);
411 412
      if (is_array($surveys['values'])) {
        foreach($surveys['values'] as $survey) {
413 414 415
          if ($lastmoddate > strtotime($survey['last_modified_date'])) { 
            continue; 
          }
416 417 418 419 420 421
          $lastmoddate = strtotime($survey['last_modified_date']);
          $survey_id = $survey['id'];
        }
      }
    }
    if (!$survey_id) {
Jamie McClelland's avatar
Jamie McClelland committed
422 423
      $msg = ts('Cannot find the petition for saving email delivery fields.');
      CRM_Core_Session::setStatus($msg);
424 425 426 427
      return;
    }

    $default_message =  $form->_submitValues['default_message'];
428
    $message_field = $form->_submitValues['message_field'];
429 430 431
    $subject = $form->_submitValues['subject'];
    $recipients = $form->_submitValues['recipients'];
    $location_type_id = $form->_submitValues['location_type_id'];
432

433
    $sql = "REPLACE INTO civicrm_petition_email (
Jamie McClelland's avatar
Jamie McClelland committed
434 435 436 437 438 439 440 441 442 443 444 445
             petition_id,
             default_message, 
             message_field, 
             subject,
             recipients,
             location_type_id
           ) VALUES ( 
             %1, 
             %2, 
             %3, 
             %4,
             %5,
446
             %6
Jamie McClelland's avatar
Jamie McClelland committed
447
    )";
448 449 450
    $params = array( 
      1 => array( $survey_id, 'Integer' ),
      2 => array( $default_message, 'String' ),
451
      3 => array( $message_field, 'String' ),
452 453
      4 => array( $subject, 'String' ),
      5 => array( $recipients, 'String' ),
454
      6 => array( $location_type_id, 'Integer' ),
455 456
    );
    $petitionemail = CRM_Core_DAO::executeQuery( $sql, $params );
457
    
458
    // delete any existing ones
Jamie McClelland's avatar
Jamie McClelland committed
459 460
    $sql = "DELETE FROM civicrm_petition_email_matching_field WHERE
      petition_id = %0";
461 462
    $params = array(0 => array($survey_id, 'Integer'));
    CRM_Core_DAO::executeQuery($sql, $params);
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478

    $i = 1;
    while($i <= PETITIONEMAIL_ALLOWED_GROUP_FIELD_COMBINATIONS_COUNT) {
      $matching_group_id = CRM_Utils_Array::value('matching_group_id' . $i, $form->_submitValues);
      $matching_field = CRM_Utils_Array::value('matching_field' . $i, $form->_submitValues);
      if(!empty($matching_group_id) && !empty($matching_field)) {
        $sql = "INSERT INTO civicrm_petition_email_matching_field SET
          petition_id = %0, matching_field = %1, matching_group_id = %2";
        $params = array(
          0 => array($survey_id, 'Integer'),
          1 => array($matching_field, 'String'),
          2 => array($matching_group_id, 'Integer')
        );
        CRM_Core_DAO::executeQuery($sql, $params);
      }
      $i++;
479
    }
480 481 482
  }
}

483 484 485 486
/**
 * Implementation of hook_civicrm_post
 *
 * Run everytime a post is made to see if it's a new profile/activity
487 488
 * that should trigger a petition email to be sent. Also clean up 
 * our tables if a petition is deleted.
489
 */
490
function petitionemail_civicrm_post( $op, $objectName, $objectId, &$objectRef ) {
491 492 493 494 495 496 497 498 499 500
  if ($objectName == 'Activity') {
    $activity_id = $objectId;

    // Only run on creation. For petition that require a confirmation,
    // after the petition has been created, see petitionemail_civicrm_pageRun().
    if($op == 'create') {
      if(petitionemail_is_actionable_activity($activity_id)) {
        petitionemail_process_signature($activity_id);
      }
    }
501
  }
502
}
503

504 505 506 507 508 509 510 511 512 513 514 515 516 517
/**
 * Implementation of hook_civicrm_pageRun
 */
function petitionemail_civicrm_pageRun(&$page) {
  // This should be fired after most of the parent run()
  // code is done, which means the activity status should
  // be converted to "complete" if it has been properly
  // verified.
  $pageName = $page->getVar('_name');
  if ($pageName == 'CRM_Campaign_Page_Petition_Confirm') { 
    // Get the activity id from the URL
    $activity_id  = CRM_Utils_Request::retrieve('a', 'String', CRM_Core_DAO::$_nullObject);
    if(petitionemail_is_actionable_activity($activity_id)) {
      petitionemail_process_signature($activity_id);
518 519 520
    }
  }
}
521

522 523
function petitionemail_get_petition_details($petition_id) {
  $ret = array();
Jamie McClelland's avatar
Jamie McClelland committed
524
  $sql = "SELECT default_message, 
525 526 527 528 529 530
               message_field, 
               subject,
               location_type_id,
               recipients
         FROM civicrm_petition_email
         WHERE petition_id = %1 GROUP BY petition_id";
531
  $params = array( 1 => array( $petition_id, 'Integer' ) );
532 533
  $petition_email = CRM_Core_DAO::executeQuery( $sql, $params );
  $petition_email->fetch();
Jamie McClelland's avatar
Jamie McClelland committed
534
  if($petition_email->N == 0) {
535
    // Must not be a petition with a target.
Jamie McClelland's avatar
Jamie McClelland committed
536
    return FALSE;;
537
  }
538

539
  // Store variables we need
540 541 542 543 544
  $ret['default_message'] = $petition_email->default_message;
  $ret['subject'] = $petition_email->subject;
  $ret['location_type_id'] = $petition_email->location_type_id;
  $ret['message_field'] = $petition_email->message_field;
  $ret['recipients'] = $petition_email->recipients;
545

546
  // Now retrieve the matching fields, if any
547 548
  $sql = "SELECT matching_field, matching_group_id FROM
    civicrm_petition_email_matching_field WHERE petition_id = %1";
549
  $params = array( 1 => array( $petition_id, 'Integer' ) );
550
  $dao = CRM_Core_DAO::executeQuery($sql, $params);
551
  $ret['matching'] = array();
552
  while($dao->fetch()) {
553
    $ret['matching'][$dao->matching_field] = $dao->matching_group_id;
554
  }
555 556 557 558 559 560 561 562 563 564 565
  return $ret;
}

function petitionemail_process_signature($activity_id) {
  $petition_id = petitionemail_get_petition_id_for_activity($activity_id);
  if(empty($petition_id)) {
    $log = "Failed to find petition id for activity id: $activity_id";
    CRM_Core_Error::debug_log_message($log);
    return FALSE;
  }
  $petition_vars = petitionemail_get_petition_details($petition_id);
Jamie McClelland's avatar
Jamie McClelland committed
566 567 568 569
  if(!$petition_vars) {
    // Nothing to process, this isn't an email target enabled petition
    return;
  }
570 571 572
  $default_message = $petition_vars['default_message'];
  $subject = $petition_vars['subject'];
  $message_field = $petition_vars['message_field'];
573

574 575 576
  // Figure out whether to use the user-supplied message or the default
  // message.
  $petition_message = NULL;
577 578 579 580 581 582 583 584
  // If the petition has specified a message field
  if(!empty($message_field)) {
    // Retrieve the value of the field for this activity
    $params = array('id' => $activity_id, 
      'return' => array($message_field, 'activity_type_id'));
    $result = civicrm_api3('Activity', 'getsingle', $params);
    if(!empty($result[$message_field])) {
      $petition_message = $result[$message_field];
585 586 587 588 589 590 591 592 593
    }
  } 

  // No user supplied message, use the default
  if(is_null($petition_message)) {
    $petition_message = $default_message;
  }
  $activity = civicrm_api3("Activity", "getsingle", array ('id' => $activity_id));
  $contact_id = $activity['source_contact_id'];
Jamie McClelland's avatar
Jamie McClelland committed
594
  $contact = civicrm_api3("Contact", "getsingle", array ('id' => $contact_id));
595

Jamie McClelland's avatar
Jamie McClelland committed
596 597 598
  $from = NULL;
  if (array_key_exists('email', $contact) && !empty($contact['email'])) {
    $from = $contact['display_name'] . ' <' . $contact['email'] . '>';
599 600 601 602
  } else {
    $domain = civicrm_api3("Domain", "get", array ());
    if ($domain['is_error'] != 0 || !is_array($domain['values'])) { 
      // Can't send email without a from address.
603 604
      $msg = "petition_email: Failed to send petition email because from
        address not sent.";
605 606 607
      CRM_Core_Error::debug_log_message($msg);
      return; 
    }
Jamie McClelland's avatar
Jamie McClelland committed
608
    $from = '"' . $contact['display_name'] . '"' . ' <' .
609 610 611 612 613 614 615 616
      $domain['values']['from_email'] . '>';
  }

  // Setup email message (except to address)
  $email_params = array( 
    'from'    => $from,
    'toName'  => NULL,
    'toEmail' => NULL,
Jamie McClelland's avatar
Jamie McClelland committed
617
    'subject' => $subject,
618
    'text'    => $petition_message, 
619
    'html'    => NULL, 
620 621 622
  );

  // Get array of recipients
623
  $recipients = petitionemail_get_recipients($contact_id, $petition_id);
624 625
  while(list(, $recipient) = each($recipients)) {
    if(!empty($recipient['email'])) {
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
      $log = "petition email: contact id ($contact_id) sending to email (" .
        $recipient['email'] . ")";
      CRM_Core_Error::debug_log_message($log);
      if(!empty($recipient['contact_id'])) {
        // Since we're sending to a recipient in the database, create this
        // as an email activity so we record it properly.

        $log = "petition email: recording email as activity against ".
          "target contact id: " . $recipient['contact_id'];
        CRM_Core_Error::debug_log_message($log);

        $contactDetails = array(0 => $recipient);
        // We are sending a text message, so ensure it's the preferred one
        $contactDetails[0]['preferred_mail_format'] = 'Text';
        $subject = $email_params['subject'];
        $text = $email_params['text'];
        $html = $email_params['html'];
        $emailAddress = $recipient['email'];
        $userID = $contact_id;
        $from = NULL; // This will be pulled from $contact_id,
        $attachments = NULL;
        $cc = NULL;
        $bcc = NULL;
        $contactIds = array($recipient['contact_id']);

        // Create/Send away.
        $ret = CRM_Activity_BAO_Activity::sendEmail(
          $contactDetails, $subject, $text, $html, $emailAddress, $userID,
          $from, $attachments, $cc, $bcc, $contactIds
        );
656

657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
        $activity_id = array_pop($ret);
        $status = array_pop($ret);
        if($status === TRUE) {
          $log = "petition email: email sent successfully";
          CRM_Core_Error::debug_log_message($log);

          // Update the activity with the petition id so we can properly
          // report on the email messages sent as a result of this petition.
          $params = array(
            'activity_id' => $activity_id,
            'source_record_id' => $petition_id
          );
          $result = civicrm_api3('Activity', 'update', $params);
          if($result['is_error'] != 0) {
            $log = "civicrm petition: failed to update activity with ".
              "source_record_id";
            CRM_Core_Error::debug_log_message($log);
          }
        }
        else {
          $log = "petition email: failed to send email as activity.";
          CRM_Core_Error::debug_log_message($log);
          CRM_Core_Error::debug_log_message(print_r($ret, TRUE));
        }
      }
      else {
        // Handle targets not in the database.
        $email_params['toName'] = $recipient['name'];
        $email_params['toEmail'] = $recipient['email'];
        $to = $email_params['toName'] . ' ' . $email_params['toEmail'];

        $log = "petition_email: sending petition to '$to' via mail function.";
        CRM_Core_Error::debug_log_message($log);

        $success = CRM_Utils_Mail::send($email_params);

        if($success == 1) {
          CRM_Core_Session::setStatus( ts('Message sent successfully to') . "$to", '', 'success' );
          $log = "petition_email: message sent.";
        } else {
          $log = "petition_email: message was not sent.";
          CRM_Core_Session::setStatus( ts('Error sending message to') . "$to" );
        }
        CRM_Core_Error::debug_log_message($log);
701 702 703 704 705
      }
    }
  }
}
 
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
/**
 * Non custom data fields allowed to be a matching field.
 *
 * All custom fields can be used as matching fields, but only
 * a subset of non-custom fields (so we can be sure to build
 * a working query to retrieve them).
 */
function petitionemail_get_allowed_matching_fields() {
  $ret = array(
    'street_name' => 'civicrm_address',
    'street_number' => 'civicrm_address',
    'street_name' => 'civicrm_address',
    'city' => 'civicrm_address',
    'county_id' => 'civicrm_address',
    'state_province_id' => 'civicrm_address',
    'postal_code' => 'civicrm_address',
    'postal_code_suffix' => 'civicrm_address',
    'country_id' => 'civicrm_address',
  );
  return $ret;
}

728 729
function petitionemail_get_recipients($contact_id, $petition_id) {
  $petition_vars = petitionemail_get_petition_details($petition_id);
Jamie McClelland's avatar
Jamie McClelland committed
730 731 732 733
  if(!$petition_vars) {
    // Not an email target enabled petition
    return;
  }
734 735 736 737 738 739 740 741 742
  $ret = array();
  // First, parse the additional recipients, if any. These get the email
  // regarldess of who signs it.
  if(!empty($petition_vars['recipients'])) {
    $recipients = explode("\n", $petition_vars['recipients']);
    while(list(,$recipient) = each($recipients)) {
      $email_parts = petitionemail_parse_email_line($recipient); 
      if(FALSE !== $email_parts) {
        $ret[] = array(
743
          'contact_id' => NULL,
744 745 746 747 748 749
          'name' => $email_parts['name'],
          'email' => $email_parts['email']
        );
      }
    }
  }
750 751 752
  // If there are any matching criteria (for a dynamic lookup) we do a
  // complex query to figure out which members of the group should be
  // included as recipients.
Jamie McClelland's avatar
Jamie McClelland committed
753
  if(count($petition_vars['matching']) > 0) {
754 755 756 757
    // This comes as an array with the key being the matching field and
    // the value being the matching_group_id.
    $matching_fields = $petition_vars['matching'];

758 759 760
    // Get the values of the matching fields for the contact. These values
    // are used to match the contact who signed the petition with the 
    // contact or contacts in the target group.
761 762 763 764 765 766

    // Given the matching fields, we're going to do an API call against
    // the contact to get the values that we will be matching on.

    // Build a return_fields array that we will pass to the api call to 
    // specify the fields we want returned with this query.
767
    $field_names = array_keys($matching_fields);
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
    $return_fields = array();
    reset($field_names);
    while(list(, $field_name) = each($field_names)) {
      // If the field_name starts with custom_ we can add it straight 
      // away.
      if(preg_match('/^custom_/', $field_name)) {
        $return_fields[] = $field_name;
        continue;
      }

      // Look for field names with a - in them - that's an indication 
      // that it's an address field which will have the location part
      // stuck into the name.
      $field_pieces = petitionemail_split_address_field($field_name);
      if($field_pieces) {
        if($field_pieces['location_name'] == 'Primary') {
          // Primary will be included via the api call, so we just need
          // the field name. If it's not primary, we'll have to do a 
          // manual SQL call below to get the value.
          $return_fields[] = $field_pieces['field_name'];
          continue;
        }
      }
791
      // FIXME If we get here, this is an error
792 793
    }
    $contact_params = array('return' => $return_fields, 'id' => $contact_id);
794 795
    $contact = civicrm_api3('Contact', 'getsingle', $contact_params);
    while(list($matching_field) = each($matching_fields)) {
796 797 798 799 800 801 802 803
      // Check if the field was returned. If not, it's probably an address field
      if(array_key_exists($matching_field, $contact)) {
        $matching_fields[$matching_field] = $contact[$matching_field];
        continue;
      }
      // This means it's probably an address field.
      $field_pieces = petitionemail_split_address_field($matching_field);
      if(!$field_pieces) {
804
        // FIXME This is an error
805 806 807 808 809 810 811
        continue;
      }
      $location_name = $field_pieces['location_name'];
      $field_name = $field_pieces['field_name'];
      if($location_name == 'Primary' && array_key_exists($field_name, $contact)) {
        // We have to unset the field that was saved as fieldname-locatiname
        unset($matching_fields[$matching_field]);
812

813 814 815 816 817 818
        // And now set the proper key
        $matching_field = $field_name;
        $matching_fields[$matching_field] = $contact[$matching_field];
        continue;
      }
      else {
819
        // FIXME This is an error
820 821 822
        continue;
      }
    } 
823 824

    // Initialize variables to build the SQL statement
825
    $from = array();
826 827
    // The master $where clause will be put together using AND
    $where = array();
828
    $params = array();
829
    $added_tables = array();
830

831 832
    // Initialize the from clause
    $from[] = 'civicrm_contact c';
833

834 835 836 837 838
    // We build a sub where clause that limits results based on the 
    // matching group and matching field that will be put together using
    // OR since we match any any of the matching field => group
    // combinations.
    $sub_where = array();
839
    reset($matching_fields);
840
    $id = 0;
841
    while(list($matching_field, $value) = each($matching_fields)) {
842 843 844 845 846 847 848 849 850 851 852 853 854 855
      // The $where_fragment will be put together using AND because
      // you have to match both the group and the field.
      $where_fragment = array();

      // Gather information about the group that is paired with this
      // matching field.
      $group_id = $petition_vars['matching'][$matching_field];
      // Retrieve details (specifically, find out if it's a smart group)
      $results = civicrm_api3('Group', 'getsingle', array('id' => $group_id));
      if(!empty($results['id'])) {
        if(!empty($results['saved_search_id'])) {
          // Populate the cache
          CRM_Contact_BAO_GroupContactCache::check($group_id);
          if(!in_array('civicrm_group_contact_cache', $added_tables)) {
Jamie McClelland's avatar
Jamie McClelland committed
856
            $from[] = 'LEFT JOIN civicrm_group_contact_cache cc ON
857 858 859 860 861 862
              c.id = cc.contact_id';
            $added_tables[] = 'civicrm_group_contact_cache';
          }
          $where_fragment[] = 'cc.group_id = %' . $id;
          $params[$id] = array($group_id, 'Integer');
          $id++;
863 864
        }
        else {
865
          if(!in_array('civicrm_group_contact', $added_tables)) {
Jamie McClelland's avatar
Jamie McClelland committed
866
            $from[] = 'LEFT JOIN civicrm_group_contact gc ON
867 868 869 870 871 872 873
              c.id = gc.contact_id';
            $added_tables[] = 'civicrm_group_contact';
          }
          $where_fragment[] = 'gc.group_id = %' . $id;
          $where_fragment[] = 'gc.status = "Added"';
          $params[$id] = array($group_id, 'Integer');
          $id++;
874
        }
875 876 877 878 879
      
        // Now add in the matching field
        if(empty($value)) {
          // We should never match in this case
          $where_fragment[] = "(0)";
880 881
        }
        else {
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
          if(preg_match('/^custom_/', $matching_field)) {
            $sql = "SELECT column_name, table_name FROM civicrm_custom_group g 
              JOIN civicrm_custom_field f ON g.id = f.custom_group_id WHERE 
              f.id = %0";
            $custom_field_id = str_replace('custom_', '', $matching_field);
            $dao = CRM_Core_DAO::executeQuery($sql, array(0 => array($custom_field_id, 'Integer')));
            $dao->fetch();
            if(!in_array($dao->table_name, $added_tables)) {
              $from[] = "LEFT JOIN " . $dao->table_name . " ON " . $dao->table_name . ".entity_id = 
                c.id";
              $added_tables[] = $dao->table_name;
            }
            $where_fragment[] = $dao->column_name . ' = %' . $id;
            // Fixme - we should use the proper data type for each custom field
            $params[$id] = array($value, 'String');
            $id++;
          }
          else {
            // Handle non-custom fields (address fields)
            if(!in_array('civicrm_address', $added_tables)) {
              $from[] = "LEFT JOIN civicrm_address a ON a.contact_id = c.id";
              $added_tables[] = 'civicrm_address';
            }
            $field_where[] = '(' . $matching_field . ' = %' . $id . ')';
            $params[$id] = array($value, 'String');
            $id++;
          }
909
        }
910
        $sub_where[] = '(' . implode(' AND ', $where_fragment) . ')';
911
      }
912 913 914 915 916 917 918
      else {
        // This is an error
      }
    }

    if(count($sub_where) > 0) {
      $where[] = '(' . implode(' OR ', $sub_where) . ')';
919 920 921 922
    }

    // Now add the right email lookup info
    $from[] = "JOIN civicrm_email e ON c.id = e.contact_id";
923
    $where[] = 'e.location_type_id = %' . $id;
924 925 926
    $params[$id] = array($petition_vars['location_type_id'], 'Integer');

    // put it all together
927 928 929
    $sql = "SELECT DISTINCT c.id, c.display_name, e.email ";
    $sql .= "FROM " . implode("\n", $from) . " ";
    $sql .= "WHERE " . implode(" AND\n", $where);
930 931 932
    $dao = CRM_Core_DAO::executeQuery($sql, $params);
    while($dao->fetch()) {
      $ret[] = array(
933
        'contact_id' => $dao->id,
934 935 936 937 938 939 940 941
        'name' => $dao->display_name,
        'email' => $dao->email
      );
    }
  }
  return $ret; 
}

942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
/**
 * Split address field name
 * 
 * Field names in profiles are stored in the format
 * fieldname-locationame (e.g. postal_code-Primary).
 * This function breaks that string into the field name
 * and location name or returns FALSE if it's not a
 * location field.
 */
function petitionemail_split_address_field($field_name) {
  $ret = FALSE;
  if(preg_match('/([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)/', $field_name, $matches)) {
    if(!empty($matches[1]) && !empty($matches[2])) {
      $ret = array(
        'field_name' => $matches[1],
        'location_name' => $matches[2],
      );
    }
  }
  return $ret;
}

964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
/**
 * Convert name + email line into name and email parts
 *
 * Thanks: http://www.regular-expressions.info/email.html
 */
function petitionemail_parse_email_line($line) {
  $ret = array();
  $recipient = trim($line);
  // First attempt to extract a valid email address
  if(preg_match('/([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6})/i', $recipient, $matches)) {
    $email = $matches[1];
    // Now remove the matching email from the string, along with any <> characters
    $remainder = trim(str_replace(array($email, '>', '<'), '', $recipient)); 
    // Trim off any opening/closing quotes
    $name = trim($remainder, '"');
    $ret['name'] = $name;
    $ret['email'] = $email;
  }
  else {
    // Could not find an email address in there any where.
    $ret = FALSE;
  }
  return $ret;
}

989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
/**
 * Given an activity id, return the related petition id 
 *
 * Return FALSE if this is not an activity that is a petition
 * signature. 
 */
function petitionemail_get_petition_id_for_activity($activity_id) {
  // If there is a related civicrm_petition_email record, we are good to go.
  // NOTE: source_record_id stores the survey_id which is the same thing
  // as the petition_id for our purposes.
  $sql = "SELECT a.source_record_id FROM civicrm_activity a JOIN
    civicrm_petition_email pe ON a.source_record_id = pe.petition_id
    WHERE a.id = %0";
  $params = array(0 => array($activity_id, 'Integer'));
  $dao = CRM_Core_DAO::executeQuery($sql, $params);
  $dao->fetch();
  if($dao->N == 0) return FALSE;
  return $dao->source_record_id; 
}

/**
 * Ensure activity_id should generate an email 
 *
 * Should have a related petition_email record and should have
 * a status of complete and should have a date.
 */
function petitionemail_is_actionable_activity($activity_id) {
  if(!petitionemail_get_petition_id_for_activity($activity_id)) {
    return FALSE;
1018
  }
1019 1020 1021 1022 1023 1024 1025
  $completed = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name');
  $sql = "SELECT id FROM civicrm_activity WHERE id = %0 AND status_id = %1";
  $params = array(0 => array($activity_id, 'Integer'), 1 => array($completed, 'Integer'));
  $dao = CRM_Core_DAO::executeQuery($sql, $params);
  $dao->fetch();
  if($dao->N == 0) return FALSE;
  return TRUE;
1026
}
1027

1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
/**
 * Helper function to get or create required profile
 *
 * This profile controls which fields can be used to match a petition 
 * signer with a petition target. We use a profile to avoid having a
 * giant list of fields presented to the user.
 *
 * This function ensures that the profile is created and if not, it
 * creates it. 
 *
 * @return integer profile id 
 */
1040
function petitionemail_get_matching_fields_profile_id() {
1041
  $group = 'petitionemail';
1042
  $key = 'petitionemail_matching_fields';
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
  $ret = CRM_Core_BAO_Setting::getItem($group, $key);
  if(!empty($ret)) {
    // Ensure it exists
    $sql = "SELECT id FROM civicrm_uf_group WHERE id = %0";
    $dao = CRM_Core_DAO::executeQuery($sql, array(0 => array($ret, 'Integer')));
    $dao->fetch();
    if($dao->N == 1) {
      return $ret;
    }
    // Delete this variable - probably the user deleted the profile not knowing
    // what it was used for.
    $sql = "DELETE FROM civicrm_setting WHERE group_name = %0 AND name = %1";
    $params = array(
      0 => array($group, 'String'),
      1 => array($key, 'String')
    );
    CRM_Core_DAO::executeQuery($sql, $params);
  }

  // Create the profile
  // We have to manually set created_id if the current user is not set
  $session = CRM_Core_Session::singleton();
  $contact_id = $session->get('userID');
  if(empty($contact_id)) {
    // Maybe we are running via drush?
    // Try the contact associated with uid 1
    $contact_id = CRM_Core_BAO_UFMatch::getContactId(1);
    if(empty($contact_id)) {
      // Last ditch effort
      $sql = "SELECT MIN(id) FROM civicrm_contact WHERE is_active = 1 AND is_deleted = 0";
      $dao = CRM_Core_DAO::executeQuery($sql);
      $dao->fetch();
      $contact_id = $dao->id;
    }
  }

  $description = ts('This profile controls which fields are available as
    matching fields when using the petition email extension. Please do
    not delete this profile.');
  $params = array(
1083
    'name' => $key,
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
    'title' => ts('Petition Email Available Matching fields'),
    'description' => $description,
    'created_id' => $contact_id
  );
  $results = civicrm_api3('UFGroup', 'create', $params);
  if($results['is_error'] != 0) {
    $session->setStatus(ts("Error creating the petition email profile group."));
    return FALSE;
  }
  $value = array_pop($results['values']);
  $id = $value['id'];

  CRM_Core_BAO_Setting::setItem($id, $group, $key);
  return $id;
}

1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
/**
 * Remove any profiles we automatically created.
 */
function petitionemail_remove_profiles() {
  $profiles_to_remove = array('petitionemail_matching_fields');
  while(list(,$key) = each($profiles_to_remove)) {
    $group = 'petitionemail';
    $ret = CRM_Core_BAO_Setting::getItem($group, $key);
    if($ret) {
      // Get a list of existing profile fields and remove those 
      // first.
      $params = array(
        'uf_group_id' => $ret,
        'return' => array('id')
      );
      $results = civicrm_api3('UFField', 'get', $params);
      if(is_array($results['values'])) {
        while(list($id) = each($results['values'])) {
          $params = array('id' => $id);
          civicrm_api3('UFField', 'delete', $params);
        }
      }
      $params = array('id' => $ret);
      civicrm_api3('UFGroup', 'delete', $params);
    }
  }
}

1128 1129 1130 1131
/**
 * Helper to remove any extension created variables
 */
function petitionemail_remove_variables() {
1132
  $group = 'petitionemail';
1133 1134 1135 1136 1137 1138
  $sql = "DELETE FROM civicrm_setting WHERE group_name = %0";
  $params = array(
    0 => array($group, 'String')
  );
  CRM_Core_DAO::executeQuery($sql, $params);
}