3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2015
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)
41 class CRM_Contact_Form_Task_EmailCommon
{
42 const MAX_EMAILS_KILL_SWITCH
= 50;
44 public $_contactDetails = array();
45 public $_allContactDetails = array();
46 public $_toContactEmails = array();
49 * @param CRM_Core_Form $form
51 public 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'
58 $form->_single
= TRUE;
61 $form->_emails
= $emails = array();
63 $session = CRM_Core_Session
::singleton();
64 $contactID = $session->get('userID');
66 $form->_contactIds
= array($contactID);
67 $contactEmails = CRM_Core_BAO_Email
::allEmails($contactID);
69 $form->_onHold
= array();
71 $fromDisplayName = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
72 $contactID, 'display_name'
75 foreach ($contactEmails as $emailId => $item) {
76 $email = $item['email'];
77 if (!$email && (count($emails) < 1)) {
78 // set it if no emails are present at all
79 $form->_noEmails
= TRUE;
83 if (in_array($email, $emails)) {
88 $emails[$emailId] = '"' . $fromDisplayName . '" <' . $email . '> ';
89 $form->_onHold
[$emailId] = $item['on_hold'];
90 $form->_noEmails
= FALSE;
94 $form->_emails
[$emailId] = $emails[$emailId];
95 $emails[$emailId] .= $item['locationType'];
97 if ($item['is_primary']) {
98 $emails[$emailId] .= ' ' . ts('(preferred)');
100 $emails[$emailId] = htmlspecialchars($emails[$emailId]);
103 $form->assign('noEmails', $form->_noEmails
);
105 if ($form->_noEmails
) {
106 CRM_Core_Error
::statusBounce(ts('Your user record does not have a valid email address'));
109 // now add domain from addresses
110 $domainEmails = array();
111 $domainFrom = CRM_Core_OptionGroup
::values('from_email_address');
112 foreach (array_keys($domainFrom) as $k) {
113 $domainEmail = $domainFrom[$k];
114 $domainEmails[$domainEmail] = htmlspecialchars($domainEmail);
115 $form->_emails
[$domainEmail] = $domainEmail;
118 $form->_fromEmails
= CRM_Utils_Array
::crmArrayMerge($emails, $domainEmails);
121 $defaultEmail = civicrm_api3('email', 'getsingle', array('id' => key($form->_fromEmails
)));
123 if (!empty($defaultEmail['signature_html'])) {
124 $defaults['html_message'] = '<br/><br/>--' . $defaultEmail['signature_html'];
126 if (!empty($defaultEmail['signature_text'])) {
127 $defaults['text_message'] = "\n\n--\n" . $defaultEmail['signature_text'];
129 $form->setDefaults($defaults);
133 * Build the form object.
136 * @param CRM_Core_Form $form
140 public static function buildQuickForm(&$form) {
141 $toArray = $ccArray = $bccArray = array();
142 $suppressedEmails = 0;
143 //here we are getting logged in user id as array but we need target contact id. CRM-5988
144 $cid = $form->get('cid');
146 $form->_contactIds
= explode(',', $cid);
148 if (count($form->_contactIds
) > 1) {
149 $form->_single
= FALSE;
152 $emailAttributes = array(
155 $to = $form->add('text', 'to', ts('To'), $emailAttributes, TRUE);
156 $cc = $form->add('text', 'cc_id', ts('CC'), $emailAttributes);
157 $bcc = $form->add('text', 'bcc_id', ts('BCC'), $emailAttributes);
160 if (property_exists($form, '_context') && $form->_context
== 'standalone') {
161 $setDefaults = FALSE;
164 $elements = array('to', 'cc', 'bcc');
165 $form->_allContactIds
= $form->_toContactIds
= $form->_contactIds
;
166 foreach ($elements as $element) {
167 if ($
$element->getValue()) {
168 $allEmails = explode(',', $
$element->getValue());
169 if ($element == 'to') {
170 $form->_toContactIds
= $form->_contactIds
= array();
173 foreach ($allEmails as $value) {
174 list($contactId, $email) = explode('::', $value);
178 $form->_contactIds
[] = $form->_toContactIds
[] = $contactId;
179 $form->_toContactEmails
[] = $email;
183 $form->_ccContactIds
[] = $contactId;
187 $form->_bccContactIds
[] = $contactId;
191 $form->_allContactIds
[] = $contactId;
199 //get the group of contacts as per selected by user in case of Find Activities
200 if (!empty($form->_activityHolderIds
)) {
201 $contact = $form->get('contacts');
202 $form->_allContactIds
= $form->_contactIds
= $contact;
205 // check if we need to setdefaults and check for valid contact emails / communication preferences
206 if (is_array($form->_allContactIds
) && $setDefaults) {
207 $returnProperties = array(
214 'preferred_mail_format' => 1,
217 // get the details for all selected contacts ( to, cc and bcc contacts )
218 list($form->_contactDetails
) = CRM_Utils_Token
::getTokenDetails($form->_allContactIds
,
224 // make a copy of all contact details
225 $form->_allContactDetails
= $form->_contactDetails
;
227 // perform all validations
228 foreach ($form->_allContactIds
as $key => $contactId) {
229 $value = $form->_contactDetails
[$contactId];
230 if ($value['do_not_email'] ||
empty($value['email']) ||
!empty($value['is_deceased']) ||
$value['on_hold']) {
233 // unset contact details for contacts that we won't be sending email. This is prevent extra computation
234 // during token evaluation etc.
235 unset($form->_contactDetails
[$contactId]);
238 $email = $value['email'];
240 // build array's which are used to setdefaults
241 if (in_array($contactId, $form->_toContactIds
)) {
242 $form->_toContactDetails
[$contactId] = $form->_contactDetails
[$contactId];
243 // If a particular address has been specified as the default, use that instead of contact's primary email
244 if (!empty($form->_toEmail
) && $form->_toEmail
['contact_id'] == $contactId) {
245 $email = $form->_toEmail
['email'];
248 'text' => '"' . $value['sort_name'] . '" <' . $email . '>',
249 'id' => "$contactId::{$email}",
252 elseif (in_array($contactId, $form->_ccContactIds
)) {
254 'text' => '"' . $value['sort_name'] . '" <' . $email . '>',
255 'id' => "$contactId::{$email}",
258 elseif (in_array($contactId, $form->_bccContactIds
)) {
260 'text' => '"' . $value['sort_name'] . '" <' . $email . '>',
261 'id' => "$contactId::{$email}",
267 if (empty($toArray)) {
268 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.'));
272 $form->assign('toContact', json_encode($toArray));
273 $form->assign('ccContact', json_encode($ccArray));
274 $form->assign('bccContact', json_encode($bccArray));
276 $form->assign('suppressedEmails', $suppressedEmails);
278 $form->assign('totalSelectedContacts', count($form->_contactIds
));
280 $form->add('text', 'subject', ts('Subject'), 'size=50 maxlength=254', TRUE);
282 $form->add('select', 'fromEmailAddress', ts('From'), $form->_fromEmails
, TRUE, array('class' => 'crm-select2 huge'));
284 CRM_Mailing_BAO_Mailing
::commonCompose($form);
287 CRM_Core_BAO_File
::buildAttachment($form, NULL);
289 if ($form->_single
) {
290 // also fix the user context stack
291 if ($form->_caseId
) {
292 $ccid = CRM_Core_DAO
::getFieldValue('CRM_Case_DAO_CaseContact', $form->_caseId
,
293 'contact_id', 'case_id'
295 $url = CRM_Utils_System
::url('civicrm/contact/view/case',
296 "&reset=1&action=view&cid={$ccid}&id={$form->_caseId}"
299 elseif ($form->_context
) {
300 $url = CRM_Utils_System
::url('civicrm/dashboard', 'reset=1');
303 $url = CRM_Utils_System
::url('civicrm/contact/view',
304 "&show=1&action=browse&cid={$form->_contactIds[0]}&selectedChild=activity"
308 $session = CRM_Core_Session
::singleton();
309 $session->replaceUserContext($url);
310 $form->addDefaultButtons(ts('Send Email'), 'upload', 'cancel');
313 $form->addDefaultButtons(ts('Send Email'), 'upload');
316 $form->addFormRule(array('CRM_Contact_Form_Task_EmailCommon', 'formRule'), $form);
317 CRM_Core_Resources
::singleton()->addScriptFile('civicrm', 'templates/CRM/Contact/Form/Task/EmailCommon.js', 0, 'html-header');
323 * @param array $fields
324 * The input form values.
325 * @param array $dontCare
327 * Additional values form 'this'.
330 * true if no errors, else array of errors
332 public static function formRule($fields, $dontCare, $self) {
334 $template = CRM_Core_Smarty
::singleton();
336 if (isset($fields['html_message'])) {
337 $htmlMessage = str_replace(array("\n", "\r"), ' ', $fields['html_message']);
338 $htmlMessage = str_replace('"', '\"', $htmlMessage);
339 $template->assign('htmlContent', $htmlMessage);
343 if (!empty($fields['saveTemplate']) && empty($fields['saveTemplateName'])) {
344 $errors['saveTemplateName'] = ts("Enter name to save message template");
347 return empty($errors) ?
TRUE : $errors;
351 * Process the form after the input has been submitted and validated.
354 * @param CRM_Core_Form $form
358 public static function postProcess(&$form) {
359 if (count($form->_contactIds
) > self
::MAX_EMAILS_KILL_SWITCH
) {
360 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.',
361 array(1 => self
::MAX_EMAILS_KILL_SWITCH
)
365 // check and ensure that
366 $formValues = $form->controller
->exportValues($form->getName());
368 $fromEmail = $formValues['fromEmailAddress'];
369 $from = CRM_Utils_Array
::value($fromEmail, $form->_emails
);
370 $subject = $formValues['subject'];
372 // CRM-13378: Append CC and BCC information at the end of Activity Details and format cc and bcc fields
373 $elements = array('cc_id', 'bcc_id');
374 $additionalDetails = NULL;
375 $ccValues = $bccValues = array();
376 foreach ($elements as $element) {
377 if (!empty($formValues[$element])) {
378 $allEmails = explode(',', $formValues[$element]);
379 foreach ($allEmails as $value) {
380 list($contactId, $email) = explode('::', $value);
381 $contactURL = CRM_Utils_System
::url('civicrm/contact/view', "reset=1&force=1&cid={$contactId}", TRUE);
384 $ccValues['email'][] = '"' . $form->_contactDetails
[$contactId]['sort_name'] . '" <' . $email . '>';
385 $ccValues['details'][] = "<a href='{$contactURL}'>" . $form->_contactDetails
[$contactId]['display_name'] . "</a>";
389 $bccValues['email'][] = '"' . $form->_contactDetails
[$contactId]['sort_name'] . '" <' . $email . '>';
390 $bccValues['details'][] = "<a href='{$contactURL}'>" . $form->_contactDetails
[$contactId]['display_name'] . "</a>";
398 if (!empty($ccValues)) {
399 $cc = implode(',', $ccValues['email']);
400 $additionalDetails .= "\ncc : " . implode(", ", $ccValues['details']);
402 if (!empty($bccValues)) {
403 $bcc = implode(',', $bccValues['email']);
404 $additionalDetails .= "\nbcc : " . implode(", ", $bccValues['details']);
407 // CRM-5916: prepend case id hash to CiviCase-originating emails’ subjects
408 if (isset($form->_caseId
) && is_numeric($form->_caseId
)) {
409 $hash = substr(sha1(CIVICRM_SITE_KEY
. $form->_caseId
), 0, 7);
410 $subject = "[case #$hash] $subject";
413 // process message template
414 if (!empty($formValues['saveTemplate']) ||
!empty($formValues['updateTemplate'])) {
415 $messageTemplate = array(
416 'msg_text' => $formValues['text_message'],
417 'msg_html' => $formValues['html_message'],
418 'msg_subject' => $formValues['subject'],
422 if (!empty($formValues['saveTemplate'])) {
423 $messageTemplate['msg_title'] = $formValues['saveTemplateName'];
424 CRM_Core_BAO_MessageTemplate
::add($messageTemplate);
427 if (!empty($formValues['template']) && !empty($formValues['updateTemplate'])) {
428 $messageTemplate['id'] = $formValues['template'];
429 unset($messageTemplate['msg_title']);
430 CRM_Core_BAO_MessageTemplate
::add($messageTemplate);
434 $attachments = array();
435 CRM_Core_BAO_File
::formatAttachment($formValues,
440 // format contact details array to handle multiple emails from same contact
441 $formattedContactDetails = array();
442 $tempEmails = array();
444 foreach ($form->_contactIds
as $key => $contactId) {
445 // if we dont have details on this contactID, we should ignore
446 // potentially this is due to the contact not wanting to receive email
447 if (!isset($form->_contactDetails
[$contactId])) {
450 $email = $form->_toContactEmails
[$key];
451 // prevent duplicate emails if same email address is selected CRM-4067
452 // we should allow same emails for different contacts
453 $emailKey = "{$contactId}::{$email}";
454 if (!in_array($emailKey, $tempEmails)) {
455 $tempEmails[] = $emailKey;
456 $details = $form->_contactDetails
[$contactId];
457 $details['email'] = $email;
458 unset($details['email_id']);
459 $formattedContactDetails[] = $details;
464 list($sent, $activityId) = CRM_Activity_BAO_Activity
::sendEmail(
465 $formattedContactDetails,
467 $formValues['text_message'],
468 $formValues['html_message'],
475 array_keys($form->_toContactDetails
),
480 $count_success = count($form->_toContactDetails
);
481 CRM_Core_Session
::setStatus(ts('One message was sent successfully.', array(
482 'plural' => '%count messages were sent successfully.',
483 'count' => $count_success,
484 )), ts('Message Sent', array('plural' => 'Messages Sent', 'count' => $count_success)), 'success');
487 // Display the name and number of contacts for those email is not sent.
488 // php 5.4 throws out a notice since the values of these below arrays are arrays.
489 // the behavior is not documented in the php manual, but it does the right thing
490 // suppressing the notices to get things in good shape going forward
491 $emailsNotSent = @array_diff_assoc
($form->_allContactDetails
, $form->_contactDetails
);
493 if ($emailsNotSent) {
495 foreach ($emailsNotSent as $contactId => $values) {
496 $displayName = $values['display_name'];
497 $email = $values['email'];
498 $contactViewUrl = CRM_Utils_System
::url('civicrm/contact/view', "reset=1&cid=$contactId");
499 $not_sent[] = "<a href='$contactViewUrl' title='$email'>$displayName</a>" . ($values['on_hold'] ?
'(' . ts('on hold') . ')' : '');
501 $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>';
502 CRM_Core_Session
::setStatus($status, ts('One Message Not Sent', array(
503 'count' => count($emailsNotSent),
504 'plural' => '%count Messages Not Sent',
508 if (isset($form->_caseId
)) {
509 // if case-id is found in the url, create case activity record
510 $cases = explode(',', $form->_caseId
);
511 foreach ($cases as $key => $val) {
512 if (is_numeric($val)) {
514 'activity_id' => $activityId,
517 CRM_Case_BAO_Case
::processCaseActivity($caseParams);