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