Commit 547a6808 authored by bgm's avatar bgm Committed by Aegir user

Remove flexmailer, it's in the platform

parent 6e31c513
# Change Log
## v0.2-alpha1
* Override core's `Mailing.preview` API to support rendering via
Flexmailer events.
* (BC Break) In the class `DefaultComposer`, change the signature for
`createMessageTemplates()` and `applyClickTracking()` to provide full
access to the event context (`$e`).
This diff is collapsed.
# org.civicrm.flexmailer
FlexMailer (`org.civicrm.flexmailer`) is an email delivery engine for CiviCRM v4.7+. It replaces the internal guts of CiviMail. It is a
drop-in replacement which enables *other* extensions to provide richer email features.
* [Introduction](docs/index.md)
* [Installation](docs/install.md)
* [Development](docs/develop/index.md)
* [CheckSendableEvent](docs/develop/CheckSendableEvent.md)
* [WalkBatchesEvent](docs/develop/WalkBatchesEvent.md)
* [ComposeBatchEvent](docs/develop/ComposeBatchEvent.md)
* [SendBatchEvent](docs/develop/SendBatchEvent.md)
The `CheckSendableEvent` (`EVENT_CHECK_SENDABLE`) determines whether a draft mailing is fully specified for delivery.
For example, some jurisdictions require that email blasts provide contact
information for the organization (eg street address) and an opt-out link.
By default, the check-sendable event will verify that this information is
provided through a mail-merge token (eg `{action.unsubscribeUrl}`).
The token validation logic depends on how the message has been encoded. If
you provide a new template language, you can implement new enforcement logic,
e.g.
```php
<?php
function mustache_civicrm_container($container) {
$container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__));
$container->findDefinition('dispatcher')->addMethodCall('addListener',
array(\Civi\FlexMailer\Validator::EVENT_CHECK_SENDABLE, '_mustache_check_sendable')
);
}
function _mustache_check_sendable(\Civi\FlexMailer\Event\CheckSendableEvent $e) {
if ($e->getMailing()->template_type !== 'mustache') return;
if (strpos('{{unsubscribeUrl}}', $e->getMailing()->body_html) === FALSE) {
$e->setError('body_html:unsubscribeUrl', E::ts('Please include the token {{unsubscribeUrl}}'));
}
}
```
The `ComposeBatchEvent` builds the email messages. Each message is represented as a `FlexMailerTask` with a list of `MailParams`.
Some listeners are "under the hood" -- they define less visible parts of the message, e.g.
* `BasicHeaders` defines `Message-Id`, `Precedence`, `From`, `Reply-To`, and others.
* `BounceTracker` defines various headers for bounce-tracking.
* `OpenTracker` appends an HTML tracking code to any HTML messages.
The heavy-lifting of composing the message content is also handled by a listener, such as
`DefaultComposer`. `DefaultComposer` replicates traditional CiviMail functionality:
* Reads email content from `$mailing->body_text` and `$mailing->body_html`.
* Interprets tokens like `{contact.display_name}` and `{mailing.viewUrl}`.
* Loads data in batches.
* Post-processes the message with Smarty (if `CIVICRM_SMARTY` is enabled).
The traditional CiviMail semantics have some problems -- e.g. the Smarty post-processing is incompatible with Smarty's
template cache, and it is difficult to securely post-process the message with Smarty. However, changing the behavior
would break existing templates.
A major goal of FlexMailer is to facilitate a migration toward different template semantics. For example, an
extension might (naively) implement support for Mustache templates using:
```php
<?php
function mustache_civicrm_container($container) {
$container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__));
$container->findDefinition('dispatcher')->addMethodCall('addListener',
array(\Civi\FlexMailer\FlexMailer::EVENT_COMPOSE, '_mustache_compose_batch')
);
}
function _mustache_compose_batch(\Civi\FlexMailer\Event\ComposeBatchEvent $event) {
if ($event->getMailing()->template_type !== 'mustache') return;
$m = new Mustache_Engine();
foreach ($event->getTasks() as $task) {
if ($task->hasContent()) continue;
$contact = civicrm_api3('Contact', 'getsingle', array(
'id' => $task->getContactId(),
));
$task->setMailParam('text', $m->render($event->getMailing()->body_text, $contact));
$task->setMailParam('html', $m->render($event->getMailing()->body_html, $contact));
}
}
```
This implementation is naive in a few ways -- it performs separate SQL queries for each recipient; it doesn't optimize
the template compilation; it has a very limited range of tokens; and it doesn't handle click-through tracking. For
more ideas about these issues, review `DefaultComposer`.
> FIXME: Core's `TokenProcessor` is useful for batch-loading token data.
> However, you currently have to use `addMessage()` and `render()` to kick it
> off -- but those are based on CiviMail template notation. We should provide
> another function that doesn't depend on the template notation -- so that
> other templates can leverage our token library.
> **Tip**: When you register a listener for `EVENT_COMPOSE`, note the weight.
> The default weight puts your listener in the middle of pipeline -- right
> before the `DefaultComposer`. However, you might want to position
> relative to other places -- e.g. `WEIGHT_PREPARE`, `WEIGHT_MAIN`,
> `WEIGHT_ALTER`, or `WEIGHT_END`.
The `SendBatchEvent` (`EVENT_SEND`) takes a batch of recipients and messages, and it delivers the messages. For example, suppose you wanted to
replace the built-in delivery mechanism with a batch-oriented web-service:
```php
<?php
function example_civicrm_container($container) {
$container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__));
$container->findDefinition('dispatcher')->addMethodCall('addListener',
array(\Civi\FlexMailer\FlexMailer::EVENT_SEND, '_example_send_batch')
);
}
function _example_send_batch(\Civi\FlexMailer\Event\SendBatchEvent $event) {
$event->stopPropagation(); // Disable standard delivery
$context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/vnd.php.serialize',
'content' => serialize($event->getTasks()),
),
));
return file_get_contents('https://example.org/batch-delivery', false, $context);
}
```
The `WalkBatchesEvent` examines the recipient list and pulls out a subset for whom you want to send email. This is useful if you need strategies for
chunking-out deliveries.
The basic formula for defining your own batch logic is:
```php
<?php
function example_civicrm_container($container) {
$container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__));
$container->findDefinition('dispatcher')->addMethodCall('addListener',
array(\Civi\FlexMailer\FlexMailer::EVENT_WALK, '_example_walk_batches')
);
}
function _example_walk_batches(\Civi\FlexMailer\Event\WalkBatchesEvent $event) {
$event->stopPropagation(); // Disable standard delivery
while (...) {
$tasks = array();
$task[] = new FlexMailerTask(...);
$task[] = new FlexMailerTask(...);
$task[] = new FlexMailerTask(...);
$event->visit($tasks);
}
}
```
## Unit tests
The [headless unit tests](https://docs.civicrm.org/dev/en/latest/testing/#headless) are based on `phpunit4` and `cv`. Simply run:
```
$ phpunit4
```
## Events
!!! tip "Symfony Events"
This documentation references the [Symfony EventDispatcher](http://symfony.com/components/EventDispatcher).
If this is unfamiliar, you can read [a general introduction to Symfony events](http://symfony.com/doc/2.7/components/event_dispatcher.html)
or [a specific introduction about CiviCRM and Symfony events](https://docs.civicrm.org/dev/en/latest/hooks/setup/symfony/).
FlexMailer is an *event* based delivery system. It defines a few events:
* [CheckSendableEvent](CheckSendableEvent.md): In this event, one examines a draft mailing to determine if it is complete enough to deliver.
* [WalkBatchesEvent](WalkBatchesEvent.md): In this event, one examines the recipient list and pulls out a subset for whom you want to send email.
* [ComposeBatchEvent](ComposeBatchEvent.md): In this event, one examines the mail content and the list of recipients -- then composes a batch of fully-formed email messages.
* [SendBatchEvent](SendBatchEvent.md): In this event, one takes a batch of fully-formed email messages and delivers the messages.
These events are not conceived in the same way as a typical *CiviCRM hook*; rather, they resemble *pipelines*. For each event, several listeners
have an opportunity to weigh-in, and the *order* of the listeners is important. As such, it helps to *inspect* the list of listeners. You can do
this with the CLI command, `cv`:
```
$ cv debug:event-dispatcher /flexmail/
[Event] civi.flexmailer.checkSendable
+-------+------------------------------------------------------------+
| Order | Callable |
+-------+------------------------------------------------------------+
| #1 | Civi\FlexMailer\Listener\Abdicator->onCheckSendable() |
| #2 | Civi\FlexMailer\Listener\RequiredFields->onCheckSendable() |
| #3 | Civi\FlexMailer\Listener\RequiredTokens->onCheckSendable() |
+-------+------------------------------------------------------------+
[Event] civi.flexmailer.walk
+-------+---------------------------------------------------+
| Order | Callable |
+-------+---------------------------------------------------+
| #1 | Civi\FlexMailer\Listener\DefaultBatcher->onWalk() |
+-------+---------------------------------------------------+
[Event] civi.flexmailer.compose
+-------+-------------------------------------------------------+
| Order | Callable |
+-------+-------------------------------------------------------+
| #1 | Civi\FlexMailer\Listener\BasicHeaders->onCompose() |
| #2 | Civi\FlexMailer\Listener\ToHeader->onCompose() |
| #3 | Civi\FlexMailer\Listener\BounceTracker->onCompose() |
| #4 | Civi\FlexMailer\Listener\DefaultComposer->onCompose() |
| #5 | Civi\FlexMailer\Listener\Attachments->onCompose() |
| #6 | Civi\FlexMailer\Listener\OpenTracker->onCompose() |
| #7 | Civi\FlexMailer\Listener\HookAdapter->onCompose() |
+-------+-------------------------------------------------------+
[Event] civi.flexmailer.send
+-------+--------------------------------------------------+
| Order | Callable |
+-------+--------------------------------------------------+
| #1 | Civi\FlexMailer\Listener\DefaultSender->onSend() |
+-------+--------------------------------------------------+
```
The above listing shows the default set of listeners at time of writing. (Run the command yourself to see how they appear on your system.)
The default listeners behave in basically the same way as CiviMail's traditional BAO-based delivery system (respecting `mailerJobSize`,
`mailThrottleTime`, `mailing_backend`, `hook_civicrm_alterMailParams`, etal).
There are a few tricks for manipulating the pipeline:
* __Register new listeners__. Each event has its own documentation which describes how to do this.
* __Manage the priority__. When registering a listener, the `addListener()` function accepts a `$priority` integer. Use this to move up or down the pipeline.
!!! note "Priority vs Order"
When writing code, you will set the *priority* of a listener. The default is `0`, and the usual range is `2000` (first) to `-2000` (last).
<!-- The default listeners have priorities based on the constants `FlexMailer::WEIGHT_PREPARE` (1000), `FlexMailer::WEIGHT_MAIN` (0),
`FlexMailer::WEIGHT_ALTER` (-1000), and `FlexMailer::WEIGHT_END` (-2000). -->
At runtime, the `EventDispatcher` will take all the listeners and sort them by priority. This produces the *order*, which simply counts up (`1`, `2`, `3`, ...).
* __Alter a listener__. Most listeners are *services*, and you can manipulate options on these services. For example, suppose you wanted to replace the default bounce-tracking mechanism.
Here's a simple way to disable the default `BounceTracker`:
```php
<?php
\Civi::service('civi_flexmailer_bounce_tracker')->setActive(FALSE);
```
Of course, this change needs to be made before the listener runs. You might use a global hook (like `hook_civicrm_config`), or you might
have your own listener which disables `civi_flexmailer_bounce_tracker` and adds its own bounce-tracking.
Most FlexMailer services support `setActive()`, which enables you to completely replace them.
Additionally, some services have their own richer methods. In this example, we modify the list of required tokens:
```php
<?php
$tokens = \Civi::service('civi_flexmailer_required_tokens')
->getRequiredTokens();
unset($tokens['domain.address']);
\Civi::service('civi_flexmailer_required_tokens')
->setRequiredTokens($tokens);
```
## Services
Most features in FlexMailer are implemented by *services*, and you can override or manipulate these features if you understand the corresponding service.
For more detailed information about how to manipulate a service, consult its docblocks.
* Listener services (`CheckSendableEvent`)
* `civi_flexmailer_required_fields` (`RequiredFields.php`): Check for fields like "Subject" and "From".
* `civi_flexmailer_required_tokens` (`RequiredTokens.php`): Check for tokens like `{action.unsubscribeUrl}` (in `traditional` mailings).
* Listener services (`WalkBatchesEvent`)
* `civi_flexmailer_default_batcher` (`DefaultBatcher.php`): Split the recipient list into smaller batches (per CiviMail settings)
* Listener services (`ComposeBatchEvent`)
* `civi_flexmailer_basic_headers` (`BasicHeaders.php`): Add `From:`, `Reply-To:`, etc
* `civi_flexmailer_to_header` (`ToHeader.php`): Add `To:` header
* `civi_flexmailer_bounce_tracker` (`BounceTracker.php`): Add bounce-tracking codes
* `civi_flexmailer_default_composer` (`DefaultComposer.php`): Read the email template and evaluate any tokens (based on CiviMail tokens)
* `civi_flexmailer_attachments` (`Attachments.php`): Add attachments
* `civi_flexmailer_open_tracker` (`OpenTracker.php`): Add open-tracking codes
* `civi_flexmailer_hooks` (`HookAdapter.php`): Backward compatibility with `hook_civicrm_alterMailParams`
* Listener services (`SendBatchEvent`)
* `civi_flexmailer_default_sender` (`DefaultSender.php`): Send the batch using CiviCRM's default delivery service
* Other services
* `civi_flexmailer_html_click_tracker` (`HtmlClickTracker.php`): Add click-tracking codes (for HTML messages)
* `civi_flexmailer_text_click_tracker` (`TextClickTracker.php`): Add click-tracking codes (for plain-text messages)
* `civi_flexmailer_api_overrides` (`Services.php.php`): Alter the `Mailing` APIs
FlexMailer (`org.civicrm.flexmailer`) is an email delivery engine for CiviCRM v4.7+. It replaces the internal guts of CiviMail. It is a
drop-in replacement which enables *other* extensions to provide richer email features.
By default, FlexMailer supports the same user interfaces, delivery algorithms, and use-cases as CiviMail. After activating FlexMailer, an
administrator does not need to take any special actions.
The distinguishing improvement here is under-the-hood: it provides better APIs and events for extension-developers. For example,
other extensions might:
* Change the template language
* Manipulate tracking codes
* Rework the delivery mechanism
* Redefine the batching algorithm
To download the latest alpha or beta version:
```bash
$ cv dl --dev flexmailer
```
To download the latest, bleeding-edge code:
```bash
$ cv dl org.civicrm.flexmailer@https://github.com/civicrm/org.civicrm.flexmailer/archive/master.zip
```
To download the latest, bleeding-edge code from git:
```bash
$ cd $(cv path -x .)
$ git clone https://github.com/civicrm/org.civicrm.flexmailer.git
$ cv en flexmailer
```
This diff is collapsed.
<?php
/**
* Civi v4.6 does not provide all the API's we would need to define
* FlexMailer in an extension, but you can patch core to simulate them.
* These define()s tell core to enable any such hacks (if available).
*/
define('CIVICRM_FLEXMAILER_HACK_DELIVER', '\Civi\FlexMailer\FlexMailer::createAndRun');
define('CIVICRM_FLEXMAILER_HACK_SENDABLE', '\Civi\FlexMailer\Validator::createAndRun');
define('CIVICRM_FLEXMAILER_HACK_REQUIRED_TOKENS', 'call://civi_flexmailer_required_tokens/getRequiredTokens');
//define('CIVICRM_FLEXMAILER_HACK_SERVICES', '\Civi\FlexMailer\Services::registerServices');
//define('CIVICRM_FLEXMAILER_HACK_LISTENERS', '\Civi\FlexMailer\Services::registerListeners');
require_once 'flexmailer.civix.php';
/**
* Define an autoloader for FlexMailer.
*
* FlexMailer uses the namespace 'Civi\FlexMailer', but the
* autoloader in Civi v4.6 doesn't support this, so we provide
* our own autoloader.
*
* TODO: Whenever v4.6 dies, remove this file and define the
* autoloader in info.xml
*
* @link http://www.php-fig.org/psr/psr-4/examples/
*/
function _flexmailer_autoload($class) {
$prefix = 'Civi\\FlexMailer\\';
$base_dir = __DIR__ . '/src/';
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
$relative_class = substr($class, $len);
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
if (file_exists($file)) {
require $file;
}
}
spl_autoload_register('_flexmailer_autoload');
/**
* Implements hook_civicrm_config().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_config
*/
function flexmailer_civicrm_config(&$config) {
_flexmailer_civix_civicrm_config($config);
}
/**
* Implements hook_civicrm_xmlMenu().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_xmlMenu
*/
function flexmailer_civicrm_xmlMenu(&$files) {
_flexmailer_civix_civicrm_xmlMenu($files);
}
/**
* Implements hook_civicrm_install().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
*/
function flexmailer_civicrm_install() {
_flexmailer_civix_civicrm_install();
}
/**
* Implements hook_civicrm_postInstall().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall
*/
function flexmailer_civicrm_postInstall() {
_flexmailer_civix_civicrm_postInstall();
}
/**
* Implements hook_civicrm_uninstall().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
*/
function flexmailer_civicrm_uninstall() {
_flexmailer_civix_civicrm_uninstall();
}
/**
* Implements hook_civicrm_enable().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
*/
function flexmailer_civicrm_enable() {
_flexmailer_civix_civicrm_enable();
}
/**
* Implements hook_civicrm_disable().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
*/
function flexmailer_civicrm_disable() {
_flexmailer_civix_civicrm_disable();
}
/**
* Implements hook_civicrm_upgrade().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade
*/
function flexmailer_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
return _flexmailer_civix_civicrm_upgrade($op, $queue);
}
/**
* Implements hook_civicrm_managed().
*
* Generate a list of entities to create/deactivate/delete when this module
* is installed, disabled, uninstalled.
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_managed
*/
function flexmailer_civicrm_managed(&$entities) {
_flexmailer_civix_civicrm_managed($entities);
}
/**
* Implements hook_civicrm_caseTypes().
*
* Generate a list of case-types.
*
* Note: This hook only runs in CiviCRM 4.4+.
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_caseTypes
*/
function flexmailer_civicrm_caseTypes(&$caseTypes) {
_flexmailer_civix_civicrm_caseTypes($caseTypes);
}
/**
* Implements hook_civicrm_angularModules().
*
* Generate a list of Angular modules.
*
* Note: This hook only runs in CiviCRM 4.5+. It may
* use features only available in v4.6+.
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_caseTypes
*/
function flexmailer_civicrm_angularModules(&$angularModules) {
_flexmailer_civix_civicrm_angularModules($angularModules);
}
/**
* Implements hook_civicrm_alterSettingsFolders().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_alterSettingsFolders
*/
function flexmailer_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
_flexmailer_civix_civicrm_alterSettingsFolders($metaDataFolders);
}
/**
* Functions below this ship commented out. Uncomment as required.
*
/**
* Implements hook_civicrm_preProcess().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_preProcess
*
function flexmailer_civicrm_preProcess($formName, &$form) {
} // */
/**
* Implements hook_civicrm_navigationMenu().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_navigationMenu
*
function flexmailer_civicrm_navigationMenu(&$menu) {
_flexmailer_civix_insert_navigation_menu($menu, NULL, array(
'label' => ts('The Page', array('domain' => 'org.civicrm.flexmailer')),
'name' => 'the_page',
'url' => 'civicrm/the-page',
'permission' => 'access CiviReport,access CiviContribute',
'operator' => 'OR',
'separator' => 0,
));
_flexmailer_civix_navigationMenu($menu);
} // */
/**
* Implements hook_civicrm_container().
*/
function flexmailer_civicrm_container($container) {
if (version_compare(\CRM_Utils_System::version(), '4.7.0', '>=')) {
$container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__));
}
\Civi\FlexMailer\Services::registerServices($container);
}
<?xml version="1.0"?>
<extension key="org.civicrm.flexmailer" type="module">
<file>flexmailer</file>
<name>FlexMailer</name>
<description>Flexible APIs for email delivery</description>
<license>AGPL-3.0</license>
<maintainer>
<author>Tim Otten</author>
<email>totten@civicrm.org</email>
</maintainer>
<urls>
<url desc="Main Extension Page">https://github.com/civicrm/org.civicrm.flexmailer</url>
<url desc="Documentation">https://github.com/civicrm/org.civicrm.flexmailer</url>
<url desc="Support">http://civicrm.stackexchange.com/</url>
<url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
</urls>
<releaseDate>2017-03-15</releaseDate>
<version>0.2-alpha5</version>
<comments>
FlexMailer is an email delivery engine which replaces the internal guts
of CiviMail. It is a drop-in replacement which enables *other* extensions
to provide richer email features.
</comments>
<compatibility>
<ver>4.7</ver>
</compatibility>
<civix>
<namespace>CRM/Flexmailer</namespace>
</civix>
</extension>
site_name: FlexMailer
repo_url: https://github.com/civicrm/org.civicrm.flexmailer
theme: material
pages:
- Introduction: index.md
- Installation: install.md
- Development:
- Overview: develop/index.md
- CheckSendableEvent: develop/CheckSendableEvent.md
- WalkBatchesEvent: develop/WalkBatchesEvent.md
- ComposeBatchEvent: develop/ComposeBatchEvent.md
- SendBatchEvent: develop/SendBatchEvent.md
markdown_extensions:
- attr_list
- admonition
- def_list
- codehilite
- toc(permalink=true)
- pymdownx.superfences
- pymdownx.inlinehilite
- pymdownx.tilde
- pymdownx.betterem
- pymdownx.mark
<?xml version="1.0"?>
<phpunit backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" syntaxCheck="false" bootstrap="tests/phpunit/bootstrap.php">
<testsuites>
<testsuite name="My Test Suite">
<directory>./tests/phpunit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./</directory>
</whitelist>
</filter>
<listeners>
<listener class="Civi\Test\CiviTestListener">
<arguments/>
</listener>
</listeners>
</phpunit>
<?php
namespace Civi\FlexMailer\API;
use Civi\FlexMailer\FlexMailer;
use Civi\FlexMailer\FlexMailerTask;
use Civi\FlexMailer\Listener\Abdicator;
class MailingPreview {
/**
* Generate a preview of how a mailing would look.
*
* @param array $apiRequest
* - entity: string
* - action: string
* - params: array
* - id: int
* - contact_id: int
* @return array
* @throws \CRM_Core_Exception
*/
public static function preview($apiRequest) {
$params = $apiRequest['params'];
/** @var \CRM_Mailing_BAO_Mailing $mailing */
$mailing = \CRM_Mailing_BAO_Mailing::findById($params['id']);
if (!Abdicator::isFlexmailPreferred($mailing)) {
require_once 'api/v3/Mailing.php';
return civicrm_api3_mailing_preview($params);
}
$contactID = \CRM_Utils_Array::value('contact_id', $params,
\CRM_Core_Session::singleton()->get('userID'));