*/
public static function getSubscribedEvents() {
return [
- 'civi.token.eval' => 'onEvaluate',
+ 'civi.token.eval' => [
+ ['setupSmartyAliases', 1000],
+ ['onEvaluate'],
+ ],
'civi.token.render' => 'onRender',
];
}
+ /**
+ * Interpret the variable `$context['smartyTokenAlias']` (e.g. `mySmartyField' => `tkn_entity.tkn_field`).
+ *
+ * We need to ensure that any tokens like `{tkn_entity.tkn_field}` are hydrated, so
+ * we pretend that they are in use.
+ *
+ * @param \Civi\Token\Event\TokenValueEvent $e
+ */
+ public function setupSmartyAliases(TokenValueEvent $e) {
+ $aliasedTokens = [];
+ foreach ($e->getRows() as $row) {
+ $aliasedTokens = array_unique(array_merge($aliasedTokens,
+ array_values($row->context['smartyTokenAlias'] ?? [])));
+ }
+
+ $fakeMessage = implode('', array_map(function ($f) {
+ return '{' . $f . '}';
+ }, $aliasedTokens));
+
+ $proc = $e->getTokenProcessor();
+ $proc->addMessage('TokenCompatSubscriber.aliases', $fakeMessage, 'text/plain');
+ }
+
/**
* Load token data.
*
}
if ($useSmarty) {
- $e->string = \CRM_Utils_String::parseOneOffStringThroughSmarty($e->string);
+ $smartyVars = [];
+ foreach ($e->context['smartyTokenAlias'] ?? [] as $smartyName => $tokenName) {
+ // Note: $e->row->tokens resolves event-based tokens (eg CRM_*_Tokens). But if the target token relies on the
+ // above bits (replaceGreetingTokens=>replaceContactTokens=>replaceHookTokens) then this lookup isn't sufficient.
+ $smartyVars[$smartyName] = \CRM_Utils_Array::pathGet($e->row->tokens, explode('.', $tokenName));
+ }
+ \CRM_Core_Smarty::singleton()->pushScope($smartyVars);
+ try {
+ $e->string = \CRM_Utils_String::parseOneOffStringThroughSmarty($e->string);
+ }
+ finally {
+ \CRM_Core_Smarty::singleton()->popScope();
+ }
}
}
*
* - controller: string, the class which is managing the mail-merge.
* - smarty: bool, whether to enable smarty support.
+ * - smartyTokenAlias: array, Define Smarty variables that are populated
+ * based on token-content. Ex: ['theInvoiceId' => 'contribution.invoice_id']
* - contactId: int, the main person/org discussed in the message.
* - contact: array, the main person/org discussed in the message.
* (Optional for performance tweaking; if omitted, will load
$this->assertEquals(2, $loops);
}
+ /**
+ * This defines a compatibility mechanism wherein an old Smarty expression can
+ * be evaluated based on a newer token expression.
+ *
+ * Ex: $tokenContext['oldSmartyVar'] = 'new_entity.new_field';
+ */
+ public function testSmartyTokenAlias_Contribution() {
+ $first = $this->contributionCreate(['contact_id' => $this->individualCreate(), 'receive_date' => '2010-01-01', 'invoice_id' => 100, 'trxn_id' => 1000]);
+ $second = $this->contributionCreate(['contact_id' => $this->individualCreate(), 'receive_date' => '2011-02-02', 'invoice_id' => 200, 'trxn_id' => 1]);
+ $this->dispatcher->addSubscriber(new TokenCompatSubscriber());
+ $this->dispatcher->addSubscriber(new \CRM_Contribute_Tokens());
+
+ $p = new TokenProcessor($this->dispatcher, [
+ 'controller' => __CLASS__,
+ 'schema' => ['contributionId'],
+ 'smarty' => TRUE,
+ 'smartyTokenAlias' => [
+ 'theInvoiceId' => 'contribution.invoice_id',
+ ],
+ ]);
+ $p->addMessage('example', 'Invoice #{$theInvoiceId}!', 'text/plain');
+ $p->addRow(['contributionId' => $first]);
+ $p->addRow(['contributionId' => $second]);
+ $p->evaluate();
+
+ $outputs = [];
+ foreach ($p->getRows() as $row) {
+ $outputs[] = $row->render('example');
+ }
+ $this->assertEquals('Invoice #100!', $outputs[0]);
+ $this->assertEquals('Invoice #200!', $outputs[1]);
+ }
+
+ ///**
+ // * This defines a compatibility mechanism wherein an old Smarty expression can
+ // * be evaluated based on a newer token expression.
+ // *
+ // * The following example doesn't work because the handling of greeting+contact
+ // * tokens still use a special override (TokenCompatSubscriber::onRender).
+ // *
+ // * Ex: $tokenContext['oldSmartyVar'] = 'new_entity.new_field';
+ // */
+ // public function testSmartyTokenAlias_Contact() {
+ // $alice = $this->individualCreate(['first_name' => 'Alice']);
+ // $bob = $this->individualCreate(['first_name' => 'Bob']);
+ // $this->dispatcher->addSubscriber(new TokenCompatSubscriber());
+ //
+ // $p = new TokenProcessor($this->dispatcher, [
+ // 'controller' => __CLASS__,
+ // 'schema' => ['contactId'],
+ // 'smarty' => TRUE,
+ // 'smartyTokenAlias' => [
+ // 'myFirstName' => 'contact.first_name',
+ // ],
+ // ]);
+ // $p->addMessage('example', 'Hello {$myFirstName}!', 'text/plain');
+ // $p->addRow(['contactId' => $alice]);
+ // $p->addRow(['contactId' => $bob]);
+ // $p->evaluate();
+ //
+ // $outputs = [];
+ // foreach ($p->getRows() as $row) {
+ // $outputs[] = $row->render('example');
+ // }
+ // $this->assertEquals('Hello Alice!', $outputs[0]);
+ // $this->assertEquals('Hello Bob!', $outputs[1]);
+ // }
+
}