Merge remote-tracking branch 'upstream/4.4' into 4.4-master-2014-07-07-15-48-59
[civicrm-core.git] / CRM / Contact / Form / Task / EmailCommon.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
06b69b18 4 | CiviCRM version 4.5 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26*/
27
28/**
29 *
30 * @package CRM
06b69b18 31 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
32 * $Id$
33 *
34 */
35
36/**
37 * This class provides the common functionality for sending email to
38 * one or a group of contact ids. This class is reused by all the search
39 * components in CiviCRM (since they all have send email as a task)
40 */
41class CRM_Contact_Form_Task_EmailCommon {
42 CONST MAX_EMAILS_KILL_SWITCH = 50;
43
44 public $_contactDetails = array();
45 public $_allContactDetails = array();
46 public $_toContactEmails = array();
47
86538308
EM
48 /**
49 * @param $form
50 */
6a488035
TO
51 static function preProcessFromAddress(&$form) {
52 $form->_single = FALSE;
53 $className = CRM_Utils_System::getClassName($form);
54 if (property_exists($form, '_context') &&
55 $form->_context != 'search' &&
56 $className == 'CRM_Contact_Form_Task_Email'
57 ) {
58 $form->_single = TRUE;
59 }
60
61 $form->_emails = $emails = array();
62
63 $session = CRM_Core_Session::singleton();
64 $contactID = $session->get('userID');
65
66 $form->_contactIds = array($contactID);
67 $contactEmails = CRM_Core_BAO_Email::allEmails($contactID);
68
69 $form->_onHold = array();
70
71 $fromDisplayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
72 $contactID, 'display_name'
73 );
74
75 foreach ($contactEmails as $emailId => $item) {
76 $email = $item['email'];
3d41f850
DL
77 if (!$email && (count($emails) < 1)) {
78 // set it if no emails are present at all
6a488035
TO
79 $form->_noEmails = TRUE;
80 }
81 else {
82 if ($email) {
83 if (in_array($email, $emails)) {
84 // CRM-3624
85 continue;
86 }
87
88 $emails[$emailId] = '"' . $fromDisplayName . '" <' . $email . '> ';
89 $form->_onHold[$emailId] = $item['on_hold'];
3d41f850 90 $form->_noEmails = FALSE;
6a488035
TO
91 }
92 }
93
94 $form->_emails[$emailId] = $emails[$emailId];
6a488035
TO
95 $emails[$emailId] .= $item['locationType'];
96
97 if ($item['is_primary']) {
98 $emails[$emailId] .= ' ' . ts('(preferred)');
99 }
100 $emails[$emailId] = htmlspecialchars($emails[$emailId]);
101 }
102
103 $form->assign('noEmails', $form->_noEmails);
104
105 if ($form->_noEmails) {
106 CRM_Core_Error::statusBounce(ts('Your user record does not have a valid email address'));
107 }
108
109 // now add domain from addresses
110 $domainEmails = array();
cbf48754 111 $domainFrom = CRM_Core_OptionGroup::values('from_email_address');
6a488035
TO
112 foreach (array_keys($domainFrom) as $k) {
113 $domainEmail = $domainFrom[$k];
114 $domainEmails[$domainEmail] = htmlspecialchars($domainEmail);
115 $form->_emails[$domainEmail] = $domainEmail;
116 }
117
118 $form->_fromEmails = CRM_Utils_Array::crmArrayMerge($emails, $domainEmails);
119 }
120
121 /**
122 * Build the form
123 *
124 * @access public
125 *
da6b46f4
EM
126 * @param $form
127 *
6a488035
TO
128 * @return void
129 */
130 static function buildQuickForm(&$form) {
131 $toArray = $ccArray = $bccArray = array();
132 $suppressedEmails = 0;
133 //here we are getting logged in user id as array but we need target contact id. CRM-5988
134 $cid = $form->get('cid');
135 if ($cid) {
9cb404b4
N
136 $form->_contactIds = explode(',',$cid);
137 }
138 if (count($form->_contactIds) > 1) {
139 $form->_single = FALSE;
6a488035 140 }
a03d5ab9 141
cac1236c
M
142 $emailAttributes = array(
143 'class' => 'huge',
144 );
145 $to = $form->add('text', 'to', ts('To'), $emailAttributes, TRUE);
146 $cc = $form->add('text', 'cc_id', ts('CC'), $emailAttributes);
147 $bcc = $form->add('text', 'bcc_id', ts('BCC'), $emailAttributes);
6a488035 148
3f77636a 149 $setDefaults = TRUE;
150 if (property_exists($form, '_context') && $form->_context == 'standalone') {
151 $setDefaults = FALSE;
152 }
153
154 $elements = array('to', 'cc', 'bcc');
16947fa6 155 $form->_allContactIds = $form->_toContactIds = $form->_contactIds;
6a488035
TO
156 foreach ($elements as $element) {
157 if ($$element->getValue()) {
3f77636a 158 $allEmails = explode(',', $$element->getValue());
159 if ($element == 'to') {
16947fa6 160 $form->_toContactIds = $form->_contactIds = array();
6a488035 161 }
6a488035 162
3f77636a 163 foreach ($allEmails as $value) {
164 list($contactId, $email) = explode('::', $value);
165 if ($contactId) {
166 switch ($element) {
167 case 'to':
168 $form->_contactIds[] = $form->_toContactIds[] = $contactId;
169 $form->_toContactEmails[] = $email;
170 break;
171 case 'cc':
172 $form->_ccContactIds[] = $contactId;
173 break;
174 case 'bcc':
175 $form->_bccContactIds[] = $contactId;
176 break;
177 }
178
179 $form->_allContactIds[] = $contactId;
180 }
6a488035 181 }
3f77636a 182
183 $setDefaults = TRUE;
6a488035 184 }
6a488035
TO
185 }
186
187 //get the group of contacts as per selected by user in case of Find Activities
188 if (!empty($form->_activityHolderIds)) {
189 $contact = $form->get('contacts');
16947fa6 190 $form->_allContactIds = $form->_contactIds = $contact;
6a488035
TO
191 }
192
3f77636a 193 // check if we need to setdefaults and check for valid contact emails / communication preferences
16947fa6 194 if (is_array($form->_allContactIds) && $setDefaults) {
6a488035
TO
195 $returnProperties = array(
196 'sort_name' => 1,
197 'email' => 1,
198 'do_not_email' => 1,
199 'is_deceased' => 1,
200 'on_hold' => 1,
201 'display_name' => 1,
202 'preferred_mail_format' => 1,
203 );
204
3f77636a 205 // get the details for all selected contacts ( to, cc and bcc contacts )
206 list($form->_contactDetails) = CRM_Utils_Token::getTokenDetails($form->_allContactIds,
6a488035
TO
207 $returnProperties,
208 FALSE,
209 FALSE
210 );
211
212 // make a copy of all contact details
213 $form->_allContactDetails = $form->_contactDetails;
214
3f77636a 215 // perform all validations
216 foreach ($form->_allContactIds as $key => $contactId) {
6a488035 217 $value = $form->_contactDetails[$contactId];
8cc574cf 218 if ($value['do_not_email'] || empty($value['email']) || !empty($value['is_deceased']) || $value['on_hold']) {
6a488035
TO
219 $suppressedEmails++;
220
221 // unset contact details for contacts that we won't be sending email. This is prevent extra computation
222 // during token evaluation etc.
223 unset($form->_contactDetails[$contactId]);
224 }
225 else {
3f77636a 226 $email = $value['email'];
227
228 // build array's which are used to setdefaults
229 if (in_array($contactId, $form->_toContactIds)) {
230 $form->_toContactDetails[$contactId] = $form->_contactDetails[$contactId];
231 $toArray[] = array(
a03d5ab9 232 'text' => '"' . $value['sort_name'] . '" <' . $email . '>',
3f77636a 233 'id' => "$contactId::{$email}",
234 );
235 }
236 elseif (in_array($contactId, $form->_ccContactIds)) {
237 $ccArray[] = array(
a03d5ab9 238 'text' => '"' . $value['sort_name'] . '" <' . $email . '>',
3f77636a 239 'id' => "$contactId::{$email}",
240 );
6a488035 241 }
3f77636a 242 elseif (in_array($contactId, $form->_bccContactIds)) {
243 $bccArray[] = array(
a03d5ab9 244 'text' => '"' . $value['sort_name'] . '" <' . $email . '>',
3f77636a 245 'id' => "$contactId::{$email}",
246 );
6a488035 247 }
6a488035
TO
248 }
249 }
250
251 if (empty($toArray)) {
252 CRM_Core_Error::statusBounce(ts('Selected contact(s) do not have a valid email address, or communication preferences specify DO NOT EMAIL, or they are deceased or Primary email address is On Hold.'));
253 }
254 }
255
256 $form->assign('toContact', json_encode($toArray));
3f77636a 257 $form->assign('ccContact', json_encode($ccArray));
258 $form->assign('bccContact', json_encode($bccArray));
259
6a488035
TO
260 $form->assign('suppressedEmails', $suppressedEmails);
261
262 $form->assign('totalSelectedContacts', count($form->_contactIds));
263
264 $form->add('text', 'subject', ts('Subject'), 'size=50 maxlength=254', TRUE);
265
f7305cbc 266 $form->add('select', 'fromEmailAddress', ts('From'), $form->_fromEmails, TRUE, array('class' => 'crm-select2 huge'));
6a488035
TO
267
268 CRM_Mailing_BAO_Mailing::commonCompose($form);
269
270 // add attachments
271 CRM_Core_BAO_File::buildAttachment($form, NULL);
272
273 if ($form->_single) {
274 // also fix the user context stack
275 if ($form->_caseId) {
276 $ccid = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseContact', $form->_caseId,
277 'contact_id', 'case_id'
278 );
279 $url = CRM_Utils_System::url('civicrm/contact/view/case',
280 "&reset=1&action=view&cid={$ccid}&id={$form->_caseId}"
281 );
282 }
283 elseif ($form->_context) {
284 $url = CRM_Utils_System::url('civicrm/dashboard', 'reset=1');
285 }
286 else {
287 $url = CRM_Utils_System::url('civicrm/contact/view',
288 "&show=1&action=browse&cid={$form->_contactIds[0]}&selectedChild=activity"
289 );
290 }
291
292 $session = CRM_Core_Session::singleton();
293 $session->replaceUserContext($url);
294 $form->addDefaultButtons(ts('Send Email'), 'upload', 'cancel');
295 }
296 else {
297 $form->addDefaultButtons(ts('Send Email'), 'upload');
298 }
299
300 $form->addFormRule(array('CRM_Contact_Form_Task_EmailCommon', 'formRule'), $form);
28530b98 301 CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'templates/CRM/Contact/Form/Task/EmailCommon.js');
6a488035
TO
302 }
303
304 /**
305 * form rule
306 *
307 * @param array $fields the input form values
308 * @param array $dontCare
309 * @param array $self additional values form 'this'
310 *
311 * @return true if no errors, else array of errors
312 * @access public
313 *
314 */
315 static function formRule($fields, $dontCare, $self) {
316 $errors = array();
317 $template = CRM_Core_Smarty::singleton();
318
319 if (isset($fields['html_message'])) {
320 $htmlMessage = str_replace(array("\n", "\r"), ' ', $fields['html_message']);
321 $htmlMessage = str_replace('"', '\"', $htmlMessage);
322 $template->assign('htmlContent', $htmlMessage);
323 }
324
325 //Added for CRM-1393
a7488080 326 if (!empty($fields['saveTemplate']) && empty($fields['saveTemplateName'])) {
6a488035
TO
327 $errors['saveTemplateName'] = ts("Enter name to save message template");
328 }
329
330 return empty($errors) ? TRUE : $errors;
331 }
332
333 /**
334 * process the form after the input has been submitted and validated
335 *
336 * @access public
337 *
fd31fa4c
EM
338 * @param $form
339 *
355ba699 340 * @return void
6a488035
TO
341 */
342 static function postProcess(&$form) {
343 if (count($form->_contactIds) > self::MAX_EMAILS_KILL_SWITCH) {
344 CRM_Core_Error::fatal(ts('Please do not use this task to send a lot of emails (greater than %1). We recommend using CiviMail instead.',
345 array(1 => self::MAX_EMAILS_KILL_SWITCH)
346 ));
347 }
348
349 // check and ensure that
350 $formValues = $form->controller->exportValues($form->getName());
351
352 $fromEmail = $formValues['fromEmailAddress'];
353 $from = CRM_Utils_Array::value($fromEmail, $form->_emails);
6a488035
TO
354 $subject = $formValues['subject'];
355
3f77636a 356 // CRM-13378: Append CC and BCC information at the end of Activity Details and format cc and bcc fields
357 $elements = array('cc_id', 'bcc_id');
c1d26519 358 $additionalDetails = NULL;
3f77636a 359 $ccValues = $bccValues = array();
c1d26519 360 foreach ($elements as $element) {
a7488080 361 if (!empty($formValues[$element])) {
3f77636a 362 $allEmails = explode(',', $formValues[$element]);
363 foreach ($allEmails as $value) {
364 list($contactId, $email) = explode('::', $value);
365 $contactURL = CRM_Utils_System::url('civicrm/contact/view', "reset=1&force=1&cid={$contactId}", true);
366 switch ($element) {
367 case 'cc_id':
368 $ccValues['email'][] = '"' . $form->_contactDetails[$contactId]['sort_name'] . '" <' . $email . '>';
369 $ccValues['details'][] = "<a href='{$contactURL}'>" . $form->_contactDetails[$contactId]['display_name'] . "</a>";
370 break;
371 case 'bcc_id':
372 $bccValues['email'][]= '"' . $form->_contactDetails[$contactId]['sort_name'] . '" <' . $email . '>';
373 $bccValues['details'][] = "<a href='{$contactURL}'>" . $form->_contactDetails[$contactId]['display_name'] . "</a>";
374 break;
375 }
c1d26519 376 }
c1d26519 377 }
378 }
379
3f77636a 380 $cc = $bcc = '';
381 if (!empty($ccValues)) {
382 $cc = implode(',', $ccValues['email']);
383 $additionalDetails .= "\ncc : " . implode(", ", $ccValues['details']);
384 }
385 if (!empty($bccValues)) {
386 $bcc = implode(',', $bccValues['email']);
387 $additionalDetails .= "\nbcc : " . implode(", ", $bccValues['details']);
388 }
389
6a488035
TO
390 // CRM-5916: prepend case id hash to CiviCase-originating emails’ subjects
391 if (isset($form->_caseId) && is_numeric($form->_caseId)) {
392 $hash = substr(sha1(CIVICRM_SITE_KEY . $form->_caseId), 0, 7);
393 $subject = "[case #$hash] $subject";
394 }
395
396 // process message template
8cc574cf 397 if (!empty($formValues['saveTemplate']) || !empty($formValues['updateTemplate'])) {
6a488035
TO
398 $messageTemplate = array(
399 'msg_text' => $formValues['text_message'],
400 'msg_html' => $formValues['html_message'],
401 'msg_subject' => $formValues['subject'],
402 'is_active' => TRUE,
403 );
404
a7488080 405 if (!empty($formValues['saveTemplate'])) {
6a488035 406 $messageTemplate['msg_title'] = $formValues['saveTemplateName'];
c6327d7d 407 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
6a488035
TO
408 }
409
8cc574cf 410 if (!empty($formValues['template']) && !empty($formValues['updateTemplate'])) {
6a488035
TO
411 $messageTemplate['id'] = $formValues['template'];
412 unset($messageTemplate['msg_title']);
c6327d7d 413 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
6a488035
TO
414 }
415 }
416
417 $attachments = array();
418 CRM_Core_BAO_File::formatAttachment($formValues,
419 $attachments,
420 NULL, NULL
421 );
422
423 // format contact details array to handle multiple emails from same contact
424 $formattedContactDetails = array();
425 $tempEmails = array();
426
427 foreach ($form->_contactIds as $key => $contactId) {
428 // if we dont have details on this contactID, we should ignore
429 // potentially this is due to the contact not wanting to receive email
430 if (!isset($form->_contactDetails[$contactId])) {
431 continue;
432 }
433 $email = $form->_toContactEmails[$key];
434 // prevent duplicate emails if same email address is selected CRM-4067
435 // we should allow same emails for different contacts
436 $emailKey = "{$contactId}::{$email}";
437 if (!in_array($emailKey, $tempEmails)) {
438 $tempEmails[] = $emailKey;
439 $details = $form->_contactDetails[$contactId];
440 $details['email'] = $email;
441 unset($details['email_id']);
442 $formattedContactDetails[] = $details;
443 }
444 }
445
446 // send the mail
447 list($sent, $activityId) = CRM_Activity_BAO_Activity::sendEmail(
448 $formattedContactDetails,
449 $subject,
450 $formValues['text_message'],
451 $formValues['html_message'],
452 NULL,
453 NULL,
454 $from,
455 $attachments,
456 $cc,
457 $bcc,
3f77636a 458 array_keys($form->_toContactDetails),
c1d26519 459 $additionalDetails
6a488035
TO
460 );
461
462 if ($sent) {
3f77636a 463 $count_success = count($form->_toContactDetails);
6a488035
TO
464 CRM_Core_Session::setStatus(ts('One message was sent successfully.', array('plural' => '%count messages were sent successfully.', 'count' => $count_success)), ts('Message Sent', array('plural' => 'Messages Sent', 'count' => $count_success)), 'success');
465 }
466
a5611c8e
DL
467 // Display the name and number of contacts for those email is not sent.
468 // php 5.4 throws out a notice since the values of these below arrays are arrays.
469 // the behavior is not documented in the php manual, but it does the right thing
470 // suppressing the notices to get things in good shape going forward
471 $emailsNotSent = @array_diff_assoc($form->_allContactDetails, $form->_contactDetails);
6a488035
TO
472
473 if ($emailsNotSent) {
474 $not_sent = array();
475 foreach ($emailsNotSent as $contactId => $values) {
476 $displayName = $values['display_name'];
477 $email = $values['email'];
478 $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid=$contactId");
479 $not_sent[] = "<a href='$contactViewUrl' title='$email'>$displayName</a>" . ($values['on_hold'] ? '(' . ts('on hold') . ')' : '');
480 }
481 $status = '(' . ts('because no email address on file or communication preferences specify DO NOT EMAIL or Contact is deceased or Primary email address is On Hold') . ')<ul><li>' . implode('</li><li>', $not_sent) . '</li></ul>';
482 CRM_Core_Session::setStatus($status, ts('One Message Not Sent', array('count' => count($emailsNotSent), 'plural' => '%count Messages Not Sent')), 'info');
483 }
484
9cb404b4 485 if (isset($form->_caseId)) {
6a488035 486 // if case-id is found in the url, create case activity record
9cb404b4
N
487 $cases = explode(',', $form->_caseId);
488 foreach($cases as $key => $val) {
489 if (is_numeric($val)) {
490 $caseParams = array(
491 'activity_id' => $activityId,
492 'case_id' => $val,
493 );
494 CRM_Case_BAO_Case::processCaseActivity($caseParams);
495 }
496 }
6a488035
TO
497 }
498 }
499 //end of function
500}