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