--- /dev/null
+<?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'),
+ ));
+ }
+
+ /**
+ * Evaluate the content of a single token.
+ *
+ * @param \Civi\Token\TokenRow $row
+ * The record for which we want token values.
+ * @param string $entity
+ * @param string $field
+ * The name of the token field.
+ * @param mixed $prefetch
+ * Any data that was returned by the prefetch().
+ *
+ * @return mixed
+ * @throws \CRM_Core_Exception
+ */
+ 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));
+ }
+
+ protected function getTrackOpenUrl(\Civi\Token\TokenRow $row) {
+ $config = CRM_Core_Config::singleton();
+ return $config->userFrameworkResourceURL . "extern/open.php?q=" . $row->context['mailingActionTarget']['id'];
+ }
+
+}
--- /dev/null
+<?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)'),
+ ));
+ }
+
+ /**
+ * Check something about being active.
+ *
+ * @param \Civi\Token\TokenProcessor $processor
+ *
+ * @return bool
+ */
+ 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,
+ );
+ }
+
+ /**
+ * Evaluate the content of a single token.
+ *
+ * @param \Civi\Token\TokenRow $row
+ * The record for which we want token values.
+ * @param string $entity
+ * @param string $field
+ * The name of the token field.
+ * @param mixed $prefetch
+ * Any data that was returned by the prefetch().
+ *
+ * @return mixed
+ */
+ 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']));
+ }
+
+}
'Civi\Token\TokenCompatSubscriber',
array()
))->addTag('kernel.event_subscriber');
+ $container->setDefinition("crm_mailing_action_tokens", new Definition(
+ "CRM_Mailing_ActionTokens",
+ array()
+ ))->addTag('kernel.event_subscriber');
- foreach (array('Activity', 'Contribute', 'Event', 'Member') as $comp) {
+ foreach (array('Activity', 'Contribute', 'Event', 'Mailing', 'Member') as $comp) {
$container->setDefinition("crm_" . strtolower($comp) . "_tokens", new Definition(
"CRM_{$comp}_Tokens",
array()
--- /dev/null
+<?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);
+ }
+
+}