Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
bc77d7c0 | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
6a488035 | 5 | | | |
bc77d7c0 TO |
6 | | This work is published under the GNU AGPLv3 license with some | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
6a488035 | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
6a488035 TO |
11 | |
12 | /** | |
13 | * | |
14 | * @package CRM | |
ca5cec67 | 15 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
6a488035 TO |
16 | */ |
17 | ||
18 | require_once 'Mail/mime.php'; | |
19 | ||
28518c90 EM |
20 | /** |
21 | * Class CRM_Mailing_Event_BAO_Reply | |
22 | */ | |
6a488035 TO |
23 | class CRM_Mailing_Event_BAO_Reply extends CRM_Mailing_Event_DAO_Reply { |
24 | ||
25 | /** | |
fe482240 | 26 | * Class constructor. |
6a488035 | 27 | */ |
00be9182 | 28 | public function __construct() { |
6a488035 TO |
29 | parent::__construct(); |
30 | } | |
31 | ||
32 | /** | |
33 | * Register a reply event. | |
34 | * | |
90c8230e TO |
35 | * @param int $job_id |
36 | * The job ID of the reply. | |
37 | * @param int $queue_id | |
38 | * The queue event id. | |
39 | * @param string $hash | |
40 | * The hash. | |
fd31fa4c EM |
41 | * |
42 | * @param null $replyto | |
6a488035 | 43 | * |
72b3a70c CW |
44 | * @return object|null |
45 | * The mailing object, or null on failure | |
6a488035 TO |
46 | */ |
47 | public static function &reply($job_id, $queue_id, $hash, $replyto = NULL) { | |
25606795 | 48 | // First make sure there's a matching queue event. |
6a488035 TO |
49 | $q = CRM_Mailing_Event_BAO_Queue::verify($job_id, $queue_id, $hash); |
50 | ||
51 | $success = NULL; | |
52 | ||
53 | if (!$q) { | |
54 | return $success; | |
55 | } | |
56 | ||
57 | $mailing = new CRM_Mailing_BAO_Mailing(); | |
58 | $mailings = CRM_Mailing_BAO_Mailing::getTableName(); | |
9da8dc8c | 59 | $jobs = CRM_Mailing_BAO_MailingJob::getTableName(); |
6a488035 | 60 | $mailing->query( |
03e04002 | 61 | "SELECT * FROM $mailings |
62 | INNER JOIN $jobs | |
6a488035 TO |
63 | ON $jobs.mailing_id = $mailings.id |
64 | WHERE $jobs.id = {$q->job_id}" | |
65 | ); | |
66 | $mailing->fetch(); | |
67 | if ($mailing->auto_responder) { | |
68 | self::autoRespond($mailing, $queue_id, $replyto); | |
69 | } | |
70 | ||
71 | $re = new CRM_Mailing_Event_BAO_Reply(); | |
72 | $re->event_queue_id = $queue_id; | |
73 | $re->time_stamp = date('YmdHis'); | |
74 | $re->save(); | |
75 | ||
76 | if (!$mailing->forward_replies || empty($mailing->replyto_email)) { | |
77 | return $success; | |
78 | } | |
79 | ||
80 | return $mailing; | |
81 | } | |
82 | ||
83 | /** | |
fe482240 | 84 | * Forward a mailing reply. |
6a488035 | 85 | * |
90c8230e TO |
86 | * @param int $queue_id |
87 | * Queue event ID of the sender. | |
88 | * @param string $mailing | |
89 | * The mailing object. | |
90 | * @param string $bodyTxt | |
91 | * Text part of the body (ignored if $fullEmail provided). | |
92 | * @param string $replyto | |
93 | * Reply-to of the incoming message. | |
94 | * @param string $bodyHTML | |
95 | * HTML part of the body (ignored if $fullEmail provided). | |
96 | * @param string $fullEmail | |
97 | * Whole email to forward in one string. | |
6a488035 TO |
98 | */ |
99 | public static function send($queue_id, &$mailing, &$bodyTxt, $replyto, &$bodyHTML = NULL, &$fullEmail = NULL) { | |
100 | $domain = CRM_Core_BAO_Domain::getDomain(); | |
101 | $emails = CRM_Core_BAO_Email::getTableName(); | |
102 | $queue = CRM_Mailing_Event_BAO_Queue::getTableName(); | |
103 | $contacts = CRM_Contact_BAO_Contact::getTableName(); | |
bf309c74 | 104 | $domain_id = CRM_Core_Config::domainID(); |
be2fb01f | 105 | $domainValues = civicrm_api3('Domain', 'get', ['sequential' => 1, 'id' => $domain_id]); |
73dd1b20 | 106 | $fromEmail = CRM_Core_BAO_Domain::getNoReplyEmailAddress(); |
6a488035 TO |
107 | |
108 | $eq = new CRM_Core_DAO(); | |
109 | $eq->query("SELECT $contacts.display_name as display_name, | |
8c8b0e30 DL |
110 | $emails.email as email, |
111 | $queue.job_id as job_id, | |
112 | $queue.hash as hash | |
6a488035 TO |
113 | FROM $queue |
114 | INNER JOIN $contacts | |
115 | ON $queue.contact_id = $contacts.id | |
116 | INNER JOIN $emails | |
117 | ON $queue.email_id = $emails.id | |
118 | WHERE $queue.id = " . CRM_Utils_Type::escape($queue_id, 'Integer') | |
119 | ); | |
120 | $eq->fetch(); | |
121 | ||
122 | if ($fullEmail) { | |
123 | // parse the email and set a new destination | |
bed98343 | 124 | $parser = new ezcMailParser(); |
6a488035 TO |
125 | $set = new ezcMailVariableSet($fullEmail); |
126 | $parsed = array_shift($parser->parseMail($set)); | |
be2fb01f | 127 | $parsed->to = [new ezcMailAddress($mailing->replyto_email)]; |
6a488035 TO |
128 | |
129 | // CRM-5567: we need to set Reply-To: so that any response | |
130 | // to the forward goes to the sender of the reply | |
fb18363b | 131 | $parsed->setHeader('Reply-To', $replyto instanceof ezcMailAddress ? $replyto : $parsed->from->__toString()); |
6a488035 | 132 | |
73dd1b20 | 133 | // Using the original from address may not be permitted by the mailer. |
134 | $fromName = empty($parsed->from->name) ? $parsed->from->email : "{$parsed->from->name} ({$parsed->from->email})"; | |
135 | $parsed->from = new ezcMailAddress($fromEmail, $fromName); | |
136 | ||
bf309c74 SL |
137 | // CRM-17754 Include re-sent headers to indicate that we have forwarded on the email |
138 | $domainEmail = $domainValues['values'][0]['from_email']; | |
139 | $parsed->setHeader('Resent-From', $domainEmail); | |
140 | $parsed->setHeader('Resent-Date', date('r')); | |
c8f920d6 | 141 | // Rewrite any invalid Return-Path headers. |
142 | $parsed->setHeader('Return-Path', $fromEmail); | |
bf309c74 | 143 | |
6a488035 TO |
144 | // $h must be an array, so we can't use generateHeaders()'s result, |
145 | // but we have to regenerate the headers because we changed To | |
146 | $parsed->generateHeaders(); | |
147 | $h = $parsed->headers->getCaseSensitiveArray(); | |
148 | $b = $parsed->generateBody(); | |
149 | ||
6a488035 TO |
150 | // FIXME: ugly hack - find the first MIME boundary in |
151 | // the body and make the boundary in the header match it | |
152 | $ct = $h['Content-Type']; | |
153 | if (substr_count($ct, 'boundary=')) { | |
be2fb01f | 154 | $matches = []; |
6a488035 TO |
155 | preg_match('/^--(.*)$/m', $b, $matches); |
156 | $boundary = rtrim($matches[1]); | |
157 | $parts = explode('boundary=', $ct); | |
158 | $ct = "{$parts[0]} boundary=\"$boundary\""; | |
159 | } | |
160 | } | |
161 | else { | |
73dd1b20 | 162 | $fromName = empty($eq->display_name) ? $eq->email : "{$eq->display_name} ({$eq->email})"; |
6a488035 TO |
163 | |
164 | $message = new Mail_mime("\n"); | |
165 | ||
be2fb01f | 166 | $headers = [ |
6a488035 TO |
167 | 'Subject' => "Re: {$mailing->subject}", |
168 | 'To' => $mailing->replyto_email, | |
73dd1b20 | 169 | 'From' => "\"$fromName\" <$fromEmail>", |
fb18363b | 170 | 'Reply-To' => empty($replyto) ? $eq->email : $replyto, |
576fcb9c | 171 | 'Return-Path' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), |
bf309c74 SL |
172 | // CRM-17754 Include re-sent headers to indicate that we have forwarded on the email |
173 | 'Resent-From' => $domainValues['values'][0]['from_email'], | |
174 | 'Resent-Date' => date('r'), | |
be2fb01f | 175 | ]; |
6a488035 TO |
176 | |
177 | $message->setTxtBody($bodyTxt); | |
178 | $message->setHTMLBody($bodyHTML); | |
179 | $b = CRM_Utils_Mail::setMimeParams($message); | |
180 | $h = $message->headers($headers); | |
181 | } | |
182 | ||
183 | CRM_Mailing_BAO_Mailing::addMessageIdHeader($h, 'r', $eq->job_id, $queue_id, $eq->hash); | |
184 | $config = CRM_Core_Config::singleton(); | |
048222df | 185 | $mailer = \Civi::service('pear_mail'); |
6a488035 | 186 | |
6a488035 | 187 | if (is_object($mailer)) { |
6a4257d4 | 188 | $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); |
6a488035 | 189 | $mailer->send($mailing->replyto_email, $h, $b); |
6a4257d4 | 190 | unset($errorScope); |
6a488035 TO |
191 | } |
192 | } | |
193 | ||
194 | /** | |
fe482240 | 195 | * Send an automated response. |
6a488035 | 196 | * |
90c8230e TO |
197 | * @param object $mailing |
198 | * The mailing object. | |
199 | * @param int $queue_id | |
200 | * The queue ID. | |
201 | * @param string $replyto | |
202 | * Optional reply-to from the reply. | |
6a488035 TO |
203 | */ |
204 | private static function autoRespond(&$mailing, $queue_id, $replyto) { | |
205 | $config = CRM_Core_Config::singleton(); | |
206 | ||
207 | $contacts = CRM_Contact_DAO_Contact::getTableName(); | |
208 | $email = CRM_Core_DAO_Email::getTableName(); | |
209 | $queue = CRM_Mailing_Event_DAO_Queue::getTableName(); | |
210 | ||
211 | $eq = new CRM_Core_DAO(); | |
212 | $eq->query( | |
213 | "SELECT $contacts.preferred_mail_format as format, | |
60bf6dfe DL |
214 | $email.email as email, |
215 | $queue.job_id as job_id, | |
216 | $queue.hash as hash | |
6a488035 TO |
217 | FROM $contacts |
218 | INNER JOIN $queue ON $queue.contact_id = $contacts.id | |
219 | INNER JOIN $email ON $queue.email_id = $email.id | |
220 | WHERE $queue.id = " . CRM_Utils_Type::escape($queue_id, 'Integer') | |
221 | ); | |
222 | $eq->fetch(); | |
223 | ||
224 | $to = empty($replyto) ? $eq->email : $replyto; | |
225 | ||
4825de4a | 226 | $component = new CRM_Mailing_BAO_MailingComponent(); |
6a488035 TO |
227 | $component->id = $mailing->reply_id; |
228 | $component->find(TRUE); | |
229 | ||
230 | $message = new Mail_Mime("\n"); | |
231 | ||
232 | $domain = CRM_Core_BAO_Domain::getDomain(); | |
233 | list($domainEmailName, $_) = CRM_Core_BAO_Domain::getNameAndEmail(); | |
234 | ||
be2fb01f | 235 | $headers = [ |
6a488035 TO |
236 | 'Subject' => $component->subject, |
237 | 'To' => $to, | |
576fcb9c | 238 | 'From' => "\"$domainEmailName\" <" . CRM_Core_BAO_Domain::getNoReplyEmailAddress() . '>', |
239 | 'Reply-To' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), | |
240 | 'Return-Path' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), | |
be2fb01f | 241 | ]; |
6a488035 | 242 | |
25606795 | 243 | // TODO: do we need reply tokens? |
6a488035 TO |
244 | $html = $component->body_html; |
245 | if ($component->body_text) { | |
246 | $text = $component->body_text; | |
247 | } | |
248 | else { | |
249 | $text = CRM_Utils_String::htmlToText($component->body_html); | |
250 | } | |
251 | ||
252 | $bao = new CRM_Mailing_BAO_Mailing(); | |
253 | $bao->body_text = $text; | |
254 | $bao->body_html = $html; | |
255 | $tokens = $bao->getTokens(); | |
256 | ||
257 | if ($eq->format == 'HTML' || $eq->format == 'Both') { | |
258 | $html = CRM_Utils_Token::replaceDomainTokens($html, $domain, TRUE, $tokens['html']); | |
259 | $html = CRM_Utils_Token::replaceMailingTokens($html, $mailing, NULL, $tokens['html']); | |
260 | $message->setHTMLBody($html); | |
261 | } | |
262 | if (!$html || $eq->format == 'Text' || $eq->format == 'Both') { | |
263 | $text = CRM_Utils_Token::replaceDomainTokens($text, $domain, FALSE, $tokens['text']); | |
264 | $text = CRM_Utils_Token::replaceMailingTokens($text, $mailing, NULL, $tokens['text']); | |
265 | $message->setTxtBody($text); | |
266 | } | |
267 | ||
268 | $b = CRM_Utils_Mail::setMimeParams($message); | |
269 | $h = $message->headers($headers); | |
270 | CRM_Mailing_BAO_Mailing::addMessageIdHeader($h, 'a', $eq->job_id, queue_id, $eq->hash); | |
271 | ||
048222df | 272 | $mailer = \Civi::service('pear_mail'); |
6a488035 | 273 | if (is_object($mailer)) { |
6a4257d4 | 274 | $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); |
6a488035 | 275 | $mailer->send($to, $h, $b); |
6a4257d4 | 276 | unset($errorScope); |
6a488035 TO |
277 | } |
278 | } | |
279 | ||
280 | /** | |
fe482240 | 281 | * Get row count for the event selector. |
6a488035 | 282 | * |
90c8230e TO |
283 | * @param int $mailing_id |
284 | * ID of the mailing. | |
285 | * @param int $job_id | |
286 | * Optional ID of a job to filter on. | |
287 | * @param bool $is_distinct | |
288 | * Group by queue ID?. | |
6a488035 | 289 | * |
a6c01b45 CW |
290 | * @return int |
291 | * Number of rows in result set | |
6a488035 | 292 | */ |
a3d7e8ee TO |
293 | public static function getTotalCount( |
294 | $mailing_id, $job_id = NULL, | |
6a488035 TO |
295 | $is_distinct = FALSE |
296 | ) { | |
297 | $dao = new CRM_Core_DAO(); | |
298 | ||
299 | $reply = self::getTableName(); | |
300 | $queue = CRM_Mailing_Event_BAO_Queue::getTableName(); | |
301 | $mailing = CRM_Mailing_BAO_Mailing::getTableName(); | |
9da8dc8c | 302 | $job = CRM_Mailing_BAO_MailingJob::getTableName(); |
6a488035 TO |
303 | |
304 | $query = " | |
305 | SELECT COUNT($reply.id) as reply | |
306 | FROM $reply | |
307 | INNER JOIN $queue | |
308 | ON $reply.event_queue_id = $queue.id | |
309 | INNER JOIN $job | |
310 | ON $queue.job_id = $job.id | |
311 | INNER JOIN $mailing | |
312 | ON $job.mailing_id = $mailing.id | |
313 | AND $job.is_test = 0 | |
314 | WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer'); | |
315 | ||
316 | if (!empty($job_id)) { | |
317 | $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer'); | |
318 | } | |
319 | ||
320 | if ($is_distinct) { | |
321 | $query .= " GROUP BY $queue.id "; | |
322 | } | |
323 | ||
324 | // query was missing | |
325 | $dao->query($query); | |
326 | ||
327 | if ($dao->fetch()) { | |
328 | return $dao->reply; | |
329 | } | |
330 | ||
331 | return NULL; | |
332 | } | |
333 | ||
334 | /** | |
fe482240 | 335 | * Get rows for the event browser. |
6a488035 | 336 | * |
90c8230e TO |
337 | * @param int $mailing_id |
338 | * ID of the mailing. | |
339 | * @param int $job_id | |
340 | * Optional ID of the job. | |
341 | * @param bool $is_distinct | |
342 | * Group by queue id?. | |
343 | * @param int $offset | |
344 | * Offset. | |
345 | * @param int $rowCount | |
346 | * Number of rows. | |
347 | * @param array $sort | |
348 | * Sort array. | |
6a488035 | 349 | * |
a6c01b45 CW |
350 | * @return array |
351 | * Result set | |
6a488035 | 352 | */ |
a3d7e8ee TO |
353 | public static function &getRows( |
354 | $mailing_id, $job_id = NULL, | |
6a488035 TO |
355 | $is_distinct = FALSE, $offset = NULL, $rowCount = NULL, $sort = NULL |
356 | ) { | |
357 | ||
0fc59d7a | 358 | $dao = new CRM_Core_DAO(); |
6a488035 TO |
359 | |
360 | $reply = self::getTableName(); | |
361 | $queue = CRM_Mailing_Event_BAO_Queue::getTableName(); | |
362 | $mailing = CRM_Mailing_BAO_Mailing::getTableName(); | |
9da8dc8c | 363 | $job = CRM_Mailing_BAO_MailingJob::getTableName(); |
6a488035 TO |
364 | $contact = CRM_Contact_BAO_Contact::getTableName(); |
365 | $email = CRM_Core_BAO_Email::getTableName(); | |
366 | ||
367 | $query = " | |
368 | SELECT $contact.display_name as display_name, | |
369 | $contact.id as contact_id, | |
370 | $email.email as email, | |
371 | $reply.time_stamp as date | |
372 | FROM $contact | |
373 | INNER JOIN $queue | |
374 | ON $queue.contact_id = $contact.id | |
375 | INNER JOIN $email | |
376 | ON $queue.email_id = $email.id | |
377 | INNER JOIN $reply | |
378 | ON $reply.event_queue_id = $queue.id | |
379 | INNER JOIN $job | |
380 | ON $queue.job_id = $job.id | |
381 | INNER JOIN $mailing | |
382 | ON $job.mailing_id = $mailing.id | |
383 | AND $job.is_test = 0 | |
384 | WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer'); | |
385 | ||
386 | if (!empty($job_id)) { | |
387 | $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer'); | |
388 | } | |
389 | ||
390 | if ($is_distinct) { | |
0ee5581e | 391 | $query .= " GROUP BY $queue.id, $contact.id, $reply.time_stamp "; |
6a488035 TO |
392 | } |
393 | ||
394 | $orderBy = "sort_name ASC, {$reply}.time_stamp DESC"; | |
395 | if ($sort) { | |
396 | if (is_string($sort)) { | |
21d32567 | 397 | $sort = CRM_Utils_Type::escape($sort, 'String'); |
6a488035 TO |
398 | $orderBy = $sort; |
399 | } | |
400 | else { | |
401 | $orderBy = trim($sort->orderBy()); | |
402 | } | |
403 | } | |
404 | ||
405 | $query .= " ORDER BY {$orderBy} "; | |
406 | ||
407 | if ($offset || $rowCount) { | |
408 | //Added "||$rowCount" to avoid displaying all records on first page | |
409 | $query .= ' LIMIT ' . CRM_Utils_Type::escape($offset, 'Integer') . ', ' . CRM_Utils_Type::escape($rowCount, 'Integer'); | |
410 | } | |
411 | ||
412 | $dao->query($query); | |
413 | ||
be2fb01f | 414 | $results = []; |
6a488035 TO |
415 | |
416 | while ($dao->fetch()) { | |
417 | $url = CRM_Utils_System::url('civicrm/contact/view', | |
418 | "reset=1&cid={$dao->contact_id}" | |
419 | ); | |
be2fb01f | 420 | $results[] = [ |
6a488035 TO |
421 | 'name' => "<a href=\"$url\">{$dao->display_name}</a>", |
422 | 'email' => $dao->email, | |
423 | 'date' => CRM_Utils_Date::customFormat($dao->date), | |
be2fb01f | 424 | ]; |
6a488035 TO |
425 | } |
426 | return $results; | |
427 | } | |
96025800 | 428 | |
6a488035 | 429 | } |