Merge pull request #22255 from MegaphoneJon/membership-14-2021
[civicrm-core.git] / CRM / Report / Form / Mailing / Detail.php
CommitLineData
6a488035 1<?php
6a488035
TO
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17class CRM_Report_Form_Mailing_Detail extends CRM_Report_Form {
18
be2fb01f 19 protected $_customGroupExtends = [
9b09f418
J
20 'Contact',
21 'Individual',
22 'Household',
23 'Organization',
be2fb01f 24 ];
9b09f418 25
6a488035 26 protected $_exposeContactID = FALSE;
2f4c2f5d 27
74cf4551 28 /**
1728e9a0 29 * This report has not been optimised for group filtering.
30 *
31 * The functionality for group filtering has been improved but not
32 * all reports have been adjusted to take care of it. This report has not
33 * and will run an inefficient query until fixed.
34 *
1728e9a0 35 * @var bool
0e480632 36 * @see https://issues.civicrm.org/jira/browse/CRM-19170
1728e9a0 37 */
38 protected $groupFilterNotOptimised = TRUE;
39
40 /**
41 * Class constructor.
74cf4551 42 */
00be9182 43 public function __construct() {
be2fb01f 44 $this->_columns = [];
6a488035 45
be2fb01f 46 $this->_columns['civicrm_contact'] = [
6a488035 47 'dao' => 'CRM_Contact_DAO_Contact',
be2fb01f
CW
48 'fields' => [
49 'id' => [
6a488035
TO
50 'name' => 'id',
51 'title' => ts('Contact ID'),
52 'required' => TRUE,
53 'no_display' => TRUE,
be2fb01f
CW
54 ],
55 'sort_name' => [
6a488035
TO
56 'title' => ts('Contact Name'),
57 'required' => TRUE,
be2fb01f
CW
58 ],
59 ],
60 'filters' => [
61 'sort_name' => [
6a488035 62 'title' => ts('Contact Name'),
be2fb01f
CW
63 ],
64 'id' => [
6a488035
TO
65 'title' => ts('Contact ID'),
66 'no_display' => TRUE,
be2fb01f
CW
67 ],
68 ],
69 'order_bys' => [
70 'sort_name' => [
9d72cede 71 'title' => ts('Contact Name'),
6a488035
TO
72 'default' => TRUE,
73 'default_order' => 'ASC',
be2fb01f
CW
74 ],
75 ],
6a488035 76 'grouping' => 'contact-fields',
be2fb01f 77 ];
6a488035 78
be2fb01f 79 $this->_columns['civicrm_mailing'] = [
6a488035 80 'dao' => 'CRM_Mailing_DAO_Mailing',
be2fb01f
CW
81 'fields' => [
82 'mailing_name' => [
6a488035 83 'name' => 'name',
fdb8835d 84 'title' => ts('Mailing Name'),
6a488035 85 'default' => TRUE,
be2fb01f
CW
86 ],
87 'mailing_subject' => [
fdb8835d
BS
88 'name' => 'subject',
89 'title' => ts('Mailing Subject'),
df28e44e 90 'default' => TRUE,
be2fb01f
CW
91 ],
92 ],
93 'filters' => [
94 'mailing_id' => [
6a488035 95 'name' => 'id',
fdb8835d 96 'title' => ts('Mailing Name'),
6a488035
TO
97 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
98 'type' => CRM_Utils_Type::T_INT,
99 'options' => CRM_Mailing_BAO_Mailing::getMailingsList(),
be2fb01f
CW
100 ],
101 'mailing_subject' => [
fdb8835d
BS
102 'name' => 'subject',
103 'title' => ts('Mailing Subject'),
df28e44e 104 'type' => CRM_Utils_Type::T_STRING,
fdb8835d 105 'operator' => 'like',
be2fb01f
CW
106 ],
107 ],
108 'order_bys' => [
109 'mailing_name' => [
6a488035 110 'name' => 'name',
fdb8835d 111 'title' => ts('Mailing Name'),
be2fb01f
CW
112 ],
113 'mailing_subject' => [
fdb8835d 114 'name' => 'subject',
df28e44e 115 'title' => ts('Mailing Subject'),
be2fb01f
CW
116 ],
117 ],
6a488035 118 'grouping' => 'mailing-fields',
be2fb01f 119 ];
6a488035
TO
120
121 // adding dao just to have alias
be2fb01f 122 $this->_columns['civicrm_mailing_event_bounce'] = [
6a488035 123 'dao' => 'CRM_Mailing_Event_DAO_Bounce',
be2fb01f 124 ];
6a488035 125
be2fb01f 126 $this->_columns['civicrm_mailing_event_delivered'] = [
6a488035 127 'dao' => 'CRM_Mailing_Event_DAO_Delivered',
be2fb01f
CW
128 'fields' => [
129 'delivery_id' => [
6a488035
TO
130 'name' => 'id',
131 'title' => ts('Delivery Status'),
132 'default' => TRUE,
be2fb01f
CW
133 ],
134 ],
135 'filters' => [
136 'delivery_status' => [
6a488035
TO
137 'name' => 'delivery_status',
138 'title' => ts('Delivery Status'),
139 'operatorType' => CRM_Report_Form::OP_SELECT,
140 'type' => CRM_Utils_Type::T_STRING,
be2fb01f 141 'options' => [
6a488035
TO
142 '' => 'Any',
143 'successful' => 'Successful',
144 'bounced' => 'Bounced',
be2fb01f
CW
145 ],
146 ],
147 ],
6a488035 148 'grouping' => 'mailing-fields',
be2fb01f 149 ];
6a488035 150
be2fb01f 151 $this->_columns['civicrm_mailing_event_unsubscribe'] = [
6a488035 152 'dao' => 'CRM_Mailing_Event_DAO_Unsubscribe',
be2fb01f
CW
153 'fields' => [
154 'unsubscribe_id' => [
6a488035
TO
155 'name' => 'id',
156 'title' => ts('Unsubscribe'),
157 'default' => TRUE,
be2fb01f
CW
158 ],
159 'optout_id' => [
6a488035
TO
160 'name' => 'id',
161 'title' => ts('Opt-out'),
162 'default' => TRUE,
163 'alias' => 'mailing_event_unsubscribe_civireport2',
be2fb01f
CW
164 ],
165 ],
166 'filters' => [
167 'is_unsubscribed' => [
6a488035
TO
168 'name' => 'id',
169 'title' => ts('Unsubscribed'),
170 'type' => CRM_Utils_Type::T_INT,
171 'operatorType' => CRM_Report_Form::OP_SELECT,
be2fb01f 172 'options' => [
9d72cede
EM
173 '' => ts('Any'),
174 '0' => ts('No'),
21dfd5f5 175 '1' => ts('Yes'),
be2fb01f 176 ],
6a488035 177 'clause' => 'mailing_event_unsubscribe_civireport.id IS NULL',
be2fb01f
CW
178 ],
179 'is_optout' => [
6a488035
TO
180 'title' => ts('Opted-out'),
181 'type' => CRM_Utils_Type::T_INT,
182 'operatorType' => CRM_Report_Form::OP_SELECT,
be2fb01f 183 'options' => [
9d72cede
EM
184 '' => ts('Any'),
185 '0' => ts('No'),
21dfd5f5 186 '1' => ts('Yes'),
be2fb01f 187 ],
6a488035 188 'clause' => 'mailing_event_unsubscribe_civireport2.id IS NULL',
be2fb01f
CW
189 ],
190 ],
6a488035 191 'grouping' => 'mailing-fields',
be2fb01f 192 ];
6a488035 193
be2fb01f 194 $this->_columns['civicrm_mailing_event_reply'] = [
6a488035 195 'dao' => 'CRM_Mailing_Event_DAO_Reply',
be2fb01f
CW
196 'fields' => [
197 'reply_id' => [
6a488035
TO
198 'name' => 'id',
199 'title' => ts('Reply'),
be2fb01f
CW
200 ],
201 ],
202 'filters' => [
203 'is_replied' => [
6a488035
TO
204 'name' => 'id',
205 'title' => ts('Replied'),
206 'type' => CRM_Utils_Type::T_INT,
207 'operatorType' => CRM_Report_Form::OP_SELECT,
be2fb01f 208 'options' => [
9d72cede
EM
209 '' => ts('Any'),
210 '0' => ts('No'),
21dfd5f5 211 '1' => ts('Yes'),
be2fb01f 212 ],
6a488035 213 'clause' => 'mailing_event_reply_civireport.id IS NULL',
be2fb01f
CW
214 ],
215 ],
6a488035 216 'grouping' => 'mailing-fields',
be2fb01f 217 ];
6a488035 218
be2fb01f 219 $this->_columns['civicrm_mailing_event_forward'] = [
6a488035 220 'dao' => 'CRM_Mailing_Event_DAO_Forward',
be2fb01f
CW
221 'fields' => [
222 'forward_id' => [
6a488035
TO
223 'name' => 'id',
224 'title' => ts('Forwarded to Email'),
be2fb01f
CW
225 ],
226 ],
227 'filters' => [
228 'is_forwarded' => [
6a488035
TO
229 'name' => 'id',
230 'title' => ts('Forwarded'),
231 'type' => CRM_Utils_Type::T_INT,
232 'operatorType' => CRM_Report_Form::OP_SELECT,
be2fb01f 233 'options' => [
9d72cede
EM
234 '' => ts('Any'),
235 '0' => ts('No'),
21dfd5f5 236 '1' => ts('Yes'),
be2fb01f 237 ],
6a488035 238 'clause' => 'mailing_event_forward_civireport.id IS NULL',
be2fb01f
CW
239 ],
240 ],
6a488035 241 'grouping' => 'mailing-fields',
be2fb01f 242 ];
6a488035 243
be2fb01f 244 $this->_columns['civicrm_email'] = [
6a488035 245 'dao' => 'CRM_Core_DAO_Email',
be2fb01f
CW
246 'fields' => [
247 'email' => [
6a488035 248 'title' => ts('Email'),
be2fb01f
CW
249 ],
250 ],
6a488035 251 'grouping' => 'contact-fields',
be2fb01f 252 ];
6a488035 253
be2fb01f 254 $this->_columns['civicrm_phone'] = [
6a488035 255 'dao' => 'CRM_Core_DAO_Phone',
be2fb01f 256 'fields' => ['phone' => NULL],
6a488035 257 'grouping' => 'contact-fields',
be2fb01f 258 ];
6a488035 259
16e2e80c
CW
260 $this->_groupFilter = TRUE;
261 $this->_tagFilter = TRUE;
6a488035
TO
262
263 parent::__construct();
264 }
265
00be9182 266 public function select() {
be2fb01f 267 $select = $columns = [];
6a488035
TO
268 foreach ($this->_columns as $tableName => $table) {
269 if (array_key_exists('fields', $table)) {
270 foreach ($table['fields'] as $fieldName => $field) {
9d72cede
EM
271 if (!empty($field['required']) ||
272 !empty($this->_params['fields'][$fieldName])
273 ) {
be2fb01f 274 if (in_array($fieldName, [
9d72cede
EM
275 'unsubscribe_id',
276 'optout_id',
277 'forward_id',
21dfd5f5 278 'reply_id',
be2fb01f 279 ])) {
6a488035 280 $select[] = "IF({$field['dbAlias']} IS NULL, 'No', 'Yes') as {$tableName}_{$fieldName}";
9c1bc317
CW
281 $this->_columnHeaders["{$tableName}_{$fieldName}"]['type'] = $field['type'] ?? NULL;
282 $this->_columnHeaders["{$tableName}_{$fieldName}"]['no_display'] = $field['no_display'] ?? NULL;
283 $this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = $field['title'] ?? NULL;
6a488035
TO
284 unset($this->_columns[$tableName]['fields'][$fieldName]);
285 $columns[$tableName][$fieldName] = $field;
286 }
287 elseif ($fieldName == 'delivery_id') {
939c5588 288 $select[] = "IF(mailing_event_bounce_civireport.id IS NOT NULL, 'Bounced', IF(mailing_event_delivered_civireport.id IS NOT NULL, 'Successful', 'Unknown')) as {$tableName}_{$fieldName}";
9c1bc317
CW
289 $this->_columnHeaders["{$tableName}_{$fieldName}"]['type'] = $field['type'] ?? NULL;
290 $this->_columnHeaders["{$tableName}_{$fieldName}"]['no_display'] = $field['no_display'] ?? NULL;
291 $this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = $field['title'] ?? NULL;
6a488035
TO
292 unset($this->_columns[$tableName]['fields'][$fieldName]);
293 $columns[$tableName][$fieldName] = $field;
294 }
295 }
296 }
297 }
298 }
299
300 parent::select();
301 if (!empty($select)) {
302 $this->_select .= ', ' . implode(', ', $select) . " ";
303 }
304
305 // put the fields that were unset, back in place
306 foreach ($columns as $tableName => $table) {
307 foreach ($table as $fieldName => $fields) {
308 $this->_columns[$tableName]['fields'][$fieldName] = $fields;
309 }
310 }
311
312 // simple sort
313 ksort($this->_columnHeaders);
314 }
315
00be9182 316 public function from() {
6a488035
TO
317 $this->_from = "
318 FROM civicrm_contact {$this->_aliases['civicrm_contact']}";
319
320 $this->_from .= "
2f4c2f5d 321 INNER JOIN civicrm_mailing_event_queue
322 ON civicrm_mailing_event_queue.contact_id = {$this->_aliases['civicrm_contact']}.id
d2c742e9 323 LEFT JOIN civicrm_email {$this->_aliases['civicrm_email']}
2f4c2f5d 324 ON civicrm_mailing_event_queue.email_id = {$this->_aliases['civicrm_email']}.id";
6a488035
TO
325
326 if (array_key_exists('delivery_id', $this->_params['fields'])) {
327 $this->_from .= "
328 LEFT JOIN civicrm_mailing_event_delivered {$this->_aliases['civicrm_mailing_event_delivered']}
329 ON {$this->_aliases['civicrm_mailing_event_delivered']}.event_queue_id = civicrm_mailing_event_queue.id
2f4c2f5d 330 LEFT JOIN civicrm_mailing_event_bounce {$this->_aliases['civicrm_mailing_event_bounce']}
331 ON {$this->_aliases['civicrm_mailing_event_bounce']}.event_queue_id = civicrm_mailing_event_queue.id";
9d72cede
EM
332 if (CRM_Utils_Array::value('delivery_status_value', $this->_params) ==
333 'bounced'
334 ) {
6a488035
TO
335 $this->_columns['civicrm_mailing_event_delivered']['filters']['delivery_status']['clause'] = "{$this->_aliases['civicrm_mailing_event_bounce']}.id IS NOT NULL";
336 }
9d72cede
EM
337 elseif (CRM_Utils_Array::value('delivery_status_value', $this->_params) ==
338 'successful'
339 ) {
939c5588 340 $this->_columns['civicrm_mailing_event_delivered']['filters']['delivery_status']['clause'] = "{$this->_aliases['civicrm_mailing_event_delivered']}.id IS NOT NULL AND {$this->_aliases['civicrm_mailing_event_bounce']}.id IS NULL";
6a488035
TO
341 }
342 }
343 else {
344 unset($this->_columns['civicrm_mailing_event_delivered']['filters']['delivery_status']);
345 }
346
347 if (array_key_exists('reply_id', $this->_params['fields']) ||
348 is_numeric(CRM_Utils_Array::value('is_replied_value', $this->_params))
349 ) {
350 if (CRM_Utils_Array::value('is_replied_value', $this->_params) == 1) {
351 $joinType = 'INNER';
352 $this->_columns['civicrm_mailing_event_reply']['filters']['is_replied']['clause'] = '(1)';
353 }
354 else {
355 $joinType = 'LEFT';
356 }
357 $this->_from .= "
358 {$joinType} JOIN civicrm_mailing_event_reply {$this->_aliases['civicrm_mailing_event_reply']}
359 ON {$this->_aliases['civicrm_mailing_event_reply']}.event_queue_id = civicrm_mailing_event_queue.id";
360 }
361 else {
362 unset($this->_columns['civicrm_mailing_event_reply']['filters']['is_replied']);
363 }
364
365 if (array_key_exists('unsubscribe_id', $this->_params['fields']) ||
366 is_numeric(CRM_Utils_Array::value('is_unsubscribed_value', $this->_params))
367 ) {
9d72cede
EM
368 if (CRM_Utils_Array::value('is_unsubscribed_value', $this->_params) == 1
369 ) {
6a488035
TO
370 $joinType = 'INNER';
371 $this->_columns['civicrm_mailing_event_unsubscribe']['filters']['is_unsubscribed']['clause'] = '(1)';
372 }
373 else {
374 $joinType = 'LEFT';
375 }
376 $this->_from .= "
377 {$joinType} JOIN civicrm_mailing_event_unsubscribe {$this->_aliases['civicrm_mailing_event_unsubscribe']}
378 ON {$this->_aliases['civicrm_mailing_event_unsubscribe']}.event_queue_id = civicrm_mailing_event_queue.id
379 AND {$this->_aliases['civicrm_mailing_event_unsubscribe']}.org_unsubscribe = 0";
380 }
381 else {
382 unset($this->_columns['civicrm_mailing_event_unsubscribe']['filters']['is_unsubscribed']);
383 }
384
385 if (array_key_exists('optout_id', $this->_params['fields']) ||
386 is_numeric(CRM_Utils_Array::value('is_optout_value', $this->_params))
387 ) {
388 if (CRM_Utils_Array::value('is_optout_value', $this->_params) == 1) {
389 $joinType = 'INNER';
390 $this->_columns['civicrm_mailing_event_unsubscribe']['filters']['is_optout']['clause'] = '(1)';
391 }
392 else {
393 $joinType = 'LEFT';
394 }
395 $this->_from .= "
396 {$joinType} JOIN civicrm_mailing_event_unsubscribe {$this->_aliases['civicrm_mailing_event_unsubscribe']}2
397 ON {$this->_aliases['civicrm_mailing_event_unsubscribe']}2.event_queue_id = civicrm_mailing_event_queue.id
398 AND {$this->_aliases['civicrm_mailing_event_unsubscribe']}2.org_unsubscribe = 1";
399 }
400 else {
401 unset($this->_columns['civicrm_mailing_event_unsubscribe']['filters']['is_optout']);
402 }
403
404 if (array_key_exists('forward_id', $this->_params['fields']) ||
405 is_numeric(CRM_Utils_Array::value('is_forwarded_value', $this->_params))
406 ) {
407 if (CRM_Utils_Array::value('is_forwarded_value', $this->_params) == 1) {
408 $joinType = 'INNER';
409 $this->_columns['civicrm_mailing_event_forward']['filters']['is_forwarded']['clause'] = '(1)';
410 }
411 else {
412 $joinType = 'LEFT';
413 }
414 $this->_from .= "
415 {$joinType} JOIN civicrm_mailing_event_forward {$this->_aliases['civicrm_mailing_event_forward']}
416 ON {$this->_aliases['civicrm_mailing_event_forward']}.event_queue_id = civicrm_mailing_event_queue.id";
417 }
418 else {
419 unset($this->_columns['civicrm_mailing_event_forward']['filters']['is_forwarded']);
420 }
421
422 $this->_from .= "
2f4c2f5d 423 INNER JOIN civicrm_mailing_job
424 ON civicrm_mailing_event_queue.job_id = civicrm_mailing_job.id
425 INNER JOIN civicrm_mailing {$this->_aliases['civicrm_mailing']}
426 ON civicrm_mailing_job.mailing_id = {$this->_aliases['civicrm_mailing']}.id
427 AND civicrm_mailing_job.is_test = 0";
6a488035 428
2a5bea53 429 $this->joinPhoneFromContact();
6a488035
TO
430 }
431
00be9182 432 public function where() {
6a488035
TO
433 parent::where();
434 $this->_where .= " AND {$this->_aliases['civicrm_mailing']}.sms_provider_id IS NULL";
435 }
436
74cf4551
EM
437 /**
438 * @return array
439 */
00be9182 440 public function mailingList() {
6a488035 441
be2fb01f 442 $data = [];
6a488035 443 $mailing = new CRM_Mailing_BAO_Mailing();
9d72cede 444 $query = "SELECT name FROM civicrm_mailing ";
6a488035
TO
445 $mailing->query($query);
446
447 while ($mailing->fetch()) {
8f56d1f5 448 $data[CRM_Core_DAO::escapeString($mailing->name)] = $mailing->name;
6a488035
TO
449 }
450
451 return $data;
452 }
453
74cf4551 454 /**
ced9bfed
EM
455 * Alter display of rows.
456 *
457 * Iterate through the rows retrieved via SQL and make changes for display purposes,
458 * such as rendering contacts as links.
459 *
460 * @param array $rows
461 * Rows generated by SQL, with an array for each row.
74cf4551 462 */
00be9182 463 public function alterDisplay(&$rows) {
6a488035
TO
464 $entryFound = FALSE;
465 foreach ($rows as $rowNum => $row) {
d2c742e9
J
466
467 // If the email address has been deleted
468 if (array_key_exists('civicrm_email_email', $row)) {
469 if (empty($rows[$rowNum]['civicrm_email_email'])) {
81ca3560 470 $rows[$rowNum]['civicrm_email_email'] = '<del>' . ts('Email address deleted.') . '</del>';
d2c742e9
J
471 }
472 $entryFound = TRUE;
473 }
474
6a488035
TO
475 // make count columns point to detail report
476 // convert display name to links
477 if (array_key_exists('civicrm_contact_sort_name', $row) &&
478 array_key_exists('civicrm_contact_id', $row)
479 ) {
480 $url = CRM_Utils_System::url('civicrm/contact/view',
481 'reset=1&cid=' . $row['civicrm_contact_id'],
482 $this->_absoluteUrl
483 );
484 $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
485 $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts("View Contact details for this contact.");
486 $entryFound = TRUE;
487 }
488
489 // skip looking further in rows, if first row itself doesn't
490 // have the column we need
491 if (!$entryFound) {
492 break;
493 }
494 }
495 }
96025800 496
6a488035 497}