Merge pull request #14806 from eileenmcnaughton/ex
[civicrm-core.git] / CRM / Core / BAO / ActionSchedule.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
7cceb474 7 +--------------------------------------------------------------------+
6a488035
TO
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
006389de 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
6a488035
TO
32 * $Id$
33 *
34 */
35
36/**
37 * This class contains functions for managing Scheduled Reminders
38 */
39class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
40
b5c2afd0 41 /**
50a23755 42 * @param array $filters
9e1bf145 43 * Filter by property (e.g. 'id').
b5c2afd0 44 * @return array
50a23755 45 * Array(scalar $id => Mapping $mapping).
b5c2afd0 46 */
50a23755 47 public static function getMappings($filters = NULL) {
6a488035
TO
48 static $_action_mapping;
49
50a23755 50 if ($_action_mapping === NULL) {
9c8748cc
TO
51 $event = \Civi\Core\Container::singleton()->get('dispatcher')
52 ->dispatch(\Civi\ActionSchedule\Events::MAPPINGS,
53 new \Civi\ActionSchedule\Event\MappingRegisterEvent());
54 $_action_mapping = $event->getMappings();
6a488035
TO
55 }
56
9e1bf145 57 if (empty($filters)) {
50a23755 58 return $_action_mapping;
6a488035 59 }
9e1bf145 60 elseif (isset($filters['id'])) {
be2fb01f 61 return [
9e1bf145 62 $filters['id'] => $_action_mapping[$filters['id']],
be2fb01f 63 ];
9e1bf145
TO
64 }
65 else {
66 throw new CRM_Core_Exception("getMappings() called with unsupported filter: " . implode(', ', array_keys($filters)));
8d657dde 67 }
8d657dde
AH
68 }
69
7f0141d8
TO
70 /**
71 * @param string|int $id
72 * @return \Civi\ActionSchedule\Mapping|NULL
73 */
74 public static function getMapping($id) {
75 $mappings = self::getMappings();
76 return isset($mappings[$id]) ? $mappings[$id] : NULL;
77 }
78
6a488035 79 /**
0905ee5d 80 * For each entity, get a list of entity-value labels.
6a488035 81 *
a6c01b45 82 * @return array
0905ee5d
TO
83 * Ex: $entityValueLabels[$mappingId][$valueId] = $valueLabel.
84 * @throws CRM_Core_Exception
6a488035 85 */
0905ee5d 86 public static function getAllEntityValueLabels() {
be2fb01f 87 $entityValueLabels = [];
0905ee5d 88 foreach (CRM_Core_BAO_ActionSchedule::getMappings() as $mapping) {
50a23755 89 /** @var \Civi\ActionSchedule\Mapping $mapping */
d3600e68 90 $entityValueLabels[$mapping->getId()] = $mapping->getValueLabels();
be2fb01f 91 $valueLabel = ['- ' . strtolower($mapping->getValueHeader()) . ' -'];
018d5e02 92 $entityValueLabels[$mapping->getId()] = $valueLabel + $entityValueLabels[$mapping->getId()];
d3600e68 93 }
0905ee5d
TO
94 return $entityValueLabels;
95 }
6a488035 96
0905ee5d
TO
97 /**
98 * For each entity, get a list of entity-status labels.
99 *
100 * @return array
101 * Ex: $entityValueLabels[$mappingId][$valueId][$statusId] = $statusLabel.
102 */
103 public static function getAllEntityStatusLabels() {
104 $entityValueLabels = self::getAllEntityValueLabels();
be2fb01f 105 $entityStatusLabels = [];
0905ee5d 106 foreach (CRM_Core_BAO_ActionSchedule::getMappings() as $mapping) {
d3600e68 107 /** @var \Civi\ActionSchedule\Mapping $mapping */
be2fb01f 108 $statusLabel = ['- ' . strtolower($mapping->getStatusHeader()) . ' -'];
9e1bf145
TO
109 $entityStatusLabels[$mapping->getId()] = $entityValueLabels[$mapping->getId()];
110 foreach ($entityStatusLabels[$mapping->getId()] as $kkey => & $vval) {
50a23755 111 $vval = $statusLabel + $mapping->getStatusLabels($kkey);
6a488035
TO
112 }
113 }
0905ee5d 114 return $entityStatusLabels;
6a488035
TO
115 }
116
117 /**
fe482240 118 * Retrieve list of Scheduled Reminders.
6a488035 119 *
6a0b768e
TO
120 * @param bool $namesOnly
121 * Return simple list of names.
da6b46f4 122 *
9d451db8 123 * @param \Civi\ActionSchedule\Mapping|null $filterMapping
77e16391
TO
124 * Filter by the schedule's mapping type.
125 * @param int $filterValue
126 * Filter by the schedule's entity_value.
6a488035 127 *
a6c01b45
CW
128 * @return array
129 * (reference) reminder list
9d451db8 130 * @throws \CRM_Core_Exception
6a488035 131 */
77e16391 132 public static function &getList($namesOnly = FALSE, $filterMapping = NULL, $filterValue = NULL) {
6a488035
TO
133 $query = "
134SELECT
135 title,
6a488035 136 cas.id as id,
50a23755 137 cas.mapping_id,
6a488035 138 cas.entity_value as entityValueIds,
6a488035
TO
139 cas.entity_status as entityStatusIds,
140 cas.start_action_date as entityDate,
141 cas.start_action_offset,
142 cas.start_action_unit,
143 cas.start_action_condition,
144 cas.absolute_date,
145 is_repeat,
146 is_active
147
148FROM civicrm_action_schedule cas
6a488035 149";
be2fb01f 150 $queryParams = [];
62933949 151 $where = " WHERE 1 ";
77e16391
TO
152 if ($filterMapping and $filterValue) {
153 $where .= " AND cas.entity_value = %1 AND cas.mapping_id = %2";
be2fb01f
CW
154 $queryParams[1] = [$filterValue, 'Integer'];
155 $queryParams[2] = [$filterMapping->getId(), 'String'];
6a488035 156 }
62933949 157 $where .= " AND cas.used_for IS NULL";
158 $query .= $where;
50a23755 159 $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
6a488035 160 while ($dao->fetch()) {
77e16391 161 /** @var Civi\ActionSchedule\Mapping $filterMapping */
be2fb01f 162 $filterMapping = CRM_Utils_Array::first(self::getMappings([
50a23755 163 'id' => $dao->mapping_id,
be2fb01f 164 ]));
6a488035
TO
165 $list[$dao->id]['id'] = $dao->id;
166 $list[$dao->id]['title'] = $dao->title;
167 $list[$dao->id]['start_action_offset'] = $dao->start_action_offset;
168 $list[$dao->id]['start_action_unit'] = $dao->start_action_unit;
169 $list[$dao->id]['start_action_condition'] = $dao->start_action_condition;
170 $list[$dao->id]['entityDate'] = ucwords(str_replace('_', ' ', $dao->entityDate));
171 $list[$dao->id]['absolute_date'] = $dao->absolute_date;
9e1bf145 172 $list[$dao->id]['entity'] = $filterMapping->getLabel();
50a23755 173 $list[$dao->id]['value'] = implode(', ', CRM_Utils_Array::subset(
77e16391 174 $filterMapping->getValueLabels(),
50a23755
TO
175 explode(CRM_Core_DAO::VALUE_SEPARATOR, $dao->entityValueIds)
176 ));
177 $list[$dao->id]['status'] = implode(', ', CRM_Utils_Array::subset(
77e16391 178 $filterMapping->getStatusLabels($dao->entityValueIds),
50a23755
TO
179 explode(CRM_Core_DAO::VALUE_SEPARATOR, $dao->entityStatusIds)
180 ));
6a488035
TO
181 $list[$dao->id]['is_repeat'] = $dao->is_repeat;
182 $list[$dao->id]['is_active'] = $dao->is_active;
183 }
184
185 return $list;
186 }
187
6a488035 188 /**
fe482240 189 * Add the schedules reminders in the db.
6a488035 190 *
6a0b768e
TO
191 * @param array $params
192 * (reference ) an assoc array of name/value pairs.
193 * @param array $ids
244bbdd8 194 * Unused variable.
6a488035 195 *
c490a46a 196 * @return CRM_Core_DAO_ActionSchedule
6a488035 197 */
be2fb01f 198 public static function add(&$params, $ids = []) {
6a488035
TO
199 $actionSchedule = new CRM_Core_DAO_ActionSchedule();
200 $actionSchedule->copyValues($params);
201
202 return $actionSchedule->save();
203 }
204
205 /**
fe482240
EM
206 * Retrieve DB object based on input parameters.
207 *
208 * It also stores all the retrieved values in the default array.
6a488035 209 *
6a0b768e
TO
210 * @param array $params
211 * (reference ) an assoc array of name/value pairs.
212 * @param array $values
213 * (reference ) an assoc array to hold the flattened values.
6a488035 214 *
16b10e64
CW
215 * @return CRM_Core_DAO_ActionSchedule|null
216 * object on success, null otherwise
6a488035 217 */
00be9182 218 public static function retrieve(&$params, &$values) {
6a488035
TO
219 if (empty($params)) {
220 return NULL;
221 }
222 $actionSchedule = new CRM_Core_DAO_ActionSchedule();
223
224 $actionSchedule->copyValues($params);
225
226 if ($actionSchedule->find(TRUE)) {
227 $ids['actionSchedule'] = $actionSchedule->id;
228
229 CRM_Core_DAO::storeValues($actionSchedule, $values);
230
231 return $actionSchedule;
232 }
233 return NULL;
234 }
235
236 /**
fe482240 237 * Delete a Reminder.
6a488035 238 *
6a0b768e
TO
239 * @param int $id
240 * ID of the Reminder to be deleted.
6a488035 241 *
6a488035 242 */
00be9182 243 public static function del($id) {
6a488035
TO
244 if ($id) {
245 $dao = new CRM_Core_DAO_ActionSchedule();
246 $dao->id = $id;
247 if ($dao->find(TRUE)) {
248 $dao->delete();
249 return;
250 }
251 }
252 CRM_Core_Error::fatal(ts('Invalid value passed to delete function.'));
253 }
254
255 /**
fe482240 256 * Update the is_active flag in the db.
6a488035 257 *
6a0b768e
TO
258 * @param int $id
259 * Id of the database record.
260 * @param bool $is_active
261 * Value we want to set the is_active field.
6a488035 262 *
8a4fede3 263 * @return bool
264 * true if we found and updated the object, else false
6a488035 265 */
00be9182 266 public static function setIsActive($id, $is_active) {
6a488035
TO
267 return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_ActionSchedule', $id, 'is_active', $is_active);
268 }
269
b5c2afd0 270 /**
100fef9d 271 * @param int $mappingID
b5c2afd0
EM
272 * @param $now
273 *
274 * @throws CRM_Core_Exception
275 */
00be9182 276 public static function sendMailings($mappingID, $now) {
be2fb01f 277 $mapping = CRM_Utils_Array::first(self::getMappings([
50a23755 278 'id' => $mappingID,
be2fb01f 279 ]));
6a488035
TO
280
281 $actionSchedule = new CRM_Core_DAO_ActionSchedule();
282 $actionSchedule->mapping_id = $mappingID;
283 $actionSchedule->is_active = 1;
284 $actionSchedule->find(FALSE);
285
6a488035 286 while ($actionSchedule->fetch()) {
5b16d8b6 287 $query = CRM_Core_BAO_ActionSchedule::prepareMailingQuery($mapping, $actionSchedule);
6a488035 288 $dao = CRM_Core_DAO::executeQuery($query,
be2fb01f 289 [1 => [$actionSchedule->id, 'Integer']]
6a488035
TO
290 );
291
07844ccf 292 $multilingual = CRM_Core_I18n::isMultilingual();
6a488035 293 while ($dao->fetch()) {
07844ccf
SV
294 // switch language if necessary
295 if ($multilingual) {
c72ba2be 296 $preferred_language = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $dao->contactID, 'preferred_language');
07844ccf
SV
297 CRM_Core_BAO_ActionSchedule::setCommunicationLanguage($actionSchedule->communication_language, $preferred_language);
298 }
299
be2fb01f 300 $errors = [];
d22bcd5b 301 try {
50a23755 302 $tokenProcessor = self::createTokenProcessor($actionSchedule, $mapping);
d22bcd5b
TO
303 $tokenProcessor->addRow()
304 ->context('contactId', $dao->contactID)
50a23755 305 ->context('actionSearchResult', (object) $dao->toArray());
d22bcd5b
TO
306 foreach ($tokenProcessor->evaluate()->getRows() as $tokenRow) {
307 if ($actionSchedule->mode == 'SMS' or $actionSchedule->mode == 'User_Preference') {
308 CRM_Utils_Array::extend($errors, self::sendReminderSms($tokenRow, $actionSchedule, $dao->contactID));
43ceab3f 309 }
6a488035 310
d22bcd5b
TO
311 if ($actionSchedule->mode == 'Email' or $actionSchedule->mode == 'User_Preference') {
312 CRM_Utils_Array::extend($errors, self::sendReminderEmail($tokenRow, $actionSchedule, $dao->contactID));
43ceab3f 313 }
d19632b1
JP
314 // insert activity log record if needed
315 if ($actionSchedule->record_activity && empty($errors)) {
316 $caseID = empty($dao->case_id) ? NULL : $dao->case_id;
317 CRM_Core_BAO_ActionSchedule::createMailingActivity($tokenRow, $mapping, $dao->contactID, $dao->entityID, $caseID);
318 }
43ceab3f 319 }
6a488035 320 }
d22bcd5b
TO
321 catch (\Civi\Token\TokenException $e) {
322 $errors['token_exception'] = $e->getMessage();
6a488035
TO
323 }
324
325 // update action log record
be2fb01f 326 $logParams = [
6a488035 327 'id' => $dao->reminderID,
d22bcd5b
TO
328 'is_error' => !empty($errors),
329 'message' => empty($errors) ? "null" : implode(' ', $errors),
6a488035 330 'action_date_time' => $now,
be2fb01f 331 ];
6a488035 332 CRM_Core_BAO_ActionLog::create($logParams);
6a488035
TO
333 }
334
6a488035
TO
335 }
336 }
337
b5c2afd0 338 /**
100fef9d 339 * @param int $mappingID
b5c2afd0
EM
340 * @param $now
341 * @param array $params
342 *
343 * @throws API_Exception
344 */
be2fb01f 345 public static function buildRecipientContacts($mappingID, $now, $params = []) {
6a488035
TO
346 $actionSchedule = new CRM_Core_DAO_ActionSchedule();
347 $actionSchedule->mapping_id = $mappingID;
348 $actionSchedule->is_active = 1;
22e263ad 349 if (!empty($params)) {
244bbdd8 350 _civicrm_api3_dao_set_filter($actionSchedule, $params, FALSE);
73f3e293 351 }
6a488035
TO
352 $actionSchedule->find();
353
354 while ($actionSchedule->fetch()) {
77e16391 355 /** @var \Civi\ActionSchedule\Mapping $mapping */
be2fb01f 356 $mapping = CRM_Utils_Array::first(self::getMappings([
50a23755 357 'id' => $mappingID,
be2fb01f 358 ]));
c09bacfd
TO
359 $builder = new \Civi\ActionSchedule\RecipientBuilder($now, $actionSchedule, $mapping);
360 $builder->build();
6a488035
TO
361 }
362 }
363
b5c2afd0
EM
364 /**
365 * @param null $now
366 * @param array $params
367 *
368 * @return array
369 */
be2fb01f 370 public static function processQueue($now = NULL, $params = []) {
6a488035
TO
371 $now = $now ? CRM_Utils_Time::setTime($now) : CRM_Utils_Time::getTime();
372
50a23755 373 $mappings = CRM_Core_BAO_ActionSchedule::getMappings();
6a488035 374 foreach ($mappings as $mappingID => $mapping) {
5b16d8b6
TO
375 CRM_Core_BAO_ActionSchedule::buildRecipientContacts($mappingID, $now, $params);
376 CRM_Core_BAO_ActionSchedule::sendMailings($mappingID, $now);
6a488035
TO
377 }
378
be2fb01f 379 $result = [
6a488035
TO
380 'is_error' => 0,
381 'messages' => ts('Sent all scheduled reminders successfully'),
be2fb01f 382 ];
6a488035
TO
383 return $result;
384 }
385
9a4df24c 386 /**
100fef9d
CW
387 * @param int $id
388 * @param int $mappingID
9a4df24c
EM
389 *
390 * @return null|string
391 */
00be9182 392 public static function isConfigured($id, $mappingID) {
6a488035
TO
393 $queryString = "SELECT count(id) FROM civicrm_action_schedule
394 WHERE mapping_id = %1 AND
395 entity_value = %2";
396
be2fb01f
CW
397 $params = [
398 1 => [$mappingID, 'String'],
399 2 => [$id, 'Integer'],
400 ];
6a488035
TO
401 return CRM_Core_DAO::singleValueQuery($queryString, $params);
402 }
403
b5c2afd0 404 /**
100fef9d 405 * @param int $mappingID
b5c2afd0
EM
406 * @param $recipientType
407 *
408 * @return array
409 */
00be9182 410 public static function getRecipientListing($mappingID, $recipientType) {
77e16391 411 if (!$mappingID) {
be2fb01f 412 return [];
6a488035
TO
413 }
414
77e16391 415 /** @var \Civi\ActionSchedule\Mapping $mapping */
be2fb01f 416 $mapping = CRM_Utils_Array::first(CRM_Core_BAO_ActionSchedule::getMappings([
50a23755 417 'id' => $mappingID,
be2fb01f 418 ]));
77e16391 419 return $mapping->getRecipientListing($recipientType);
6a488035 420 }
96025800 421
07844ccf
SV
422 /**
423 * @param $communication_language
424 * @param $preferred_language
425 */
426 public static function setCommunicationLanguage($communication_language, $preferred_language) {
c86241f4 427 $currentLocale = CRM_Core_I18n::getLocale();
8ea23016 428 $language = $currentLocale;
fadd4d37 429
07844ccf
SV
430 // prepare the language for the email
431 if ($communication_language == CRM_Core_I18n::AUTO) {
432 if (!empty($preferred_language)) {
433 $language = $preferred_language;
434 }
435 }
436 else {
437 $language = $communication_language;
438 }
439
440 // language not in the existing language, use default
273f9849 441 $languages = CRM_Core_I18n::languages(TRUE);
8ea23016
SV
442 if (!array_key_exists($language, $languages)) {
443 $language = $currentLocale;
07844ccf
SV
444 }
445
446 // change the language
e8576142 447 $i18n = CRM_Core_I18n::singleton();
8ea23016 448 $i18n->setLocale($language);
07844ccf
SV
449 }
450
c72ba2be
TO
451 /**
452 * Save a record about the delivery of a reminder email.
453 *
454 * WISHLIST: Instead of saving $actionSchedule->body_html, call this immediately after
455 * sending the message and pass in the fully rendered text of the message.
456 *
d19632b1 457 * @param object $tokenRow
50a23755 458 * @param Civi\ActionSchedule\Mapping $mapping
c72ba2be
TO
459 * @param int $contactID
460 * @param int $entityID
e97c66ff 461 * @param int|null $caseID
c72ba2be
TO
462 * @throws CRM_Core_Exception
463 */
d19632b1 464 protected static function createMailingActivity($tokenRow, $mapping, $contactID, $entityID, $caseID) {
c72ba2be
TO
465 $session = CRM_Core_Session::singleton();
466
9e1bf145 467 if ($mapping->getEntity() == 'civicrm_membership') {
d66c61b6 468 // @todo - not required with api
c72ba2be 469 $activityTypeID
d66c61b6 470 = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Membership Renewal Reminder');
c72ba2be
TO
471 }
472 else {
d66c61b6 473 // @todo - not required with api
c72ba2be 474 $activityTypeID
d66c61b6 475 = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Reminder Sent');
c72ba2be
TO
476 }
477
be2fb01f 478 $activityParams = [
d19632b1
JP
479 'subject' => $tokenRow->render('subject'),
480 'details' => $tokenRow->render('body_html'),
c72ba2be
TO
481 'source_contact_id' => $session->get('userID') ? $session->get('userID') : $contactID,
482 'target_contact_id' => $contactID,
d66c61b6 483 // @todo - not required with api
c72ba2be 484 'activity_date_time' => CRM_Utils_Time::getTime('YmdHis'),
d66c61b6 485 // @todo - not required with api
486 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'),
c72ba2be
TO
487 'activity_type_id' => $activityTypeID,
488 'source_record_id' => $entityID,
be2fb01f 489 ];
d66c61b6 490 // @todo use api, remove all the above wrangling
c72ba2be
TO
491 $activity = CRM_Activity_BAO_Activity::create($activityParams);
492
493 //file reminder on case if source activity is a case activity
494 if (!empty($caseID)) {
be2fb01f 495 $caseActivityParams = [];
c72ba2be
TO
496 $caseActivityParams['case_id'] = $caseID;
497 $caseActivityParams['activity_id'] = $activity->id;
498 CRM_Case_BAO_Case::processCaseActivity($caseActivityParams);
499 }
500 }
501
c72ba2be 502 /**
f9ec2da6
TO
503 * @param \Civi\ActionSchedule\MappingInterface $mapping
504 * @param \CRM_Core_DAO_ActionSchedule $actionSchedule
c72ba2be
TO
505 * @return string
506 */
507 protected static function prepareMailingQuery($mapping, $actionSchedule) {
f9ec2da6
TO
508 $select = CRM_Utils_SQL_Select::from('civicrm_action_log reminder')
509 ->select("reminder.id as reminderID, reminder.contact_id as contactID, reminder.entity_table as entityTable, reminder.*, e.id AS entityID")
510 ->join('e', "!casMailingJoinType !casMappingEntity e ON !casEntityJoinExpr")
511 ->select("e.id as entityID, e.*")
512 ->where("reminder.action_schedule_id = #casActionScheduleId")
513 ->where("reminder.action_date_time IS NULL")
be2fb01f 514 ->param([
f9ec2da6
TO
515 'casActionScheduleId' => $actionSchedule->id,
516 'casMailingJoinType' => ($actionSchedule->limit_to == 0) ? 'LEFT JOIN' : 'INNER JOIN',
517 'casMappingId' => $mapping->getId(),
518 'casMappingEntity' => $mapping->getEntity(),
519 'casEntityJoinExpr' => 'e.id = reminder.entity_id',
be2fb01f 520 ]);
a1466c59
TO
521
522 if ($actionSchedule->limit_to == 0) {
a1466c59
TO
523 $select->where("e.id = reminder.entity_id OR reminder.entity_table = 'civicrm_contact'");
524 }
c72ba2be 525
f9ec2da6
TO
526 \Civi\Core\Container::singleton()->get('dispatcher')
527 ->dispatch(
528 \Civi\ActionSchedule\Events::MAILING_QUERY,
529 new \Civi\ActionSchedule\Event\MailingQueryEvent($actionSchedule, $mapping, $select)
530 );
c72ba2be 531
a1466c59 532 return $select->toSQL();
c72ba2be
TO
533 }
534
43ceab3f 535 /**
09c2328a 536 * @param \Civi\Token\TokenRow $tokenRow
d22bcd5b
TO
537 * @param CRM_Core_DAO_ActionSchedule $schedule
538 * @param int $toContactID
43ceab3f 539 * @throws CRM_Core_Exception
d22bcd5b
TO
540 * @return array
541 * List of error messages.
43ceab3f 542 */
d22bcd5b
TO
543 protected static function sendReminderSms($tokenRow, $schedule, $toContactID) {
544 $toPhoneNumber = self::pickSmsPhoneNumber($toContactID);
545 if (!$toPhoneNumber) {
be2fb01f 546 return ["sms_phone_missing" => "Couldn't find recipient's phone number."];
d22bcd5b
TO
547 }
548
51c566a3
SL
549 // dev/core#369 If an SMS provider is deleted then the relevant row in the action_schedule_table is set to NULL
550 // So we need to exclude them.
551 if (CRM_Utils_System::isNull($schedule->sms_provider_id)) {
7f38cf7f 552 return ["sms_provider_missing" => "SMS reminder cannot be sent because the SMS provider has been deleted."];
51c566a3
SL
553 }
554
43ceab3f
TO
555 $messageSubject = $tokenRow->render('subject');
556 $sms_body_text = $tokenRow->render('sms_body_text');
557
558 $session = CRM_Core_Session::singleton();
559 $userID = $session->get('userID') ? $session->get('userID') : $tokenRow->context['contactId'];
be2fb01f 560 $smsParams = [
43ceab3f
TO
561 'To' => $toPhoneNumber,
562 'provider_id' => $schedule->sms_provider_id,
563 'activity_subject' => $messageSubject,
be2fb01f 564 ];
3c49839d 565 $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'SMS');
be2fb01f 566 $activityParams = [
43ceab3f
TO
567 'source_contact_id' => $userID,
568 'activity_type_id' => $activityTypeID,
569 'activity_date_time' => date('YmdHis'),
570 'subject' => $messageSubject,
571 'details' => $sms_body_text,
593dbb07 572 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'),
be2fb01f 573 ];
43ceab3f
TO
574
575 $activity = CRM_Activity_BAO_Activity::create($activityParams);
576
577 CRM_Activity_BAO_Activity::sendSMSMessage($tokenRow->context['contactId'],
578 $sms_body_text,
579 $smsParams,
580 $activity->id,
581 $userID
582 );
d22bcd5b 583
be2fb01f 584 return [];
43ceab3f
TO
585 }
586
587 /**
588 * @param CRM_Core_DAO_ActionSchedule $actionSchedule
589 * @return string
590 * Ex: "Alice <alice@example.org>".
591 */
592 protected static function pickFromEmail($actionSchedule) {
593 $domainValues = CRM_Core_BAO_Domain::getNameAndEmail();
594 $fromEmailAddress = "$domainValues[0] <$domainValues[1]>";
595 if ($actionSchedule->from_email) {
596 $fromEmailAddress = "$actionSchedule->from_name <$actionSchedule->from_email>";
597 return $fromEmailAddress;
598 }
599 return $fromEmailAddress;
600 }
601
602 /**
09c2328a 603 * @param \Civi\Token\TokenRow $tokenRow
d22bcd5b
TO
604 * @param CRM_Core_DAO_ActionSchedule $schedule
605 * @param int $toContactID
606 * @return array
607 * List of error messages.
43ceab3f 608 */
d22bcd5b
TO
609 protected static function sendReminderEmail($tokenRow, $schedule, $toContactID) {
610 $toEmail = CRM_Contact_BAO_Contact::getPrimaryEmail($toContactID);
611 if (!$toEmail) {
be2fb01f 612 return ["email_missing" => "Couldn't find recipient's email address."];
d22bcd5b
TO
613 }
614
43ceab3f
TO
615 $body_text = $tokenRow->render('body_text');
616 $body_html = $tokenRow->render('body_html');
617 if (!$schedule->body_text) {
618 $body_text = CRM_Utils_String::htmlToText($body_html);
619 }
620
621 // set up the parameters for CRM_Utils_Mail::send
be2fb01f 622 $mailParams = [
43ceab3f
TO
623 'groupName' => 'Scheduled Reminder Sender',
624 'from' => self::pickFromEmail($schedule),
625 'toName' => $tokenRow->context['contact']['display_name'],
626 'toEmail' => $toEmail,
627 'subject' => $tokenRow->render('subject'),
628 'entity' => 'action_schedule',
629 'entity_id' => $schedule->id,
be2fb01f 630 ];
43ceab3f
TO
631
632 if (!$body_html || $tokenRow->context['contact']['preferred_mail_format'] == 'Text' ||
633 $tokenRow->context['contact']['preferred_mail_format'] == 'Both'
634 ) {
635 // render the &amp; entities in text mode, so that the links work
636 $mailParams['text'] = str_replace('&amp;', '&', $body_text);
637 }
638 if ($body_html && ($tokenRow->context['contact']['preferred_mail_format'] == 'HTML' ||
639 $tokenRow->context['contact']['preferred_mail_format'] == 'Both'
640 )
641 ) {
642 $mailParams['html'] = $body_html;
643 }
644 $result = CRM_Utils_Mail::send($mailParams);
d22bcd5b 645 if (!$result || is_a($result, 'PEAR_Error')) {
be2fb01f 646 return ['email_fail' => 'Failed to send message'];
d22bcd5b
TO
647 }
648
be2fb01f 649 return [];
43ceab3f
TO
650 }
651
652 /**
653 * @param CRM_Core_DAO_ActionSchedule $schedule
50a23755 654 * @param \Civi\ActionSchedule\Mapping $mapping
43ceab3f
TO
655 * @return \Civi\Token\TokenProcessor
656 */
50a23755 657 protected static function createTokenProcessor($schedule, $mapping) {
be2fb01f 658 $tp = new \Civi\Token\TokenProcessor(\Civi\Core\Container::singleton()->get('dispatcher'), [
43ceab3f
TO
659 'controller' => __CLASS__,
660 'actionSchedule' => $schedule,
50a23755 661 'actionMapping' => $mapping,
43ceab3f 662 'smarty' => TRUE,
be2fb01f 663 ]);
43ceab3f
TO
664 $tp->addMessage('body_text', $schedule->body_text, 'text/plain');
665 $tp->addMessage('body_html', $schedule->body_html, 'text/html');
666 $tp->addMessage('sms_body_text', $schedule->sms_body_text, 'text/plain');
667 $tp->addMessage('subject', $schedule->subject, 'text/plain');
668 return $tp;
669 }
670
d22bcd5b 671 /**
54957108 672 * Pick SMS phone number.
673 *
674 * @param int $smsToContactId
675 *
676 * @return NULL|string
d22bcd5b
TO
677 */
678 protected static function pickSmsPhoneNumber($smsToContactId) {
be2fb01f 679 $toPhoneNumbers = CRM_Core_BAO_Phone::allPhones($smsToContactId, FALSE, 'Mobile', [
d22bcd5b
TO
680 'is_deceased' => 0,
681 'is_deleted' => 0,
682 'do_not_sms' => 0,
be2fb01f 683 ]);
d22bcd5b
TO
684 //to get primary mobile ph,if not get a first mobile phONE
685 if (!empty($toPhoneNumbers)) {
686 $toPhoneNumberDetails = reset($toPhoneNumbers);
687 $toPhoneNumber = CRM_Utils_Array::value('phone', $toPhoneNumberDetails);
688 return $toPhoneNumber;
689 }
690 return NULL;
691 }
692
673c1c81
TO
693 /**
694 * Get the list of generic recipient types supported by all entities/mappings.
695 *
696 * @return array
697 * array(mixed $value => string $label).
698 */
0905ee5d 699 public static function getAdditionalRecipients() {
be2fb01f 700 return [
673c1c81
TO
701 'manual' => ts('Choose Recipient(s)'),
702 'group' => ts('Select Group'),
be2fb01f 703 ];
673c1c81
TO
704 }
705
6a488035 706}