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