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