3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
11 namespace Civi\FlexMailer
;
13 use Civi\FlexMailer\Event\ComposeBatchEvent
;
14 use Civi\FlexMailer\Event\RunEvent
;
15 use Civi\FlexMailer\Event\SendBatchEvent
;
16 use Civi\FlexMailer\Event\WalkBatchesEvent
;
17 use Symfony\Component\EventDispatcher\EventDispatcherInterface
;
21 * @package Civi\FlexMailer
23 * The FlexMailer is a mail-blaster which supports batching and events.
24 * Specifically, there are five key events:
25 * - WalkBatchesEvent: Examine the recipient list and pull out a subset
26 * for whom you want to send email.
27 * - ComposeBatchEvent: Given a batch of recipients, prepare an email message
29 * - SendBatchEvent: Given a batch of recipients and their messages, send
31 * - RunEvent: Execute the main-loop (with all of the above steps).
33 * The events are based on Symfony's EventDispatcher. You may register event
34 * listeners using hook_civicrm_container, e.g.
36 * function mymod_civicrm_container(ContainerBuilder $container) {
38 * ->setDefinition('mymod_subscriber', new Definition('MymodSubscriber', array()))
39 * ->addTag('kernel.event_subscriber');
42 * FlexMailer includes default listeners for all of these events. They
43 * behaves in basically the same way as CiviMail's traditional BAO-based
44 * delivery system (respecting mailerJobSize, mailThrottleTime,
45 * mailing_backend, hook_civicrm_alterMailParams, etal). However, you
46 * can replace any of the major functions, e.g.
48 * - If you send large blasts across multiple servers, then you may
49 * prefer a different algorithm for splitting the recipient list.
50 * Listen for WalkBatchesEvent.
51 * - If you want to compose messages in a new way (e.g. a different
52 * templating language), then listen for ComposeBatchEvent.
53 * - If you want to deliver messages through a different medium
54 * (such as web-services or batched SMTP), listen for SendBatchEvent.
56 * In all cases, your function can listen to the event and then decide what
57 * to do. If your listener does the work required for the event, then
58 * you can disable the default listener by calling `$event->stopPropagation()`.
60 * @link http://symfony.com/doc/current/components/event_dispatcher.html
64 const WEIGHT_START
= 2000;
65 const WEIGHT_PREPARE
= 1000;
66 const WEIGHT_MAIN
= 0;
67 const WEIGHT_ALTER
= -1000;
68 const WEIGHT_END
= -2000;
70 const EVENT_RUN
= 'civi.flexmailer.run';
71 const EVENT_WALK
= 'civi.flexmailer.walk';
72 const EVENT_COMPOSE
= 'civi.flexmailer.compose';
73 const EVENT_SEND
= 'civi.flexmailer.send';
77 * Array(string $event => string $class).
79 public static function getEventTypes() {
81 self
::EVENT_RUN
=> 'Civi\\FlexMailer\\Event\\RunEvent',
82 self
::EVENT_WALK
=> 'Civi\\FlexMailer\\Event\\WalkBatchesEvent',
83 self
::EVENT_COMPOSE
=> 'Civi\\FlexMailer\\Event\\ComposeBatchEvent',
84 self
::EVENT_SEND
=> 'Civi\\FlexMailer\\Event\\SendBatchEvent',
90 * An array which must define options:
91 * - mailing: \CRM_Mailing_BAO_Mailing
92 * - job: \CRM_Mailing_BAO_MailingJob
93 * - attachments: array
96 * Additional options may be passed. To avoid naming conflicts, use prefixing.
101 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
106 * Create a new FlexMailer instance, using data available in the CiviMail runJobs().
108 * @param \CRM_Mailing_BAO_MailingJob $job
109 * @param object $deprecatedMessageMailer
110 * @param array $deprecatedTestParams
112 * TRUE if delivery completed.
114 public static function createAndRun($job, $deprecatedMessageMailer, $deprecatedTestParams) {
115 $flexMailer = new \Civi\FlexMailer\
FlexMailer(array(
116 'mailing' => \CRM_Mailing_BAO_Mailing
::findById($job->mailing_id
),
118 'attachments' => \CRM_Core_BAO_File
::getEntityFile('civicrm_mailing', $job->mailing_id
),
119 'deprecatedMessageMailer' => $deprecatedMessageMailer,
120 'deprecatedTestParams' => $deprecatedTestParams,
122 return $flexMailer->run();
126 * FlexMailer constructor.
127 * @param array $context
128 * An array which must define options:
129 * - mailing: \CRM_Mailing_BAO_Mailing
130 * - job: \CRM_Mailing_BAO_MailingJob
131 * - attachments: array
132 * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
134 public function __construct($context = array(), EventDispatcherInterface
$dispatcher = NULL) {
135 $this->context
= $context;
136 $this->dispatcher
= $dispatcher ?
$dispatcher : \Civi
::service('dispatcher');
141 * TRUE if delivery completed.
142 * @throws \CRM_Core_Exception
144 public function run() {
148 if (count($this->validate()) > 0) {
149 throw new \
CRM_Core_Exception("FlexMailer cannot execute: invalid context");
152 $run = $this->fireRun();
153 if ($run->isPropagationStopped()) {
154 return $run->getCompleted();
157 $walkBatches = $this->fireWalkBatches(function ($tasks) use ($flexMailer) {
158 $flexMailer->fireComposeBatch($tasks);
159 $sendBatch = $flexMailer->fireSendBatch($tasks);
160 return $sendBatch->getCompleted();
163 return $walkBatches->getCompleted();
168 * List of error messages
170 public function validate() {
172 if (empty($this->context
['mailing'])) {
173 $errors['mailing'] = 'Missing \"mailing\"';
175 if (empty($this->context
['job'])) {
176 $errors['job'] = 'Missing \"job\"';
182 * @return \Civi\FlexMailer\Event\RunEvent
184 public function fireRun() {
185 $event = new RunEvent($this->context
);
186 $this->dispatcher
->dispatch(self
::EVENT_RUN
, $event);
191 * @param callable $onVisitBatch
192 * @return \Civi\FlexMailer\Event\WalkBatchesEvent
194 public function fireWalkBatches($onVisitBatch) {
195 $event = new WalkBatchesEvent($this->context
, $onVisitBatch);
196 $this->dispatcher
->dispatch(self
::EVENT_WALK
, $event);
201 * @param array<FlexMailerTask> $tasks
202 * @return \Civi\FlexMailer\Event\ComposeBatchEvent
204 public function fireComposeBatch($tasks) {
205 // This isn't a great place for this, but it ensures consistent cleanup.
206 $mailing = $this->context
['mailing'];
207 if (property_exists($mailing, 'language') && $mailing->language
&& $mailing->language
!= 'en_US') {
208 $swapLang = \CRM_Utils_AutoClean
::swap('call://i18n/getLocale', 'call://i18n/setLocale', $mailing->language
);
211 $event = new ComposeBatchEvent($this->context
, $tasks);
212 $this->dispatcher
->dispatch(self
::EVENT_COMPOSE
, $event);
217 * @param array<FlexMailerTask> $tasks
218 * @return \Civi\FlexMailer\Event\SendBatchEvent
220 public function fireSendBatch($tasks) {
221 $event = new SendBatchEvent($this->context
, $tasks);
222 $this->dispatcher
->dispatch(self
::EVENT_SEND
, $event);