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-8526, CRM-19786, CRM-18141, CRM-19757 : Fix Schedule Reminder tokens #9661

Merged
merged 6 commits into from
Jan 17, 2017
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
18 changes: 12 additions & 6 deletions CRM/Activity/Tokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@ class CRM_Activity_Tokens extends \Civi\Token\AbstractTokenSubscriber {
* CRM_Activity_Tokens constructor.
*/
public function __construct() {
parent::__construct('activity', array(
'activity_id' => ts('Activity ID'),
'activity_type' => ts('Activity Type'),
'subject' => ts('Activity Subject'),
'details' => ts('Activity Details'),
'activity_date_time' => ts('Activity Date-Time'),
parent::__construct('activity', array_merge(
array(
'activity_id' => ts('Activity ID'),
'activity_type' => ts('Activity Type'),
'subject' => ts('Activity Subject'),
'details' => ts('Activity Details'),
'activity_date_time' => ts('Activity Date-Time'),
),
$this->getCustomTokens('Activity')
));
}

Expand Down Expand Up @@ -107,6 +110,9 @@ public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefe
elseif (isset($actionSearchResult->$field)) {
$row->tokens($entity, $field, $actionSearchResult->$field);
}
elseif ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) {
$row->customToken($entity, $cfID, $actionSearchResult->entity_id);
}
else {
$row->tokens($entity, $field, '');
}
Expand Down
4 changes: 4 additions & 0 deletions CRM/Contribute/Tokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public function __construct() {
$tokens['source'] = ts('Contribution Source');
$tokens['status'] = ts('Contribution Status');
$tokens['type'] = ts('Financial Type');
$tokens = array_merge($tokens, $this->getCustomTokens('Contribution'));
parent::__construct('contribution', $tokens);
}

Expand Down Expand Up @@ -135,6 +136,9 @@ public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefe
elseif (isset($aliasTokens[$field])) {
$row->dbToken($entity, $field, 'CRM_Contribute_BAO_Contribution', $aliasTokens[$field], $fieldValue);
}
elseif ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) {
$row->customToken($entity, $cfID, $actionSearchResult->entity_id);
}
else {
$row->dbToken($entity, $field, 'CRM_Contribute_BAO_Contribution', $field, $fieldValue);
}
Expand Down
36 changes: 21 additions & 15 deletions CRM/Event/Tokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,24 @@ class CRM_Event_Tokens extends \Civi\Token\AbstractTokenSubscriber {
* Class constructor.
*/
public function __construct() {
parent::__construct('event', array(
'event_type' => ts('Event Type'),
'title' => ts('Event Title'),
'event_id' => ts('Event ID'),
'start_date' => ts('Event Start Date'),
'end_date' => ts('Event End Date'),
'summary' => ts('Event Summary'),
'description' => ts('Event Description'),
'location' => ts('Event Location'),
'info_url' => ts('Event Info URL'),
'registration_url' => ts('Event Registration URL'),
'fee_amount' => ts('Event Fee'),
'contact_email' => ts('Event Contact (Email)'),
'contact_phone' => ts('Event Contact (Phone)'),
'balance' => ts('Event Balance'),
parent::__construct('event', array_merge(
array(
'event_type' => ts('Event Type'),
'title' => ts('Event Title'),
'event_id' => ts('Event ID'),
'start_date' => ts('Event Start Date'),
'end_date' => ts('Event End Date'),
'summary' => ts('Event Summary'),
'description' => ts('Event Description'),
'location' => ts('Event Location'),
'info_url' => ts('Event Info URL'),
'registration_url' => ts('Event Registration URL'),
'fee_amount' => ts('Event Fee'),
'contact_email' => ts('Event Contact (Email)'),
'contact_phone' => ts('Event Contact (Phone)'),
'balance' => ts('Event Balance'),
),
$this->getCustomTokens('Event')
));
}

Expand Down Expand Up @@ -139,6 +142,9 @@ public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefe
elseif (isset($actionSearchResult->$field)) {
$row->tokens($entity, $field, $actionSearchResult->$field);
}
elseif ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) {
$row->customToken($entity, $cfID, $actionSearchResult->entity_id);
}
else {
$row->tokens($entity, $field, '');
}
Expand Down
22 changes: 14 additions & 8 deletions CRM/Member/Tokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,17 @@ class CRM_Member_Tokens extends \Civi\Token\AbstractTokenSubscriber {
* Class constructor.
*/
public function __construct() {
parent::__construct('membership', array(
'fee' => ts('Membership Fee'),
'id' => ts('Membership ID'),
'join_date' => ts('Membership Join Date'),
'start_date' => ts('Membership Start Date'),
'end_date' => ts('Membership End Date'),
'status' => ts('Membership Status'),
'type' => ts('Membership Type'),
parent::__construct('membership', array_merge(
array(
'fee' => ts('Membership Fee'),
'id' => ts('Membership ID'),
'join_date' => ts('Membership Join Date'),
'start_date' => ts('Membership Start Date'),
'end_date' => ts('Membership End Date'),
'status' => ts('Membership Status'),
'type' => ts('Membership Type'),
),
$this->getCustomTokens('Membership')
));
}

Expand Down Expand Up @@ -95,6 +98,9 @@ public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefe
elseif (isset($actionSearchResult->$field)) {
$row->tokens($entity, $field, $actionSearchResult->$field);
}
elseif ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) {
$row->customToken($entity, $cfID, $actionSearchResult->entity_id);
}
else {
$row->tokens($entity, $field, '');
}
Expand Down
21 changes: 17 additions & 4 deletions Civi/Token/AbstractTokenSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,22 @@ public function registerTokens(TokenRegisterEvent $e) {
}
}

/**
* Get all custom field tokens of $entity
*
* @param string $entity
* @return array $customTokens
* return custom field tokens in array('custom_N' => 'label') format
*/
public function getCustomTokens($entity) {
$customTokens = array();
foreach (\CRM_Core_BAO_CustomField::getFields($entity) as $id => $info) {
$customTokens["custom_$id"] = $info['label'];
}

return $customTokens;
}

/**
* Alter the query which prepopulates mailing data
* for scheduled reminders.
Expand Down Expand Up @@ -148,14 +164,11 @@ public function evaluateTokens(TokenValueEvent $e) {
}

$activeTokens = array_intersect($messageTokens[$this->entity], array_keys($this->tokenNames));
if (empty($activeTokens)) {
return;
}

$prefetch = $this->prefetch($e);

foreach ($e->getRows() as $row) {
foreach ($activeTokens as $field) {
foreach ((array) $activeTokens as $field) {
$this->evaluateToken($row, $this->entity, $field, $prefetch);
}
}
Expand Down
17 changes: 14 additions & 3 deletions Civi/Token/TokenCompatSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ public function onEvaluate(TokenValueEvent $e) {
// FIXME: Need to differentiate errors which kill the batch vs the individual row.
throw new TokenException("Failed to generate token data. Invalid contact ID: " . $row->context['contactId']);
}

//update value of custom field token
if (!empty($messageTokens['contact'])) {
foreach ($messageTokens['contact'] as $token) {
if (\CRM_Core_BAO_CustomField::getKeyID($token)) {
$contact[$token] = civicrm_api3('Contact', 'getvalue', array(
'return' => $token,
'id' => $contactId,
));
}
}
}
}
else {
$contact = $row->context['contact'];
Expand All @@ -76,7 +88,6 @@ public function onEvaluate(TokenValueEvent $e) {

// Note: This is a small contract change from the past; data should be missing
// less randomly.
//\CRM_Utils_Hook::tokenValues($contact, $row->context['contactId']);
\CRM_Utils_Hook::tokenValues($contactArray,
(array) $contactId,
empty($row->context['mailingJob']) ? NULL : $row->context['mailingJob']->id,
Expand Down Expand Up @@ -104,12 +115,12 @@ public function onRender(TokenRenderEvent $e) {
$e->string = \CRM_Utils_Token::replaceDomainTokens($e->string, \CRM_Core_BAO_Domain::getDomain(), $isHtml, $e->message['tokens'], $useSmarty);

if (!empty($e->context['contact'])) {
$e->string = \CRM_Utils_Token::replaceContactTokens($e->string, $e->context['contact'], $isHtml, $e->message['tokens'], FALSE, $useSmarty);
$e->string = \CRM_Utils_Token::replaceContactTokens($e->string, $e->context['contact'], $isHtml, $e->message['tokens'], TRUE, $useSmarty);

// FIXME: This may depend on $contact being merged with hook values.
$e->string = \CRM_Utils_Token::replaceHookTokens($e->string, $e->context['contact'], $e->context['hookTokenCategories'], $isHtml, $useSmarty);

\CRM_Utils_Token::replaceGreetingTokens($e->string, NULL, $e->context['contact']['contact_id'], NULL, $useSmarty);
\CRM_Utils_Token::replaceGreetingTokens($e->string, $e->context['contact'], $e->context['contact']['contact_id'], NULL, $useSmarty);
}

if ($useSmarty) {
Expand Down
23 changes: 23 additions & 0 deletions Civi/Token/TokenRow.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,29 @@ public function tokens($a = NULL, $b = NULL, $c = NULL) {
return $this;
}

/**
* Update the value of a custom field token.
*
* @param string $entity
* @param int $customFieldID
* @param int $entityID
* @return TokenRow
*/
public function customToken($entity, $customFieldID, $entityID) {
$customFieldName = "custom_" . $customFieldID;
$fieldValue = civicrm_api3($entity, 'getvalue', array(
'return' => $customFieldName,
'id' => $entityID,
));

// format the raw custom field value into proper display value
if ($fieldValue) {
$fieldValue = \CRM_Core_BAO_CustomField::displayValue($fieldValue, $customFieldID);
}

return $this->tokens($entity, $customFieldName, $fieldValue);
}

/**
* Update the value of a token. Apply formatting based on DB schema.
*
Expand Down
55 changes: 47 additions & 8 deletions tests/phpunit/CRM/Core/BAO/ActionScheduleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ public function setUp() {
'contact_type' => 'Individual',
'email' => 'test-member@example.com',
'gender_id' => 'Female',
'first_name' => 'Churmondleia',
'last_name' => 'Ōtākou',
);
$this->fixtures['contact_birthdate'] = array(
'is_deceased' => 0,
Expand Down Expand Up @@ -593,6 +595,28 @@ public function setUp() {
'subject' => 'subject send reminder every unit after membership_end_date',
);

$customGroup = $this->callAPISuccess('CustomGroup', 'create', array(
'title' => ts('Test Contact Custom group'),
'name' => 'test_contact_cg',
'extends' => 'Contact',
'domain_id' => CRM_Core_Config::domainID(),
'is_active' => 1,
'collapse_adv_display' => 0,
'collapse_display' => 0,
));
$customField = $this->callAPISuccess('CustomField', 'create', array(
'label' => 'Test Text',
'data_type' => 'String',
'html_type' => 'Text',
'custom_group_id' => $customGroup['id'],
));
$this->fixtures['contact_custom_token'] = array(
'id' => $customField['id'],
'token' => sprintf('{contact.custom_%s}', $customField['id']),
'name' => sprintf('custom_%s', $customField['id']),
'value' => 'text ' . substr(sha1(rand()), 0, 7),
);

$this->_setUp();
}

Expand All @@ -603,7 +627,6 @@ public function setUp() {
*/
public function tearDown() {
parent::tearDown();

$this->mut->clearMessages();
$this->mut->stop();
unset($this->mut);
Expand All @@ -615,36 +638,48 @@ public function tearDown() {
'civicrm_event',
'civicrm_email',
));
$this->callAPISuccess('CustomField', 'delete', array('id' => $this->fixtures['contact_custom_token']['id']));
$this->callAPISuccess('CustomGroup', 'delete', array(
'id' => CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', 'test_contact_cg', 'id', 'name'),
));
$this->_tearDown();
}

public function mailerExamples() {
$cases = array();

$manyTokensTmpl = implode(';;', array(
// Some tokens - short as subject has 128char limit in DB.
$someTokensTmpl = implode(';;', array(
'{contact.display_name}', // basic contact token
'{contact.gender}', // funny legacy contact token
'{contact.gender_id}', // funny legacy contact token
'{domain.name}', // domain token
'{activity.activity_type}', // action-scheduler token
));
// Further tokens can be tested in the body text/html.
$manyTokensTmpl = implode(';;', array(
$someTokensTmpl,
'{contact.email_greeting}',
$this->fixture['contact_custom_token']['token'],
));
// Note: The behavior of domain-tokens on a scheduled reminder is undefined. All we
// can really do is check that it has something.
$manyTokensExpected = 'test-member@example.com;;Female;;Female;;[a-zA-Z0-9 ]+;;Phone Call';
$someTokensExpected = 'Churmondleia Ōtākou;;Female;;Female;;[a-zA-Z0-9 ]+;;Phone Call';
$manyTokensExpected = sprintf('%s;;Dear Churmondleia;;%s', $someTokensExpected, $this->fixture['contact_custom_token']['value']);

// In this example, we use a lot of tokens cutting across multiple components..
// In this example, we use a lot of tokens cutting across multiple components.
$cases[0] = array(
// Schedule definition.
array(
'subject' => "subj $manyTokensTmpl",
'subject' => "subj $someTokensTmpl",
'body_html' => "html $manyTokensTmpl",
'body_text' => "text $manyTokensTmpl",
),
// Assertions (regex).
array(
'from_name' => "/^FIXME\$/",
'from_email' => "/^info@EXAMPLE.ORG\$/",
'subject' => "/^subj $manyTokensExpected\$/",
'subject' => "/^subj $someTokensExpected\$/",
'body_html' => "/^html $manyTokensExpected\$/",
'body_text' => "/^text $manyTokensExpected\$/",
),
Expand Down Expand Up @@ -717,7 +752,12 @@ public function testMailer($schedule, $patterns) {

$activity = $this->createTestObject('CRM_Activity_DAO_Activity', $this->fixtures['phonecall']);
$this->assertTrue(is_numeric($activity->id));
$contact = $this->callAPISuccess('contact', 'create', $this->fixtures['contact']);
$contact = $this->callAPISuccess('contact', 'create', array_merge(
$this->fixtures['contact'],
array(
$this->fixtures['contact_custom_token']['name'] => $this->fixtures['contact_custom_token']['value'],
)
));
$activity->save();

$source['contact_id'] = $contact['id'];
Expand Down Expand Up @@ -755,7 +795,6 @@ public function testMailer($schedule, $patterns) {
}
}
$this->mut->clearMessages();

}

public function testActivityDateTimeMatchNonRepeatableSchedule() {
Expand Down