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