Merge pull request #22753 from eileenmcnaughton/mail
[civicrm-core.git] / api / v3 / Mailing.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * APIv3 functions for registering/processing mailing events.
15 *
16 * @package CiviCRM_APIv3
17 */
18
19 /**
20 * Handle a create event.
21 *
22 * @param array $params
23 *
24 * @return array
25 * API Success Array
26 * @throws \API_Exception
27 * @throws \Civi\API\Exception\UnauthorizedException
28 */
29 function civicrm_api3_mailing_create($params) {
30 if (isset($params['template_options']) && is_array($params['template_options'])) {
31 $params['template_options'] = ($params['template_options'] === []) ? '{}' : json_encode($params['template_options']);
32 }
33 $safeParams = $params;
34 $timestampCheck = TRUE;
35 if (!empty($params['id']) && !empty($params['modified_date'])) {
36 $timestampCheck = _civicrm_api3_compare_timestamps($safeParams['modified_date'], $safeParams['id'], 'Mailing');
37 unset($safeParams['modified_date']);
38 }
39 if (!$timestampCheck) {
40 throw new API_Exception("Mailing has not been saved, Content maybe out of date, please refresh the page and try again");
41 }
42
43 // FlexMailer is a refactoring of CiviMail which provides new hooks/APIs/docs. If the sysadmin has opted to enable it, then use that instead of CiviMail.
44 $safeParams['_evil_bao_validator_'] = \CRM_Utils_Constant::value('CIVICRM_FLEXMAILER_HACK_SENDABLE', 'CRM_Mailing_BAO_Mailing::checkSendable');
45 $result = _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $safeParams, 'Mailing');
46 return _civicrm_api3_mailing_get_formatResult($result);
47 }
48
49 /**
50 * Get tokens for one or more entity type
51 *
52 * Output will be formatted either as a flat list,
53 * or pass sequential=1 to retrieve as a hierarchy formatted for select2.
54 *
55 * @param array $params
56 * Should contain an array of entities to retrieve tokens for.
57 *
58 * @return array
59 * @throws \API_Exception
60 */
61 function civicrm_api3_mailing_gettokens($params) {
62 $tokens = [];
63 foreach ((array) $params['entity'] as $ent) {
64 $func = lcfirst($ent) . 'Tokens';
65 if (!method_exists('CRM_Core_SelectValues', $func)) {
66 throw new API_Exception('Unknown token entity: ' . $ent);
67 }
68 $tokens = array_merge(CRM_Core_SelectValues::$func(), $tokens);
69 }
70 if (!empty($params['sequential'])) {
71 $tokens = CRM_Utils_Token::formatTokensForDisplay($tokens);
72 }
73 return civicrm_api3_create_success($tokens, $params, 'Mailing', 'gettokens');
74 }
75
76 /**
77 * Adjust Metadata for Create action.
78 *
79 * The metadata is used for setting defaults, documentation & validation.
80 *
81 * @param array $params
82 * Array of parameters determined by getfields.
83 */
84 function _civicrm_api3_mailing_gettokens_spec(&$params) {
85 $params['entity'] = [
86 'api.default' => ['contact'],
87 'api.required' => 1,
88 'api.multiple' => 1,
89 'title' => 'Entity',
90 'options' => [],
91 ];
92 // Fetch a list of token functions and format to look like entity names
93 foreach (get_class_methods('CRM_Core_SelectValues') as $func) {
94 if (strpos($func, 'Tokens')) {
95 $ent = ucfirst(str_replace('Tokens', '', $func));
96 $params['entity']['options'][$ent] = $ent;
97 }
98 }
99 }
100
101 /**
102 * Adjust Metadata for Create action.
103 *
104 * The metadata is used for setting defaults, documentation & validation.
105 *
106 * @param array $params
107 * Array of parameters determined by getfields.
108 */
109 function _civicrm_api3_mailing_create_spec(&$params) {
110 $params['created_id']['api.default'] = 'user_contact_id';
111
112 $params['override_verp']['api.default'] = !CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, 'track_civimail_replies');
113 $params['visibility']['api.default'] = 'Public Pages';
114 $params['dedupe_email']['api.default'] = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, 'dedupe_email_default');
115
116 $params['forward_replies']['api.default'] = FALSE;
117 $params['auto_responder']['api.default'] = FALSE;
118 $params['open_tracking']['api.default'] = Civi::settings()->get('open_tracking_default');
119 $params['url_tracking']['api.default'] = Civi::settings()->get('url_tracking_default');
120
121 $params['header_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('Header', '');
122 $params['footer_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('Footer', '');
123 $params['optout_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('OptOut', '');
124 $params['reply_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('Reply', '');
125 $params['resubscribe_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('Resubscribe', '');
126 $params['unsubscribe_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('Unsubscribe', '');
127 $params['mailing_type']['api.default'] = 'standalone';
128 $defaultAddress = CRM_Core_BAO_Domain::getNameAndEmail(TRUE, TRUE);
129 foreach ($defaultAddress as $value) {
130 if (preg_match('/"(.*)" <(.*)>/', $value, $match)) {
131 $params['from_email']['api.default'] = $match[2];
132 $params['from_name']['api.default'] = $match[1];
133 }
134 }
135 }
136
137 /**
138 * Adjust metadata for clone spec action.
139 *
140 * @param array $spec
141 */
142 function _civicrm_api3_mailing_clone_spec(&$spec) {
143 $mailingFields = CRM_Mailing_DAO_Mailing::fields();
144 $spec['id'] = $mailingFields['id'];
145 $spec['id']['api.required'] = 1;
146 }
147
148 /**
149 * Clone mailing.
150 *
151 * @param array $params
152 *
153 * @return array
154 * @throws \CiviCRM_API3_Exception
155 */
156 function civicrm_api3_mailing_clone($params) {
157 $BLACKLIST = [
158 'id',
159 'is_completed',
160 'created_id',
161 'created_date',
162 'scheduled_id',
163 'scheduled_date',
164 'approver_id',
165 'approval_date',
166 'approval_status_id',
167 'approval_note',
168 'is_archived',
169 'hash',
170 'mailing_type',
171 ];
172
173 $get = civicrm_api3('Mailing', 'getsingle', ['id' => $params['id']]);
174
175 $newParams = [];
176 $newParams['debug'] = $params['debug'] ?? NULL;
177 $newParams['groups']['include'] = [];
178 $newParams['groups']['exclude'] = [];
179 $newParams['mailings']['include'] = [];
180 $newParams['mailings']['exclude'] = [];
181 foreach ($get as $field => $value) {
182 if (!in_array($field, $BLACKLIST)) {
183 $newParams[$field] = $value;
184 }
185 }
186
187 $dao = new CRM_Mailing_DAO_MailingGroup();
188 $dao->mailing_id = $params['id'];
189 $dao->find();
190 while ($dao->fetch()) {
191 // CRM-11431; account for multi-lingual
192 $entity = (substr($dao->entity_table, 0, 15) == 'civicrm_mailing') ? 'mailings' : 'groups';
193 $newParams[$entity][strtolower($dao->group_type)][] = $dao->entity_id;
194 }
195
196 return civicrm_api3('Mailing', 'create', $newParams);
197 }
198
199 /**
200 * Handle a delete event.
201 *
202 * @param array $params
203 *
204 * @return array
205 * API Success Array
206 */
207 function civicrm_api3_mailing_delete($params) {
208 return _civicrm_api3_basic_delete(_civicrm_api3_get_BAO(__FUNCTION__), $params);
209 }
210
211 /**
212 * Handle a get event.
213 *
214 * @param array $params
215 *
216 * @return array
217 */
218 function civicrm_api3_mailing_get($params) {
219 $result = _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, TRUE, 'Mailing');
220 return _civicrm_api3_mailing_get_formatResult($result);
221 }
222
223 /**
224 * Format definition.
225 *
226 * @param array $result
227 *
228 * @return array
229 * @throws \CRM_Core_Exception
230 */
231 function _civicrm_api3_mailing_get_formatResult($result) {
232 if (isset($result['values']) && is_array($result['values'])) {
233 foreach ($result['values'] as $key => $caseType) {
234 if (isset($result['values'][$key]['template_options']) && is_string($result['values'][$key]['template_options'])) {
235 $result['values'][$key]['template_options'] = json_decode($result['values'][$key]['template_options'], TRUE);
236 }
237 }
238 }
239 return $result;
240 }
241
242 /**
243 * Adjust metadata for mailing submit api function.
244 *
245 * @param array $spec
246 */
247 function _civicrm_api3_mailing_submit_spec(&$spec) {
248 $mailingFields = CRM_Mailing_DAO_Mailing::fields();
249 $spec['id'] = $mailingFields['id'];
250 $spec['scheduled_date'] = $mailingFields['scheduled_date'];
251 $spec['approval_date'] = $mailingFields['approval_date'];
252 $spec['approval_status_id'] = $mailingFields['approval_status_id'];
253 $spec['approval_note'] = $mailingFields['approval_note'];
254 // _skip_evil_bao_auto_recipients_: bool
255 }
256
257 /**
258 * Mailing submit.
259 *
260 * @param array $params
261 *
262 * @return array
263 * @throws API_Exception
264 */
265 function civicrm_api3_mailing_submit($params) {
266 civicrm_api3_verify_mandatory($params, 'CRM_Mailing_DAO_Mailing', ['id']);
267
268 if (!isset($params['scheduled_date']) && !isset($updateParams['approval_date'])) {
269 throw new API_Exception("Missing parameter scheduled_date and/or approval_date");
270 }
271 if (!is_numeric(CRM_Core_Session::getLoggedInContactID())) {
272 throw new API_Exception("Failed to determine current user");
273 }
274
275 $updateParams = [];
276 $updateParams['id'] = $params['id'];
277
278 // Note: we'll pass along scheduling/approval fields, but they may get ignored
279 // if we don't have permission.
280 if (isset($params['scheduled_date'])) {
281 $updateParams['scheduled_date'] = $params['scheduled_date'];
282 $updateParams['scheduled_id'] = CRM_Core_Session::getLoggedInContactID();
283 }
284 if (isset($params['approval_date'])) {
285 $updateParams['approval_date'] = $params['approval_date'];
286 $updateParams['approver_id'] = CRM_Core_Session::getLoggedInContactID();
287 $updateParams['approval_status_id'] = CRM_Utils_Array::value('approval_status_id', $updateParams, CRM_Core_OptionGroup::getDefaultValue('mail_approval_status'));
288 }
289 if (isset($params['approval_note'])) {
290 $updateParams['approval_note'] = $params['approval_note'];
291 }
292 if (isset($params['_skip_evil_bao_auto_recipients_'])) {
293 $updateParams['_skip_evil_bao_auto_recipients_'] = $params['_skip_evil_bao_auto_recipients_'];
294 }
295
296 $updateParams['options']['reload'] = 1;
297 return civicrm_api3('Mailing', 'create', $updateParams);
298 }
299
300 /**
301 * Process a bounce event by passing through to the BAOs.
302 *
303 * @param array $params
304 *
305 * @throws API_Exception
306 * @return array
307 */
308 function civicrm_api3_mailing_event_bounce($params) {
309 $body = $params['body'];
310 unset($params['body']);
311
312 $params += CRM_Mailing_BAO_BouncePattern::match($body);
313
314 if (CRM_Mailing_Event_BAO_Bounce::create($params)) {
315 return civicrm_api3_create_success($params);
316 }
317 else {
318 throw new API_Exception(ts('Queue event could not be found'), 'no_queue_event');
319 }
320 }
321
322 /**
323 * Adjust Metadata for bounce_spec action.
324 *
325 * The metadata is used for setting defaults, documentation & validation.
326 *
327 * @param array $params
328 * Array of parameters determined by getfields.
329 */
330 function _civicrm_api3_mailing_event_bounce_spec(&$params) {
331 $params['job_id']['api.required'] = 1;
332 $params['job_id']['title'] = 'Job ID';
333 $params['event_queue_id']['api.required'] = 1;
334 $params['event_queue_id']['title'] = 'Event Queue ID';
335 $params['hash']['api.required'] = 1;
336 $params['hash']['title'] = 'Hash';
337 $params['body']['api.required'] = 1;
338 $params['body']['title'] = 'Body';
339 }
340
341 /**
342 * Handle a confirm event.
343 *
344 * @deprecated
345 *
346 * @param array $params
347 *
348 * @return array
349 */
350 function civicrm_api3_mailing_event_confirm($params) {
351 return civicrm_api('MailingEventConfirm', 'create', $params);
352 }
353
354 /**
355 * Declare deprecated functions.
356 *
357 * @deprecated api notice
358 * @return array
359 * Array of deprecated actions
360 */
361 function _civicrm_api3_mailing_deprecation() {
362 return ['event_confirm' => 'Mailing api "event_confirm" action is deprecated. Use the mailing_event_confirm api instead.'];
363 }
364
365 /**
366 * Handle a reply event.
367 *
368 * @param array $params
369 *
370 * @return array
371 */
372 function civicrm_api3_mailing_event_reply($params) {
373 $job = $params['job_id'];
374 $queue = $params['event_queue_id'];
375 $hash = $params['hash'];
376 $replyto = $params['replyTo'];
377 $bodyTxt = $params['bodyTxt'] ?? NULL;
378 $bodyHTML = $params['bodyHTML'] ?? NULL;
379 $fullEmail = $params['fullEmail'] ?? NULL;
380
381 $mailing = CRM_Mailing_Event_BAO_Reply::reply($job, $queue, $hash, $replyto);
382
383 if (empty($mailing)) {
384 return civicrm_api3_create_error('Queue event could not be found');
385 }
386
387 CRM_Mailing_Event_BAO_Reply::send($queue, $mailing, $bodyTxt, $replyto, $bodyHTML, $fullEmail);
388
389 return civicrm_api3_create_success($params);
390 }
391
392 /**
393 * Adjust Metadata for event_reply action.
394 *
395 * The metadata is used for setting defaults, documentation & validation.
396 *
397 * @param array $params
398 * Array of parameters determined by getfields.
399 */
400 function _civicrm_api3_mailing_event_reply_spec(&$params) {
401 $params['job_id']['api.required'] = 1;
402 $params['job_id']['title'] = 'Job ID';
403 $params['event_queue_id']['api.required'] = 1;
404 $params['event_queue_id']['title'] = 'Event Queue ID';
405 $params['hash']['api.required'] = 1;
406 $params['hash']['title'] = 'Hash';
407 $params['replyTo']['api.required'] = 0;
408 //doesn't really explain adequately
409 $params['replyTo']['title'] = 'Reply To';
410 }
411
412 /**
413 * Handle a forward event.
414 *
415 * @param array $params
416 *
417 * @return array
418 */
419 function civicrm_api3_mailing_event_forward($params) {
420 $job = $params['job_id'];
421 $queue = $params['event_queue_id'];
422 $hash = $params['hash'];
423 $email = $params['email'];
424 $fromEmail = $params['fromEmail'] ?? NULL;
425 $params = $params['params'] ?? NULL;
426
427 $forward = CRM_Mailing_Event_BAO_Forward::forward($job, $queue, $hash, $email, $fromEmail, $params);
428
429 if ($forward) {
430 return civicrm_api3_create_success($params);
431 }
432
433 return civicrm_api3_create_error('Queue event could not be found');
434 }
435
436 /**
437 * Adjust Metadata for event_forward action.
438 *
439 * The metadata is used for setting defaults, documentation & validation.
440 *
441 * @param array $params
442 * Array of parameters determined by getfields.
443 */
444 function _civicrm_api3_mailing_event_forward_spec(&$params) {
445 $params['job_id']['api.required'] = 1;
446 $params['job_id']['title'] = 'Job ID';
447 $params['event_queue_id']['api.required'] = 1;
448 $params['event_queue_id']['title'] = 'Event Queue ID';
449 $params['hash']['api.required'] = 1;
450 $params['hash']['title'] = 'Hash';
451 $params['email']['api.required'] = 1;
452 $params['email']['title'] = 'Forwarded to Email';
453 }
454
455 /**
456 * Handle a click event.
457 *
458 * @param array $params
459 *
460 * @return array
461 */
462 function civicrm_api3_mailing_event_click($params) {
463 civicrm_api3_verify_mandatory($params,
464 'CRM_Mailing_Event_DAO_TrackableURLOpen',
465 ['event_queue_id', 'url_id'],
466 FALSE
467 );
468
469 $url_id = $params['url_id'];
470 $queue = $params['event_queue_id'];
471
472 $url = CRM_Mailing_Event_BAO_TrackableURLOpen::track($queue, $url_id);
473
474 $values = [];
475 $values['url'] = $url;
476 $values['is_error'] = 0;
477
478 return civicrm_api3_create_success($values);
479 }
480
481 /**
482 * Handle an open event.
483 *
484 * @param array $params
485 *
486 * @return array
487 */
488 function civicrm_api3_mailing_event_open($params) {
489
490 civicrm_api3_verify_mandatory($params,
491 'CRM_Mailing_Event_DAO_Opened',
492 ['event_queue_id'],
493 FALSE
494 );
495
496 $queue = $params['event_queue_id'];
497 $success = CRM_Mailing_Event_BAO_Opened::open($queue);
498
499 if (!$success) {
500 return civicrm_api3_create_error('mailing open event failed');
501 }
502
503 return civicrm_api3_create_success($params);
504 }
505
506 /**
507 * Preview mailing.
508 *
509 * @param array $params
510 * Array per getfields metadata.
511 *
512 * @return array
513 * @throws \API_Exception
514 */
515 function civicrm_api3_mailing_preview($params) {
516 $fromEmail = NULL;
517 if (!empty($params['from_email'])) {
518 $fromEmail = $params['from_email'];
519 }
520
521 $mailing = new CRM_Mailing_BAO_Mailing();
522 $mailingID = $params['id'] ?? NULL;
523 if ($mailingID) {
524 $mailing->id = $mailingID;
525 $mailing->find(TRUE);
526 }
527 else {
528 $mailing->copyValues($params);
529 }
530
531 $session = CRM_Core_Session::singleton();
532
533 CRM_Mailing_BAO_Mailing::tokenReplace($mailing);
534
535 // get and format attachments
536 $attachments = CRM_Core_BAO_File::getEntityFile('civicrm_mailing', $mailing->id);
537
538 $returnProperties = $mailing->getReturnProperties();
539 $contactID = $params['contact_id'] ?? NULL;
540 if (!$contactID) {
541 // If we still don't have a userID in a session because we are annon then set contactID to be 0
542 $contactID = empty($session->get('userID')) ? 0 : $session->get('userID');
543 }
544 $mailingParams = ['contact_id' => $contactID];
545
546 if (!$contactID) {
547 $details = CRM_Utils_Token::getAnonymousTokenDetails($mailingParams, $returnProperties, TRUE, TRUE, NULL, $mailing->getFlattenedTokens());
548 $details = $details[0][0] ?? NULL;
549 }
550 else {
551 [$details] = CRM_Utils_Token::getTokenDetails($mailingParams, $returnProperties, TRUE, TRUE, NULL, $mailing->getFlattenedTokens());
552 $details = $details[$contactID];
553 }
554
555 $mime = $mailing->compose(NULL, NULL, NULL, $contactID, $fromEmail, $fromEmail,
556 TRUE, $details, $attachments
557 );
558
559 return civicrm_api3_create_success([
560 'id' => $mailingID,
561 'contact_id' => $contactID,
562 'subject' => CRM_Utils_Array::value('Subject', $mime->headers(), ''),
563 'body_html' => $mime->getHTMLBody(),
564 'body_text' => $mime->getTXTBody(),
565 ]);
566 }
567
568 /**
569 * Adjust metadata for send test function.
570 *
571 * @param array $spec
572 */
573 function _civicrm_api3_mailing_send_test_spec(&$spec) {
574 $spec['test_group']['title'] = 'Test Group ID';
575 $spec['test_email']['title'] = 'Test Email Address';
576 $spec['mailing_id']['api.required'] = TRUE;
577 $spec['mailing_id']['title'] = ts('Mailing Id');
578 }
579
580 /**
581 * Send test mailing.
582 *
583 * @param array $params
584 *
585 * @return array
586 * @throws \API_Exception
587 * @throws \CiviCRM_API3_Exception
588 */
589 function civicrm_api3_mailing_send_test($params) {
590 if (!array_key_exists('test_group', $params) && !array_key_exists('test_email', $params)) {
591 throw new API_Exception("Mandatory key(s) missing from params array: test_group and/or test_email field are required");
592 }
593 civicrm_api3_verify_mandatory($params,
594 'CRM_Mailing_DAO_MailingJob',
595 ['mailing_id'],
596 FALSE
597 );
598
599 $testEmailParams = _civicrm_api3_generic_replace_base_params($params);
600 if (isset($testEmailParams['id'])) {
601 unset($testEmailParams['id']);
602 }
603
604 $testEmailParams['is_test'] = 1;
605 $testEmailParams['status'] = 'Scheduled';
606 $testEmailParams['scheduled_date'] = CRM_Utils_Date::processDate(date('Y-m-d'), date('H:i:s'));
607 $testEmailParams['is_calling_function_updated_to_reflect_deprecation'] = TRUE;
608 $job = civicrm_api3('MailingJob', 'create', $testEmailParams);
609 CRM_Mailing_BAO_Mailing::getRecipients($testEmailParams['mailing_id']);
610 $testEmailParams['job_id'] = $job['id'];
611 $testEmailParams['emails'] = array_key_exists('test_email', $testEmailParams) ? explode(',', strtolower($testEmailParams['test_email'])) : NULL;
612 if (!empty($params['test_email'])) {
613 $query = CRM_Utils_SQL_Select::from('civicrm_email e')
614 ->select(['e.id', 'e.contact_id', 'e.email'])
615 ->join('c', 'INNER JOIN civicrm_contact c ON e.contact_id = c.id')
616 ->where('e.email IN (@emails)', ['@emails' => $testEmailParams['emails']])
617 ->where('e.on_hold = 0')
618 ->where('c.is_opt_out = 0')
619 ->where('c.do_not_email = 0')
620 ->where('c.is_deceased = 0')
621 ->where('c.is_deleted = 0')
622 ->groupBy('e.id')
623 ->orderBy(['e.is_bulkmail DESC', 'e.is_primary DESC'])
624 ->toSQL();
625 $dao = CRM_Core_DAO::executeQuery($query);
626 $emailDetail = [];
627 // fetch contact_id and email id for all existing emails
628 while ($dao->fetch()) {
629 $emailDetail[strtolower($dao->email)] = [
630 'contact_id' => $dao->contact_id,
631 'email_id' => $dao->id,
632 ];
633 }
634 foreach ($testEmailParams['emails'] as $key => $email) {
635 $email = trim($email);
636 $contactId = $emailId = NULL;
637 if (array_key_exists($email, $emailDetail)) {
638 $emailId = $emailDetail[$email]['email_id'];
639 $contactId = $emailDetail[$email]['contact_id'];
640 }
641 if (!$contactId && CRM_Core_Permission::check('add contacts')) {
642 //create new contact.
643 $contact = civicrm_api3('Contact', 'create',
644 [
645 'contact_type' => 'Individual',
646 'email' => $email,
647 'api.Email.get' => ['return' => 'id'],
648 ]
649 );
650 $contactId = $contact['id'];
651 $emailId = $contact['values'][$contactId]['api.Email.get']['id'];
652 }
653 if ($emailId && $contactId) {
654 civicrm_api3('MailingEventQueue', 'create',
655 [
656 'job_id' => $job['id'],
657 'email_id' => $emailId,
658 'contact_id' => $contactId,
659 ]
660 );
661 }
662 }
663 }
664
665 $isComplete = FALSE;
666
667 while (!$isComplete) {
668 // Q: In CRM_Mailing_BAO_Mailing::processQueue(), the three runJobs*()
669 // functions are all called. Why does Mailing.send_test only call one?
670 // CRM_Mailing_BAO_MailingJob::runJobs_pre($mailerJobSize, NULL);
671 $isComplete = CRM_Mailing_BAO_MailingJob::runJobs($testEmailParams);
672 // CRM_Mailing_BAO_MailingJob::runJobs_post(NULL);
673 }
674
675 //return delivered mail info
676 $mailDelivered = CRM_Mailing_Event_BAO_Delivered::getRows($params['mailing_id'], $job['id'], TRUE, NULL, NULL, NULL, TRUE);
677
678 return civicrm_api3_create_success($mailDelivered);
679 }
680
681 /**
682 * Adjust Metadata for send_mail action.
683 *
684 * The metadata is used for setting defaults, documentation & validation.
685 *
686 * @param array $params
687 * Array of parameters determined by getfields.
688 */
689 function _civicrm_api3_mailing_stats_spec(&$params) {
690 $params['date']['api.default'] = 'now';
691 $params['date']['title'] = 'Date';
692 $params['is_distinct']['api.default'] = FALSE;
693 $params['is_distinct']['title'] = 'Is Distinct';
694 }
695
696 /**
697 * Function which needs to be explained.
698 *
699 * @param array $params
700 *
701 * @return array
702 * @throws \API_Exception
703 */
704 function civicrm_api3_mailing_stats($params) {
705 civicrm_api3_verify_mandatory($params,
706 'CRM_Mailing_DAO_MailingJob',
707 ['mailing_id'],
708 FALSE
709 );
710
711 if ($params['date'] == 'now') {
712 $params['date'] = date('YmdHis');
713 }
714 else {
715 $params['date'] = CRM_Utils_Date::processDate($params['date'] . ' ' . $params['date_time']);
716 }
717
718 $stats[$params['mailing_id']] = [];
719 if (empty($params['job_id'])) {
720 $params['job_id'] = NULL;
721 }
722 foreach (['Delivered', 'Bounces', 'Unsubscribers', 'Unique Clicks', 'Opened'] as $detail) {
723 switch ($detail) {
724 case 'Delivered':
725 $stats[$params['mailing_id']] += [
726 $detail => CRM_Mailing_Event_BAO_Delivered::getTotalCount($params['mailing_id'], $params['job_id'], (bool) $params['is_distinct'], $params['date']),
727 ];
728 break;
729
730 case 'Bounces':
731 $stats[$params['mailing_id']] += [
732 $detail => CRM_Mailing_Event_BAO_Bounce::getTotalCount($params['mailing_id'], $params['job_id'], (bool) $params['is_distinct'], $params['date']),
733 ];
734 break;
735
736 case 'Unsubscribers':
737 $stats[$params['mailing_id']] += [
738 $detail => CRM_Mailing_Event_BAO_Unsubscribe::getTotalCount($params['mailing_id'], $params['job_id'], (bool) $params['is_distinct'], NULL, $params['date']),
739 ];
740 break;
741
742 case 'Unique Clicks':
743 $stats[$params['mailing_id']] += [
744 $detail => CRM_Mailing_Event_BAO_TrackableURLOpen::getTotalCount($params['mailing_id'], $params['job_id'], (bool) $params['is_distinct'], NULL, $params['date']),
745 ];
746 break;
747
748 case 'Opened':
749 $stats[$params['mailing_id']] += [
750 $detail => CRM_Mailing_Event_BAO_Opened::getTotalCount($params['mailing_id'], $params['job_id'], (bool) $params['is_distinct'], $params['date']),
751 ];
752 break;
753 }
754 }
755 $stats[$params['mailing_id']]['delivered_rate'] = $stats[$params['mailing_id']]['opened_rate'] = $stats[$params['mailing_id']]['clickthrough_rate'] = '0.00%';
756 if (!empty(CRM_Mailing_Event_BAO_Queue::getTotalCount($params['mailing_id'], $params['job_id']))) {
757 $stats[$params['mailing_id']]['delivered_rate'] = round((100.0 * $stats[$params['mailing_id']]['Delivered']) / CRM_Mailing_Event_BAO_Queue::getTotalCount($params['mailing_id'], $params['job_id']), 2) . '%';
758 }
759 if (!empty($stats[$params['mailing_id']]['Delivered'])) {
760 $stats[$params['mailing_id']]['opened_rate'] = round($stats[$params['mailing_id']]['Opened'] / $stats[$params['mailing_id']]['Delivered'] * 100.0, 2) . '%';
761 $stats[$params['mailing_id']]['clickthrough_rate'] = round($stats[$params['mailing_id']]['Unique Clicks'] / $stats[$params['mailing_id']]['Delivered'] * 100.0, 2) . '%';
762 }
763 return civicrm_api3_create_success($stats);
764 }
765
766 function _civicrm_api3_mailing_update_email_resetdate_spec(&$spec) {
767 $spec['minDays']['title'] = 'Number of days to wait without a bounce to assume successful delivery (default 3)';
768 $spec['minDays']['type'] = CRM_Utils_Type::T_INT;
769 $spec['minDays']['api.default'] = 3;
770 $spec['minDays']['api.required'] = 1;
771
772 $spec['maxDays']['title'] = 'Analyze mailings since this many days ago (default 7)';
773 $spec['maxDays']['type'] = CRM_Utils_Type::T_INT;
774 $spec['maxDays']['api.default'] = 7;
775 $spec['maxDays']['api.required'] = 1;
776 }
777
778 /**
779 * Fix the reset dates on the email record based on when a mail was last delivered.
780 *
781 * We only consider mailings that were completed and finished in the last 3 to 7 days
782 * Both the min and max days can be set via the params
783 *
784 * @param array $params
785 *
786 * @return array
787 */
788 function civicrm_api3_mailing_update_email_resetdate($params) {
789 CRM_Mailing_Event_BAO_Delivered::updateEmailResetDate((int) $params['minDays'], (int) $params['maxDays']);
790 return civicrm_api3_create_success();
791 }