CRM-13380 : fix status and date filters for mailing search pages
[civicrm-core.git] / CRM / Mailing / Selector / Browse.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
232624b1 4 | CiviCRM version 4.4 |
6a488035
TO
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
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-2013
32 * $Id$
33 *
34 */
35
36/**
37 * This class is used to browse past mailings.
38 */
39class CRM_Mailing_Selector_Browse extends CRM_Core_Selector_Base implements CRM_Core_Selector_API {
40
41 /**
42 * array of supported links, currenly null
43 *
44 * @var array
45 * @static
46 */
47 static $_links = NULL;
48
49 /**
50 * we use desc to remind us what that column is, name is used in the tpl
51 *
52 * @var array
53 * @static
54 */
55 static $_columnHeaders;
56
57 protected $_parent;
58
59 /**
60 * Class constructor
61 *
62 * @param
63 *
64 * @return CRM_Contact_Selector_Profile
65 * @access public
66 */
67 function __construct() {
68 }
69 //end of constructor
70
71 /**
72 * This method returns the links that are given for each search row.
73 *
74 * @return array
75 * @access public
76 *
77 */
78 static function &links() {
79 return self::$_links;
80 }
81 //end of function
82
83 /**
84 * getter for array of the parameters required for creating pager.
85 *
86 * @param
87 * @access public
88 */
89 function getPagerParams($action, &$params) {
90 $params['csvString'] = NULL;
91 $params['rowCount'] = CRM_Utils_Pager::ROWCOUNT;
92 $params['status'] = ts('Mailings %%StatusMessage%%');
93 $params['buttonTop'] = 'PagerTopButton';
94 $params['buttonBottom'] = 'PagerBottomButton';
95 }
96 //end of function
97
98 /**
99 * returns the column headers as an array of tuples:
100 * (name, sortName (key to the sort array))
101 *
102 * @param string $action the action being performed
103 * @param enum $output what should the result set include (web/email/csv)
104 *
105 * @return array the column headers that need to be displayed
106 * @access public
107 */
108 function &getColumnHeaders($action = NULL, $output = NULL) {
109 $mailing = CRM_Mailing_BAO_Mailing::getTableName();
9da8dc8c 110 $job = CRM_Mailing_BAO_MailingJob::getTableName();
6a488035
TO
111 if (!isset(self::$_columnHeaders)) {
112 $completedOrder = NULL;
113
114 // Set different default sort depending on type of mailings (CRM-7652)
115 $unscheduledOrder = $scheduledOrder = $archivedOrder = CRM_Utils_Sort::DONTCARE;
116 if ($this->_parent->get('unscheduled')) {
117 $unscheduledOrder = CRM_Utils_Sort::DESCENDING;
118 }
119 elseif ($this->_parent->get('scheduled')) {
120 $scheduledOrder = CRM_Utils_Sort::DESCENDING;
121 }
122 else {
123 // sort by completed date for archived and undefined get
124 $completedOrder = CRM_Utils_Sort::DESCENDING;
125 }
126 $nameHeaderLabel = ($this->_parent->get('sms')) ? ts('SMS Name') : ts('Mailing Name');
127
128 self::$_columnHeaders = array(
129 array(
130 'name' => $nameHeaderLabel,
131 'sort' => 'name',
132 'direction' => CRM_Utils_Sort::DONTCARE,
133 ),
134 array(
135 'name' => ts('Status'),
136 'sort' => 'status',
137 'direction' => CRM_Utils_Sort::DONTCARE,
138 ),
139 array(
140 'name' => ts('Created By'),
141 'sort' => 'created_by',
142 'direction' => CRM_Utils_Sort::DONTCARE,
143 ),
144 array(
145 'name' => ts('Created Date'),
146 'sort' => 'created_date',
147 'direction' => $unscheduledOrder,
148 ),
149 array(
150 'name' => ts('Sent By'),
151 'sort' => 'scheduled_by',
152 'direction' => CRM_Utils_Sort::DONTCARE,
153 ),
154 array(
155 'name' => ts('Scheduled'),
156 'sort' => 'scheduled_date',
157 'direction' => $scheduledOrder,
158 ),
159 array(
160 'name' => ts('Started'),
161 'sort' => 'start_date',
162 'direction' => CRM_Utils_Sort::DONTCARE,
163 ),
164 array(
165 'name' => ts('Completed'),
166 'sort' => 'end_date',
167 'direction' => $completedOrder,
168 ),
169 );
170
171 if (CRM_Campaign_BAO_Campaign::isCampaignEnable()) {
172 self::$_columnHeaders[] = array('name' => ts('Campaign'),
173 'sort' => 'campaign_id',
174 'direction' => CRM_Utils_Sort::DONTCARE,
175 );
176 }
177
178 if ($output != CRM_Core_Selector_Controller::EXPORT) {
179 self::$_columnHeaders[] = array('name' => ts('Action'));
180 }
181 }
182 return self::$_columnHeaders;
183 }
184
185 /**
186 * Returns total number of rows for the query.
187 *
188 * @param
189 *
190 * @return int Total number of rows
191 * @access public
192 */
193 function getTotalCount($action) {
9da8dc8c 194 $job = CRM_Mailing_BAO_MailingJob::getTableName();
6a488035
TO
195 $mailing = CRM_Mailing_BAO_Mailing::getTableName();
196 $mailingACL = CRM_Mailing_BAO_Mailing::mailingACL();
197
198 //get the where clause.
199 $params = array();
200 $whereClause = "$mailingACL AND " . $this->whereClause($params);
201
202 // CRM-11919 added addition ON clauses to mailing_job to match getRows
203 $query = "
204 SELECT COUNT( DISTINCT $mailing.id ) as count
205 FROM $mailing
206LEFT JOIN $job ON ( $mailing.id = $job.mailing_id AND civicrm_mailing_job.is_test = 0 AND civicrm_mailing_job.parent_id IS NULL )
207LEFT JOIN civicrm_contact createdContact ON ( $mailing.created_id = createdContact.id )
208LEFT JOIN civicrm_contact scheduledContact ON ( $mailing.scheduled_id = scheduledContact.id )
209 WHERE $whereClause";
210
211 return CRM_Core_DAO::singleValueQuery($query, $params);
212 }
213
214 /**
215 * returns all the rows in the given offset and rowCount
216 *
217 * @param enum $action the action being performed
218 * @param int $offset the row number to start from
219 * @param int $rowCount the number of rows to return
220 * @param string $sort the sql string that describes the sort order
221 * @param enum $output what should the result set include (web/email/csv)
222 *
223 * @return int the total number of rows for this action
224 */
225 function &getRows($action, $offset, $rowCount, $sort, $output = NULL) {
226 static $actionLinks = NULL;
227 if (empty($actionLinks)) {
228 $cancelExtra = ts('Are you sure you want to cancel this mailing?');
229 $deleteExtra = ts('Are you sure you want to delete this mailing?');
230 $archiveExtra = ts('Are you sure you want to archive this mailing?');
231
232 $actionLinks = array(
233 CRM_Core_Action::ENABLE => array(
234 'name' => ts('Approve/Reject'),
235 'url' => 'civicrm/mailing/approve',
236 'qs' => 'mid=%%mid%%&reset=1',
237 'title' => ts('Approve/Reject Mailing'),
238 ),
239 CRM_Core_Action::VIEW => array(
240 'name' => ts('Report'),
241 'url' => 'civicrm/mailing/report',
242 'qs' => 'mid=%%mid%%&reset=1',
243 'title' => ts('View Mailing Report'),
244 ),
245 CRM_Core_Action::UPDATE => array(
246 'name' => ts('Re-Use'),
247 'url' => 'civicrm/mailing/send',
248 'qs' => 'mid=%%mid%%&reset=1',
249 'title' => ts('Re-Send Mailing'),
250 ),
251 CRM_Core_Action::DISABLE => array(
252 'name' => ts('Cancel'),
253 'url' => 'civicrm/mailing/browse',
254 'qs' => 'action=disable&mid=%%mid%%&reset=1',
255 'extra' => 'onclick="if (confirm(\'' . $cancelExtra . '\')) this.href+=\'&amp;confirmed=1\'; else return false;"',
256 'title' => ts('Cancel Mailing'),
257 ),
258 CRM_Core_Action::PREVIEW => array(
259 'name' => ts('Continue'),
260 'url' => 'civicrm/mailing/send',
261 'qs' => 'mid=%%mid%%&continue=true&reset=1',
262 'title' => ts('Continue Mailing'),
263 ),
264 CRM_Core_Action::DELETE => array(
265 'name' => ts('Delete'),
266 'url' => 'civicrm/mailing/browse',
267 'qs' => 'action=delete&mid=%%mid%%&reset=1',
268 'extra' => 'onclick="if (confirm(\'' . $deleteExtra . '\')) this.href+=\'&amp;confirmed=1\'; else return false;"',
269 'title' => ts('Delete Mailing'),
270 ),
271 CRM_Core_Action::RENEW => array(
272 'name' => ts('Archive'),
273 'url' => 'civicrm/mailing/browse/archived',
274 'qs' => 'action=renew&mid=%%mid%%&reset=1',
275 'extra' => 'onclick="if (confirm(\'' . $archiveExtra . '\')) this.href+=\'&amp;confirmed=1\'; else return false;"',
276 'title' => ts('Archive Mailing'),
277 ),
278 );
279 }
280
281 $allAccess = TRUE;
282 $workFlow = $showApprovalLinks = $showScheduleLinks = $showCreateLinks = FALSE;
283 if (CRM_Mailing_Info::workflowEnabled()) {
284 $allAccess = FALSE;
285 $workFlow = TRUE;
286 //supercedes all permission
287 if (CRM_Core_Permission::check('access CiviMail')) {
288 $allAccess = TRUE;
289 }
290
291 if (CRM_Core_Permission::check('approve mailings')) {
292 $showApprovalLinks = TRUE;
293 }
294
295 if (CRM_Core_Permission::check('create mailings')) {
296 $showCreateLinks = TRUE;
297 }
298
299 if (CRM_Core_Permission::check('schedule mailings')) {
300 $showScheduleLinks = TRUE;
301 }
302 }
303 $mailing = new CRM_Mailing_BAO_Mailing();
304
305 $params = array();
306
307 $whereClause = ' AND ' . $this->whereClause($params);
308
309 if (empty($params)) {
310 $this->_parent->assign('isSearch', 0);
311 }
312 else {
313 $this->_parent->assign('isSearch', 1);
314 }
315 $rows = &$mailing->getRows($offset, $rowCount, $sort, $whereClause, $params);
316
317 //get the search base mailing Ids, CRM-3711.
318 $searchMailings = $mailing->searchMailingIDs();
319
320 //check for delete CRM-4418
321 $allowToDelete = CRM_Core_Permission::check('delete in CiviMail');
322
323 if ($output != CRM_Core_Selector_Controller::EXPORT) {
324
325 //create the appropriate $op to use for hook_civicrm_links
326 $pageTypes = array('view', 'mailing', 'browse');
327 if ($this->_parent->_unscheduled) {
328 $pageTypes[] = 'unscheduled';
329 }
330 if ($this->_parent->_scheduled) {
331 $pageTypes[] = 'scheduled';
332 }
333 if ($this->_parent->_archived) {
334 $pageTypes[] = 'archived';
335 }
336 $opString = implode('.', $pageTypes);
337
338 foreach ($rows as $key => $row) {
339 $actionMask = NULL;
340 if ($row['sms_provider_id']) {
341 $actionLinks[CRM_Core_Action::PREVIEW]['url'] = 'civicrm/sms/send';
342 }
343
344 if (!($row['status'] == 'Not scheduled') && !$row['sms_provider_id']) {
345 if ($allAccess || $showCreateLinks) {
346 $actionMask = CRM_Core_Action::VIEW;
347 }
348
349 if (!in_array($row['id'], $searchMailings)) {
350 if ($allAccess || $showCreateLinks) {
351 $actionMask |= CRM_Core_Action::UPDATE;
352 }
353 }
354 }
355 else {
356 if ($allAccess || ($showCreateLinks || $showScheduleLinks)) {
357 $actionMask = CRM_Core_Action::PREVIEW;
358 }
359 }
360 if (in_array($row['status'], array(
361 'Scheduled', 'Running', 'Paused'))) {
362 if ($allAccess ||
363 ($showApprovalLinks && $showCreateLinks && $showScheduleLinks)
364 ) {
365
366 $actionMask |= CRM_Core_Action::DISABLE;
367 }
368 if ($row['status'] == 'Scheduled' &&
369 empty($row['approval_status_id'])
370 ) {
371 if ($workFlow && ($allAccess || $showApprovalLinks)) {
372 $actionMask |= CRM_Core_Action::ENABLE;
373 }
374 }
375 }
376
377 if ($row['status'] == 'Complete' && !$row['archived']) {
378 if ($allAccess || $showCreateLinks) {
379 $actionMask |= CRM_Core_Action::RENEW;
380 }
381 }
382
383 //check for delete permission.
384 if ($allowToDelete) {
385 $actionMask |= CRM_Core_Action::DELETE;
386 }
387
388 if ($actionMask == NULL) {
389 $actionMask = CRM_Core_Action::ADD;
390 }
391 //get status strings as per locale settings CRM-4411.
9da8dc8c 392 $rows[$key]['status'] = CRM_Mailing_BAO_MailingJob::status($row['status']);
6a488035
TO
393
394 $rows[$key]['action'] = CRM_Core_Action::formLink($actionLinks,
395 $actionMask,
396 array('mid' => $row['id']),
397 "more",
398 FALSE,
399 $opString,
400 "Mailing",
401 $row['id']
402 );
403
404 //unset($rows[$key]['id']);
405 // if the scheduled date is 0, replace it with an empty string
406 if ($rows[$key]['scheduled_iso'] == '0000-00-00 00:00:00') {
407 $rows[$key]['scheduled'] = '';
408 }
409 unset($rows[$key]['scheduled_iso']);
410 }
411 }
412
413 // also initialize the AtoZ pager
414 $this->pagerAtoZ();
415 return $rows;
416 }
417
418 /**
419 * name of export file.
420 *
421 * @param string $output type of output
422 *
423 * @return string name of the file
424 */
425 function getExportFileName($output = 'csv') {
426 return ts('CiviMail Mailings');
427 }
428
429 function setParent($parent) {
430 $this->_parent = $parent;
431 }
432
433 function whereClause(&$params, $sortBy = TRUE) {
434 $values = $clauses = array();
435
436 $title = $this->_parent->get('mailing_name');
437
438 if ($title) {
439 $clauses[] = 'name LIKE %1';
440 if (strpos($title, '%') !== FALSE) {
441 $params[1] = array($title, 'String', FALSE);
442 }
443 else {
444 $params[1] = array($title, 'String', TRUE);
445 }
446 }
447
6a488035
TO
448 $from = $this->_parent->get('mailing_from');
449 if (!CRM_Utils_System::isNull($from)) {
2d882552
PJ
450 if ($this->_parent->get('unscheduled')) {
451 $dateClause1[] = 'civicrm_mailing.created_date >= %2';
452 }
453 else {
454 $dateClause1[] = 'civicrm_mailing_job.start_date >= %2';
455 $dateClause2[] = 'civicrm_mailing_job.scheduled_date >= %2';
456 }
457 $params[2] = array($from, 'String');
6a488035
TO
458 }
459
460 $to = $this->_parent->get('mailing_to');
461 if (!CRM_Utils_System::isNull($to)) {
2d882552
PJ
462 if ($this->_parent->get('unscheduled')) {
463 $dateClause1[] = ' civicrm_mailing.created_date <= %3 ';
464 }
465 else {
466 $dateClause1[] = 'civicrm_mailing_job.start_date <= %3';
467 $dateClause2[] = 'civicrm_mailing_job.scheduled_date <= %3';
468 }
6a488035
TO
469 $params[3] = array($to, 'String');
470 }
471
2d882552 472 $addtlStatuses = $this->_parent->get('archived') ? ", 'Canceled'" : '';
6a488035 473 if (!empty($dateClause1)) {
2d882552
PJ
474 if (!$this->_parent->get('unscheduled')) {
475 $dateClause1[] = "civicrm_mailing_job.status IN ('Complete', 'Running' $addtlStatuses)";
476 $dateClause2[] = "civicrm_mailing_job.status IN ('Scheduled' $addtlStatuses)";
477 }
478
479 if (!empty($dateClause1)) {
480 $dateClause1 = implode(' AND ', $dateClause1);
481 }
482 if (!empty($dateClause2)) {
483 $dateClause2 = implode(' AND ', $dateClause2);
484 }
485
486 $dateC = NULL;
487 if (!empty($dateClause1)) {
488 $dateC = " ({$dateClause1}) ";
489 }
490 if (!empty($dateClause2)) {
491 if ($dateC) {
492 $dateC .= " OR ({$dateClause2}) ";
493 }
494 else {
495 $dateC = " ({$dateClause2}) ";
496 }
497 }
498 $clauses[] = " ({$dateC}) ";
6a488035
TO
499 }
500
501 if ($this->_parent->get('unscheduled')) {
502 $clauses[] = "civicrm_mailing_job.status is null";
503 $clauses[] = "civicrm_mailing.scheduled_id IS NULL";
504 }
505
6a488035
TO
506 if ($this->_parent->get('sms')) {
507 $clauses[] = "(civicrm_mailing.sms_provider_id IS NOT NULL)";
508 }
509 else {
510 $clauses[] = "(civicrm_mailing.sms_provider_id IS NULL)";
511 }
512
513 // CRM-4290, do not show archived or unscheduled mails
514 // on 'Scheduled and Sent Mailing' page selector
515 if ($this->_parent->get('scheduled')) {
516 $clauses[] = "civicrm_mailing.scheduled_id IS NOT NULL";
517 $clauses[] = "( civicrm_mailing.is_archived IS NULL OR civicrm_mailing.is_archived = 0 )";
2d882552
PJ
518 }
519 if (!$this->_parent->get('unscheduled')) {
6a488035
TO
520 $status = $this->_parent->get('mailing_status');
521 if (!empty($status)) {
522 $status = array_keys($status);
523 $status = implode("','", $status);
524 $clauses[] = "civicrm_mailing_job.status IN ('$status')";
525 }
2d882552 526 elseif ($this->_parent->get('scheduled')) {
6a488035
TO
527 $clauses[] = "civicrm_mailing_job.status IN ('Scheduled', 'Complete', 'Running')";
528 }
529 }
530
2d882552
PJ
531 if ($this->_parent->get('archived')) {
532 // CRM-6446: archived view should also show cancelled mailings
533 $clauses[] = "(civicrm_mailing.is_archived = 1 OR civicrm_mailing_job.status = 'Canceled')";
534 }
535
6a488035
TO
536 if ($sortBy &&
537 $this->_parent->_sortByCharacter !== NULL
538 ) {
539 $clauses[] = "name LIKE '" . strtolower(CRM_Core_DAO::escapeWildCardString($this->_parent->_sortByCharacter)) . "%'";
540 }
541
542 // dont do a the below assignement when doing a
543 // AtoZ pager clause
544 if ($sortBy) {
545 if (count($clauses) > 1) {
546 $this->_parent->assign('isSearch', 1);
547 }
548 else {
549 $this->_parent->assign('isSearch', 0);
550 }
551 }
552
553 $createOrSentBy = $this->_parent->get('sort_name');
554 if (!CRM_Utils_System::isNull($createOrSentBy)) {
555 $clauses[] = '(createdContact.sort_name LIKE %4 OR scheduledContact.sort_name LIKE %4)';
556 $params[4] = array('%' . $createOrSentBy . '%', 'String');
557 }
558
559 $createdId = $this->_parent->get('createdId');
560 if ($createdId) {
561 $clauses[] = "(created_id = {$createdId})";
562 $params[5] = array($createdId, 'Integer');
563 }
564
565 $campainIds = $this->_parent->get('campaign_id');
566 if (!CRM_Utils_System::isNull($campainIds)) {
567 if (!is_array($campainIds)) {
568 $campaignIds = array($campaignIds);
569 }
570 $clauses[] = '( campaign_id IN ( ' . implode(' , ', array_values($campainIds)) . ' ) )';
571 }
572
573 if (empty($clauses)) {
574 return 1;
575 }
576
577 return implode(' AND ', $clauses);
578 }
579
580 function pagerAtoZ() {
581
582 $params = array();
583 $whereClause = $this->whereClause($params, FALSE);
584
585 $query = "
586SELECT DISTINCT UPPER(LEFT(name, 1)) as sort_name
587FROM civicrm_mailing
588LEFT JOIN civicrm_mailing_job ON (civicrm_mailing_job.mailing_id = civicrm_mailing.id)
589LEFT JOIN civicrm_contact createdContact ON ( civicrm_mailing.created_id = createdContact.id )
590LEFT JOIN civicrm_contact scheduledContact ON ( civicrm_mailing.scheduled_id = scheduledContact.id )
591WHERE $whereClause
592ORDER BY LEFT(name, 1)
593";
594
595 $dao = CRM_Core_DAO::executeQuery($query, $params);
596
597 $aToZBar = CRM_Utils_PagerAToZ::getAToZBar($dao, $this->_parent->_sortByCharacter, TRUE);
598 $this->_parent->assign('aToZ', $aToZBar);
599 }
600}
601//end of class
602