INFRA-132 - CRM/Mailing - Convert single-line @param to multi-line
[civicrm-core.git] / CRM / Mailing / Event / BAO / Reply.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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-2014
32 * $Id$
33 *
34 */
35
36 require_once 'Mail/mime.php';
37
38 require_once 'ezc/Base/src/ezc_bootstrap.php';
39 require_once 'ezc/autoload/mail_autoload.php';
40
41 /**
42 * Class CRM_Mailing_Event_BAO_Reply
43 */
44 class CRM_Mailing_Event_BAO_Reply extends CRM_Mailing_Event_DAO_Reply {
45
46 /**
47 * Class constructor
48 */
49 public function __construct() {
50 parent::__construct();
51 }
52
53 /**
54 * Register a reply event.
55 *
56 * @param int $job_id
57 * The job ID of the reply.
58 * @param int $queue_id
59 * The queue event id.
60 * @param string $hash
61 * The hash.
62 *
63 * @param null $replyto
64 *
65 * @return object|null The mailing object, or null on failure
66 * @static
67 */
68 public static function &reply($job_id, $queue_id, $hash, $replyto = NULL) {
69 /* First make sure there's a matching queue event */
70
71 $q = CRM_Mailing_Event_BAO_Queue::verify($job_id, $queue_id, $hash);
72
73 $success = NULL;
74
75 if (!$q) {
76 return $success;
77 }
78
79 $mailing = new CRM_Mailing_BAO_Mailing();
80 $mailings = CRM_Mailing_BAO_Mailing::getTableName();
81 $jobs = CRM_Mailing_BAO_MailingJob::getTableName();
82 $mailing->query(
83 "SELECT * FROM $mailings
84 INNER JOIN $jobs
85 ON $jobs.mailing_id = $mailings.id
86 WHERE $jobs.id = {$q->job_id}"
87 );
88 $mailing->fetch();
89 if ($mailing->auto_responder) {
90 self::autoRespond($mailing, $queue_id, $replyto);
91 }
92
93 $re = new CRM_Mailing_Event_BAO_Reply();
94 $re->event_queue_id = $queue_id;
95 $re->time_stamp = date('YmdHis');
96 $re->save();
97
98 if (!$mailing->forward_replies || empty($mailing->replyto_email)) {
99 return $success;
100 }
101
102 return $mailing;
103 }
104
105 /**
106 * Forward a mailing reply
107 *
108 * @param int $queue_id
109 * Queue event ID of the sender.
110 * @param string $mailing
111 * The mailing object.
112 * @param string $bodyTxt
113 * Text part of the body (ignored if $fullEmail provided).
114 * @param string $replyto
115 * Reply-to of the incoming message.
116 * @param string $bodyHTML
117 * HTML part of the body (ignored if $fullEmail provided).
118 * @param string $fullEmail
119 * Whole email to forward in one string.
120 *
121 * @return void
122 * @static
123 */
124 public static function send($queue_id, &$mailing, &$bodyTxt, $replyto, &$bodyHTML = NULL, &$fullEmail = NULL) {
125 $domain = CRM_Core_BAO_Domain::getDomain();
126 $emails = CRM_Core_BAO_Email::getTableName();
127 $queue = CRM_Mailing_Event_BAO_Queue::getTableName();
128 $contacts = CRM_Contact_BAO_Contact::getTableName();
129
130 $eq = new CRM_Core_DAO();
131 $eq->query("SELECT $contacts.display_name as display_name,
132 $emails.email as email,
133 $queue.job_id as job_id,
134 $queue.hash as hash
135 FROM $queue
136 INNER JOIN $contacts
137 ON $queue.contact_id = $contacts.id
138 INNER JOIN $emails
139 ON $queue.email_id = $emails.id
140 WHERE $queue.id = " . CRM_Utils_Type::escape($queue_id, 'Integer')
141 );
142 $eq->fetch();
143
144 if ($fullEmail) {
145 // parse the email and set a new destination
146 $parser = new ezcMailParser;
147 $set = new ezcMailVariableSet($fullEmail);
148 $parsed = array_shift($parser->parseMail($set));
149 $parsed->to = array(new ezcMailAddress($mailing->replyto_email));
150
151 // CRM-5567: we need to set Reply-To: so that any response
152 // to the forward goes to the sender of the reply
153 $parsed->setHeader('Reply-To', $replyto instanceof ezcMailAddress ? $replyto : $parsed->from->__toString());
154
155 // $h must be an array, so we can't use generateHeaders()'s result,
156 // but we have to regenerate the headers because we changed To
157 $parsed->generateHeaders();
158 $h = $parsed->headers->getCaseSensitiveArray();
159 $b = $parsed->generateBody();
160
161 // strip Return-Path of possible bounding brackets, CRM-4502
162 if (!empty($h['Return-Path'])) {
163 $h['Return-Path'] = trim($h['Return-Path'], '<>');
164 }
165
166 // FIXME: ugly hack - find the first MIME boundary in
167 // the body and make the boundary in the header match it
168 $ct = $h['Content-Type'];
169 if (substr_count($ct, 'boundary=')) {
170 $matches = array();
171 preg_match('/^--(.*)$/m', $b, $matches);
172 $boundary = rtrim($matches[1]);
173 $parts = explode('boundary=', $ct);
174 $ct = "{$parts[0]} boundary=\"$boundary\"";
175 }
176 }
177 else {
178 $emailDomain = CRM_Core_BAO_MailSettings::defaultDomain();
179
180 if (empty($eq->display_name)) {
181 $from = $eq->email;
182 }
183 else {
184 $from = "\"{$eq->display_name}\" <{$eq->email}>";
185 }
186
187 $message = new Mail_mime("\n");
188
189 $headers = array(
190 'Subject' => "Re: {$mailing->subject}",
191 'To' => $mailing->replyto_email,
192 'From' => $from,
193 'Reply-To' => empty($replyto) ? $eq->email : $replyto,
194 'Return-Path' => "do-not-reply@{$emailDomain}",
195 );
196
197 $message->setTxtBody($bodyTxt);
198 $message->setHTMLBody($bodyHTML);
199 $b = CRM_Utils_Mail::setMimeParams($message);
200 $h = $message->headers($headers);
201 }
202
203 CRM_Mailing_BAO_Mailing::addMessageIdHeader($h, 'r', $eq->job_id, $queue_id, $eq->hash);
204 $config = CRM_Core_Config::singleton();
205 $mailer = $config->getMailer();
206
207 if (is_object($mailer)) {
208 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
209 $mailer->send($mailing->replyto_email, $h, $b);
210 unset($errorScope);
211 }
212 }
213
214 /**
215 * Send an automated response
216 *
217 * @param object $mailing
218 * The mailing object.
219 * @param int $queue_id
220 * The queue ID.
221 * @param string $replyto
222 * Optional reply-to from the reply.
223 *
224 * @return void
225 * @static
226 */
227 private static function autoRespond(&$mailing, $queue_id, $replyto) {
228 $config = CRM_Core_Config::singleton();
229
230 $contacts = CRM_Contact_DAO_Contact::getTableName();
231 $email = CRM_Core_DAO_Email::getTableName();
232 $queue = CRM_Mailing_Event_DAO_Queue::getTableName();
233
234 $eq = new CRM_Core_DAO();
235 $eq->query(
236 "SELECT $contacts.preferred_mail_format as format,
237 $email.email as email,
238 $queue.job_id as job_id,
239 $queue.hash as hash
240 FROM $contacts
241 INNER JOIN $queue ON $queue.contact_id = $contacts.id
242 INNER JOIN $email ON $queue.email_id = $email.id
243 WHERE $queue.id = " . CRM_Utils_Type::escape($queue_id, 'Integer')
244 );
245 $eq->fetch();
246
247 $to = empty($replyto) ? $eq->email : $replyto;
248
249 $component = new CRM_Mailing_BAO_Component();
250 $component->id = $mailing->reply_id;
251 $component->find(TRUE);
252
253 $message = new Mail_Mime("\n");
254
255 $domain = CRM_Core_BAO_Domain::getDomain();
256 list($domainEmailName, $_) = CRM_Core_BAO_Domain::getNameAndEmail();
257
258 $emailDomain = CRM_Core_BAO_MailSettings::defaultDomain();
259
260 $headers = array(
261 'Subject' => $component->subject,
262 'To' => $to,
263 'From' => "\"$domainEmailName\" <do-not-reply@$emailDomain>",
264 'Reply-To' => "do-not-reply@$emailDomain",
265 'Return-Path' => "do-not-reply@$emailDomain",
266 );
267
268 /* TODO: do we need reply tokens? */
269
270 $html = $component->body_html;
271 if ($component->body_text) {
272 $text = $component->body_text;
273 }
274 else {
275 $text = CRM_Utils_String::htmlToText($component->body_html);
276 }
277
278 $bao = new CRM_Mailing_BAO_Mailing();
279 $bao->body_text = $text;
280 $bao->body_html = $html;
281 $tokens = $bao->getTokens();
282
283 if ($eq->format == 'HTML' || $eq->format == 'Both') {
284 $html = CRM_Utils_Token::replaceDomainTokens($html, $domain, TRUE, $tokens['html']);
285 $html = CRM_Utils_Token::replaceMailingTokens($html, $mailing, NULL, $tokens['html']);
286 $message->setHTMLBody($html);
287 }
288 if (!$html || $eq->format == 'Text' || $eq->format == 'Both') {
289 $text = CRM_Utils_Token::replaceDomainTokens($text, $domain, FALSE, $tokens['text']);
290 $text = CRM_Utils_Token::replaceMailingTokens($text, $mailing, NULL, $tokens['text']);
291 $message->setTxtBody($text);
292 }
293
294 $b = CRM_Utils_Mail::setMimeParams($message);
295 $h = $message->headers($headers);
296 CRM_Mailing_BAO_Mailing::addMessageIdHeader($h, 'a', $eq->job_id, queue_id, $eq->hash);
297
298 $mailer = $config->getMailer();
299 if (is_object($mailer)) {
300 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
301 $mailer->send($to, $h, $b);
302 unset($errorScope);
303 }
304 }
305
306 /**
307 * Get row count for the event selector
308 *
309 * @param int $mailing_id
310 * ID of the mailing.
311 * @param int $job_id
312 * Optional ID of a job to filter on.
313 * @param bool $is_distinct
314 * Group by queue ID?.
315 *
316 * @return int Number of rows in result set
317 * @static
318 */
319 public static function getTotalCount($mailing_id, $job_id = NULL,
320 $is_distinct = FALSE
321 ) {
322 $dao = new CRM_Core_DAO();
323
324 $reply = self::getTableName();
325 $queue = CRM_Mailing_Event_BAO_Queue::getTableName();
326 $mailing = CRM_Mailing_BAO_Mailing::getTableName();
327 $job = CRM_Mailing_BAO_MailingJob::getTableName();
328
329 $query = "
330 SELECT COUNT($reply.id) as reply
331 FROM $reply
332 INNER JOIN $queue
333 ON $reply.event_queue_id = $queue.id
334 INNER JOIN $job
335 ON $queue.job_id = $job.id
336 INNER JOIN $mailing
337 ON $job.mailing_id = $mailing.id
338 AND $job.is_test = 0
339 WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer');
340
341 if (!empty($job_id)) {
342 $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer');
343 }
344
345 if ($is_distinct) {
346 $query .= " GROUP BY $queue.id ";
347 }
348
349 // query was missing
350 $dao->query($query);
351
352 if ($dao->fetch()) {
353 return $dao->reply;
354 }
355
356 return NULL;
357 }
358
359 /**
360 * Get rows for the event browser
361 *
362 * @param int $mailing_id
363 * ID of the mailing.
364 * @param int $job_id
365 * Optional ID of the job.
366 * @param bool $is_distinct
367 * Group by queue id?.
368 * @param int $offset
369 * Offset.
370 * @param int $rowCount
371 * Number of rows.
372 * @param array $sort
373 * Sort array.
374 *
375 * @return array Result set
376 * @static
377 */
378 public static function &getRows($mailing_id, $job_id = NULL,
379 $is_distinct = FALSE, $offset = NULL, $rowCount = NULL, $sort = NULL
380 ) {
381
382 $dao = new CRM_Core_Dao();
383
384 $reply = self::getTableName();
385 $queue = CRM_Mailing_Event_BAO_Queue::getTableName();
386 $mailing = CRM_Mailing_BAO_Mailing::getTableName();
387 $job = CRM_Mailing_BAO_MailingJob::getTableName();
388 $contact = CRM_Contact_BAO_Contact::getTableName();
389 $email = CRM_Core_BAO_Email::getTableName();
390
391 $query = "
392 SELECT $contact.display_name as display_name,
393 $contact.id as contact_id,
394 $email.email as email,
395 $reply.time_stamp as date
396 FROM $contact
397 INNER JOIN $queue
398 ON $queue.contact_id = $contact.id
399 INNER JOIN $email
400 ON $queue.email_id = $email.id
401 INNER JOIN $reply
402 ON $reply.event_queue_id = $queue.id
403 INNER JOIN $job
404 ON $queue.job_id = $job.id
405 INNER JOIN $mailing
406 ON $job.mailing_id = $mailing.id
407 AND $job.is_test = 0
408 WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer');
409
410 if (!empty($job_id)) {
411 $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer');
412 }
413
414 if ($is_distinct) {
415 $query .= " GROUP BY $queue.id ";
416 }
417
418 $orderBy = "sort_name ASC, {$reply}.time_stamp DESC";
419 if ($sort) {
420 if (is_string($sort)) {
421 $sort = CRM_Utils_Type::escape($sort, 'String');
422 $orderBy = $sort;
423 }
424 else {
425 $orderBy = trim($sort->orderBy());
426 }
427 }
428
429 $query .= " ORDER BY {$orderBy} ";
430
431 if ($offset || $rowCount) {
432 //Added "||$rowCount" to avoid displaying all records on first page
433 $query .= ' LIMIT ' . CRM_Utils_Type::escape($offset, 'Integer') . ', ' . CRM_Utils_Type::escape($rowCount, 'Integer');
434 }
435
436 $dao->query($query);
437
438 $results = array();
439
440 while ($dao->fetch()) {
441 $url = CRM_Utils_System::url('civicrm/contact/view',
442 "reset=1&cid={$dao->contact_id}"
443 );
444 $results[] = array(
445 'name' => "<a href=\"$url\">{$dao->display_name}</a>",
446 'email' => $dao->email,
447 'date' => CRM_Utils_Date::customFormat($dao->date),
448 );
449 }
450 return $results;
451 }
452 }