Merge pull request #23184 from totten/master-bd-drush
[civicrm-core.git] / CRM / Mailing / Event / BAO / Forward.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 class CRM_Mailing_Event_BAO_Forward extends CRM_Mailing_Event_DAO_Forward {
18
19 /**
20 * Create a new forward event, create a new contact if necessary
21 *
22 * @param $job_id
23 * @param $queue_id
24 * @param $hash
25 * @param $forward_email
26 * @param string|null $fromEmail
27 * @param array|null $comment
28 *
29 * @return bool
30 */
31 public static function &forward($job_id, $queue_id, $hash, $forward_email, $fromEmail = NULL, $comment = NULL) {
32 $q = CRM_Mailing_Event_BAO_Queue::verify($job_id, $queue_id, $hash);
33
34 $successfulForward = FALSE;
35 $contact_id = NULL;
36 if (!$q) {
37 return $successfulForward;
38 }
39
40 // Find the email address/contact, if it exists.
41
42 $contact = CRM_Contact_BAO_Contact::getTableName();
43 $email = CRM_Core_BAO_Email::getTableName();
44 $queueTable = CRM_Mailing_Event_BAO_Queue::getTableName();
45 $job = CRM_Mailing_BAO_MailingJob::getTableName();
46
47 $dao = new CRM_Core_DAO();
48 $dao->query("
49 SELECT $contact.id as contact_id,
50 $email.id as email_id,
51 $contact.do_not_email as do_not_email,
52 $queueTable.id as queue_id
53 FROM ($email, $job as temp_job)
54 INNER JOIN $contact
55 ON $email.contact_id = $contact.id
56 LEFT JOIN $queueTable
57 ON $email.id = $queueTable.email_id
58 LEFT JOIN $job
59 ON $queueTable.job_id = $job.id
60 AND temp_job.mailing_id = $job.mailing_id
61 WHERE $queueTable.job_id = $job_id
62 AND $email.email = '" .
63 CRM_Utils_Type::escape($forward_email, 'String') . "'"
64 );
65
66 $dao->fetch();
67
68 $transaction = new CRM_Core_Transaction();
69
70 if (isset($dao->queue_id) ||
71 (isset($dao->do_not_email) && $dao->do_not_email == 1)
72 ) {
73 // We already sent this mailing to $forward_email, or we should
74 // never email this contact. Give up.
75
76 return $successfulForward;
77 }
78
79 require_once 'api/api.php';
80 $contactParams = [
81 'email' => $forward_email,
82 'version' => 3,
83 ];
84 $contactValues = civicrm_api('contact', 'get', $contactParams);
85 $count = $contactValues['count'];
86
87 if ($count == 0) {
88 // If the contact does not exist, create one.
89
90 $formatted = [
91 'contact_type' => 'Individual',
92 'version' => 3,
93 ];
94 $locationType = CRM_Core_BAO_LocationType::getDefault();
95 $value = [
96 'email' => $forward_email,
97 'location_type_id' => $locationType->id,
98 ];
99 self::_civicrm_api3_deprecated_add_formatted_param($value, $formatted);
100 $formatted['onDuplicate'] = CRM_Import_Parser::DUPLICATE_SKIP;
101 $formatted['fixAddress'] = TRUE;
102 $contact = civicrm_api('contact', 'create', $formatted);
103 if (civicrm_error($contact)) {
104 return $successfulForward;
105 }
106 $contact_id = $contact['id'];
107 }
108 $email = new CRM_Core_DAO_Email();
109 $email->email = $forward_email;
110 $email->find(TRUE);
111 $email_id = $email->id;
112 if (!$contact_id) {
113 $contact_id = $email->contact_id;
114 }
115
116 // Create a new queue event.
117
118 $queue_params = [
119 'email_id' => $email_id,
120 'contact_id' => $contact_id,
121 'job_id' => $job_id,
122 ];
123
124 $queue = CRM_Mailing_Event_BAO_Queue::create($queue_params);
125
126 $forward = new CRM_Mailing_Event_BAO_Forward();
127 $forward->time_stamp = date('YmdHis');
128 $forward->event_queue_id = $queue_id;
129 $forward->dest_queue_id = $queue->id;
130 $forward->save();
131
132 $dao->reset();
133 $dao->query(" SELECT $job.mailing_id as mailing_id
134 FROM $job
135 WHERE $job.id = " .
136 CRM_Utils_Type::escape($job_id, 'Integer')
137 );
138 $dao->fetch();
139 $mailing_obj = new CRM_Mailing_BAO_Mailing();
140 $mailing_obj->id = $dao->mailing_id;
141 $mailing_obj->find(TRUE);
142
143 $config = CRM_Core_Config::singleton();
144 $mailer = \Civi::service('pear_mail');
145
146 $recipient = NULL;
147 $attachments = NULL;
148 $message = $mailing_obj->compose($job_id, $queue->id, $queue->hash,
149 $queue->contact_id, $forward_email, $recipient, FALSE, NULL, $attachments, TRUE, $fromEmail
150 );
151 //append comment if added while forwarding.
152 if (count($comment)) {
153 $message->_txtbody = CRM_Utils_Array::value('body_text', $comment) . $message->_txtbody;
154 if (!empty($comment['body_html'])) {
155 $message->_htmlbody = $comment['body_html'] . '<br />---------------Original message---------------------<br />' . $message->_htmlbody;
156 }
157 }
158
159 $body = $message->get();
160 $headers = $message->headers();
161
162 $result = NULL;
163 if (is_object($mailer)) {
164 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
165 $result = $mailer->send($recipient, $headers, $body);
166 unset($errorScope);
167 }
168
169 $params = [
170 'event_queue_id' => $queue->id,
171 'job_id' => $job_id,
172 'hash' => $queue->hash,
173 ];
174 if (is_a($result, 'PEAR_Error')) {
175 // Register the bounce event.
176
177 $params = array_merge($params,
178 CRM_Mailing_BAO_BouncePattern::match($result->getMessage())
179 );
180 CRM_Mailing_Event_BAO_Bounce::create($params);
181 }
182 else {
183 $successfulForward = TRUE;
184 // Register the delivery event.
185
186 CRM_Mailing_Event_BAO_Delivered::create($params);
187 }
188
189 $transaction->commit();
190
191 return $successfulForward;
192 }
193
194 /**
195 * This function adds the contact variable in $values to the
196 * parameter list $params. For most cases, $values should have length 1. If
197 * the variable being added is a child of Location, a location_type_id must
198 * also be included. If it is a child of phone, a phone_type must be included.
199 *
200 * @param array $values
201 * The variable(s) to be added.
202 * @param array $params
203 * The structured parameter list.
204 *
205 * @return bool|CRM_Utils_Error
206 */
207 protected static function _civicrm_api3_deprecated_add_formatted_param(&$values, &$params) {
208 // @todo - most of this code is UNREACHABLE.
209 // Crawl through the possible classes:
210 // Contact
211 // Individual
212 // Household
213 // Organization
214 // Location
215 // Address
216 // Email
217 // Phone
218 // IM
219 // Note
220 // Custom
221
222 // Cache the various object fields
223 static $fields = NULL;
224
225 if ($fields == NULL) {
226 $fields = [];
227 }
228
229 // first add core contact values since for other Civi modules they are not added
230 require_once 'CRM/Contact/BAO/Contact.php';
231 $contactFields = CRM_Contact_DAO_Contact::fields();
232 _civicrm_api3_store_values($contactFields, $values, $params);
233
234 // get the formatted location blocks into params - w/ 3.0 format, CRM-4605
235 if (!empty($values['location_type_id'])) {
236 static $fields = NULL;
237 if ($fields == NULL) {
238 $fields = [];
239 }
240
241 foreach (['Phone', 'Email', 'IM', 'OpenID', 'Phone_Ext'] as $block) {
242 $name = strtolower($block);
243 if (!array_key_exists($name, $values)) {
244 continue;
245 }
246
247 if ($name === 'phone_ext') {
248 $block = 'Phone';
249 }
250
251 // block present in value array.
252 if (!array_key_exists($name, $params) || !is_array($params[$name])) {
253 $params[$name] = [];
254 }
255
256 if (!array_key_exists($block, $fields)) {
257 $className = "CRM_Core_DAO_$block";
258 $fields[$block] =& $className::fields();
259 }
260
261 $blockCnt = count($params[$name]);
262
263 // copy value to dao field name.
264 if ($name == 'im') {
265 $values['name'] = $values[$name];
266 }
267
268 _civicrm_api3_store_values($fields[$block], $values,
269 $params[$name][++$blockCnt]
270 );
271
272 if (empty($params['id']) && ($blockCnt == 1)) {
273 $params[$name][$blockCnt]['is_primary'] = TRUE;
274 }
275
276 // we only process single block at a time.
277 return TRUE;
278 }
279
280 // handle address fields.
281 if (!array_key_exists('address', $params) || !is_array($params['address'])) {
282 $params['address'] = [];
283 }
284
285 $addressCnt = 1;
286 foreach ($params['address'] as $cnt => $addressBlock) {
287 if (CRM_Utils_Array::value('location_type_id', $values) ==
288 CRM_Utils_Array::value('location_type_id', $addressBlock)
289 ) {
290 $addressCnt = $cnt;
291 break;
292 }
293 $addressCnt++;
294 }
295
296 if (!array_key_exists('Address', $fields)) {
297 $fields['Address'] = CRM_Core_DAO_Address::fields();
298 }
299
300 // Note: we doing multiple value formatting here for address custom fields, plus putting into right format.
301 // The actual formatting (like date, country ..etc) for address custom fields is taken care of while saving
302 // the address in CRM_Core_BAO_Address::create method
303 if (!empty($values['location_type_id'])) {
304 static $customFields = [];
305 if (empty($customFields)) {
306 $customFields = CRM_Core_BAO_CustomField::getFields('Address');
307 }
308 // make a copy of values, as we going to make changes
309 $newValues = $values;
310 foreach ($values as $key => $val) {
311 $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key);
312 if ($customFieldID && array_key_exists($customFieldID, $customFields)) {
313 // mark an entry in fields array since we want the value of custom field to be copied
314 $fields['Address'][$key] = NULL;
315
316 $htmlType = $customFields[$customFieldID]['html_type'] ?? NULL;
317 if (CRM_Core_BAO_CustomField::isSerialized($customFields[$customFieldID]) && $val) {
318 $mulValues = explode(',', $val);
319 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
320 $newValues[$key] = [];
321 foreach ($mulValues as $v1) {
322 foreach ($customOption as $v2) {
323 if ((strtolower($v2['label']) == strtolower(trim($v1))) ||
324 (strtolower($v2['value']) == strtolower(trim($v1)))
325 ) {
326 if ($htmlType == 'CheckBox') {
327 $newValues[$key][$v2['value']] = 1;
328 }
329 else {
330 $newValues[$key][] = $v2['value'];
331 }
332 }
333 }
334 }
335 }
336 }
337 }
338 // consider new values
339 $values = $newValues;
340 }
341
342 _civicrm_api3_store_values($fields['Address'], $values, $params['address'][$addressCnt]);
343
344 $addressFields = [
345 'county',
346 'country',
347 'state_province',
348 'supplemental_address_1',
349 'supplemental_address_2',
350 'supplemental_address_3',
351 'StateProvince.name',
352 ];
353
354 foreach ($addressFields as $field) {
355 if (array_key_exists($field, $values)) {
356 if (!array_key_exists('address', $params)) {
357 $params['address'] = [];
358 }
359 $params['address'][$addressCnt][$field] = $values[$field];
360 }
361 }
362
363 if ($addressCnt == 1) {
364
365 $params['address'][$addressCnt]['is_primary'] = TRUE;
366 }
367 return TRUE;
368 }
369 }
370
371 /**
372 * Get row count for the event selector.
373 *
374 * @param int $mailing_id
375 * ID of the mailing.
376 * @param int $job_id
377 * Optional ID of a job to filter on.
378 * @param bool $is_distinct
379 * Group by queue ID?.
380 *
381 * @return int
382 * Number of rows in result set
383 */
384 public static function getTotalCount(
385 $mailing_id, $job_id = NULL,
386 $is_distinct = FALSE
387 ) {
388 $dao = new CRM_Core_DAO();
389
390 $forward = self::getTableName();
391 $queue = CRM_Mailing_Event_BAO_Queue::getTableName();
392 $mailing = CRM_Mailing_BAO_Mailing::getTableName();
393 $job = CRM_Mailing_BAO_MailingJob::getTableName();
394
395 $query = "
396 SELECT COUNT($forward.id) as forward
397 FROM $forward
398 INNER JOIN $queue
399 ON $forward.event_queue_id = $queue.id
400 INNER JOIN $job
401 ON $queue.job_id = $job.id
402 INNER JOIN $mailing
403 ON $job.mailing_id = $mailing.id
404 AND $job.is_test = 0
405 WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer');
406
407 if (!empty($job_id)) {
408 $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer');
409 }
410
411 if ($is_distinct) {
412 $query .= " GROUP BY $queue.id ";
413 }
414
415 // query was missing
416 $dao->query($query);
417
418 if ($dao->fetch()) {
419 return $dao->forward;
420 }
421
422 return NULL;
423 }
424
425 /**
426 * Get rows for the event browser.
427 *
428 * @param int $mailing_id
429 * ID of the mailing.
430 * @param int $job_id
431 * Optional ID of the job.
432 * @param bool $is_distinct
433 * Group by queue id?.
434 * @param int $offset
435 * Offset.
436 * @param int $rowCount
437 * Number of rows.
438 * @param array $sort
439 * Sort array.
440 *
441 * @return array
442 * Result set
443 */
444 public static function &getRows(
445 $mailing_id, $job_id = NULL,
446 $is_distinct = FALSE, $offset = NULL, $rowCount = NULL, $sort = NULL
447 ) {
448
449 $dao = new CRM_Core_DAO();
450
451 $forward = self::getTableName();
452 $queue = CRM_Mailing_Event_BAO_Queue::getTableName();
453 $mailing = CRM_Mailing_BAO_Mailing::getTableName();
454 $job = CRM_Mailing_BAO_MailingJob::getTableName();
455 $contact = CRM_Contact_BAO_Contact::getTableName();
456 $email = CRM_Core_BAO_Email::getTableName();
457
458 $query = "
459 SELECT $contact.display_name as from_name,
460 $contact.id as from_id,
461 $email.email as from_email,
462 dest_contact.id as dest_id,
463 dest_email.email as dest_email,
464 $forward.time_stamp as date
465 FROM $contact
466 INNER JOIN $queue
467 ON $queue.contact_id = $contact.id
468 INNER JOIN $email
469 ON $queue.email_id = $email.id
470 INNER JOIN $forward
471 ON $forward.event_queue_id = $queue.id
472 INNER JOIN $queue as dest_queue
473 ON $forward.dest_queue_id = dest_queue.id
474 INNER JOIN $contact as dest_contact
475 ON dest_queue.contact_id = dest_contact.id
476 INNER JOIN $email as dest_email
477 ON dest_queue.email_id = dest_email.id
478 INNER JOIN $job
479 ON $queue.job_id = $job.id
480 INNER JOIN $mailing
481 ON $job.mailing_id = $mailing.id
482 AND $job.is_test = 0
483 WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer');
484
485 if (!empty($job_id)) {
486 $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer');
487 }
488
489 if ($is_distinct) {
490 $query .= " GROUP BY $queue.id, dest_contact.id, dest_email.email, $forward.time_stamp ";
491 }
492
493 $orderBy = "$contact.sort_name ASC, {$forward}.time_stamp DESC";
494 if ($sort) {
495 if (is_string($sort)) {
496 $sort = CRM_Utils_Type::escape($sort, 'String');
497 $orderBy = $sort;
498 }
499 else {
500 $orderBy = trim($sort->orderBy());
501 }
502 }
503
504 $query .= " ORDER BY {$orderBy} ";
505
506 if ($offset || $rowCount) {
507 //Added "||$rowCount" to avoid displaying all records on first page
508 $query .= ' LIMIT ' . CRM_Utils_Type::escape($offset, 'Integer') . ', ' . CRM_Utils_Type::escape($rowCount, 'Integer');
509 }
510
511 $dao->query($query);
512
513 $results = [];
514
515 while ($dao->fetch()) {
516 $from_url = CRM_Utils_System::url('civicrm/contact/view',
517 "reset=1&cid={$dao->from_id}"
518 );
519 $dest_url = CRM_Utils_System::url('civicrm/contact/view',
520 "reset=1&cid={$dao->dest_id}"
521 );
522 $results[] = [
523 'from_name' => "<a href=\"$from_url\">{$dao->from_name}</a>",
524 'from_email' => $dao->from_email,
525 'dest_email' => "<a href=\"$dest_url\">{$dao->dest_email}</a>",
526 'date' => CRM_Utils_Date::customFormat($dao->date),
527 ];
528 }
529 return $results;
530 }
531
532 }