fixed spelling of address on lines 122 and 247
[civicrm-core.git] / CRM / Contact / Form / Task / SMSCommon.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 +--------------------------------------------------------------------+
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 */
21class 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}