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