CRM-13379 prevent double click on confirm button on main contribution page to prevent...
[civicrm-core.git] / CRM / Contact / Form / Task / EmailCommon.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
232624b1 4 | CiviCRM version 4.4 |
6a488035
TO
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 */
41class CRM_Contact_Form_Task_EmailCommon {
42 CONST MAX_EMAILS_KILL_SWITCH = 50;
43
44 public $_contactDetails = array();
c1d26519 45 public $_additionalContactDetails = array();
6a488035
TO
46 public $_allContactDetails = array();
47 public $_toContactEmails = array();
48
49 static function preProcessFromAddress(&$form) {
50 $form->_single = FALSE;
51 $className = CRM_Utils_System::getClassName($form);
52 if (property_exists($form, '_context') &&
53 $form->_context != 'search' &&
54 $className == 'CRM_Contact_Form_Task_Email'
55 ) {
56 $form->_single = TRUE;
57 }
58
59 $form->_emails = $emails = array();
60
61 $session = CRM_Core_Session::singleton();
62 $contactID = $session->get('userID');
63
64 $form->_contactIds = array($contactID);
65 $contactEmails = CRM_Core_BAO_Email::allEmails($contactID);
66
67 $form->_onHold = array();
68
69 $fromDisplayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
70 $contactID, 'display_name'
71 );
72
73 foreach ($contactEmails as $emailId => $item) {
74 $email = $item['email'];
3d41f850
DL
75 if (!$email && (count($emails) < 1)) {
76 // set it if no emails are present at all
6a488035
TO
77 $form->_noEmails = TRUE;
78 }
79 else {
80 if ($email) {
81 if (in_array($email, $emails)) {
82 // CRM-3624
83 continue;
84 }
85
86 $emails[$emailId] = '"' . $fromDisplayName . '" <' . $email . '> ';
87 $form->_onHold[$emailId] = $item['on_hold'];
3d41f850 88 $form->_noEmails = FALSE;
6a488035
TO
89 }
90 }
91
92 $form->_emails[$emailId] = $emails[$emailId];
6a488035
TO
93 $emails[$emailId] .= $item['locationType'];
94
95 if ($item['is_primary']) {
96 $emails[$emailId] .= ' ' . ts('(preferred)');
97 }
98 $emails[$emailId] = htmlspecialchars($emails[$emailId]);
99 }
100
101 $form->assign('noEmails', $form->_noEmails);
102
103 if ($form->_noEmails) {
104 CRM_Core_Error::statusBounce(ts('Your user record does not have a valid email address'));
105 }
106
107 // now add domain from addresses
108 $domainEmails = array();
cbf48754 109 $domainFrom = CRM_Core_OptionGroup::values('from_email_address');
6a488035
TO
110 foreach (array_keys($domainFrom) as $k) {
111 $domainEmail = $domainFrom[$k];
112 $domainEmails[$domainEmail] = htmlspecialchars($domainEmail);
113 $form->_emails[$domainEmail] = $domainEmail;
114 }
115
116 $form->_fromEmails = CRM_Utils_Array::crmArrayMerge($emails, $domainEmails);
117 }
118
119 /**
120 * Build the form
121 *
122 * @access public
123 *
124 * @return void
125 */
126 static function buildQuickForm(&$form) {
127 $toArray = $ccArray = $bccArray = array();
128 $suppressedEmails = 0;
129 //here we are getting logged in user id as array but we need target contact id. CRM-5988
130 $cid = $form->get('cid');
131 if ($cid) {
132 $form->_contactIds = array($cid);
133 }
134
135 $to = $form->add('text', 'to', ts('To'), '', TRUE);
136 $cc = $form->add('text', 'cc_id', ts('CC'));
137 $bcc = $form->add('text', 'bcc_id', ts('BCC'));
138
139 $elements = array('cc', 'bcc');
140 foreach ($elements as $element) {
141 if ($$element->getValue()) {
142 preg_match_all('!"(.*?)"\s+<\s*(.*?)\s*>!', $$element->getValue(), $matches);
143 $elementValues = array();
144 for ($i = 0; $i < count($matches[0]); $i++) {
145 $name = '"' . $matches[1][$i] . '" &lt;' . $matches[2][$i] . '&gt;';
146 $elementValues[] = array(
147 'name' => $name,
148 'id' => $matches[0][$i],
149 );
c1d26519 150 $id = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Email', $matches[2][$i] , 'contact_id', 'email');
151 $form->_additionalContactDetails[$element][$id] = CRM_Contact_BAO_Contact::displayName($id);
6a488035 152 }
6a488035
TO
153 $var = "{$element}Contact";
154 $form->assign($var, json_encode($elementValues));
155 }
156 }
157
158 $toSetDefault = TRUE;
159 if (property_exists($form, '_context') && $form->_context == 'standalone') {
160 $toSetDefault = FALSE;
161 }
162 // when form is submitted recompute contactIds
163 $allToEmails = array();
164 if ($to->getValue()) {
165 $allToEmails = explode(',', $to->getValue());
166 $form->_contactIds = array();
167 foreach ($allToEmails as $value) {
168 list($contactId, $email) = explode('::', $value);
169 if ($contactId) {
170 $form->_contactIds[] = $contactId;
171 $form->_toContactEmails[] = $email;
172 }
173 }
174 $toSetDefault = TRUE;
175 }
176
177 //get the group of contacts as per selected by user in case of Find Activities
178 if (!empty($form->_activityHolderIds)) {
179 $contact = $form->get('contacts');
180 $form->_contactIds = $contact;
181 }
182
183 if (is_array($form->_contactIds) && $toSetDefault) {
184 $returnProperties = array(
185 'sort_name' => 1,
186 'email' => 1,
187 'do_not_email' => 1,
188 'is_deceased' => 1,
189 'on_hold' => 1,
190 'display_name' => 1,
191 'preferred_mail_format' => 1,
192 );
193
194 list($form->_contactDetails) = CRM_Utils_Token::getTokenDetails($form->_contactIds,
195 $returnProperties,
196 FALSE,
197 FALSE
198 );
199
200 // make a copy of all contact details
201 $form->_allContactDetails = $form->_contactDetails;
202
203 foreach ($form->_contactIds as $key => $contactId) {
204 $value = $form->_contactDetails[$contactId];
205 if ($value['do_not_email'] || empty($value['email']) || CRM_Utils_Array::value('is_deceased', $value) || $value['on_hold']) {
206 $suppressedEmails++;
207
208 // unset contact details for contacts that we won't be sending email. This is prevent extra computation
209 // during token evaluation etc.
210 unset($form->_contactDetails[$contactId]);
211 }
212 else {
213 if (empty($form->_toContactEmails)) {
214 $email = $value['email'];
215 }
216 else {
217 $email = $form->_toContactEmails[$key];
218 }
219 $toArray[] = array(
220 'name' => '"' . $value['sort_name'] . '" &lt;' . $email . '&gt;',
221 'id' => "$contactId::{$email}",
222 );
223 }
224 }
225
226 if (empty($toArray)) {
227 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.'));
228 }
229 }
230
231 $form->assign('toContact', json_encode($toArray));
232 $form->assign('suppressedEmails', $suppressedEmails);
233
234 $form->assign('totalSelectedContacts', count($form->_contactIds));
235
236 $form->add('text', 'subject', ts('Subject'), 'size=50 maxlength=254', TRUE);
237
238 $form->add('select', 'fromEmailAddress', ts('From'), $form->_fromEmails, TRUE);
239
240 CRM_Mailing_BAO_Mailing::commonCompose($form);
241
242 // add attachments
243 CRM_Core_BAO_File::buildAttachment($form, NULL);
244
245 if ($form->_single) {
246 // also fix the user context stack
247 if ($form->_caseId) {
248 $ccid = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseContact', $form->_caseId,
249 'contact_id', 'case_id'
250 );
251 $url = CRM_Utils_System::url('civicrm/contact/view/case',
252 "&reset=1&action=view&cid={$ccid}&id={$form->_caseId}"
253 );
254 }
255 elseif ($form->_context) {
256 $url = CRM_Utils_System::url('civicrm/dashboard', 'reset=1');
257 }
258 else {
259 $url = CRM_Utils_System::url('civicrm/contact/view',
260 "&show=1&action=browse&cid={$form->_contactIds[0]}&selectedChild=activity"
261 );
262 }
263
264 $session = CRM_Core_Session::singleton();
265 $session->replaceUserContext($url);
266 $form->addDefaultButtons(ts('Send Email'), 'upload', 'cancel');
267 }
268 else {
269 $form->addDefaultButtons(ts('Send Email'), 'upload');
270 }
271
272 $form->addFormRule(array('CRM_Contact_Form_Task_EmailCommon', 'formRule'), $form);
ba933ceb 273 CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'templates/CRM/Core/Form.js');
6a488035
TO
274 }
275
276 /**
277 * form rule
278 *
279 * @param array $fields the input form values
280 * @param array $dontCare
281 * @param array $self additional values form 'this'
282 *
283 * @return true if no errors, else array of errors
284 * @access public
285 *
286 */
287 static function formRule($fields, $dontCare, $self) {
288 $errors = array();
289 $template = CRM_Core_Smarty::singleton();
290
291 if (isset($fields['html_message'])) {
292 $htmlMessage = str_replace(array("\n", "\r"), ' ', $fields['html_message']);
293 $htmlMessage = str_replace('"', '\"', $htmlMessage);
294 $template->assign('htmlContent', $htmlMessage);
295 }
296
297 //Added for CRM-1393
298 if (CRM_Utils_Array::value('saveTemplate', $fields) && empty($fields['saveTemplateName'])) {
299 $errors['saveTemplateName'] = ts("Enter name to save message template");
300 }
301
302 return empty($errors) ? TRUE : $errors;
303 }
304
305 /**
306 * process the form after the input has been submitted and validated
307 *
308 * @access public
309 *
310 * @return None
311 */
312 static function postProcess(&$form) {
313 if (count($form->_contactIds) > self::MAX_EMAILS_KILL_SWITCH) {
314 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.',
315 array(1 => self::MAX_EMAILS_KILL_SWITCH)
316 ));
317 }
318
319 // check and ensure that
320 $formValues = $form->controller->exportValues($form->getName());
321
322 $fromEmail = $formValues['fromEmailAddress'];
323 $from = CRM_Utils_Array::value($fromEmail, $form->_emails);
324 $cc = CRM_Utils_Array::value('cc_id', $formValues);
325 $bcc = CRM_Utils_Array::value('bcc_id', $formValues);
326 $subject = $formValues['subject'];
327
c1d26519 328
329 // CRM-13378: Append CC and BCC information at the end of Activity Details
330 $elements = array('cc', 'bcc');
331 $additionalDetails = NULL;
332 foreach ($elements as $element) {
333 if (isset($form->_additionalContactDetails[$element])) {
334 foreach ($form->_additionalContactDetails[$element] as $id => $display_name) {
335 $url = CRM_Utils_System::url('civicrm/contact/view', "reset=1&force=1&cid={$id}");
336 $form->_additionalContactDetails[$element][$id] = "<a href=$url>$display_name</a>";
337 }
338 $additionalDetails .= "\n$element : " . implode(", ", $form->_additionalContactDetails[$element]);
339 unset($form->_additionalContactDetails[$element]);
340 }
341 }
342
6a488035
TO
343 // CRM-5916: prepend case id hash to CiviCase-originating emails’ subjects
344 if (isset($form->_caseId) && is_numeric($form->_caseId)) {
345 $hash = substr(sha1(CIVICRM_SITE_KEY . $form->_caseId), 0, 7);
346 $subject = "[case #$hash] $subject";
347 }
348
349 // process message template
350 if (CRM_Utils_Array::value('saveTemplate', $formValues)
351 || CRM_Utils_Array::value('updateTemplate', $formValues)
352 ) {
353 $messageTemplate = array(
354 'msg_text' => $formValues['text_message'],
355 'msg_html' => $formValues['html_message'],
356 'msg_subject' => $formValues['subject'],
357 'is_active' => TRUE,
358 );
359
360 if (CRM_Utils_Array::value('saveTemplate', $formValues)) {
361 $messageTemplate['msg_title'] = $formValues['saveTemplateName'];
c6327d7d 362 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
6a488035
TO
363 }
364
365 if (CRM_Utils_Array::value('template', $formValues) &&
366 CRM_Utils_Array::value('updateTemplate', $formValues)
367 ) {
368 $messageTemplate['id'] = $formValues['template'];
369 unset($messageTemplate['msg_title']);
c6327d7d 370 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
6a488035
TO
371 }
372 }
373
374 $attachments = array();
375 CRM_Core_BAO_File::formatAttachment($formValues,
376 $attachments,
377 NULL, NULL
378 );
379
380 // format contact details array to handle multiple emails from same contact
381 $formattedContactDetails = array();
382 $tempEmails = array();
383
384 foreach ($form->_contactIds as $key => $contactId) {
385 // if we dont have details on this contactID, we should ignore
386 // potentially this is due to the contact not wanting to receive email
387 if (!isset($form->_contactDetails[$contactId])) {
388 continue;
389 }
390 $email = $form->_toContactEmails[$key];
391 // prevent duplicate emails if same email address is selected CRM-4067
392 // we should allow same emails for different contacts
393 $emailKey = "{$contactId}::{$email}";
394 if (!in_array($emailKey, $tempEmails)) {
395 $tempEmails[] = $emailKey;
396 $details = $form->_contactDetails[$contactId];
397 $details['email'] = $email;
398 unset($details['email_id']);
399 $formattedContactDetails[] = $details;
400 }
401 }
402
403 // send the mail
404 list($sent, $activityId) = CRM_Activity_BAO_Activity::sendEmail(
405 $formattedContactDetails,
406 $subject,
407 $formValues['text_message'],
408 $formValues['html_message'],
409 NULL,
410 NULL,
411 $from,
412 $attachments,
413 $cc,
414 $bcc,
c1d26519 415 array_keys($form->_contactDetails),
416 $additionalDetails
6a488035
TO
417 );
418
419 if ($sent) {
420 $count_success = count($form->_contactDetails);
421 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');
422 }
423
a5611c8e
DL
424 // Display the name and number of contacts for those email is not sent.
425 // php 5.4 throws out a notice since the values of these below arrays are arrays.
426 // the behavior is not documented in the php manual, but it does the right thing
427 // suppressing the notices to get things in good shape going forward
428 $emailsNotSent = @array_diff_assoc($form->_allContactDetails, $form->_contactDetails);
6a488035
TO
429
430 if ($emailsNotSent) {
431 $not_sent = array();
432 foreach ($emailsNotSent as $contactId => $values) {
433 $displayName = $values['display_name'];
434 $email = $values['email'];
435 $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid=$contactId");
436 $not_sent[] = "<a href='$contactViewUrl' title='$email'>$displayName</a>" . ($values['on_hold'] ? '(' . ts('on hold') . ')' : '');
437 }
438 $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>';
439 CRM_Core_Session::setStatus($status, ts('One Message Not Sent', array('count' => count($emailsNotSent), 'plural' => '%count Messages Not Sent')), 'info');
440 }
441
442 if (isset($form->_caseId) && is_numeric($form->_caseId)) {
443 // if case-id is found in the url, create case activity record
444 $caseParams = array(
445 'activity_id' => $activityId,
446 'case_id' => $form->_caseId,
447 );
448 CRM_Case_BAO_Case::processCaseActivity($caseParams);
449 }
450 }
451 //end of function
452}