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