CiviMail - Restore support for previewing action-tokens via TokenProcessor/Flexmailer
authorTim Otten <totten@civicrm.org>
Mon, 29 Apr 2019 20:35:48 +0000 (13:35 -0700)
committerTim Otten <totten@civicrm.org>
Tue, 30 Apr 2019 00:00:27 +0000 (17:00 -0700)
Overview
--------

When using `TokenProcessor` to generate a mailing (e.g.  as with Flexmailer/Mosaico), the action-tokens (e.g.
`{action.optOutUrl}`) are generated via `CRM_Mailing_ActionTokens`.  To properly generate them,
`CRM_Mailing_ActionTokens` relies on certain information (e.g.  mailing/job ID).  However, that information is no
longer available when performing a "Preview" -- leading to misbehavior in previews.  This patch allows Flexmailer to
restore parity for previewing those tokens.

Before (Pre-5.6)
----------------

* When a user begins composing a mailing, CiviMail creates a draft mailing with a concrete ID (e.g.  `mailing #123`).
* To preview the mailing, the UI calls `Mailing.preview` API with the ID of the mailing.
* Flexmailer/Mosaico generates the preview by calling `TokenProcessor` and therefore `CRM_Mailing_ActionTokens`.
* `CRM_Mailing_ActionTokens` has strictness checks. These pass because the ID is available.

Before (5.6-5.12)
----------------

As a performance enhancement, CiviCRM 5.6 (PR #12509; [dev/mail#20](https://lab.civicrm.org/dev/mail/issues/20)) revised
the signature for `Mailing.preview` API to allow previews *without* having a specific mailing record/job/ID. Consequently:

* When a user begins composing a mailing, CiviMail creates a draft mailing with a concrete ID (e.g.  `mailing #123`).
* To preview the mailing, the UI calls `Mailing.preview` API ~~with~~ **without** the ID of the mailing.
* Flexmailer/Mosaico generates the preview by calling `TokenProcessor` and therefore `CRM_Mailing_ActionTokens`.
* `CRM_Mailing_ActionTokens` has strictness checks. These ~~pass~~ **fail** because the ID is ~~available~~ **unavailable**.

After
----------------

* When a user begins composing a mailing, CiviMail creates a draft mailing with a concrete ID (e.g.  `mailing #123`).
* To preview the mailing, the UI calls `Mailing.preview` API ~~with~~ **without** the ID of the mailing.
* Flexmailer/Mosaico generates the preview by calling `TokenProcessor` and therefore `CRM_Mailing_ActionTokens`.
* `CRM_Mailing_ActionTokens` has ~~strictness~~ **less strict** checks. These **pass** because the `context[schema]` hints that
  a mailing ID *will be available* when needed.

CRM/Mailing/ActionTokens.php

index 85e893ca4dfd0c24ff662bdde2d840d5f15645db..8e609c0d1478431e841f991e5014e4cb979ca7cc 100644 (file)
@@ -65,7 +65,8 @@ class CRM_Mailing_ActionTokens extends \Civi\Token\AbstractTokenSubscriber {
    * @inheritDoc
    */
   public function checkActive(\Civi\Token\TokenProcessor $processor) {
-    return !empty($processor->context['mailingId']) || !empty($processor->context['mailing']);
+    return !empty($processor->context['mailingId']) || !empty($processor->context['mailing'])
+      || in_array('mailingId', $processor->context['schema']) || in_array('mailing', $processor->context['schema']);
   }
 
   /**
@@ -85,7 +86,9 @@ class CRM_Mailing_ActionTokens extends \Civi\Token\AbstractTokenSubscriber {
     // 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.");
+      // Strictly speaking, it doesn't make much sense to generate action-tokens when there's no job ID, but traditional CiviMail
+      // does this in v5.6+ for "Preview" functionality. Relaxing this strictness check ensures parity between newer+older styles.
+      // throw new \CRM_Core_Exception("Error: Cannot use action tokens unless context defines mailingJobId and mailingActionTarget.");
     }
 
     if ($field === 'eventQueueId') {