3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
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-2013
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();
48 static function preProcessFromAddress(&$form) {
49 $form->_single
= FALSE;
50 $className = CRM_Utils_System
::getClassName($form);
51 if (property_exists($form, '_context') &&
52 $form->_context
!= 'search' &&
53 $className == 'CRM_Contact_Form_Task_Email'
55 $form->_single
= TRUE;
58 $form->_emails
= $emails = array();
60 $session = CRM_Core_Session
::singleton();
61 $contactID = $session->get('userID');
63 $form->_contactIds
= array($contactID);
64 $contactEmails = CRM_Core_BAO_Email
::allEmails($contactID);
66 $form->_onHold
= array();
68 $fromDisplayName = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
69 $contactID, 'display_name'
72 foreach ($contactEmails as $emailId => $item) {
73 $email = $item['email'];
74 if (!$email && (count($emails) < 1)) {
75 // set it if no emails are present at all
76 $form->_noEmails
= TRUE;
80 if (in_array($email, $emails)) {
85 $emails[$emailId] = '"' . $fromDisplayName . '" <' . $email . '> ';
86 $form->_onHold
[$emailId] = $item['on_hold'];
87 $form->_noEmails
= FALSE;
91 $form->_emails
[$emailId] = $emails[$emailId];
92 $emails[$emailId] .= $item['locationType'];
94 if ($item['is_primary']) {
95 $emails[$emailId] .= ' ' . ts('(preferred)');
97 $emails[$emailId] = htmlspecialchars($emails[$emailId]);
100 $form->assign('noEmails', $form->_noEmails
);
102 if ($form->_noEmails
) {
103 CRM_Core_Error
::statusBounce(ts('Your user record does not have a valid email address'));
106 // now add domain from addresses
107 $domainEmails = array();
108 $domainFrom = CRM_Core_OptionGroup
::values('from_email_address');
109 foreach (array_keys($domainFrom) as $k) {
110 $domainEmail = $domainFrom[$k];
111 $domainEmails[$domainEmail] = htmlspecialchars($domainEmail);
112 $form->_emails
[$domainEmail] = $domainEmail;
115 $form->_fromEmails
= CRM_Utils_Array
::crmArrayMerge($emails, $domainEmails);
125 static function buildQuickForm(&$form) {
126 $toArray = $ccArray = $bccArray = array();
127 $suppressedEmails = 0;
128 //here we are getting logged in user id as array but we need target contact id. CRM-5988
129 $cid = $form->get('cid');
131 $form->_contactIds
= array($cid);
134 $to = $form->add('text', 'to', ts('To'), '', TRUE);
135 $cc = $form->add('text', 'cc_id', ts('CC'));
136 $bcc = $form->add('text', 'bcc_id', ts('BCC'));
138 $elements = array('cc', 'bcc');
139 foreach ($elements as $element) {
140 if ($
$element->getValue()) {
141 preg_match_all('!"(.*?)"\s+<\s*(.*?)\s*>!', $
$element->getValue(), $matches);
142 $elementValues = array();
143 for ($i = 0; $i < count($matches[0]); $i++
) {
144 $name = '"' . $matches[1][$i] . '" <' . $matches[2][$i] . '>';
145 $elementValues[] = array(
147 'id' => $matches[0][$i],
151 $var = "{$element}Contact";
152 $form->assign($var, json_encode($elementValues));
156 $toSetDefault = TRUE;
157 if (property_exists($form, '_context') && $form->_context
== 'standalone') {
158 $toSetDefault = FALSE;
160 // when form is submitted recompute contactIds
161 $allToEmails = array();
162 if ($to->getValue()) {
163 $allToEmails = explode(',', $to->getValue());
164 $form->_contactIds
= array();
165 foreach ($allToEmails as $value) {
166 list($contactId, $email) = explode('::', $value);
168 $form->_contactIds
[] = $contactId;
169 $form->_toContactEmails
[] = $email;
172 $toSetDefault = TRUE;
175 //get the group of contacts as per selected by user in case of Find Activities
176 if (!empty($form->_activityHolderIds
)) {
177 $contact = $form->get('contacts');
178 $form->_contactIds
= $contact;
181 if (is_array($form->_contactIds
) && $toSetDefault) {
182 $returnProperties = array(
189 'preferred_mail_format' => 1,
192 list($form->_contactDetails
) = CRM_Utils_Token
::getTokenDetails($form->_contactIds
,
198 // make a copy of all contact details
199 $form->_allContactDetails
= $form->_contactDetails
;
201 foreach ($form->_contactIds
as $key => $contactId) {
202 $value = $form->_contactDetails
[$contactId];
203 if ($value['do_not_email'] ||
empty($value['email']) || CRM_Utils_Array
::value('is_deceased', $value) ||
$value['on_hold']) {
206 // unset contact details for contacts that we won't be sending email. This is prevent extra computation
207 // during token evaluation etc.
208 unset($form->_contactDetails
[$contactId]);
211 if (empty($form->_toContactEmails
)) {
212 $email = $value['email'];
215 $email = $form->_toContactEmails
[$key];
218 'name' => '"' . $value['sort_name'] . '" <' . $email . '>',
219 'id' => "$contactId::{$email}",
224 if (empty($toArray)) {
225 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.'));
229 $form->assign('toContact', json_encode($toArray));
230 $form->assign('suppressedEmails', $suppressedEmails);
232 $form->assign('totalSelectedContacts', count($form->_contactIds
));
234 $form->add('text', 'subject', ts('Subject'), 'size=50 maxlength=254', TRUE);
236 $form->add('select', 'fromEmailAddress', ts('From'), $form->_fromEmails
, TRUE);
238 CRM_Mailing_BAO_Mailing
::commonCompose($form);
241 CRM_Core_BAO_File
::buildAttachment($form, NULL);
243 if ($form->_single
) {
244 // also fix the user context stack
245 if ($form->_caseId
) {
246 $ccid = CRM_Core_DAO
::getFieldValue('CRM_Case_DAO_CaseContact', $form->_caseId
,
247 'contact_id', 'case_id'
249 $url = CRM_Utils_System
::url('civicrm/contact/view/case',
250 "&reset=1&action=view&cid={$ccid}&id={$form->_caseId}"
253 elseif ($form->_context
) {
254 $url = CRM_Utils_System
::url('civicrm/dashboard', 'reset=1');
257 $url = CRM_Utils_System
::url('civicrm/contact/view',
258 "&show=1&action=browse&cid={$form->_contactIds[0]}&selectedChild=activity"
262 $session = CRM_Core_Session
::singleton();
263 $session->replaceUserContext($url);
264 $form->addDefaultButtons(ts('Send Email'), 'upload', 'cancel');
267 $form->addDefaultButtons(ts('Send Email'), 'upload');
270 $form->addFormRule(array('CRM_Contact_Form_Task_EmailCommon', 'formRule'), $form);
276 * @param array $fields the input form values
277 * @param array $dontCare
278 * @param array $self additional values form 'this'
280 * @return true if no errors, else array of errors
284 static function formRule($fields, $dontCare, $self) {
286 $template = CRM_Core_Smarty
::singleton();
288 if (isset($fields['html_message'])) {
289 $htmlMessage = str_replace(array("\n", "\r"), ' ', $fields['html_message']);
290 $htmlMessage = str_replace('"', '\"', $htmlMessage);
291 $template->assign('htmlContent', $htmlMessage);
295 if (CRM_Utils_Array
::value('saveTemplate', $fields) && empty($fields['saveTemplateName'])) {
296 $errors['saveTemplateName'] = ts("Enter name to save message template");
299 return empty($errors) ?
TRUE : $errors;
303 * process the form after the input has been submitted and validated
309 static function postProcess(&$form) {
310 if (count($form->_contactIds
) > self
::MAX_EMAILS_KILL_SWITCH
) {
311 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.',
312 array(1 => self
::MAX_EMAILS_KILL_SWITCH
)
316 // check and ensure that
317 $formValues = $form->controller
->exportValues($form->getName());
319 $fromEmail = $formValues['fromEmailAddress'];
320 $from = CRM_Utils_Array
::value($fromEmail, $form->_emails
);
321 $cc = CRM_Utils_Array
::value('cc_id', $formValues);
322 $bcc = CRM_Utils_Array
::value('bcc_id', $formValues);
323 $subject = $formValues['subject'];
325 // CRM-5916: prepend case id hash to CiviCase-originating emails’ subjects
326 if (isset($form->_caseId
) && is_numeric($form->_caseId
)) {
327 $hash = substr(sha1(CIVICRM_SITE_KEY
. $form->_caseId
), 0, 7);
328 $subject = "[case #$hash] $subject";
331 // process message template
332 if (CRM_Utils_Array
::value('saveTemplate', $formValues)
333 || CRM_Utils_Array
::value('updateTemplate', $formValues)
335 $messageTemplate = array(
336 'msg_text' => $formValues['text_message'],
337 'msg_html' => $formValues['html_message'],
338 'msg_subject' => $formValues['subject'],
342 if (CRM_Utils_Array
::value('saveTemplate', $formValues)) {
343 $messageTemplate['msg_title'] = $formValues['saveTemplateName'];
344 CRM_Core_BAO_MessageTemplates
::add($messageTemplate);
347 if (CRM_Utils_Array
::value('template', $formValues) &&
348 CRM_Utils_Array
::value('updateTemplate', $formValues)
350 $messageTemplate['id'] = $formValues['template'];
351 unset($messageTemplate['msg_title']);
352 CRM_Core_BAO_MessageTemplates
::add($messageTemplate);
356 $attachments = array();
357 CRM_Core_BAO_File
::formatAttachment($formValues,
362 // format contact details array to handle multiple emails from same contact
363 $formattedContactDetails = array();
364 $tempEmails = array();
366 foreach ($form->_contactIds
as $key => $contactId) {
367 // if we dont have details on this contactID, we should ignore
368 // potentially this is due to the contact not wanting to receive email
369 if (!isset($form->_contactDetails
[$contactId])) {
372 $email = $form->_toContactEmails
[$key];
373 // prevent duplicate emails if same email address is selected CRM-4067
374 // we should allow same emails for different contacts
375 $emailKey = "{$contactId}::{$email}";
376 if (!in_array($emailKey, $tempEmails)) {
377 $tempEmails[] = $emailKey;
378 $details = $form->_contactDetails
[$contactId];
379 $details['email'] = $email;
380 unset($details['email_id']);
381 $formattedContactDetails[] = $details;
386 list($sent, $activityId) = CRM_Activity_BAO_Activity
::sendEmail(
387 $formattedContactDetails,
389 $formValues['text_message'],
390 $formValues['html_message'],
397 array_keys($form->_contactDetails
)
401 $count_success = count($form->_contactDetails
);
402 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');
405 // Display the name and number of contacts for those email is not sent.
406 // php 5.4 throws out a notice since the values of these below arrays are arrays.
407 // the behavior is not documented in the php manual, but it does the right thing
408 // suppressing the notices to get things in good shape going forward
409 $emailsNotSent = @array_diff_assoc
($form->_allContactDetails
, $form->_contactDetails
);
411 if ($emailsNotSent) {
413 foreach ($emailsNotSent as $contactId => $values) {
414 $displayName = $values['display_name'];
415 $email = $values['email'];
416 $contactViewUrl = CRM_Utils_System
::url('civicrm/contact/view', "reset=1&cid=$contactId");
417 $not_sent[] = "<a href='$contactViewUrl' title='$email'>$displayName</a>" . ($values['on_hold'] ?
'(' . ts('on hold') . ')' : '');
419 $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>';
420 CRM_Core_Session
::setStatus($status, ts('One Message Not Sent', array('count' => count($emailsNotSent), 'plural' => '%count Messages Not Sent')), 'info');
423 if (isset($form->_caseId
) && is_numeric($form->_caseId
)) {
424 // if case-id is found in the url, create case activity record
426 'activity_id' => $activityId,
427 'case_id' => $form->_caseId
,
429 CRM_Case_BAO_Case
::processCaseActivity($caseParams);