Merge pull request #22586 from braders/checkTemplateFileExists-consistency-phpdoc
[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 if (isset($values['contact_type'])) {
235 // we're an individual/household/org property
236
237 $fields[$values['contact_type']] = CRM_Contact_DAO_Contact::fields();
238
239 _civicrm_api3_store_values($fields[$values['contact_type']], $values, $params);
240 return TRUE;
241 }
242
243 if (isset($values['individual_prefix'])) {
244 if (!empty($params['prefix_id'])) {
245 $prefixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id');
246 $params['prefix'] = $prefixes[$params['prefix_id']];
247 }
248 else {
249 $params['prefix'] = $values['individual_prefix'];
250 }
251 return TRUE;
252 }
253
254 if (isset($values['individual_suffix'])) {
255 if (!empty($params['suffix_id'])) {
256 $suffixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id');
257 $params['suffix'] = $suffixes[$params['suffix_id']];
258 }
259 else {
260 $params['suffix'] = $values['individual_suffix'];
261 }
262 return TRUE;
263 }
264
265 // CRM-4575
266 if (isset($values['email_greeting'])) {
267 if (!empty($params['email_greeting_id'])) {
268 $emailGreetingFilter = [
269 'contact_type' => $params['contact_type'] ?? NULL,
270 'greeting_type' => 'email_greeting',
271 ];
272 $emailGreetings = CRM_Core_PseudoConstant::greeting($emailGreetingFilter);
273 $params['email_greeting'] = $emailGreetings[$params['email_greeting_id']];
274 }
275 else {
276 $params['email_greeting'] = $values['email_greeting'];
277 }
278
279 return TRUE;
280 }
281
282 if (isset($values['postal_greeting'])) {
283 if (!empty($params['postal_greeting_id'])) {
284 $postalGreetingFilter = [
285 'contact_type' => $params['contact_type'] ?? NULL,
286 'greeting_type' => 'postal_greeting',
287 ];
288 $postalGreetings = CRM_Core_PseudoConstant::greeting($postalGreetingFilter);
289 $params['postal_greeting'] = $postalGreetings[$params['postal_greeting_id']];
290 }
291 else {
292 $params['postal_greeting'] = $values['postal_greeting'];
293 }
294 return TRUE;
295 }
296
297 if (isset($values['addressee'])) {
298 $params['addressee'] = $values['addressee'];
299 return TRUE;
300 }
301
302 if (isset($values['gender'])) {
303 if (!empty($params['gender_id'])) {
304 $genders = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id');
305 $params['gender'] = $genders[$params['gender_id']];
306 }
307 else {
308 $params['gender'] = $values['gender'];
309 }
310 return TRUE;
311 }
312
313 if (!empty($values['preferred_communication_method'])) {
314 $comm = [];
315 $pcm = array_change_key_case(array_flip(CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method')), CASE_LOWER);
316
317 $preffComm = explode(',', $values['preferred_communication_method']);
318 foreach ($preffComm as $v) {
319 $v = strtolower(trim($v));
320 if (array_key_exists($v, $pcm)) {
321 $comm[$pcm[$v]] = 1;
322 }
323 }
324
325 $params['preferred_communication_method'] = $comm;
326 return TRUE;
327 }
328
329 // format the website params.
330 if (!empty($values['url'])) {
331 static $websiteFields;
332 if (!is_array($websiteFields)) {
333 require_once 'CRM/Core/DAO/Website.php';
334 $websiteFields = CRM_Core_DAO_Website::fields();
335 }
336 if (!array_key_exists('website', $params) ||
337 !is_array($params['website'])
338 ) {
339 $params['website'] = [];
340 }
341
342 $websiteCount = count($params['website']);
343 _civicrm_api3_store_values($websiteFields, $values,
344 $params['website'][++$websiteCount]
345 );
346
347 return TRUE;
348 }
349
350 // get the formatted location blocks into params - w/ 3.0 format, CRM-4605
351 if (!empty($values['location_type_id'])) {
352 static $fields = NULL;
353 if ($fields == NULL) {
354 $fields = [];
355 }
356
357 foreach (['Phone', 'Email', 'IM', 'OpenID', 'Phone_Ext'] as $block) {
358 $name = strtolower($block);
359 if (!array_key_exists($name, $values)) {
360 continue;
361 }
362
363 if ($name === 'phone_ext') {
364 $block = 'Phone';
365 }
366
367 // block present in value array.
368 if (!array_key_exists($name, $params) || !is_array($params[$name])) {
369 $params[$name] = [];
370 }
371
372 if (!array_key_exists($block, $fields)) {
373 $className = "CRM_Core_DAO_$block";
374 $fields[$block] =& $className::fields();
375 }
376
377 $blockCnt = count($params[$name]);
378
379 // copy value to dao field name.
380 if ($name == 'im') {
381 $values['name'] = $values[$name];
382 }
383
384 _civicrm_api3_store_values($fields[$block], $values,
385 $params[$name][++$blockCnt]
386 );
387
388 if (empty($params['id']) && ($blockCnt == 1)) {
389 $params[$name][$blockCnt]['is_primary'] = TRUE;
390 }
391
392 // we only process single block at a time.
393 return TRUE;
394 }
395
396 // handle address fields.
397 if (!array_key_exists('address', $params) || !is_array($params['address'])) {
398 $params['address'] = [];
399 }
400
401 $addressCnt = 1;
402 foreach ($params['address'] as $cnt => $addressBlock) {
403 if (CRM_Utils_Array::value('location_type_id', $values) ==
404 CRM_Utils_Array::value('location_type_id', $addressBlock)
405 ) {
406 $addressCnt = $cnt;
407 break;
408 }
409 $addressCnt++;
410 }
411
412 if (!array_key_exists('Address', $fields)) {
413 $fields['Address'] = CRM_Core_DAO_Address::fields();
414 }
415
416 // Note: we doing multiple value formatting here for address custom fields, plus putting into right format.
417 // The actual formatting (like date, country ..etc) for address custom fields is taken care of while saving
418 // the address in CRM_Core_BAO_Address::create method
419 if (!empty($values['location_type_id'])) {
420 static $customFields = [];
421 if (empty($customFields)) {
422 $customFields = CRM_Core_BAO_CustomField::getFields('Address');
423 }
424 // make a copy of values, as we going to make changes
425 $newValues = $values;
426 foreach ($values as $key => $val) {
427 $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key);
428 if ($customFieldID && array_key_exists($customFieldID, $customFields)) {
429 // mark an entry in fields array since we want the value of custom field to be copied
430 $fields['Address'][$key] = NULL;
431
432 $htmlType = $customFields[$customFieldID]['html_type'] ?? NULL;
433 if (CRM_Core_BAO_CustomField::isSerialized($customFields[$customFieldID]) && $val) {
434 $mulValues = explode(',', $val);
435 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
436 $newValues[$key] = [];
437 foreach ($mulValues as $v1) {
438 foreach ($customOption as $v2) {
439 if ((strtolower($v2['label']) == strtolower(trim($v1))) ||
440 (strtolower($v2['value']) == strtolower(trim($v1)))
441 ) {
442 if ($htmlType == 'CheckBox') {
443 $newValues[$key][$v2['value']] = 1;
444 }
445 else {
446 $newValues[$key][] = $v2['value'];
447 }
448 }
449 }
450 }
451 }
452 }
453 }
454 // consider new values
455 $values = $newValues;
456 }
457
458 _civicrm_api3_store_values($fields['Address'], $values, $params['address'][$addressCnt]);
459
460 $addressFields = [
461 'county',
462 'country',
463 'state_province',
464 'supplemental_address_1',
465 'supplemental_address_2',
466 'supplemental_address_3',
467 'StateProvince.name',
468 ];
469
470 foreach ($addressFields as $field) {
471 if (array_key_exists($field, $values)) {
472 if (!array_key_exists('address', $params)) {
473 $params['address'] = [];
474 }
475 $params['address'][$addressCnt][$field] = $values[$field];
476 }
477 }
478
479 if ($addressCnt == 1) {
480
481 $params['address'][$addressCnt]['is_primary'] = TRUE;
482 }
483 return TRUE;
484 }
485
486 if (isset($values['note'])) {
487 // add a note field
488 if (!isset($params['note'])) {
489 $params['note'] = [];
490 }
491 $noteBlock = count($params['note']) + 1;
492
493 $params['note'][$noteBlock] = [];
494 if (!isset($fields['Note'])) {
495 $fields['Note'] = CRM_Core_DAO_Note::fields();
496 }
497
498 // get the current logged in civicrm user
499 $session = CRM_Core_Session::singleton();
500 $userID = $session->get('userID');
501
502 if ($userID) {
503 $values['contact_id'] = $userID;
504 }
505
506 _civicrm_api3_store_values($fields['Note'], $values, $params['note'][$noteBlock]);
507
508 return TRUE;
509 }
510
511 // Check for custom field values
512
513 if (empty($fields['custom'])) {
514 $fields['custom'] = &CRM_Core_BAO_CustomField::getFields(CRM_Utils_Array::value('contact_type', $values),
515 FALSE, FALSE, NULL, NULL, FALSE, FALSE, FALSE
516 );
517 }
518
519 foreach ($values as $key => $value) {
520 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
521 // check if it's a valid custom field id
522
523 if (!array_key_exists($customFieldID, $fields['custom'])) {
524 return civicrm_api3_create_error('Invalid custom field ID');
525 }
526 else {
527 $params[$key] = $value;
528 }
529 }
530 }
531 }
532
533 /**
534 * Get row count for the event selector.
535 *
536 * @param int $mailing_id
537 * ID of the mailing.
538 * @param int $job_id
539 * Optional ID of a job to filter on.
540 * @param bool $is_distinct
541 * Group by queue ID?.
542 *
543 * @return int
544 * Number of rows in result set
545 */
546 public static function getTotalCount(
547 $mailing_id, $job_id = NULL,
548 $is_distinct = FALSE
549 ) {
550 $dao = new CRM_Core_DAO();
551
552 $forward = self::getTableName();
553 $queue = CRM_Mailing_Event_BAO_Queue::getTableName();
554 $mailing = CRM_Mailing_BAO_Mailing::getTableName();
555 $job = CRM_Mailing_BAO_MailingJob::getTableName();
556
557 $query = "
558 SELECT COUNT($forward.id) as forward
559 FROM $forward
560 INNER JOIN $queue
561 ON $forward.event_queue_id = $queue.id
562 INNER JOIN $job
563 ON $queue.job_id = $job.id
564 INNER JOIN $mailing
565 ON $job.mailing_id = $mailing.id
566 AND $job.is_test = 0
567 WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer');
568
569 if (!empty($job_id)) {
570 $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer');
571 }
572
573 if ($is_distinct) {
574 $query .= " GROUP BY $queue.id ";
575 }
576
577 // query was missing
578 $dao->query($query);
579
580 if ($dao->fetch()) {
581 return $dao->forward;
582 }
583
584 return NULL;
585 }
586
587 /**
588 * Get rows for the event browser.
589 *
590 * @param int $mailing_id
591 * ID of the mailing.
592 * @param int $job_id
593 * Optional ID of the job.
594 * @param bool $is_distinct
595 * Group by queue id?.
596 * @param int $offset
597 * Offset.
598 * @param int $rowCount
599 * Number of rows.
600 * @param array $sort
601 * Sort array.
602 *
603 * @return array
604 * Result set
605 */
606 public static function &getRows(
607 $mailing_id, $job_id = NULL,
608 $is_distinct = FALSE, $offset = NULL, $rowCount = NULL, $sort = NULL
609 ) {
610
611 $dao = new CRM_Core_DAO();
612
613 $forward = self::getTableName();
614 $queue = CRM_Mailing_Event_BAO_Queue::getTableName();
615 $mailing = CRM_Mailing_BAO_Mailing::getTableName();
616 $job = CRM_Mailing_BAO_MailingJob::getTableName();
617 $contact = CRM_Contact_BAO_Contact::getTableName();
618 $email = CRM_Core_BAO_Email::getTableName();
619
620 $query = "
621 SELECT $contact.display_name as from_name,
622 $contact.id as from_id,
623 $email.email as from_email,
624 dest_contact.id as dest_id,
625 dest_email.email as dest_email,
626 $forward.time_stamp as date
627 FROM $contact
628 INNER JOIN $queue
629 ON $queue.contact_id = $contact.id
630 INNER JOIN $email
631 ON $queue.email_id = $email.id
632 INNER JOIN $forward
633 ON $forward.event_queue_id = $queue.id
634 INNER JOIN $queue as dest_queue
635 ON $forward.dest_queue_id = dest_queue.id
636 INNER JOIN $contact as dest_contact
637 ON dest_queue.contact_id = dest_contact.id
638 INNER JOIN $email as dest_email
639 ON dest_queue.email_id = dest_email.id
640 INNER JOIN $job
641 ON $queue.job_id = $job.id
642 INNER JOIN $mailing
643 ON $job.mailing_id = $mailing.id
644 AND $job.is_test = 0
645 WHERE $mailing.id = " . CRM_Utils_Type::escape($mailing_id, 'Integer');
646
647 if (!empty($job_id)) {
648 $query .= " AND $job.id = " . CRM_Utils_Type::escape($job_id, 'Integer');
649 }
650
651 if ($is_distinct) {
652 $query .= " GROUP BY $queue.id, dest_contact.id, dest_email.email, $forward.time_stamp ";
653 }
654
655 $orderBy = "$contact.sort_name ASC, {$forward}.time_stamp DESC";
656 if ($sort) {
657 if (is_string($sort)) {
658 $sort = CRM_Utils_Type::escape($sort, 'String');
659 $orderBy = $sort;
660 }
661 else {
662 $orderBy = trim($sort->orderBy());
663 }
664 }
665
666 $query .= " ORDER BY {$orderBy} ";
667
668 if ($offset || $rowCount) {
669 //Added "||$rowCount" to avoid displaying all records on first page
670 $query .= ' LIMIT ' . CRM_Utils_Type::escape($offset, 'Integer') . ', ' . CRM_Utils_Type::escape($rowCount, 'Integer');
671 }
672
673 $dao->query($query);
674
675 $results = [];
676
677 while ($dao->fetch()) {
678 $from_url = CRM_Utils_System::url('civicrm/contact/view',
679 "reset=1&cid={$dao->from_id}"
680 );
681 $dest_url = CRM_Utils_System::url('civicrm/contact/view',
682 "reset=1&cid={$dao->dest_id}"
683 );
684 $results[] = [
685 'from_name' => "<a href=\"$from_url\">{$dao->from_name}</a>",
686 'from_email' => $dao->from_email,
687 'dest_email' => "<a href=\"$dest_url\">{$dao->dest_email}</a>",
688 'date' => CRM_Utils_Date::customFormat($dao->date),
689 ];
690 }
691 return $results;
692 }
693
694 }