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