-
-
Notifications
You must be signed in to change notification settings - Fork 825
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9563 from totten/master-19690-tokens
CRM-19690 - CRM_Mailing_Tokens - Add TokenProcessor support
- Loading branch information
Showing
5 changed files
with
344 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<?php | ||
|
||
/* | ||
+--------------------------------------------------------------------+ | ||
| CiviCRM version 4.7 | | ||
+--------------------------------------------------------------------+ | ||
| Copyright CiviCRM LLC (c) 2004-2016 | | ||
+--------------------------------------------------------------------+ | ||
| This file is a part of CiviCRM. | | ||
| | | ||
| CiviCRM is free software; you can copy, modify, and distribute it | | ||
| under the terms of the GNU Affero General Public License | | ||
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | ||
| | | ||
| CiviCRM is distributed in the hope that it will be useful, but | | ||
| WITHOUT ANY WARRANTY; without even the implied warranty of | | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | ||
| See the GNU Affero General Public License for more details. | | ||
| | | ||
| You should have received a copy of the GNU Affero General Public | | ||
| License and the CiviCRM Licensing Exception along | | ||
| with this program; if not, contact CiviCRM LLC | | ||
| at info[AT]civicrm[DOT]org. If you have questions about the | | ||
| GNU Affero General Public License or the licensing of CiviCRM, | | ||
| see the CiviCRM license FAQ at http://civicrm.org/licensing | | ||
+--------------------------------------------------------------------+ | ||
*/ | ||
|
||
/** | ||
* Class CRM_Mailing_ActionTokens | ||
* | ||
* Generate "action.*" tokens for mailings. | ||
* | ||
* To activate these tokens, the TokenProcessor context must specify: | ||
* "mailingJobId" (int) | ||
* "mailingActionTarget" (array) with keys: | ||
* 'id' => int, event queue ID | ||
* 'hash' => string, event queue hash code | ||
* 'contact_id' => int, contact_id, | ||
* 'email' => string, email | ||
* 'phone' => string, phone | ||
*/ | ||
class CRM_Mailing_ActionTokens extends \Civi\Token\AbstractTokenSubscriber { | ||
|
||
/** | ||
* Class constructor. | ||
*/ | ||
public function __construct() { | ||
// TODO: Think about supporting dynamic tokens like "{action.subscribe.\d+}" | ||
parent::__construct('action', array( | ||
'subscribeUrl' => ts('Subscribe URL (Action)'), | ||
'forward' => ts('Forward URL (Action)'), | ||
'optOut' => ts('Opt-Out (Action)'), | ||
'optOutUrl' => ts('Opt-Out URL (Action)'), | ||
'reply' => ts('Reply (Action)'), | ||
'unsubscribe' => ts('Unsubscribe (Action)'), | ||
'unsubscribeUrl' => ts('Unsubscribe URL (Action)'), | ||
'resubscribe' => ts('Resubscribe (Action)'), | ||
'resubscribeUrl' => ts('Resubscribe URL (Action)'), | ||
'eventQueueId' => ts('Event Queue ID'), | ||
)); | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function evaluateToken( | ||
\Civi\Token\TokenRow $row, | ||
$entity, | ||
$field, | ||
$prefetch = NULL | ||
) { | ||
// Most CiviMail action tokens were implemented via getActionTokenReplacement(). | ||
// However, {action.subscribeUrl} has a second implementation via | ||
// replaceSubscribeInviteTokens(). The two appear mostly the same. | ||
// We use getActionTokenReplacement() since it's more consistent. However, | ||
// this doesn't provide the dynamic/parameterized tokens of | ||
// replaceSubscribeInviteTokens(). | ||
|
||
if (empty($row->context['mailingJobId']) || empty($row->context['mailingActionTarget']['hash'])) { | ||
throw new \CRM_Core_Exception("Error: Cannot use action tokens unless context defines mailingJobId and mailingActionTarget."); | ||
} | ||
|
||
if ($field === 'eventQueueId') { | ||
$row->format('text/plain')->tokens($entity, $field, $row->context['mailingActionTarget']['id']); | ||
return; | ||
} | ||
|
||
list($verp, $urls) = CRM_Mailing_BAO_Mailing::getVerpAndUrls( | ||
$row->context['mailingJobId'], | ||
$row->context['mailingActionTarget']['id'], | ||
$row->context['mailingActionTarget']['hash'], | ||
// Note: Behavior is already undefined for SMS/'phone' mailings... | ||
$row->context['mailingActionTarget']['email'] | ||
); | ||
|
||
$row->format('text/plain')->tokens($entity, $field, | ||
CRM_Utils_Token::getActionTokenReplacement( | ||
$field, $verp, $urls, FALSE)); | ||
$row->format('text/html')->tokens($entity, $field, | ||
CRM_Utils_Token::getActionTokenReplacement( | ||
$field, $verp, $urls, TRUE)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<?php | ||
|
||
/* | ||
+--------------------------------------------------------------------+ | ||
| CiviCRM version 4.7 | | ||
+--------------------------------------------------------------------+ | ||
| Copyright CiviCRM LLC (c) 2004-2016 | | ||
+--------------------------------------------------------------------+ | ||
| This file is a part of CiviCRM. | | ||
| | | ||
| CiviCRM is free software; you can copy, modify, and distribute it | | ||
| under the terms of the GNU Affero General Public License | | ||
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | ||
| | | ||
| CiviCRM is distributed in the hope that it will be useful, but | | ||
| WITHOUT ANY WARRANTY; without even the implied warranty of | | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | ||
| See the GNU Affero General Public License for more details. | | ||
| | | ||
| You should have received a copy of the GNU Affero General Public | | ||
| License and the CiviCRM Licensing Exception along | | ||
| with this program; if not, contact CiviCRM LLC | | ||
| at info[AT]civicrm[DOT]org. If you have questions about the | | ||
| GNU Affero General Public License or the licensing of CiviCRM, | | ||
| see the CiviCRM license FAQ at http://civicrm.org/licensing | | ||
+--------------------------------------------------------------------+ | ||
*/ | ||
|
||
/** | ||
* Class CRM_Mailing_Tokens | ||
* | ||
* Generate "mailing.*" tokens. | ||
* | ||
* To activate these tokens, the TokenProcessor context must specify either | ||
* "mailingId" (int) or "mailing" (CRM_Mailing_BAO_Mailing). | ||
*/ | ||
class CRM_Mailing_Tokens extends \Civi\Token\AbstractTokenSubscriber { | ||
|
||
/** | ||
* Class constructor. | ||
*/ | ||
public function __construct() { | ||
parent::__construct('mailing', array( | ||
'id' => ts('Mailing ID'), | ||
'name' => ts('Mailing Name'), | ||
'group' => ts('Mailing Group(s)'), | ||
'subject' => ts('Mailing Subject'), | ||
'viewUrl' => ts('Mailing URL (View)'), | ||
'editUrl' => ts('Mailing URL (Edit)'), | ||
'scheduleUrl' => ts('Mailing URL (Schedule)'), | ||
'html' => ts('Mailing HTML'), | ||
'approvalStatus' => ts('Mailing Approval Status'), | ||
'approvalNote' => ts('Mailing Approval Note'), | ||
'approveUrl' => ts('Mailing Approval URL'), | ||
'creator' => ts('Mailing Creator (Name)'), | ||
'creatorEmail' => ts('Mailing Creator (Email)'), | ||
)); | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function checkActive(\Civi\Token\TokenProcessor $processor) { | ||
return !empty($processor->context['mailingId']) || !empty($processor->context['mailing']); | ||
} | ||
|
||
public function prefetch(\Civi\Token\Event\TokenValueEvent $e) { | ||
$processor = $e->getTokenProcessor(); | ||
$mailing = isset($processor->context['mailing']) | ||
? $processor->context['mailing'] | ||
: CRM_Mailing_BAO_Mailing::findById($processor->context['mailingId']); | ||
|
||
return array( | ||
'mailing' => $mailing, | ||
); | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefetch = NULL) { | ||
$row->format('text/plain')->tokens($entity, $field, | ||
(string) CRM_Utils_Token::getMailingTokenReplacement($field, $prefetch['mailing'])); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
<?php | ||
|
||
/** | ||
* @group headless | ||
*/ | ||
class CRM_Mailing_TokensTest extends \CiviUnitTestCase { | ||
protected function setUp() { | ||
$this->useTransaction(); | ||
parent::setUp(); | ||
$this->callAPISuccess('mail_settings', 'get', | ||
array('api.mail_settings.create' => array('domain' => 'chaos.org'))); | ||
} | ||
|
||
public function getExampleTokens() { | ||
$cases = array(); | ||
|
||
$cases[] = array('text/plain', 'The {mailing.id}!', ';The [0-9]+!;'); | ||
$cases[] = array('text/plain', 'The {mailing.name}!', ';The Example Name!;'); | ||
$cases[] = array('text/plain', 'The {mailing.editUrl}!', ';The http.*civicrm/mailing/send.*!;'); | ||
$cases[] = array('text/plain', 'To subscribe: {action.subscribeUrl}!', ';To subscribe: http.*civicrm/mailing/subscribe.*!;'); | ||
$cases[] = array('text/plain', 'To optout: {action.optOutUrl}!', ';To optout: http.*civicrm/mailing/optout.*!;'); | ||
$cases[] = array('text/plain', 'To unsubscribe: {action.unsubscribe}!', ';To unsubscribe: u\.123\.456\.abcd1234@chaos.org!;'); | ||
|
||
// TODO: Think about supporting dynamic tokens like "{action.subscribe.\d+}" | ||
|
||
return $cases; | ||
} | ||
|
||
/** | ||
* Check that mailing-tokens are generated (given a mailing_id as input). | ||
* | ||
* @param string $inputTemplateFormat | ||
* Ex: 'text/plain' or 'text/html' | ||
* @param string $inputTemplate | ||
* Ex: 'Hello, {contact.first_name}'. | ||
* @param string $expectRegex | ||
* @dataProvider getExampleTokens | ||
*/ | ||
public function testTokensWithMailingId($inputTemplateFormat, $inputTemplate, $expectRegex) { | ||
$mailing = CRM_Core_DAO::createTestObject('CRM_Mailing_DAO_Mailing', array( | ||
'name' => 'Example Name', | ||
)); | ||
$contact = CRM_Core_DAO::createTestObject('CRM_Contact_DAO_Contact'); | ||
|
||
$p = new \Civi\Token\TokenProcessor(Civi::service('dispatcher'), array( | ||
'mailingId' => $mailing->id, | ||
)); | ||
$p->addMessage('example', $inputTemplate, $inputTemplateFormat); | ||
$p->addRow()->context(array( | ||
'contactId' => $contact->id, | ||
'mailingJobId' => 123, | ||
'mailingActionTarget' => array( | ||
'id' => 456, | ||
'hash' => 'abcd1234', | ||
'email' => 'someone@example.com', | ||
), | ||
)); | ||
$p->evaluate(); | ||
$count = 0; | ||
foreach ($p->getRows() as $row) { | ||
$this->assertRegExp($expectRegex, $row->render('example')); | ||
$count++; | ||
} | ||
$this->assertEquals(1, $count); | ||
} | ||
|
||
/** | ||
* Check that mailing-tokens are generated (given a mailing DAO as input). | ||
*/ | ||
public function testTokensWithMailingObject() { | ||
// We only need one case to see that the mailing-object works as | ||
// an alternative to the mailing-id. | ||
$inputTemplateFormat = 'text/plain'; | ||
$inputTemplate = 'To optout: {action.optOutUrl}!'; | ||
$expectRegex = ';To optout: http.*civicrm/mailing/optout.*!;'; | ||
|
||
$mailing = CRM_Core_DAO::createTestObject('CRM_Mailing_DAO_Mailing', array( | ||
'name' => 'Example Name', | ||
)); | ||
$contact = CRM_Core_DAO::createTestObject('CRM_Contact_DAO_Contact'); | ||
|
||
$p = new \Civi\Token\TokenProcessor(Civi::service('dispatcher'), array( | ||
'mailing' => $mailing, | ||
)); | ||
$p->addMessage('example', $inputTemplate, $inputTemplateFormat); | ||
$p->addRow()->context(array( | ||
'contactId' => $contact->id, | ||
'mailingJobId' => 123, | ||
'mailingActionTarget' => array( | ||
'id' => 456, | ||
'hash' => 'abcd1234', | ||
'email' => 'someone@example.com', | ||
), | ||
)); | ||
$p->evaluate(); | ||
$count = 0; | ||
foreach ($p->getRows() as $row) { | ||
$this->assertRegExp($expectRegex, $row->render('example')); | ||
$count++; | ||
} | ||
$this->assertEquals(1, $count); | ||
} | ||
|
||
/** | ||
* Check the behavior in the erroneous situation where someone uses | ||
* a mailing-related token without providing a mailing ID. | ||
*/ | ||
public function testTokensWithoutMailing() { | ||
// We only need one case to see that the mailing-object works as | ||
// an alternative to the mailing-id. | ||
$inputTemplateFormat = 'text/plain'; | ||
$inputTemplate = 'To optout: {action.optOutUrl}!'; | ||
|
||
$mailing = CRM_Core_DAO::createTestObject('CRM_Mailing_DAO_Mailing', array( | ||
'name' => 'Example Name', | ||
)); | ||
$contact = CRM_Core_DAO::createTestObject('CRM_Contact_DAO_Contact'); | ||
|
||
$p = new \Civi\Token\TokenProcessor(Civi::service('dispatcher'), array( | ||
'mailing' => $mailing, | ||
)); | ||
$p->addMessage('example', $inputTemplate, $inputTemplateFormat); | ||
$p->addRow()->context(array( | ||
'contactId' => $contact->id, | ||
)); | ||
try { | ||
$p->evaluate(); | ||
$this->fail('TokenProcessor::evaluate() should have thrown an exception'); | ||
} | ||
catch (CRM_Core_Exception $e) { | ||
$this->assertRegExp(';Cannot use action tokens unless context defines mailingJobId and mailingActionTarget;', $e->getMessage()); | ||
} | ||
} | ||
|
||
} |