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 | +--------------------------------------------------------------------+ | |
6a488035 TO |
10 | */ |
11 | ||
12 | /** | |
13 | * | |
14 | * @package CRM | |
ca5cec67 | 15 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
6a488035 TO |
16 | */ |
17 | ||
18 | /** | |
424029e3 | 19 | * This class provides the common functionality for sending sms to one or a group of contact ids. |
6a488035 TO |
20 | */ |
21 | class CRM_Contact_Form_Task_SMSCommon { | |
7da04cde | 22 | const RECIEVED_SMS_ACTIVITY_SUBJECT = "SMS Received"; |
6a488035 | 23 | |
be2fb01f | 24 | public $_contactDetails = []; |
6a488035 | 25 | |
be2fb01f | 26 | public $_allContactDetails = []; |
6a488035 | 27 | |
be2fb01f | 28 | public $_toContactPhone = []; |
6a488035 | 29 | |
86538308 | 30 | /** |
424029e3 | 31 | * Pre process the provider. |
32 | * | |
c490a46a | 33 | * @param CRM_Core_Form $form |
86538308 | 34 | */ |
00be9182 | 35 | public static function preProcessProvider(&$form) { |
6a488035 TO |
36 | $form->_single = FALSE; |
37 | $className = CRM_Utils_System::getClassName($form); | |
38 | ||
39 | if (property_exists($form, '_context') && | |
40 | $form->_context != 'search' && | |
41 | $className == 'CRM_Contact_Form_Task_SMS' | |
42 | ) { | |
43 | $form->_single = TRUE; | |
44 | } | |
45 | ||
46 | $providersCount = CRM_SMS_BAO_Provider::activeProviderCount(); | |
47 | ||
48 | if (!$providersCount) { | |
49 | CRM_Core_Error::statusBounce(ts('There are no SMS providers configured, or no SMS providers are set active')); | |
50 | } | |
51 | ||
52 | if ($className == 'CRM_Activity_Form_Task_SMS') { | |
53 | $activityCheck = 0; | |
54 | foreach ($form->_activityHolderIds as $value) { | |
55 | if (CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $value, 'subject', 'id') != self::RECIEVED_SMS_ACTIVITY_SUBJECT) { | |
56 | $activityCheck++; | |
57 | } | |
58 | } | |
59 | if ($activityCheck == count($form->_activityHolderIds)) { | |
60 | CRM_Core_Error::statusBounce(ts("The Reply SMS Could only be sent for activities with '%1' subject.", | |
be2fb01f | 61 | [1 => self::RECIEVED_SMS_ACTIVITY_SUBJECT] |
353ffa53 | 62 | )); |
6a488035 TO |
63 | } |
64 | } | |
65 | } | |
66 | ||
67 | /** | |
fe482240 | 68 | * Build the form object. |
6a488035 | 69 | * |
c490a46a | 70 | * @param CRM_Core_Form $form |
6a488035 | 71 | */ |
00be9182 | 72 | public static function buildQuickForm(&$form) { |
6a488035 | 73 | |
be2fb01f | 74 | $toArray = []; |
6a488035 | 75 | |
6a488035 TO |
76 | $providers = CRM_SMS_BAO_Provider::getProviders(NULL, NULL, TRUE, 'is_default desc'); |
77 | ||
be2fb01f | 78 | $providerSelect = []; |
6a488035 TO |
79 | foreach ($providers as $provider) { |
80 | $providerSelect[$provider['id']] = $provider['title']; | |
81 | } | |
82 | $suppressedSms = 0; | |
83 | //here we are getting logged in user id as array but we need target contact id. CRM-5988 | |
84 | $cid = $form->get('cid'); | |
85 | ||
86 | if ($cid) { | |
be2fb01f | 87 | $form->_contactIds = [$cid]; |
6a488035 TO |
88 | } |
89 | ||
be2fb01f CW |
90 | $to = $form->add('text', 'to', ts('To'), ['class' => 'huge'], TRUE); |
91 | $form->add('text', 'activity_subject', ts('Name The SMS'), ['class' => 'huge'], TRUE); | |
6a488035 TO |
92 | |
93 | $toSetDefault = TRUE; | |
94 | if (property_exists($form, '_context') && $form->_context == 'standalone') { | |
95 | $toSetDefault = FALSE; | |
96 | } | |
97 | ||
98 | // when form is submitted recompute contactIds | |
be2fb01f | 99 | $allToSMS = []; |
6a488035 TO |
100 | if ($to->getValue()) { |
101 | $allToPhone = explode(',', $to->getValue()); | |
102 | ||
be2fb01f | 103 | $form->_contactIds = []; |
6a488035 TO |
104 | foreach ($allToPhone as $value) { |
105 | list($contactId, $phone) = explode('::', $value); | |
106 | if ($contactId) { | |
107 | $form->_contactIds[] = $contactId; | |
108 | $form->_toContactPhone[] = $phone; | |
109 | } | |
110 | } | |
111 | $toSetDefault = TRUE; | |
112 | } | |
113 | ||
114 | //get the group of contacts as per selected by user in case of Find Activities | |
115 | if (!empty($form->_activityHolderIds)) { | |
116 | $extendTargetContacts = 0; | |
117 | $invalidActivity = 0; | |
118 | $validActivities = 0; | |
119 | foreach ($form->_activityHolderIds as $key => $id) { | |
120 | //valid activity check | |
121 | if (CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $id, 'subject', 'id') != self::RECIEVED_SMS_ACTIVITY_SUBJECT) { | |
122 | $invalidActivity++; | |
123 | continue; | |
124 | } | |
125 | ||
44f817d4 | 126 | $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); |
034500d4 | 127 | $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); |
6a488035 | 128 | //target contacts limit check |
034500d4 | 129 | $ids = array_keys(CRM_Activity_BAO_ActivityContact::getNames($id, $targetID)); |
6a488035 TO |
130 | |
131 | if (count($ids) > 1) { | |
132 | $extendTargetContacts++; | |
133 | continue; | |
134 | } | |
135 | $validActivities++; | |
136 | $form->_contactIds = empty($form->_contactIds) ? $ids : array_unique(array_merge($form->_contactIds, $ids)); | |
137 | } | |
138 | ||
139 | if (!$validActivities) { | |
140 | $errorMess = ""; | |
141 | if ($extendTargetContacts) { | |
be2fb01f | 142 | $errorMess = ts('One selected activity consists of more than one target contact.', [ |
353ffa53 | 143 | 'count' => $extendTargetContacts, |
bed98343 | 144 | 'plural' => '%count selected activities consist of more than one target contact.', |
be2fb01f | 145 | ]); |
6a488035 TO |
146 | } |
147 | if ($invalidActivity) { | |
148 | $errorMess = ($errorMess ? ' ' : ''); | |
be2fb01f | 149 | $errorMess .= ts('The selected activity is invalid.', [ |
353ffa53 | 150 | 'count' => $invalidActivity, |
bed98343 | 151 | 'plural' => '%count selected activities are invalid.', |
be2fb01f | 152 | ]); |
6a488035 | 153 | } |
be2fb01f | 154 | CRM_Core_Error::statusBounce(ts("%1: SMS Reply will not be sent.", [1 => $errorMess])); |
6a488035 TO |
155 | } |
156 | } | |
157 | ||
f53ea1ce | 158 | if (is_array($form->_contactIds) && !empty($form->_contactIds) && $toSetDefault) { |
be2fb01f | 159 | $returnProperties = [ |
6a488035 TO |
160 | 'sort_name' => 1, |
161 | 'phone' => 1, | |
162 | 'do_not_sms' => 1, | |
163 | 'is_deceased' => 1, | |
164 | 'display_name' => 1, | |
be2fb01f | 165 | ]; |
6a488035 TO |
166 | |
167 | list($form->_contactDetails) = CRM_Utils_Token::getTokenDetails($form->_contactIds, | |
168 | $returnProperties, | |
169 | FALSE, | |
170 | FALSE | |
171 | ); | |
172 | ||
173 | // make a copy of all contact details | |
174 | $form->_allContactDetails = $form->_contactDetails; | |
175 | ||
176 | foreach ($form->_contactIds as $key => $contactId) { | |
177 | $value = $form->_contactDetails[$contactId]; | |
178 | ||
179 | //to check if the phone type is "Mobile" | |
180 | $phoneTypes = CRM_Core_OptionGroup::values('phone_type', TRUE, FALSE, FALSE, NULL, 'name'); | |
181 | ||
182 | if (CRM_Utils_System::getClassName($form) == 'CRM_Activity_Form_Task_SMS') { | |
183 | //to check for "if the contact id belongs to a specified activity type" | |
a59cecb1 | 184 | // @todo use the api instead - function is deprecated. |
6a488035 TO |
185 | $actDetails = CRM_Activity_BAO_Activity::getContactActivity($contactId); |
186 | if (self::RECIEVED_SMS_ACTIVITY_SUBJECT != | |
187 | CRM_Utils_Array::retrieveValueRecursive($actDetails, 'subject') | |
188 | ) { | |
189 | $suppressedSms++; | |
190 | unset($form->_contactDetails[$contactId]); | |
191 | continue; | |
192 | } | |
193 | } | |
194 | ||
8cc574cf | 195 | if ((isset($value['phone_type_id']) && $value['phone_type_id'] != CRM_Utils_Array::value('Mobile', $phoneTypes)) || $value['do_not_sms'] || empty($value['phone']) || !empty($value['is_deceased'])) { |
6a488035 TO |
196 | |
197 | //if phone is not primary check if non-primary phone is "Mobile" | |
198 | if (!empty($value['phone']) | |
353ffa53 TO |
199 | && $value['phone_type_id'] != CRM_Utils_Array::value('Mobile', $phoneTypes) && empty($value['is_deceased']) |
200 | ) { | |
be2fb01f | 201 | $filter = ['do_not_sms' => 0]; |
6a488035 TO |
202 | $contactPhones = CRM_Core_BAO_Phone::allPhones($contactId, FALSE, 'Mobile', $filter); |
203 | if (count($contactPhones) > 0) { | |
204 | $mobilePhone = CRM_Utils_Array::retrieveValueRecursive($contactPhones, 'phone'); | |
353ffa53 | 205 | $form->_contactDetails[$contactId]['phone_id'] = CRM_Utils_Array::retrieveValueRecursive($contactPhones, 'id'); |
6a488035 | 206 | $form->_contactDetails[$contactId]['phone'] = $mobilePhone; |
9c1bc317 | 207 | $form->_contactDetails[$contactId]['phone_type_id'] = $phoneTypes['Mobile'] ?? NULL; |
6a488035 TO |
208 | } |
209 | else { | |
210 | $suppressedSms++; | |
211 | unset($form->_contactDetails[$contactId]); | |
212 | continue; | |
213 | } | |
214 | } | |
215 | else { | |
216 | $suppressedSms++; | |
217 | unset($form->_contactDetails[$contactId]); | |
218 | continue; | |
219 | } | |
220 | } | |
221 | ||
222 | if (isset($mobilePhone)) { | |
223 | $phone = $mobilePhone; | |
224 | } | |
225 | elseif (empty($form->_toContactPhone)) { | |
226 | $phone = $value['phone']; | |
227 | } | |
228 | else { | |
9c1bc317 | 229 | $phone = $form->_toContactPhone[$key] ?? NULL; |
6a488035 TO |
230 | } |
231 | ||
232 | if ($phone) { | |
be2fb01f | 233 | $toArray[] = [ |
b792e485 | 234 | 'text' => '"' . $value['sort_name'] . '" (' . $phone . ')', |
6a488035 | 235 | 'id' => "$contactId::{$phone}", |
be2fb01f | 236 | ]; |
6a488035 TO |
237 | } |
238 | } | |
239 | ||
240 | if (empty($toArray)) { | |
241 | CRM_Core_Error::statusBounce(ts('Selected contact(s) do not have a valid Phone, or communication preferences specify DO NOT SMS, or they are deceased')); | |
242 | } | |
243 | } | |
244 | ||
245 | //activity related variables | |
246 | if (isset($invalidActivity)) { | |
247 | $form->assign('invalidActivity', $invalidActivity); | |
248 | } | |
249 | if (isset($extendTargetContacts)) { | |
250 | $form->assign('extendTargetContacts', $extendTargetContacts); | |
251 | } | |
252 | ||
6a488035 TO |
253 | $form->assign('toContact', json_encode($toArray)); |
254 | $form->assign('suppressedSms', $suppressedSms); | |
255 | $form->assign('totalSelectedContacts', count($form->_contactIds)); | |
256 | ||
257 | $form->add('select', 'sms_provider_id', ts('From'), $providerSelect, TRUE); | |
258 | ||
259 | CRM_Mailing_BAO_Mailing::commonCompose($form); | |
260 | ||
261 | if ($form->_single) { | |
262 | // also fix the user context stack | |
263 | if ($form->_context) { | |
264 | $url = CRM_Utils_System::url('civicrm/dashboard', 'reset=1'); | |
265 | } | |
266 | else { | |
267 | $url = CRM_Utils_System::url('civicrm/contact/view', | |
268 | "&show=1&action=browse&cid={$form->_contactIds[0]}&selectedChild=activity" | |
269 | ); | |
270 | } | |
271 | ||
272 | $session = CRM_Core_Session::singleton(); | |
273 | $session->replaceUserContext($url); | |
274 | $form->addDefaultButtons(ts('Send SMS'), 'upload', 'cancel'); | |
275 | } | |
276 | else { | |
277 | $form->addDefaultButtons(ts('Send SMS'), 'upload'); | |
278 | } | |
279 | ||
be2fb01f | 280 | $form->addFormRule(['CRM_Contact_Form_Task_SMSCommon', 'formRule'], $form); |
6a488035 TO |
281 | } |
282 | ||
283 | /** | |
fe482240 | 284 | * Form rule. |
6a488035 | 285 | * |
77c5b619 TO |
286 | * @param array $fields |
287 | * The input form values. | |
6a488035 | 288 | * @param array $dontCare |
77c5b619 TO |
289 | * @param array $self |
290 | * Additional values form 'this'. | |
6a488035 | 291 | * |
72b3a70c CW |
292 | * @return bool|array |
293 | * true if no errors, else array of errors | |
6a488035 | 294 | */ |
00be9182 | 295 | public static function formRule($fields, $dontCare, $self) { |
be2fb01f | 296 | $errors = []; |
6a488035 TO |
297 | |
298 | $template = CRM_Core_Smarty::singleton(); | |
299 | ||
1e035d58 | 300 | if (empty($fields['sms_text_message'])) { |
301 | $errors['sms_text_message'] = ts('Please provide Text message.'); | |
6a488035 TO |
302 | } |
303 | else { | |
1e035d58 | 304 | if (!empty($fields['sms_text_message'])) { |
9c1bc317 | 305 | $messageCheck = $fields['sms_text_message'] ?? NULL; |
6a488035 TO |
306 | $messageCheck = str_replace("\r\n", "\n", $messageCheck); |
307 | if ($messageCheck && (strlen($messageCheck) > CRM_SMS_Provider::MAX_SMS_CHAR)) { | |
be2fb01f | 308 | $errors['sms_text_message'] = ts("You can configure the SMS message body up to %1 characters", [1 => CRM_SMS_Provider::MAX_SMS_CHAR]); |
6a488035 TO |
309 | } |
310 | } | |
311 | } | |
312 | ||
313 | //Added for CRM-1393 | |
a7ea8fd5 J |
314 | if (!empty($fields['SMSsaveTemplate']) && empty($fields['SMSsaveTemplateName'])) { |
315 | $errors['SMSsaveTemplateName'] = ts("Enter name to save message template"); | |
6a488035 TO |
316 | } |
317 | ||
318 | return empty($errors) ? TRUE : $errors; | |
319 | } | |
320 | ||
321 | /** | |
fe482240 | 322 | * Process the form after the input has been submitted and validated. |
6a488035 | 323 | * |
c490a46a | 324 | * @param CRM_Core_Form $form |
6a488035 | 325 | */ |
00be9182 | 326 | public static function postProcess(&$form) { |
6a488035 TO |
327 | |
328 | // check and ensure that | |
329 | $thisValues = $form->controller->exportValues($form->getName()); | |
330 | ||
331 | $fromSmsProviderId = $thisValues['sms_provider_id']; | |
332 | ||
333 | // process message template | |
a7ea8fd5 | 334 | if (!empty($thisValues['SMSsaveTemplate']) || !empty($thisValues['SMSupdateTemplate'])) { |
be2fb01f | 335 | $messageTemplate = [ |
1e035d58 | 336 | 'msg_text' => $thisValues['sms_text_message'], |
6a488035 | 337 | 'is_active' => TRUE, |
a7ea8fd5 | 338 | 'is_sms' => TRUE, |
be2fb01f | 339 | ]; |
6a488035 | 340 | |
a7ea8fd5 J |
341 | if (!empty($thisValues['SMSsaveTemplate'])) { |
342 | $messageTemplate['msg_title'] = $thisValues['SMSsaveTemplateName']; | |
c6327d7d | 343 | CRM_Core_BAO_MessageTemplate::add($messageTemplate); |
6a488035 TO |
344 | } |
345 | ||
a7ea8fd5 J |
346 | if (!empty($thisValues['SMStemplate']) && !empty($thisValues['SMSupdateTemplate'])) { |
347 | $messageTemplate['id'] = $thisValues['SMStemplate']; | |
6a488035 | 348 | unset($messageTemplate['msg_title']); |
c6327d7d | 349 | CRM_Core_BAO_MessageTemplate::add($messageTemplate); |
6a488035 TO |
350 | } |
351 | } | |
352 | ||
353 | // format contact details array to handle multiple sms from same contact | |
be2fb01f CW |
354 | $formattedContactDetails = []; |
355 | $tempPhones = []; | |
6a488035 TO |
356 | |
357 | foreach ($form->_contactIds as $key => $contactId) { | |
358 | $phone = $form->_toContactPhone[$key]; | |
359 | ||
360 | if ($phone) { | |
361 | $phoneKey = "{$contactId}::{$phone}"; | |
362 | if (!in_array($phoneKey, $tempPhones)) { | |
363 | $tempPhones[] = $phoneKey; | |
a7488080 | 364 | if (!empty($form->_contactDetails[$contactId])) { |
6a488035 TO |
365 | $formattedContactDetails[] = $form->_contactDetails[$contactId]; |
366 | } | |
367 | } | |
368 | } | |
369 | } | |
370 | ||
371 | // $smsParams carries all the arguments provided on form (or via hooks), to the provider->send() method | |
372 | // this gives flexibity to the users / implementors to add their own args via hooks specific to their sms providers | |
373 | $smsParams = $thisValues; | |
1e035d58 | 374 | unset($smsParams['sms_text_message']); |
6a488035 | 375 | $smsParams['provider_id'] = $fromSmsProviderId; |
f53ea1ce DS |
376 | $contactIds = array_keys($form->_contactDetails); |
377 | $allContactIds = array_keys($form->_allContactDetails); | |
6a488035 | 378 | |
f53ea1ce | 379 | list($sent, $activityId, $countSuccess) = CRM_Activity_BAO_Activity::sendSMS($formattedContactDetails, |
6a488035 TO |
380 | $thisValues, |
381 | $smsParams, | |
f53ea1ce | 382 | $contactIds |
6a488035 TO |
383 | ); |
384 | ||
c5a6413b | 385 | if ($countSuccess > 0) { |
be2fb01f | 386 | CRM_Core_Session::setStatus(ts('One message was sent successfully.', [ |
69078420 SL |
387 | 'plural' => '%count messages were sent successfully.', |
388 | 'count' => $countSuccess, | |
389 | ]), ts('Message Sent', ['plural' => 'Messages Sent', 'count' => $countSuccess]), 'success'); | |
6a488035 TO |
390 | } |
391 | ||
c5a6413b DS |
392 | if (is_array($sent)) { |
393 | // At least one PEAR_Error object was generated. | |
394 | // Display the error messages to the user. | |
395 | $status = '<ul>'; | |
396 | foreach ($sent as $errMsg) { | |
397 | $status .= '<li>' . $errMsg . '</li>'; | |
6a488035 | 398 | } |
c5a6413b | 399 | $status .= '</ul>'; |
be2fb01f | 400 | CRM_Core_Session::setStatus($status, ts('One Message Not Sent', [ |
69078420 SL |
401 | 'count' => count($sent), |
402 | 'plural' => '%count Messages Not Sent', | |
403 | ]), 'info'); | |
0db6c3e1 TO |
404 | } |
405 | else { | |
c5a6413b DS |
406 | //Display the name and number of contacts for those sms is not sent. |
407 | $smsNotSent = array_diff_assoc($allContactIds, $contactIds); | |
408 | ||
409 | if (!empty($smsNotSent)) { | |
be2fb01f | 410 | $not_sent = []; |
c5a6413b | 411 | foreach ($smsNotSent as $index => $contactId) { |
353ffa53 TO |
412 | $displayName = $form->_allContactDetails[$contactId]['display_name']; |
413 | $phone = $form->_allContactDetails[$contactId]['phone']; | |
c5a6413b DS |
414 | $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid=$contactId"); |
415 | $not_sent[] = "<a href='$contactViewUrl' title='$phone'>$displayName</a>"; | |
416 | } | |
417 | $status = '(' . ts('because no phone number on file or communication preferences specify DO NOT SMS or Contact is deceased'); | |
418 | if (CRM_Utils_System::getClassName($form) == 'CRM_Activity_Form_Task_SMS') { | |
be2fb01f | 419 | $status .= ' ' . ts("or the contact is not part of the activity '%1'", [1 => self::RECIEVED_SMS_ACTIVITY_SUBJECT]); |
c5a6413b DS |
420 | } |
421 | $status .= ')<ul><li>' . implode('</li><li>', $not_sent) . '</li></ul>'; | |
be2fb01f | 422 | CRM_Core_Session::setStatus($status, ts('One Message Not Sent', [ |
69078420 SL |
423 | 'count' => count($smsNotSent), |
424 | 'plural' => '%count Messages Not Sent', | |
425 | ]), 'info'); | |
6a488035 | 426 | } |
6a488035 | 427 | } |
6a488035 | 428 | } |
96025800 | 429 | |
6a488035 | 430 | } |