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