Merge pull request #3204 from GinkgoFJG/CRM-14662
[civicrm-core.git] / CRM / Mailing / Event / BAO / Subscribe.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
06b69b18 4 | CiviCRM version 4.5 |
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
36
37require_once 'Mail/mime.php';
38class CRM_Mailing_Event_BAO_Subscribe extends CRM_Mailing_Event_DAO_Subscribe {
39
40 /**
41 * class constructor
42 */
43 function __construct() {
44 parent::__construct();
45 }
46
47 /**
48 * Register a subscription event. Create a new contact if one does not
49 * already exist.
50 *
6c8f6e67
EM
51 * @param int $group_id The group id to subscribe to
52 * @param string $email The email address of the (new) contact
53 * @param null $contactId
54 * @param null $context
6a488035
TO
55 * @params int $contactId Currently used during event registration/contribution.
56 * Specifically to avoid linking group to wrong duplicate contact
57 * during event registration.
58 *
59 * @return int|null $se_id The id of the subscription event, null on failure
60 * @access public
61 * @static
62 */
63 public static function &subscribe($group_id, $email, $contactId = NULL, $context = NULL) {
64 // CRM-1797 - allow subscription only to public groups
65 $params = array('id' => (int) $group_id);
66 $defaults = array();
67 $contact_id = NULL;
68 $success = NULL;
69
70 $bao = CRM_Contact_BAO_Group::retrieve($params, $defaults);
71 if ($bao && substr($bao->visibility, 0, 6) != 'Public' && $context != 'profile') {
72 return $success;
73 }
74
75 $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
76 $email = $strtolower($email);
77
78 // process the query only if no contactId
79 if ($contactId) {
80 $contact_id = $contactId;
81 }
82 else {
83 /* First, find out if the contact already exists */
84
85 $query = "
86 SELECT DISTINCT contact_a.id as contact_id
87 FROM civicrm_contact contact_a
88LEFT JOIN civicrm_email ON contact_a.id = civicrm_email.contact_id
89 WHERE civicrm_email.email = %1 AND contact_a.is_deleted = 0";
90
91 $params = array(1 => array($email, 'String'));
92 $dao = CRM_Core_DAO::executeQuery($query, $params);
93 $id = array();
94 // lets just use the first contact id we got
95 if ($dao->fetch()) {
96 $contact_id = $dao->contact_id;
97 }
98 $dao->free();
99 }
100
101 $transaction = new CRM_Core_Transaction();
102
103 if (!$contact_id) {
104 require_once 'CRM/Utils/DeprecatedUtils.php';
105
106 /* If the contact does not exist, create one. */
107
108 $formatted = array(
109 'contact_type' => 'Individual',
110 'version' => 3,
111 );
112 $locationType = CRM_Core_BAO_LocationType::getDefault();
113 $value = array(
114 'email' => $email,
115 'location_type_id' => $locationType->id,
116 );
117 _civicrm_api3_deprecated_add_formatted_param($value, $formatted);
118
a05662ef 119 $formatted['onDuplicate'] = CRM_Import_Parser::DUPLICATE_SKIP;
6a488035
TO
120 $formatted['fixAddress'] = TRUE;
121 require_once 'api/api.php';
122 $contact = civicrm_api('contact', 'create', $formatted);
123 if (civicrm_error($contact)) {
124 return $success;
125 }
126 $contact_id = $contact['id'];
127 }
128 elseif (!is_numeric($contact_id) &&
129 (int ) $contact_id > 0
130 ) {
131 // make sure contact_id is numeric
132 return $success;
133 }
134
135
136 /* Get the primary email id from the contact to use as a hash input */
137
138 $dao = new CRM_Core_DAO();
139
140 $query = "
141SELECT civicrm_email.id as email_id
142 FROM civicrm_email
143 WHERE civicrm_email.email = %1
144 AND civicrm_email.contact_id = %2";
145 $params = array(1 => array($email, 'String'),
146 2 => array($contact_id, 'Integer'),
147 );
148 $dao = CRM_Core_DAO::executeQuery($query, $params);
149
150 if (!$dao->fetch()) {
151 CRM_Core_Error::fatal('Please file an issue with the backtrace');
152 return $success;
153 }
154
155 $se = new CRM_Mailing_Event_BAO_Subscribe();
156 $se->group_id = $group_id;
157 $se->contact_id = $contact_id;
158 $se->time_stamp = date('YmdHis');
159 $se->hash = substr(sha1("{$group_id}:{$contact_id}:{$dao->email_id}:" . time()),
160 0, 16
161 );
162 $se->save();
163
164 $contacts = array($contact_id);
165 CRM_Contact_BAO_GroupContact::addContactsToGroup($contacts, $group_id,
166 'Email', 'Pending', $se->id
167 );
168
169 $transaction->commit();
170 return $se;
171 }
172
173 /**
174 * Verify the hash of a subscription event
175 *
176 * @param int $contact_id ID of the contact
177 * @param int $subscribe_id ID of the subscription event
178 * @param string $hash Hash to verify
179 *
180 * @return object|null The subscribe event object, or null on failure
181 * @access public
182 * @static
183 */
184 public static function &verify($contact_id, $subscribe_id, $hash) {
185 $success = NULL;
186 $se = new CRM_Mailing_Event_BAO_Subscribe();
187 $se->contact_id = $contact_id;
188 $se->id = $subscribe_id;
189 $se->hash = $hash;
190 if ($se->find(TRUE)) {
191 $success = $se;
192 }
193 return $success;
194 }
195
196 /**
197 * Ask a contact for subscription confirmation (opt-in)
198 *
199 * @param string $email The email address
200 *
201 * @return void
202 * @access public
203 */
204 public function send_confirm_request($email) {
205 $config = CRM_Core_Config::singleton();
206
207 $domain = CRM_Core_BAO_Domain::getDomain();
208
209 //get the default domain email address.
210 list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain::getNameAndEmail();
211
212 $localpart = CRM_Core_BAO_MailSettings::defaultLocalpart();
213 $emailDomain = CRM_Core_BAO_MailSettings::defaultDomain();
214
215 $confirm = implode($config->verpSeparator,
216 array(
217 $localpart . 'c',
218 $this->contact_id,
219 $this->id,
220 $this->hash,
221 )
222 ) . "@$emailDomain";
223
224 $group = new CRM_Contact_BAO_Group();
225 $group->id = $this->group_id;
226 $group->find(TRUE);
227
228 $component = new CRM_Mailing_BAO_Component();
229 $component->is_default = 1;
230 $component->is_active = 1;
231 $component->component_type = 'Subscribe';
232
233 $component->find(TRUE);
234
235 $headers = array(
236 'Subject' => $component->subject,
237 'From' => "\"{$domainEmailName}\" <{$domainEmailAddress}>",
238 'To' => $email,
239 'Reply-To' => $confirm,
240 'Return-Path' => "do-not-reply@$emailDomain",
241 );
242
243 $url = CRM_Utils_System::url('civicrm/mailing/confirm',
244 "reset=1&cid={$this->contact_id}&sid={$this->id}&h={$this->hash}",
245 TRUE
246 );
247
248 $html = $component->body_html;
249
250 if ($component->body_text) {
251 $text = $component->body_text;
252 }
253 else {
254 $text = CRM_Utils_String::htmlToText($component->body_html);
255 }
256
257 $bao = new CRM_Mailing_BAO_Mailing();
258 $bao->body_text = $text;
259 $bao->body_html = $html;
260 $tokens = $bao->getTokens();
261
262 $html = CRM_Utils_Token::replaceDomainTokens($html, $domain, TRUE, $tokens['html']);
263 $html = CRM_Utils_Token::replaceSubscribeTokens($html,
264 $group->title,
265 $url, TRUE
266 );
267
268 $text = CRM_Utils_Token::replaceDomainTokens($text, $domain, FALSE, $tokens['text']);
269 $text = CRM_Utils_Token::replaceSubscribeTokens($text,
270 $group->title,
271 $url, FALSE
272 );
273 // render the &amp; entities in text mode, so that the links work
274 $text = str_replace('&amp;', '&', $text);
275
276 $message = new Mail_mime("\n");
277
278 $message->setHTMLBody($html);
279 $message->setTxtBody($text);
280 $b = CRM_Utils_Mail::setMimeParams($message);
281 $h = $message->headers($headers);
282 CRM_Mailing_BAO_Mailing::addMessageIdHeader($h, 's',
283 $this->contact_id,
284 $this->id,
285 $this->hash
286 );
287 $mailer = $config->getMailer();
288
6a488035 289 if (is_object($mailer)) {
6a4257d4 290 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
6a488035 291 $mailer->send($email, $h, $b);
6a4257d4 292 unset($errorScope);
6a488035
TO
293 }
294 }
295
296 /**
297 * Get the domain object given a subscribe event
298 *
299 * @param int $subscribe_id ID of the subscribe event
300 *
301 * @return object $domain The domain owning the event
302 * @access public
303 * @static
304 */
305 public static function &getDomain($subscribe_id) {
306 return CRM_Core_BAO_Domain::getDomain();
307 }
308
309 /**
310 * Get the group details to which given email belongs
311 *
312 * @param string $email email of the contact
313 * @param int $contactID contactID if we want an exact match
314 *
315 * @return array $groups array of group ids
316 * @access public
317 */
2dd4cf8e 318 public static function getContactGroups($email, $contactID = NULL) {
6a488035
TO
319 if ($contactID) {
320 $query = "
321 SELECT DISTINCT group_a.group_id, group_a.status, civicrm_group.title
322 FROM civicrm_group_contact group_a
323 LEFT JOIN civicrm_group ON civicrm_group.id = group_a.group_id
324 LEFT JOIN civicrm_contact ON ( group_a.contact_id = civicrm_contact.id )
325 WHERE civicrm_contact.id = %1";
326
327 $params = array(1 => array($contactID, 'Integer'));
328 }
329 else {
330 $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
331 $email = $strtolower($email);
332
333 $query = "
334 SELECT DISTINCT group_a.group_id, group_a.status, civicrm_group.title
335 FROM civicrm_group_contact group_a
336 LEFT JOIN civicrm_group ON civicrm_group.id = group_a.group_id
337 LEFT JOIN civicrm_contact ON ( group_a.contact_id = civicrm_contact.id ) AND civicrm_contact.is_deleted = 0
338 LEFT JOIN civicrm_email ON civicrm_contact.id = civicrm_email.contact_id
339 WHERE civicrm_email.email = %1";
340
341 $params = array(1 => array($email, 'String'));
342 }
343
344 $dao = CRM_Core_DAO::executeQuery($query, $params);
345 $groups = array();
346 while ($dao->fetch()) {
347 $groups[$dao->group_id] = array(
348 'id' => $dao->group_id,
349 'title' => $dao->title,
350 'status' => $dao->status,
351 );
352 }
353
354 $dao->free();
355 return $groups;
356 }
357
358 /**
359 * Function to send subscribe mail
360 *
361 * @params array $groups the list of group ids for subscribe
362 * @params array $params the list of email
363 * @params int $contactId Currently used during event registration/contribution.
364 * Specifically to avoid linking group to wrong duplicate contact
365 * during event registration.
77b97be7 366 *
6a488035
TO
367 * @public
368 *
77b97be7
EM
369 * @param $groups
370 * @param $params
371 * @param null $contactId
372 * @param null $context
373 *
6a488035
TO
374 * @return void
375 * @static
376 * @access public
377 */
378 public static function commonSubscribe(&$groups, &$params, $contactId = NULL, $context = NULL) {
379 $contactGroups = CRM_Mailing_Event_BAO_Subscribe::getContactGroups($params['email'], $contactId);
380 $group = array();
381 $success = NULL;
382 foreach ($groups as $groupID) {
383 $title = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $groupID, 'title');
384 if (array_key_exists($groupID, $contactGroups) && $contactGroups[$groupID]['status'] != 'Removed') {
385 $group[$groupID]['title'] = $contactGroups[$groupID]['title'];
386
387 $group[$groupID]['status'] = $contactGroups[$groupID]['status'];
388 $status = ts('You are already subscribed in %1, your subscription is %2.', array(1 => $group[$groupID]['title'], 2 => $group[$groupID]['status']));
389 CRM_Utils_System::setUFMessage($status);
390 continue;
391 }
392
393 $se = self::subscribe($groupID,
394 $params['email'], $contactId, $context
395 );
396 if ($se !== NULL) {
397 $success = TRUE;
398 $groupAdded[] = $title;
399
400 /* Ask the contact for confirmation */
401
402 $se->send_confirm_request($params['email']);
403 }
404 else {
405 $success = FALSE;
406 $groupFailed[] = $title;
407 }
408 }
409 if ($success) {
e93afd65
DG
410 $groupTitle = implode(', ', $groupAdded);
411 CRM_Utils_System::setUFMessage(ts('Your subscription request has been submitted for %1. Check your inbox shortly for the confirmation email(s). If you do not see a confirmation email, please check your spam/junk mail folder.', array(1 => $groupTitle)));
6a488035
TO
412 }
413 elseif ($success === FALSE) {
414 $groupTitle = implode(',', $groupFailed);
e93afd65 415 CRM_Utils_System::setUFMessage(ts('We had a problem processing your subscription request for %1. You have tried to subscribe to a private group and/or we encountered a database error. Please contact the site administrator.', array(1 => $groupTitle)));
6a488035
TO
416 }
417 }
418 //end of function
419}
420