-- CRM-12682 applied patch and made some modifications
[civicrm-core.git] / CRM / Contact / Form / Task / EmailCommon.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
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
31 * @copyright CiviCRM LLC (c) 2004-2013
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 */
41 class 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
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'
54 ) {
55 $form->_single = TRUE;
56 }
57
58 $form->_emails = $emails = array();
59
60 $session = CRM_Core_Session::singleton();
61 $contactID = $session->get('userID');
62
63 $form->_contactIds = array($contactID);
64 $contactEmails = CRM_Core_BAO_Email::allEmails($contactID);
65
66 $form->_onHold = array();
67
68 $fromDisplayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
69 $contactID, 'display_name'
70 );
71
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;
77 }
78 else {
79 if ($email) {
80 if (in_array($email, $emails)) {
81 // CRM-3624
82 continue;
83 }
84
85 $emails[$emailId] = '"' . $fromDisplayName . '" <' . $email . '> ';
86 $form->_onHold[$emailId] = $item['on_hold'];
87 $form->_noEmails = FALSE;
88 }
89 }
90
91 $form->_emails[$emailId] = $emails[$emailId];
92 $emails[$emailId] .= $item['locationType'];
93
94 if ($item['is_primary']) {
95 $emails[$emailId] .= ' ' . ts('(preferred)');
96 }
97 $emails[$emailId] = htmlspecialchars($emails[$emailId]);
98 }
99
100 $form->assign('noEmails', $form->_noEmails);
101
102 if ($form->_noEmails) {
103 CRM_Core_Error::statusBounce(ts('Your user record does not have a valid email address'));
104 }
105
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;
113 }
114
115 $form->_fromEmails = CRM_Utils_Array::crmArrayMerge($emails, $domainEmails);
116 }
117
118 /**
119 * Build the form
120 *
121 * @access public
122 *
123 * @return void
124 */
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');
130 if ($cid) {
131 $form->_contactIds = array($cid);
132 }
133
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'));
137
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] . '" &lt;' . $matches[2][$i] . '&gt;';
145 $elementValues[] = array(
146 'name' => $name,
147 'id' => $matches[0][$i],
148 );
149 }
150
151 $var = "{$element}Contact";
152 $form->assign($var, json_encode($elementValues));
153 }
154 }
155
156 $toSetDefault = TRUE;
157 if (property_exists($form, '_context') && $form->_context == 'standalone') {
158 $toSetDefault = FALSE;
159 }
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);
167 if ($contactId) {
168 $form->_contactIds[] = $contactId;
169 $form->_toContactEmails[] = $email;
170 }
171 }
172 $toSetDefault = TRUE;
173 }
174
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;
179 }
180
181 if (is_array($form->_contactIds) && $toSetDefault) {
182 $returnProperties = array(
183 'sort_name' => 1,
184 'email' => 1,
185 'do_not_email' => 1,
186 'is_deceased' => 1,
187 'on_hold' => 1,
188 'display_name' => 1,
189 'preferred_mail_format' => 1,
190 );
191
192 list($form->_contactDetails) = CRM_Utils_Token::getTokenDetails($form->_contactIds,
193 $returnProperties,
194 FALSE,
195 FALSE
196 );
197
198 // make a copy of all contact details
199 $form->_allContactDetails = $form->_contactDetails;
200
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']) {
204 $suppressedEmails++;
205
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]);
209 }
210 else {
211 if (empty($form->_toContactEmails)) {
212 $email = $value['email'];
213 }
214 else {
215 $email = $form->_toContactEmails[$key];
216 }
217 $toArray[] = array(
218 'name' => '"' . $value['sort_name'] . '" &lt;' . $email . '&gt;',
219 'id' => "$contactId::{$email}",
220 );
221 }
222 }
223
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.'));
226 }
227 }
228
229 $form->assign('toContact', json_encode($toArray));
230 $form->assign('suppressedEmails', $suppressedEmails);
231
232 $form->assign('totalSelectedContacts', count($form->_contactIds));
233
234 $form->add('text', 'subject', ts('Subject'), 'size=50 maxlength=254', TRUE);
235
236 $form->add('select', 'fromEmailAddress', ts('From'), $form->_fromEmails, TRUE);
237
238 CRM_Mailing_BAO_Mailing::commonCompose($form);
239
240 // add attachments
241 CRM_Core_BAO_File::buildAttachment($form, NULL);
242
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'
248 );
249 $url = CRM_Utils_System::url('civicrm/contact/view/case',
250 "&reset=1&action=view&cid={$ccid}&id={$form->_caseId}"
251 );
252 }
253 elseif ($form->_context) {
254 $url = CRM_Utils_System::url('civicrm/dashboard', 'reset=1');
255 }
256 else {
257 $url = CRM_Utils_System::url('civicrm/contact/view',
258 "&show=1&action=browse&cid={$form->_contactIds[0]}&selectedChild=activity"
259 );
260 }
261
262 $session = CRM_Core_Session::singleton();
263 $session->replaceUserContext($url);
264 $form->addDefaultButtons(ts('Send Email'), 'upload', 'cancel');
265 }
266 else {
267 $form->addDefaultButtons(ts('Send Email'), 'upload');
268 }
269
270 $form->addFormRule(array('CRM_Contact_Form_Task_EmailCommon', 'formRule'), $form);
271 }
272
273 /**
274 * form rule
275 *
276 * @param array $fields the input form values
277 * @param array $dontCare
278 * @param array $self additional values form 'this'
279 *
280 * @return true if no errors, else array of errors
281 * @access public
282 *
283 */
284 static function formRule($fields, $dontCare, $self) {
285 $errors = array();
286 $template = CRM_Core_Smarty::singleton();
287
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);
292 }
293
294 //Added for CRM-1393
295 if (CRM_Utils_Array::value('saveTemplate', $fields) && empty($fields['saveTemplateName'])) {
296 $errors['saveTemplateName'] = ts("Enter name to save message template");
297 }
298
299 return empty($errors) ? TRUE : $errors;
300 }
301
302 /**
303 * process the form after the input has been submitted and validated
304 *
305 * @access public
306 *
307 * @return None
308 */
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)
313 ));
314 }
315
316 // check and ensure that
317 $formValues = $form->controller->exportValues($form->getName());
318
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'];
324
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";
329 }
330
331 // process message template
332 if (CRM_Utils_Array::value('saveTemplate', $formValues)
333 || CRM_Utils_Array::value('updateTemplate', $formValues)
334 ) {
335 $messageTemplate = array(
336 'msg_text' => $formValues['text_message'],
337 'msg_html' => $formValues['html_message'],
338 'msg_subject' => $formValues['subject'],
339 'is_active' => TRUE,
340 );
341
342 if (CRM_Utils_Array::value('saveTemplate', $formValues)) {
343 $messageTemplate['msg_title'] = $formValues['saveTemplateName'];
344 CRM_Core_BAO_MessageTemplates::add($messageTemplate);
345 }
346
347 if (CRM_Utils_Array::value('template', $formValues) &&
348 CRM_Utils_Array::value('updateTemplate', $formValues)
349 ) {
350 $messageTemplate['id'] = $formValues['template'];
351 unset($messageTemplate['msg_title']);
352 CRM_Core_BAO_MessageTemplates::add($messageTemplate);
353 }
354 }
355
356 $attachments = array();
357 CRM_Core_BAO_File::formatAttachment($formValues,
358 $attachments,
359 NULL, NULL
360 );
361
362 // format contact details array to handle multiple emails from same contact
363 $formattedContactDetails = array();
364 $tempEmails = array();
365
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])) {
370 continue;
371 }
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;
382 }
383 }
384
385 // send the mail
386 list($sent, $activityId) = CRM_Activity_BAO_Activity::sendEmail(
387 $formattedContactDetails,
388 $subject,
389 $formValues['text_message'],
390 $formValues['html_message'],
391 NULL,
392 NULL,
393 $from,
394 $attachments,
395 $cc,
396 $bcc,
397 array_keys($form->_contactDetails)
398 );
399
400 if ($sent) {
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');
403 }
404
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);
410
411 if ($emailsNotSent) {
412 $not_sent = array();
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') . ')' : '');
418 }
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');
421 }
422
423 if (isset($form->_caseId) && is_numeric($form->_caseId)) {
424 // if case-id is found in the url, create case activity record
425 $caseParams = array(
426 'activity_id' => $activityId,
427 'case_id' => $form->_caseId,
428 );
429 CRM_Case_BAO_Case::processCaseActivity($caseParams);
430 }
431 }
432 //end of function
433 }