PageRenderTime 49ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/sites/all/modules/contrib/civicrm/CRM/Mailing/Event/BAO/Subscribe.php

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