Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRM-20845: create alterMailingRecipients hook #10673

Merged
merged 5 commits into from
Jul 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 29 additions & 25 deletions CRM/Mailing/BAO/Mailing.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,35 +228,38 @@ public static function getRecipients($mailingID) {
);

if ($isSMSmode) {
$includeFilters = array(
"mg.group_type = 'Include'",
'mg.search_id IS NULL',
"$contact.is_opt_out = 0",
"$contact.is_deceased <> 1",
"$entityTable.phone_type_id = " . CRM_Core_PseudoConstant::getKey('CRM_Core_DAO_Phone', 'phone_type_id', 'Mobile'),
"$entityTable.phone IS NOT NULL",
"$entityTable.phone != ''",
"$entityTable.is_primary = 1",
"mg.mailing_id = #mailingID",
'temp.contact_id IS null',
$criteria = array(
'is_opt_out' => CRM_Utils_SQL_Select::fragment()->where("$contact.is_opt_out = 0"),
'is_deceased' => CRM_Utils_SQL_Select::fragment()->where("$contact.is_deceased <> 1"),
'location_filter' => CRM_Utils_SQL_Select::fragment()->where("$entityTable.phone_type_id = " . CRM_Core_PseudoConstant::getKey('CRM_Core_DAO_Phone', 'phone_type_id', 'Mobile')),
'phone_not_null' => CRM_Utils_SQL_Select::fragment()->where("$entityTable.phone IS NOT NULL"),
'phone_not_empty' => CRM_Utils_SQL_Select::fragment()->where("$entityTable.phone != ''"),
'is_primary' => CRM_Utils_SQL_Select::fragment()->where("$entityTable.is_primary = 1"),
'mailing_id' => CRM_Utils_SQL_Select::fragment()->where("mg.mailing_id = #mailingID"),
'temp_contact_null' => CRM_Utils_SQL_Select::fragment()->where('temp.contact_id IS null'),
'order_by' => CRM_Utils_SQL_Select::fragment()->orderBy("$entityTable.is_primary = 1"),
);
$order_by = array("$entityTable.is_primary = 1");
}
else {
// Criterias to filter recipients that need to be included
$includeFilters = array(
"$contact.do_not_email = 0",
"$contact.is_opt_out = 0",
"$contact.is_deceased <> 1",
$location_filter,
"$entityTable.email IS NOT NULL",
"$entityTable.email != ''",
"$entityTable.on_hold = 0",
"mg.mailing_id = #mailingID",
'temp.contact_id IS NULL',
$criteria = array(
'do_not_email' => CRM_Utils_SQL_Select::fragment()->where("$contact.do_not_email = 0"),
'is_opt_out' => CRM_Utils_SQL_Select::fragment()->where("$contact.is_opt_out = 0"),
'is_deceased' => CRM_Utils_SQL_Select::fragment()->where("$contact.is_deceased <> 1"),
'location_filter' => CRM_Utils_SQL_Select::fragment()->where($location_filter),
'email_not_null' => CRM_Utils_SQL_Select::fragment()->where("$entityTable.email IS NOT NULL"),
'email_not_empty' => CRM_Utils_SQL_Select::fragment()->where("$entityTable.email != ''"),
'email_not_on_hold' => CRM_Utils_SQL_Select::fragment()->where("$entityTable.on_hold = 0"),
'mailing_id' => CRM_Utils_SQL_Select::fragment()->where("mg.mailing_id = #mailingID"),
'temp_contact_null' => CRM_Utils_SQL_Select::fragment()->where('temp.contact_id IS NULL'),
'order_by' => CRM_Utils_SQL_Select::fragment()->orderBy($order_by),
);
}

// Allow user to alter query responsible to fetch mailing recipients before build,
// by changing the mail filters identified $params
CRM_Utils_Hook::alterMailingRecipients($mailingObj, $criteria, 'pre');

// Get the group contacts, but only those which are not in the
// exclusion temp table.
if (!empty($recipientsGroup['Include'])) {
Expand All @@ -267,7 +270,7 @@ public static function getRecipients($mailingID) {
->join('mg', " INNER JOIN civicrm_mailing_group mg ON gc.group_id = mg.entity_id AND mg.search_id IS NULL ")
->join('temp', " LEFT JOIN $excludeTempTablename temp ON $contact.id = temp.contact_id ")
->where('gc.group_id IN (#groups) AND gc.status = "Added"')
->where($includeFilters)
->merge($criteria)
->groupBy(array("$contact.id", "$entityTable.id"))
->replaceInto($includedTempTablename, array('contact_id', $entityColumn))
->param('#groups', $recipientsGroup['Include'])
Expand All @@ -293,8 +296,7 @@ public static function getRecipients($mailingID) {
->join('mg', " INNER JOIN civicrm_mailing_group mg ON gc.group_id = mg.entity_id AND mg.search_id IS NULL ")
->join('temp', " LEFT JOIN $excludeTempTablename temp ON $contact.id = temp.contact_id ")
->where('gc.group_id IN (#groups)')
->where($includeFilters)
->orderBy($order_by)
->merge($criteria)
->replaceInto($includedTempTablename, array('contact_id', $entityColumn))
->param('#groups', $includeSmartGroupIDs)
->param('#mailingID', $mailingID)
Expand Down Expand Up @@ -370,6 +372,8 @@ public static function getRecipients($mailingID) {
$mailingGroup->reset();
$mailingGroup->query(" DROP TEMPORARY TABLE $excludeTempTablename ");
$mailingGroup->query(" DROP TEMPORARY TABLE $includedTempTablename ");

CRM_Utils_Hook::alterMailingRecipients($mailingObj, $criteria, 'post');
}

/**
Expand Down
21 changes: 21 additions & 0 deletions CRM/Utils/Hook.php
Original file line number Diff line number Diff line change
Expand Up @@ -2450,4 +2450,25 @@ public static function postJob($job, $params, $result) {
);
}

/**
* This hook is called before and after constructing mail recipients.
* Allows user to alter filter and/or search query to fetch mail recipients
*
* @param CRM_Mailing_DAO_Mailing $mailingObject
* @param array $criteria
* A list of SQL criteria; you can add/remove/replace/modify criteria.
* Array(string $name => CRM_Utils_SQL_Select $criterion).
* Ex: array('do_not_email' => CRM_Utils_SQL_Select::fragment()->where("$contact.do_not_email = 0")).
* @param string $context
* Ex: 'pre', 'post'
* @return mixed
*/
public static function alterMailingRecipients(&$mailingObject, &$criteria, $context) {
return self::singleton()->invoke(array('mailingObject', 'params', 'context'),
$mailingObject, $criteria, $context,
self::$_nullObject, self::$_nullObject, self::$_nullObject,
'civicrm_alterMailingRecipients'
);
}

}
9 changes: 8 additions & 1 deletion CRM/Utils/SQL/Select.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public function copy() {
/**
* Merge something or other.
*
* @param CRM_Utils_SQL_Select $other
* @param array|CRM_Utils_SQL_Select $other
* @param array|NULL $parts
* ex: 'joins', 'wheres'
* @return CRM_Utils_SQL_Select
Expand All @@ -187,6 +187,13 @@ public function merge($other, $parts = NULL) {
return $this;
}

if (is_array($other)) {
foreach ($other as $fragment) {
$this->merge($fragment, $parts);
}
return $this;
}

if ($this->mode === self::INTERPOLATE_AUTO) {
$this->mode = $other->mode;
}
Expand Down
76 changes: 76 additions & 0 deletions tests/phpunit/CRM/Mailing/BAO/MailingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -456,4 +456,80 @@ public function testgetRecipientsSMS() {
$this->contactDelete($contactID2);
}

/**
* Test alterMailingRecipients Hook which is called twice when we create a Mailing,
* 1. In the first call we will modify the mailing filter to include only deceased recipients
* 2. In the second call we will check if only deceased recipient is populated in MailingRecipient table
*/
public function testAlterMailingRecipientsHook() {
$groupID = $this->groupCreate();
$this->tagCreate(array('name' => 'Tagged'));

// Create deseased Contact 1 and add in group
$contactID1 = $this->individualCreate(array('email' => 'abc@test.com', 'is_deceased' => 1), 0);
// Create deseased Contact 2 and add in group
$contactID2 = $this->individualCreate(array('email' => 'def@test.com'), 1);
// Create deseased Contact 3 and add in group
$contactID3 = $this->individualCreate(array('email' => 'ghi@test.com', 'is_deceased' => 1), 2);

// Add both the created contacts in group
$this->callAPISuccess('GroupContact', 'Create', array(
'group_id' => $groupID,
'contact_id' => $contactID1,
));
$this->callAPISuccess('GroupContact', 'Create', array(
'group_id' => $groupID,
'contact_id' => $contactID2,
));
$this->callAPISuccess('GroupContact', 'Create', array(
'group_id' => $groupID,
'contact_id' => $contactID3,
));
$this->entityTagAdd(array('contact_id' => $contactID3, 'tag_id' => 'Tagged'));

// trigger the alterMailingRecipients hook
$this->hookClass->setHook('civicrm_alterMailingRecipients', array($this, 'alterMailingRecipients'));

// create mailing that will trigger alterMailingRecipients hook
$params = array(
'name' => 'mailing name',
'subject' => 'Test Subject',
'body_html' => '<p>HTML Body</p>',
'text_html' => 'Text Body',
'created_id' => 1,
'groups' => array('include' => array($groupID)),
'scheduled_date' => 'now',
);
$this->callAPISuccess('Mailing', 'create', $params);
}

/**
* @implements CRM_Utils_Hook::alterMailingRecipients
*
* @param object $mailingObject
* @param array $criteria
* @param string $context
*/
public function alterMailingRecipients(&$mailingObject, &$criteria, $context) {
if ($context == 'pre') {
// modify the filter to include only deceased recipient(s) that is Tagged
$criteria['is_deceased'] = CRM_Utils_SQL_Select::fragment()->where("civicrm_contact.is_deceased = 1");
$criteria['tagged_contact'] = CRM_Utils_SQL_Select::fragment()
->join('civicrm_entity_tag', "INNER JOIN civicrm_entity_tag et ON et.entity_id = civicrm_contact.id AND et.entity_table = 'civicrm_contact'")
->join('civicrm_tag', "INNER JOIN civicrm_tag t ON t.id = et.tag_id")
->where("t.name = 'Tagged'");
}
else {
$mailingRecipients = $this->callAPISuccess('MailingRecipients', 'get', array(
'mailing_id' => $mailingObject->id,
'api.Email.getvalue' => array(
'id' => '$value.email_id',
'return' => 'email',
),
));
$this->assertEquals(1, $mailingRecipients['count'], 'Check recipient count');
$this->assertEquals('ghi@test.com', $mailingRecipients['values'][$mailingRecipients['id']]['api.Email.getvalue'], 'Check if recipient email belong to deceased contact');
}
}

}