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