Error handling - Replace calls to CRM_Core_Error::ignoreException with CRM_Core_Tempo...
[civicrm-core.git] / CRM / Mailing / Event / BAO / Unsubscribe.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
36require_once 'Mail/mime.php';
37class CRM_Mailing_Event_BAO_Unsubscribe extends CRM_Mailing_Event_DAO_Unsubscribe {
38
39 /**
40 * class constructor
41 */
42 function __construct() {
43 parent::__construct();
44 }
45
46 /**
47 * Unsubscribe a contact from the domain
48 *
49 * @param int $job_id The job ID
50 * @param int $queue_id The Queue Event ID of the recipient
51 * @param string $hash The hash
52 *
ceb10dc7 53 * @return boolean Was the contact successfully unsubscribed?
6a488035
TO
54 * @access public
55 * @static
56 */
57 public static function unsub_from_domain($job_id, $queue_id, $hash) {
58 $q = CRM_Mailing_Event_BAO_Queue::verify($job_id, $queue_id, $hash);
59 if (!$q) {
60 return FALSE;
61 }
62
63 $transaction = new CRM_Core_Transaction();
64
65 $now = date('YmdHis');
66 if (CRM_Core_BAO_Email::isMultipleBulkMail()) {
67 $email = new CRM_Core_BAO_Email();
68 $email->id = $q->email_id;
69 if ($email->find(TRUE)) {
70 $sql = "
71UPDATE civicrm_email
72SET on_hold = 2,
73 hold_date = %1
74WHERE email = %2
75";
76 $sqlParams = array(1 => array($now, 'Timestamp'),
77 2 => array($email->email, 'String'),
78 );
79 CRM_Core_DAO::executeQuery($sql, $sqlParams);
80 }
81 }
82 else {
83 $contact = new CRM_Contact_BAO_Contact();
84 $contact->id = $q->contact_id;
85 $contact->is_opt_out = TRUE;
86 $contact->save();
87 }
88
89 $ue = new CRM_Mailing_Event_BAO_Unsubscribe();
90 $ue->event_queue_id = $queue_id;
91 $ue->org_unsubscribe = 1;
92 $ue->time_stamp = $now;
93 $ue->save();
94
95 $shParams = array(
96 'contact_id' => $q->contact_id,
97 'group_id' => NULL,
98 'status' => 'Removed',
99 'method' => 'Email',
100 'tracking' => $ue->id,
101 );
102 CRM_Contact_BAO_SubscriptionHistory::create($shParams);
103
104 $transaction->commit();
105
106 return TRUE;
107 }
108
109 /**
110 * Unsubscribe a contact from all groups that received this mailing
111 *
112 * @param int $job_id The job ID
113 * @param int $queue_id The Queue Event ID of the recipient
114 * @param string $hash The hash
115 * @param boolean $return If true return the list of groups.
116 *
117 * @return array|null $groups Array of all groups from which the contact was removed, or null if the queue event could not be found.
118 * @access public
119 * @static
120 */
121 public static function &unsub_from_mailing($job_id, $queue_id, $hash, $return = FALSE) {
122 /* First make sure there's a matching queue event */
123
124 $q = CRM_Mailing_Event_BAO_Queue::verify($job_id, $queue_id, $hash);
125 $success = NULL;
126 if (!$q) {
127 return $success;
128 }
129
130 $contact_id = $q->contact_id;
131 $transaction = new CRM_Core_Transaction();
132
133 $do = new CRM_Core_DAO();
04124b30 134 $mgObject = new CRM_Mailing_DAO_MailingGroup();
6a488035 135 $mg = $mgObject->getTableName();
9da8dc8c 136 $jobObject = new CRM_Mailing_BAO_MailingJob();
6a488035
TO
137 $job = $jobObject->getTableName();
138 $mailingObject = new CRM_Mailing_BAO_Mailing();
139 $mailing = $mailingObject->getTableName();
140 $groupObject = new CRM_Contact_BAO_Group();
141 $group = $groupObject->getTableName();
142 $gcObject = new CRM_Contact_BAO_GroupContact();
143 $gc = $gcObject->getTableName();
144
145 //We Need the mailing Id for the hook...
ceb10dc7
DL
146 $do->query("SELECT $job.mailing_id as mailing_id
147 FROM $job
6a488035
TO
148 WHERE $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer'));
149 $do->fetch();
150 $mailing_id = $do->mailing_id;
151
152 $do->query("
153 SELECT $mg.entity_table as entity_table,
154 $mg.entity_id as entity_id,
155 $mg.group_type as group_type
156 FROM $mg
157 INNER JOIN $job
158 ON $job.mailing_id = $mg.mailing_id
159 INNER JOIN $group
160 ON $mg.entity_id = $group.id
161 WHERE $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer') . "
ceb10dc7 162 AND $mg.group_type IN ('Include', 'Base')
6a488035
TO
163 AND $group.is_hidden = 0"
164 );
165
ceb10dc7 166 /* Make a list of groups and a list of prior mailings that received
6a488035
TO
167 * this mailing */
168
169
170 $groups = array();
171 $base_groups = array();
172 $mailings = array();
173
174 while ($do->fetch()) {
175 if ($do->entity_table == $group) {
176 if ($do->group_type == 'Base') {
177 $base_groups[$do->entity_id] = NULL;
178 }
179 else {
180 $groups[$do->entity_id] = NULL;
181 }
182 }
183 elseif ($do->entity_table == $mailing) {
184 $mailings[] = $do->entity_id;
185 }
186 }
187
188 /* As long as we have prior mailings, find their groups and add to the
189 * list */
190
191 while (!empty($mailings)) {
192 $do->query("
193 SELECT $mg.entity_table as entity_table,
194 $mg.entity_id as entity_id
195 FROM $mg
196 WHERE $mg.mailing_id IN (" . implode(', ', $mailings) . ")
197 AND $mg.group_type = 'Include'");
198
199 $mailings = array();
200
201 while ($do->fetch()) {
202 if ($do->entity_table == $group) {
203 $groups[$do->entity_id] = TRUE;
204 }
205 elseif ($do->entity_table == $mailing) {
206 $mailings[] = $do->entity_id;
207 }
208 }
209 }
210
211 //Pass the groups to be unsubscribed from through a hook.
212 $group_ids = array_keys($groups);
213 $base_group_ids = array_keys($base_groups);
214 CRM_Utils_Hook::unsubscribeGroups('unsubscribe', $mailing_id, $contact_id, $group_ids, $base_group_ids);
215
216 /* Now we have a complete list of recipient groups. Filter out all
217 * those except smart groups, those that the contact belongs to and
218 * base groups from search based mailings */
219
220 $baseGroupClause = '';
221 if (!empty($base_group_ids)) {
222 $baseGroupClause = "OR $group.id IN(" . implode(', ', $base_group_ids) . ")";
223 }
224 $do->query("
225 SELECT $group.id as group_id,
226 $group.title as title,
227 $group.description as description
228 FROM $group
229 LEFT JOIN $gc
230 ON $gc.group_id = $group.id
231 WHERE $group.id IN (" . implode(', ', array_merge($group_ids, $base_group_ids)) . ")
232 AND $group.is_hidden = 0
233 AND ($group.saved_search_id is not null
234 OR ($gc.contact_id = $contact_id
235 AND $gc.status = 'Added')
236 $baseGroupClause
237 )");
238
239 if ($return) {
240 $returnGroups = array();
241 while ($do->fetch()) {
242 $returnGroups[$do->group_id] = array(
243 'title' => $do->title,
244 'description' => $do->description,
245 );
246 }
247 return $returnGroups;
248 }
249 else {
250 while ($do->fetch()) {
251 $groups[$do->group_id] = $do->title;
252 }
253 }
254
255 $contacts = array($contact_id);
256 foreach ($groups as $group_id => $group_name) {
257 $notremoved = FALSE;
258 if ($group_name) {
259 if (in_array($group_id, $base_group_ids)) {
260 list($total, $removed, $notremoved) = CRM_Contact_BAO_GroupContact::addContactsToGroup($contacts, $group_id, 'Email', 'Removed');
261 }
262 else {
263 list($total, $removed, $notremoved) = CRM_Contact_BAO_GroupContact::removeContactsFromGroup($contacts, $group_id, 'Email');
264 }
265 }
266 if ($notremoved) {
267 unset($groups[$group_id]);
268 }
269 }
270
271 $ue = new CRM_Mailing_Event_BAO_Unsubscribe();
272 $ue->event_queue_id = $queue_id;
273 $ue->org_unsubscribe = 0;
274 $ue->time_stamp = date('YmdHis');
275 $ue->save();
276
277 $transaction->commit();
278 return $groups;
279 }
280
281 /**
282 * Send a reponse email informing the contact of the groups from which he
283 * has been unsubscribed.
284 *
285 * @param string $queue_id The queue event ID
286 * @param array $groups List of group IDs
287 * @param bool $is_domain Is this domain-level?
288 * @param int $job The job ID
289 *
290 * @return void
291 * @access public
292 * @static
293 */
294 public static function send_unsub_response($queue_id, $groups, $is_domain = FALSE, $job) {
295 $config = CRM_Core_Config::singleton();
296 $domain = CRM_Core_BAO_Domain::getDomain();
297
9da8dc8c 298 $jobObject = new CRM_Mailing_BAO_MailingJob();
6a488035
TO
299 $jobTable = $jobObject->getTableName();
300 $mailingObject = new CRM_Mailing_DAO_Mailing();
301 $mailingTable = $mailingObject->getTableName();
302 $contactsObject = new CRM_Contact_DAO_Contact();
303 $contacts = $contactsObject->getTableName();
304 $emailObject = new CRM_Core_DAO_Email();
305 $email = $emailObject->getTableName();
306 $queueObject = new CRM_Mailing_Event_BAO_Queue();
307 $queue = $queueObject->getTableName();
308
309 //get the default domain email address.
310 list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain::getNameAndEmail();
311
312 $dao = new CRM_Mailing_BAO_Mailing();
ceb10dc7 313 $dao->query(" SELECT * FROM $mailingTable
6a488035 314 INNER JOIN $jobTable ON
ceb10dc7 315 $jobTable.mailing_id = $mailingTable.id
6a488035
TO
316 WHERE $jobTable.id = $job");
317 $dao->fetch();
318
319 $component = new CRM_Mailing_BAO_Component();
320
321 if ($is_domain) {
322 $component->id = $dao->optout_id;
323 }
324 else {
325 $component->id = $dao->unsubscribe_id;
326 }
327 $component->find(TRUE);
328
329 $html = $component->body_html;
330 if ($component->body_text) {
331 $text = $component->body_text;
332 }
333 else {
334 $text = CRM_Utils_String::htmlToText($component->body_html);
335 }
336
337 $eq = new CRM_Core_DAO();
338 $eq->query(
339 "SELECT $contacts.preferred_mail_format as format,
340 $contacts.id as contact_id,
341 $email.email as email,
342 $queue.hash as hash
343 FROM $contacts
344 INNER JOIN $queue ON $queue.contact_id = $contacts.id
345 INNER JOIN $email ON $queue.email_id = $email.id
346 WHERE $queue.id = " . CRM_Utils_Type::escape($queue_id, 'Integer')
347 );
348 $eq->fetch();
349
350 if ($groups) {
351 foreach ($groups as $key => $value) {
352 if (!$value) {
353 unset($groups[$key]);
354 }
355 }
356 }
357
358 $message = new Mail_mime("\n");
359
360 list($addresses, $urls) = CRM_Mailing_BAO_Mailing::getVerpAndUrls($job, $queue_id, $eq->hash, $eq->email);
361 $bao = new CRM_Mailing_BAO_Mailing();
362 $bao->body_text = $text;
363 $bao->body_html = $html;
364 $tokens = $bao->getTokens();
365 if ($eq->format == 'HTML' || $eq->format == 'Both') {
366 $html = CRM_Utils_Token::replaceDomainTokens($html, $domain, TRUE, $tokens['html']);
367 $html = CRM_Utils_Token::replaceUnsubscribeTokens($html, $domain, $groups, TRUE, $eq->contact_id, $eq->hash);
368 $html = CRM_Utils_Token::replaceActionTokens($html, $addresses, $urls, TRUE, $tokens['html']);
369 $html = CRM_Utils_Token::replaceMailingTokens($html, $dao, NULL, $tokens['html']);
370 $message->setHTMLBody($html);
371 }
372 if (!$html || $eq->format == 'Text' || $eq->format == 'Both') {
373 $text = CRM_Utils_Token::replaceDomainTokens($text, $domain, FALSE, $tokens['text']);
374 $text = CRM_Utils_Token::replaceUnsubscribeTokens($text, $domain, $groups, FALSE, $eq->contact_id, $eq->hash);
375 $text = CRM_Utils_Token::replaceActionTokens($text, $addresses, $urls, FALSE, $tokens['text']);
376 $text = CRM_Utils_Token::replaceMailingTokens($text, $dao, NULL, $tokens['text']);
377 $message->setTxtBody($text);
378 }
379
380 $emailDomain = CRM_Core_BAO_MailSettings::defaultDomain();
381
382 $headers = array(
383 'Subject' => $component->subject,
384 'From' => "\"$domainEmailName\" <do-not-reply@$emailDomain>",
385 'To' => $eq->email,
386 'Reply-To' => "do-not-reply@$emailDomain",
387 'Return-Path' => "do-not-reply@$emailDomain",
388 );
389 CRM_Mailing_BAO_Mailing::addMessageIdHeader($headers, 'u', $job, $queue_id, $eq->hash);
390
391 $b = CRM_Utils_Mail::setMimeParams($message);
392 $h = $message->headers($headers);
393
394 $mailer = $config->getMailer();
395
6a488035 396 if (is_object($mailer)) {
6a4257d4 397 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
6a488035 398 $mailer->send($eq->email, $h, $b);
6a4257d4 399 unset($errorScope);
6a488035
TO
400 }
401 }
402
403 /**
404 * Get row count for the event selector
405 *
406 * @param int $mailing_id ID of the mailing
407 * @param int $job_id Optional ID of a job to filter on
408 * @param boolean $is_distinct Group by queue ID?
409 *
410 * @return int Number of rows in result set
411 * @access public
412 * @static
413 */
414 public static function getTotalCount($mailing_id, $job_id = NULL,
415 $is_distinct = FALSE, $org_unsubscribe = NULL
416 ) {
417 $dao = new CRM_Core_DAO();
418
419 $unsub = self::$_tableName;
420 $queueObject = new CRM_Mailing_Event_BAO_Queue();
421 $queue = $queueObject->getTableName();
422 $mailingObject = new CRM_Mailing_BAO_Mailing();
423 $mailing = $mailingObject->getTableName();
9da8dc8c 424 $jobObject = new CRM_Mailing_BAO_MailingJob();
6a488035
TO
425 $job = $jobObject->getTableName();
426
427 $query = "
428 SELECT COUNT($unsub.id) as unsubs
429 FROM $unsub
430 INNER JOIN $queue
431 ON $unsub.event_queue_id = $queue.id
432 INNER JOIN $job
433 ON $queue.job_id = $job.id
434 INNER JOIN $mailing
435 ON $job.mailing_id = $mailing.id
436 AND $job.is_test = 0
437 WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer');
438
439 if (!empty($job_id)) {
440 $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer');
441 }
442
443 if ($org_unsubscribe !== NULL) {
444 $query .= " AND $unsub.org_unsubscribe = " . ($org_unsubscribe ? 0 : 1);
445 }
446
447 if ($is_distinct) {
448 $query .= " GROUP BY $queue.id ";
449 }
450
451 $dao->query($query);
452 $dao->fetch();
453 if ($is_distinct) {
454 return $dao->N;
455 }
456 else {
457 return $dao->unsubs ? $dao->unsubs : 0;
458 }
459 }
460
461 /**
462 * Get rows for the event browser
463 *
464 * @param int $mailing_id ID of the mailing
465 * @param int $job_id optional ID of the job
466 * @param boolean $is_distinct Group by queue id?
467 * @param int $offset Offset
468 * @param int $rowCount Number of rows
469 * @param array $sort sort array
470 *
471 * @return array Result set
472 * @access public
473 * @static
474 */
475 public static function &getRows($mailing_id, $job_id = NULL,
476 $is_distinct = FALSE, $offset = NULL, $rowCount = NULL, $sort = NULL,
477 $org_unsubscribe = NULL
478 ) {
479
480 $dao = new CRM_Core_Dao();
481
482 $unsub = self::$_tableName;
483 $queueObject = new CRM_Mailing_Event_BAO_Queue();
484 $queue = $queueObject->getTableName();
485 $mailingObject = new CRM_Mailing_BAO_Mailing();
486 $mailing = $mailingObject->getTableName();
9da8dc8c 487 $jobObject = new CRM_Mailing_BAO_MailingJob();
6a488035
TO
488 $job = $jobObject->getTableName();
489 $contactObject = new CRM_Contact_BAO_Contact();
490 $contact = $contactObject->getTableName();
491 $emailObject = new CRM_Core_BAO_Email();
492 $email = $emailObject->getTableName();
493
494 $query = "
495 SELECT $contact.display_name as display_name,
496 $contact.id as contact_id,
497 $email.email as email,
498 $unsub.time_stamp as date,
499 $unsub.org_unsubscribe as org_unsubscribe
500 FROM $contact
501 INNER JOIN $queue
502 ON $queue.contact_id = $contact.id
503 INNER JOIN $email
504 ON $queue.email_id = $email.id
505 INNER JOIN $unsub
506 ON $unsub.event_queue_id = $queue.id
507 INNER JOIN $job
508 ON $queue.job_id = $job.id
509 INNER JOIN $mailing
510 ON $job.mailing_id = $mailing.id
511 AND $job.is_test = 0
512 WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer');
513
514 if (!empty($job_id)) {
515 $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer');
516 }
517
518 if ($org_unsubscribe !== NULL) {
519 $query .= " AND $unsub.org_unsubscribe = " . ($org_unsubscribe ? 0 : 1);
520 }
521
522 if ($is_distinct) {
523 $query .= " GROUP BY $queue.id ";
524 }
525
526 $orderBy = "sort_name ASC, {$unsub}.time_stamp DESC";
527 if ($sort) {
528 if (is_string($sort)) {
21d32567 529 $sort = CRM_Utils_Type::escape($sort, 'String');
6a488035
TO
530 $orderBy = $sort;
531 }
532 else {
533 $orderBy = trim($sort->orderBy());
534 }
535 }
536
537 $query .= " ORDER BY {$orderBy} ";
538
539 if ($offset || $rowCount) {
540 //Added "||$rowCount" to avoid displaying all records on first page
541 $query .= ' LIMIT ' . CRM_Utils_Type::escape($offset, 'Integer') . ', ' . CRM_Utils_Type::escape($rowCount, 'Integer');
542 }
543
544 $dao->query($query);
545
546 $results = array();
547
548 while ($dao->fetch()) {
549 $url = CRM_Utils_System::url('civicrm/contact/view',
550 "reset=1&cid={$dao->contact_id}"
551 );
552 $results[] = array(
553 'name' => "<a href=\"$url\">{$dao->display_name}</a>",
554 'email' => $dao->email,
555 'org' => $dao->org_unsubscribe ? ts('Yes') : ts('No'),
556 'date' => CRM_Utils_Date::customFormat($dao->date),
557 );
558 }
559 return $results;
560 }
561
562 public static function getContactInfo($queueID) {
563 $query = "
564SELECT DISTINCT(civicrm_mailing_event_queue.contact_id) as contact_id,
565 civicrm_contact.display_name as display_name
566 civicrm_email.email as email
567 FROM civicrm_mailing_event_queue,
568 civicrm_contact,
569 civicrm_email
570 WHERE civicrm_mailing_event_queue.contact_id = civicrm_contact.id
571 AND civicrm_mailing_event_queue.email_id = civicrm_email.id
572 AND civicrm_mailing_event_queue.id = " . CRM_Utils_Type::escape($queueID, 'Integer');
573
574 $dao = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
575
576 $displayName = 'Unknown';
577 $email = 'Unknown';
578 if ($dao->fetch()) {
579 $displayName = $dao->display_name;
580 $email = $dao->email;
581 }
582
583 return array($displayName, $email);
584 }
585}
586