Ian province abbreviation patch - issue 724
[civicrm-core.git] / CRM / Report / Form / Mailing / Summary.php
... / ...
CommitLineData
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
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 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2015
32 * $Id$
33 *
34 */
35class CRM_Report_Form_Mailing_Summary extends CRM_Report_Form {
36
37 protected $_summary = NULL;
38
39 # just a toggle we use to build the from
40 protected $_mailingidField = FALSE;
41
42 protected $_customGroupExtends = array();
43
44 protected $_add2groupSupported = FALSE;
45
46 public $_drilldownReport = array('mailing/detail' => 'Link to Detail Report');
47
48 protected $_charts = array(
49 '' => 'Tabular',
50 'bar_3dChart' => 'Bar Chart',
51 );
52
53 public $campaignEnabled = FALSE;
54
55 /**
56 */
57 /**
58 */
59 public function __construct() {
60 $this->_columns = array();
61
62 $this->_columns['civicrm_mailing'] = array(
63 'dao' => 'CRM_Mailing_DAO_Mailing',
64 'fields' => array(
65 'id' => array(
66 'name' => 'id',
67 'title' => ts('Mailing ID'),
68 'required' => TRUE,
69 'no_display' => TRUE,
70 ),
71 'name' => array(
72 'title' => ts('Mailing Name'),
73 'required' => TRUE,
74 ),
75 'created_date' => array(
76 'title' => ts('Date Created'),
77 ),
78 'subject' => array(
79 'title' => ts('Subject'),
80 ),
81 ),
82 'filters' => array(
83 'is_completed' => array(
84 'title' => ts('Mailing Status'),
85 'operatorType' => CRM_Report_Form::OP_SELECT,
86 'type' => CRM_Utils_Type::T_INT,
87 'options' => array(
88 0 => 'Incomplete',
89 1 => 'Complete',
90 ),
91 //'operator' => 'like',
92 'default' => 1,
93 ),
94 'mailing_name' => array(
95 'name' => 'name',
96 'title' => ts('Mailing'),
97 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
98 'type' => CRM_Utils_Type::T_STRING,
99 'options' => self::mailing_select(),
100 'operator' => 'like',
101 ),
102 ),
103 );
104
105 $this->_columns['civicrm_mailing_job'] = array(
106 'dao' => 'CRM_Mailing_DAO_MailingJob',
107 'fields' => array(
108 'start_date' => array(
109 'title' => ts('Start Date'),
110 ),
111 'end_date' => array(
112 'title' => ts('End Date'),
113 ),
114 ),
115 'filters' => array(
116 'status' => array(
117 'type' => CRM_Utils_Type::T_STRING,
118 'default' => 'Complete',
119 'no_display' => TRUE,
120 ),
121 'is_test' => array(
122 'type' => CRM_Utils_Type::T_INT,
123 'default' => 0,
124 'no_display' => TRUE,
125 ),
126 'start_date' => array(
127 'title' => ts('Start Date'),
128 'default' => 'this.year',
129 'operatorType' => CRM_Report_Form::OP_DATE,
130 'type' => CRM_Utils_Type::T_DATE,
131 ),
132 'end_date' => array(
133 'title' => ts('End Date'),
134 'default' => 'this.year',
135 'operatorType' => CRM_Report_Form::OP_DATE,
136 'type' => CRM_Utils_Type::T_DATE,
137 ),
138 ),
139 );
140
141 $this->_columns['civicrm_mailing_event_queue'] = array(
142 'dao' => 'CRM_Mailing_DAO_Mailing',
143 'fields' => array(
144 'queue_count' => array(
145 'name' => 'id',
146 'title' => ts('Intended Recipients'),
147 ),
148 ),
149 );
150
151 $this->_columns['civicrm_mailing_event_delivered'] = array(
152 'dao' => 'CRM_Mailing_DAO_Mailing',
153 'fields' => array(
154 'delivered_count' => array(
155 'name' => 'event_queue_id',
156 'title' => ts('Delivered'),
157 ),
158 'accepted_rate' => array(
159 'title' => 'Accepted Rate',
160 'statistics' => array(
161 'calc' => 'PERCENTAGE',
162 'top' => 'civicrm_mailing_event_delivered.delivered_count',
163 'base' => 'civicrm_mailing_event_queue.queue_count',
164 ),
165 ),
166 ),
167 );
168
169 $this->_columns['civicrm_mailing_event_bounce'] = array(
170 'dao' => 'CRM_Mailing_DAO_Mailing',
171 'fields' => array(
172 'bounce_count' => array(
173 'name' => 'event_queue_id',
174 'title' => ts('Bounce'),
175 ),
176 'bounce_rate' => array(
177 'title' => 'Bounce Rate',
178 'statistics' => array(
179 'calc' => 'PERCENTAGE',
180 'top' => 'civicrm_mailing_event_bounce.bounce_count',
181 'base' => 'civicrm_mailing_event_queue.queue_count',
182 ),
183 ),
184 ),
185 );
186
187 $this->_columns['civicrm_mailing_event_opened'] = array(
188 'dao' => 'CRM_Mailing_DAO_Mailing',
189 'fields' => array(
190 'unique_open_count' => array(
191 'name' => 'id',
192 'alias' => 'mailing_event_opened_civireport',
193 'dbAlias' => 'mailing_event_opened_civireport.event_queue_id',
194 'title' => ts('Unique Opens'),
195 ),
196 'unique_open_rate' => array(
197 'title' => 'Unique Open Rate',
198 'statistics' => array(
199 'calc' => 'PERCENTAGE',
200 'top' => 'civicrm_mailing_event_opened.unique_open_count',
201 'base' => 'civicrm_mailing_event_delivered.delivered_count',
202 ),
203 ),
204 'open_count' => array(
205 'name' => 'event_queue_id',
206 'title' => ts('Total Opens'),
207 ),
208 'open_rate' => array(
209 'title' => 'Total Open Rate',
210 'statistics' => array(
211 'calc' => 'PERCENTAGE',
212 'top' => 'civicrm_mailing_event_opened.open_count',
213 'base' => 'civicrm_mailing_event_delivered.delivered_count',
214 ),
215 ),
216 ),
217 );
218
219 $this->_columns['civicrm_mailing_event_trackable_url_open'] = array(
220 'dao' => 'CRM_Mailing_DAO_Mailing',
221 'fields' => array(
222 'click_count' => array(
223 'name' => 'event_queue_id',
224 'title' => ts('Clicks'),
225 ),
226 'CTR' => array(
227 'title' => 'Click through Rate',
228 'default' => 0,
229 'statistics' => array(
230 'calc' => 'PERCENTAGE',
231 'top' => 'civicrm_mailing_event_trackable_url_open.click_count',
232 'base' => 'civicrm_mailing_event_delivered.delivered_count',
233 ),
234 ),
235 'CTO' => array(
236 'title' => 'Click to Open Rate',
237 'default' => 0,
238 'statistics' => array(
239 'calc' => 'PERCENTAGE',
240 'top' => 'civicrm_mailing_event_trackable_url_open.click_count',
241 'base' => 'civicrm_mailing_event_opened.open_count',
242 ),
243 ),
244 ),
245 );
246
247 $this->_columns['civicrm_mailing_event_unsubscribe'] = array(
248 'dao' => 'CRM_Mailing_DAO_Mailing',
249 'fields' => array(
250 'unsubscribe_count' => array(
251 'name' => 'id',
252 'title' => ts('Unsubscribe'),
253 'alias' => 'mailing_event_unsubscribe_civireport',
254 'dbAlias' => 'mailing_event_unsubscribe_civireport.event_queue_id',
255 ),
256 'optout_count' => array(
257 'name' => 'id',
258 'title' => ts('Opt-outs'),
259 'alias' => 'mailing_event_optout_civireport',
260 'dbAlias' => 'mailing_event_optout_civireport.event_queue_id',
261 ),
262 ),
263 );
264 $config = CRM_Core_Config::singleton();
265 $this->campaignEnabled = in_array("CiviCampaign", $config->enableComponents);
266 if ($this->campaignEnabled) {
267 $this->_columns['civicrm_campaign'] = array(
268 'dao' => 'CRM_Campaign_DAO_Campaign',
269 'fields' => array(
270 'title' => array(
271 'title' => ts('Campaign Name'),
272 ),
273 ),
274 'filters' => array(
275 'title' => array(
276 'type' => CRM_Utils_Type::T_STRING,
277 ),
278 ),
279 );
280 }
281 parent::__construct();
282 }
283
284 /**
285 * @return array
286 */
287 public function mailing_select() {
288
289 $data = array();
290
291 $mailing = new CRM_Mailing_BAO_Mailing();
292 $query = "SELECT name FROM civicrm_mailing WHERE sms_provider_id IS NULL";
293 $mailing->query($query);
294
295 while ($mailing->fetch()) {
296 $data[mysql_real_escape_string($mailing->name)] = $mailing->name;
297 }
298
299 return $data;
300 }
301
302 public function preProcess() {
303 $this->assign('chartSupported', TRUE);
304 parent::preProcess();
305 }
306
307 /**
308 * manipulate the select function to query count functions.
309 */
310 public function select() {
311
312 $count_tables = array(
313 'civicrm_mailing_event_queue',
314 'civicrm_mailing_event_delivered',
315 'civicrm_mailing_event_opened',
316 'civicrm_mailing_event_bounce',
317 'civicrm_mailing_event_trackable_url_open',
318 'civicrm_mailing_event_unsubscribe',
319 );
320
321 $select = array();
322 $this->_columnHeaders = array();
323 foreach ($this->_columns as $tableName => $table) {
324 if (array_key_exists('fields', $table)) {
325 foreach ($table['fields'] as $fieldName => $field) {
326 if (!empty($field['required']) || !empty($this->_params['fields'][$fieldName])) {
327
328 # for statistics
329 if (!empty($field['statistics'])) {
330 switch ($field['statistics']['calc']) {
331 case 'PERCENTAGE':
332 $base_table_column = explode('.', $field['statistics']['base']);
333 $top_table_column = explode('.', $field['statistics']['top']);
334
335 $select[] = "CONCAT(round(
336 count(DISTINCT {$this->_columns[$top_table_column[0]]['fields'][$top_table_column[1]]['dbAlias']}) /
337 count(DISTINCT {$this->_columns[$base_table_column[0]]['fields'][$base_table_column[1]]['dbAlias']}) * 100, 2
338 ), '%') as {$tableName}_{$fieldName}";
339 break;
340 }
341 }
342 else {
343 if (in_array($tableName, $count_tables)) {
344 $select[] = "count(DISTINCT {$field['dbAlias']}) as {$tableName}_{$fieldName}";
345 }
346 else {
347 $select[] = "{$field['dbAlias']} as {$tableName}_{$fieldName}";
348 }
349 }
350 $this->_columnHeaders["{$tableName}_{$fieldName}"]['type'] = CRM_Utils_Array::value('type', $field);
351 $this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = $field['title'];
352 }
353 }
354 }
355 }
356
357 $this->_select = "SELECT " . implode(', ', $select) . " ";
358 //print_r($this->_select);
359 }
360
361 public function from() {
362
363 $this->_from = "
364 FROM civicrm_mailing {$this->_aliases['civicrm_mailing']}
365 LEFT JOIN civicrm_mailing_job {$this->_aliases['civicrm_mailing_job']}
366 ON {$this->_aliases['civicrm_mailing']}.id = {$this->_aliases['civicrm_mailing_job']}.mailing_id
367 LEFT JOIN civicrm_mailing_event_queue {$this->_aliases['civicrm_mailing_event_queue']}
368 ON {$this->_aliases['civicrm_mailing_event_queue']}.job_id = {$this->_aliases['civicrm_mailing_job']}.id
369 LEFT JOIN civicrm_mailing_event_bounce {$this->_aliases['civicrm_mailing_event_bounce']}
370 ON {$this->_aliases['civicrm_mailing_event_bounce']}.event_queue_id = {$this->_aliases['civicrm_mailing_event_queue']}.id
371 LEFT JOIN civicrm_mailing_event_delivered {$this->_aliases['civicrm_mailing_event_delivered']}
372 ON {$this->_aliases['civicrm_mailing_event_delivered']}.event_queue_id = {$this->_aliases['civicrm_mailing_event_queue']}.id
373 AND {$this->_aliases['civicrm_mailing_event_bounce']}.id IS null
374 LEFT JOIN civicrm_mailing_event_opened {$this->_aliases['civicrm_mailing_event_opened']}
375 ON {$this->_aliases['civicrm_mailing_event_opened']}.event_queue_id = {$this->_aliases['civicrm_mailing_event_queue']}.id
376 LEFT JOIN civicrm_mailing_event_trackable_url_open {$this->_aliases['civicrm_mailing_event_trackable_url_open']}
377 ON {$this->_aliases['civicrm_mailing_event_trackable_url_open']}.event_queue_id = {$this->_aliases['civicrm_mailing_event_queue']}.id
378 LEFT JOIN civicrm_mailing_event_unsubscribe {$this->_aliases['civicrm_mailing_event_unsubscribe']}
379 ON {$this->_aliases['civicrm_mailing_event_unsubscribe']}.event_queue_id = {$this->_aliases['civicrm_mailing_event_queue']}.id AND {$this->_aliases['civicrm_mailing_event_unsubscribe']}.org_unsubscribe = 0
380 LEFT JOIN civicrm_mailing_event_unsubscribe mailing_event_optout_civireport
381 ON mailing_event_optout_civireport.event_queue_id = {$this->_aliases['civicrm_mailing_event_queue']}.id AND mailing_event_optout_civireport.org_unsubscribe = 1";
382
383 if ($this->campaignEnabled) {
384 $this->_from .= "
385 LEFT JOIN civicrm_campaign {$this->_aliases['civicrm_campaign']}
386 ON {$this->_aliases['civicrm_campaign']}.id = {$this->_aliases['civicrm_mailing']}.campaign_id";
387 }
388
389 // need group by and order by
390
391 //print_r($this->_from);
392 }
393
394 public function where() {
395 $clauses = array();
396 //to avoid the sms listings
397 $clauses[] = "{$this->_aliases['civicrm_mailing']}.sms_provider_id IS NULL";
398
399 foreach ($this->_columns as $tableName => $table) {
400 if (array_key_exists('filters', $table)) {
401 foreach ($table['filters'] as $fieldName => $field) {
402 $clause = NULL;
403 if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE) {
404 $relative = CRM_Utils_Array::value("{$fieldName}_relative", $this->_params);
405 $from = CRM_Utils_Array::value("{$fieldName}_from", $this->_params);
406 $to = CRM_Utils_Array::value("{$fieldName}_to", $this->_params);
407
408 $clause = $this->dateClause($this->_aliases[$tableName] . '.' . $field['name'], $relative, $from, $to, $field['type']);
409 }
410 else {
411 $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params);
412
413 if ($op) {
414 if ($fieldName == 'relationship_type_id') {
415 $clause = "{$this->_aliases['civicrm_relationship']}.relationship_type_id=" . $this->relationshipId;
416 }
417 else {
418 $clause = $this->whereClause($field,
419 $op,
420 CRM_Utils_Array::value("{$fieldName}_value", $this->_params),
421 CRM_Utils_Array::value("{$fieldName}_min", $this->_params),
422 CRM_Utils_Array::value("{$fieldName}_max", $this->_params)
423 );
424 }
425 }
426 }
427
428 if (!empty($clause)) {
429 $clauses[] = $clause;
430 }
431 }
432 }
433 }
434
435 if (empty($clauses)) {
436 $this->_where = "WHERE ( 1 )";
437 }
438 else {
439 $this->_where = "WHERE " . implode(' AND ', $clauses);
440 }
441
442 // if ( $this->_aclWhere ) {
443 // $this->_where .= " AND {$this->_aclWhere} ";
444 // }
445 }
446
447 public function groupBy() {
448 $this->_groupBy = "GROUP BY {$this->_aliases['civicrm_mailing']}.id";
449 }
450
451 public function orderBy() {
452 $this->_orderBy = " ORDER BY {$this->_aliases['civicrm_mailing_job']}.end_date DESC ";
453 }
454
455 public function postProcess() {
456
457 $this->beginPostProcess();
458
459 // get the acl clauses built before we assemble the query
460 $this->buildACLClause(CRM_Utils_Array::value('civicrm_contact', $this->_aliases));
461
462 $sql = $this->buildQuery(TRUE);
463
464 // print_r($sql);
465
466 $rows = $graphRows = array();
467 $this->buildRows($sql, $rows);
468
469 $this->formatDisplay($rows);
470 $this->doTemplateAssignment($rows);
471 $this->endPostProcess($rows);
472 }
473
474 /**
475 * @return array
476 */
477 public static function getChartCriteria() {
478 return array(
479 'count' => array(
480 'civicrm_mailing_event_delivered_delivered_count' => ts('Delivered'),
481 'civicrm_mailing_event_bounce_bounce_count' => ts('Bounce'),
482 'civicrm_mailing_event_opened_open_count' => ts('Total Opens'),
483 'civicrm_mailing_event_opened_unique_open_count' => ts('Unique Opens'),
484 'civicrm_mailing_event_trackable_url_open_click_count' => ts('Clicks'),
485 'civicrm_mailing_event_unsubscribe_unsubscribe_count' => ts('Unsubscribe'),
486 ),
487 'rate' => array(
488 'civicrm_mailing_event_delivered_accepted_rate' => ts('Accepted Rate'),
489 'civicrm_mailing_event_bounce_bounce_rate' => ts('Bounce Rate'),
490 'civicrm_mailing_event_opened_open_rate' => ts('Total Open Rate'),
491 'civicrm_mailing_event_opened_unique_open_rate' => ts('Unique Open Rate'),
492 'civicrm_mailing_event_trackable_url_open_CTR' => ts('Click through Rate'),
493 'civicrm_mailing_event_trackable_url_open_CTO' => ts('Click to Open Rate'),
494 ),
495 );
496 }
497
498 /**
499 * @param $fields
500 * @param $files
501 * @param $self
502 *
503 * @return array
504 */
505 public static function formRule($fields, $files, $self) {
506 $errors = array();
507
508 if (empty($fields['charts'])) {
509 return $errors;
510 }
511
512 $criteria = self::getChartCriteria();
513 $isError = TRUE;
514 foreach ($fields['fields'] as $fld => $isActive) {
515 if (in_array($fld, array(
516 'delivered_count',
517 'bounce_count',
518 'open_count',
519 'click_count',
520 'unsubscribe_count',
521 'accepted_rate',
522 'bounce_rate',
523 'open_rate',
524 'CTR',
525 'CTO',
526 'unique_open_rate',
527 'unique_open_count',
528 ))) {
529 $isError = FALSE;
530 }
531 }
532
533 if ($isError) {
534 $errors['_qf_default'] = ts('For Chart view, please select at least one field from %1 OR %2.', array(
535 1 => implode(', ', $criteria['count']),
536 2 => implode(', ', $criteria['rate']),
537 ));
538 }
539
540 return $errors;
541 }
542
543 /**
544 * @param $rows
545 */
546 public function buildChart(&$rows) {
547 if (empty($rows)) {
548 return;
549 }
550
551 $criteria = self::getChartCriteria();
552
553 $chartInfo = array(
554 'legend' => ts('Mail Summary'),
555 'xname' => ts('Mailing'),
556 'yname' => ts('Statistics'),
557 'xLabelAngle' => 20,
558 'tip' => array(),
559 );
560
561 $plotRate = $plotCount = TRUE;
562 foreach ($rows as $row) {
563 $chartInfo['values'][$row['civicrm_mailing_name']] = array();
564 if ($plotCount) {
565 foreach ($criteria['count'] as $criteria => $label) {
566 if (isset($row[$criteria])) {
567 $chartInfo['values'][$row['civicrm_mailing_name']][$label] = $row[$criteria];
568 $chartInfo['tip'][$label] = "{$label} #val#";
569 $plotRate = FALSE;
570 }
571 elseif (isset($criteria['count'][$criteria])) {
572 unset($criteria['count'][$criteria]);
573 }
574 }
575 }
576 if ($plotRate) {
577 foreach ($criteria['rate'] as $criteria => $label) {
578 if (isset($row[$criteria])) {
579 $chartInfo['values'][$row['civicrm_mailing_name']][$label] = $row[$criteria];
580 $chartInfo['tip'][$label] = "{$label} #val#";
581 $plotCount = FALSE;
582 }
583 elseif (isset($criteria['rate'][$criteria])) {
584 unset($criteria['rate'][$criteria]);
585 }
586 }
587 }
588 }
589
590 if ($plotCount) {
591 $criteria = $criteria['count'];
592 }
593 else {
594 $criteria = $criteria['rate'];
595 }
596
597 $chartInfo['criteria'] = array_values($criteria);
598
599 // dynamically set the graph size
600 $chartInfo['xSize'] = ((count($rows) * 125) + (count($rows) * count($criteria) * 40));
601
602 // build the chart.
603 CRM_Utils_OpenFlashChart::buildChart($chartInfo, $this->_params['charts']);
604 $this->assign('chartType', $this->_params['charts']);
605 }
606
607 /**
608 * Alter display of rows.
609 *
610 * Iterate through the rows retrieved via SQL and make changes for display purposes,
611 * such as rendering contacts as links.
612 *
613 * @param array $rows
614 * Rows generated by SQL, with an array for each row.
615 */
616 public function alterDisplay(&$rows) {
617 $entryFound = FALSE;
618 foreach ($rows as $rowNum => $row) {
619 // CRM-16506
620 if (array_key_exists('civicrm_mailing_name', $row) &&
621 array_key_exists('civicrm_mailing_id', $row)
622 ) {
623 $rows[$rowNum]['civicrm_mailing_name_link'] = CRM_Report_Utils_Report::getNextUrl('mailing/detail',
624 'reset=1&force=1&mailing_id_op=eq&mailing_id_value=' . $row['civicrm_mailing_id'],
625 $this->_absoluteUrl, $this->_id, $this->_drilldownReport
626 );
627 $rows[$rowNum]['civicrm_mailing_name_hover'] = ts('View Mailing details for this mailing');
628 $entryFound = TRUE;
629 }
630 // skip looking further in rows, if first row itself doesn't
631 // have the column we need
632 if (!$entryFound) {
633 break;
634 }
635 }
636 }
637
638}