3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2019
35 * This class is used to browse past mailings.
37 class CRM_Mailing_Selector_Browse
extends CRM_Core_Selector_Base
implements CRM_Core_Selector_API
{
40 * Array of supported links, currently null
44 public static $_links = NULL;
47 * We use desc to remind us what that column is, name is used in the tpl
51 public static $_columnHeaders;
59 * @return \CRM_Mailing_Selector_Browse
61 public function __construct() {
65 * This method returns the links that are given for each search row.
69 public static function &links() {
74 * Getter for array of the parameters required for creating pager.
77 * @param array $params
79 public function getPagerParams($action, &$params) {
80 $params['csvString'] = NULL;
81 $params['rowCount'] = CRM_Utils_Pager
::ROWCOUNT
;
82 $params['status'] = ts('Mailings %%StatusMessage%%');
83 $params['buttonTop'] = 'PagerTopButton';
84 $params['buttonBottom'] = 'PagerBottomButton';
88 * Returns the column headers as an array of tuples:
89 * (name, sortName (key to the sort array))
91 * @param string $action
92 * The action being performed.
93 * @param string $output
94 * What should the result set include (web/email/csv).
97 * the column headers that need to be displayed
99 public function &getColumnHeaders($action = NULL, $output = NULL) {
100 $mailing = CRM_Mailing_BAO_Mailing
::getTableName();
101 $job = CRM_Mailing_BAO_MailingJob
::getTableName();
102 if (!isset(self
::$_columnHeaders)) {
103 $completedOrder = NULL;
105 // Set different default sort depending on type of mailings (CRM-7652)
106 $unscheduledOrder = $scheduledOrder = $archivedOrder = CRM_Utils_Sort
::DONTCARE
;
107 if ($this->_parent
->get('unscheduled')) {
108 $unscheduledOrder = CRM_Utils_Sort
::DESCENDING
;
110 elseif ($this->_parent
->get('scheduled')) {
111 $scheduledOrder = CRM_Utils_Sort
::DESCENDING
;
114 // sort by completed date for archived and undefined get
115 $completedOrder = CRM_Utils_Sort
::DESCENDING
;
117 $nameHeaderLabel = ($this->_parent
->get('sms')) ?
ts('SMS Name') : ts('Mailing Name');
119 self
::$_columnHeaders = [
121 'name' => $nameHeaderLabel,
123 'direction' => CRM_Utils_Sort
::DONTCARE
,
127 if (CRM_Core_I18n
::isMultilingual()) {
128 self
::$_columnHeaders = array_merge(
129 self
::$_columnHeaders,
132 'name' => ts('Language'),
133 'sort' => 'language',
134 'direction' => CRM_Utils_Sort
::DONTCARE
,
140 self
::$_columnHeaders = array_merge(
141 self
::$_columnHeaders,
144 'name' => ts('Status'),
146 'direction' => CRM_Utils_Sort
::DONTCARE
,
149 'name' => ts('Created By'),
150 'sort' => 'created_by',
151 'direction' => CRM_Utils_Sort
::DONTCARE
,
154 'name' => ts('Created Date'),
155 'sort' => 'created_date',
156 'direction' => $unscheduledOrder,
159 'name' => ts('Sent By'),
160 'sort' => 'scheduled_by',
161 'direction' => CRM_Utils_Sort
::DONTCARE
,
164 'name' => ts('Scheduled'),
165 'sort' => 'scheduled_date',
166 'direction' => $scheduledOrder,
169 'name' => ts('Started'),
170 'sort' => 'start_date',
171 'direction' => CRM_Utils_Sort
::DONTCARE
,
174 'name' => ts('Completed'),
175 'sort' => 'end_date',
176 'direction' => $completedOrder,
181 if (CRM_Campaign_BAO_Campaign
::isCampaignEnable()) {
182 self
::$_columnHeaders[] = [
183 'name' => ts('Campaign'),
184 'sort' => 'campaign_id',
185 'direction' => CRM_Utils_Sort
::DONTCARE
,
189 if ($output != CRM_Core_Selector_Controller
::EXPORT
) {
190 self
::$_columnHeaders[] = ['name' => ts('Action')];
194 CRM_Core_Smarty
::singleton()->assign('multilingual', CRM_Core_I18n
::isMultilingual());
195 return self
::$_columnHeaders;
199 * Returns total number of rows for the query.
201 * @param string $action
204 * Total number of rows
206 public function getTotalCount($action) {
207 $job = CRM_Mailing_BAO_MailingJob
::getTableName();
208 $mailing = CRM_Mailing_BAO_Mailing
::getTableName();
209 $mailingACL = CRM_Mailing_BAO_Mailing
::mailingACL();
211 // get the where clause.
213 $whereClause = "$mailingACL AND " . $this->whereClause($params);
215 // CRM-11919 added addition ON clauses to mailing_job to match getRows
217 SELECT COUNT( DISTINCT $mailing.id ) as count
219 LEFT JOIN $job ON ( $mailing.id = $job.mailing_id AND civicrm_mailing_job.is_test = 0 AND civicrm_mailing_job.parent_id IS NULL )
220 LEFT JOIN civicrm_contact createdContact ON ( $mailing.created_id = createdContact.id )
221 LEFT JOIN civicrm_contact scheduledContact ON ( $mailing.scheduled_id = scheduledContact.id )
224 return CRM_Core_DAO
::singleValueQuery($query, $params);
228 * Returns all the rows in the given offset and rowCount.
230 * @param string $action
231 * The action being performed.
233 * The row number to start from.
234 * @param int $rowCount
235 * The number of rows to return.
236 * @param string $sort
237 * The sql string that describes the sort order.
238 * @param string $output
239 * What should the result set include (web/email/csv).
242 * the total number of rows for this action
244 public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) {
245 static $actionLinks = NULL;
246 if (empty($actionLinks)) {
247 $cancelExtra = ts('Are you sure you want to cancel this mailing?');
248 $deleteExtra = ts('Are you sure you want to delete this mailing?');
249 $archiveExtra = ts('Are you sure you want to archive this mailing?');
252 CRM_Core_Action
::ENABLE
=> [
253 'name' => ts('Approve/Reject'),
254 'url' => 'civicrm/mailing/approve',
255 'qs' => 'mid=%%mid%%&reset=1',
256 'title' => ts('Approve/Reject Mailing'),
258 CRM_Core_Action
::VIEW
=> [
259 'name' => ts('Report'),
260 'url' => 'civicrm/mailing/report',
261 'qs' => 'mid=%%mid%%&reset=1',
262 'title' => ts('View Mailing Report'),
264 CRM_Core_Action
::UPDATE
=> [
265 'name' => ts('Re-Use'),
266 'url' => 'civicrm/mailing/send',
267 'qs' => 'mid=%%mid%%&reset=1',
268 'title' => ts('Re-Send Mailing'),
270 CRM_Core_Action
::DISABLE
=> [
271 'name' => ts('Cancel'),
272 'url' => 'civicrm/mailing/browse',
273 'qs' => 'action=disable&mid=%%mid%%&reset=1',
274 'extra' => 'onclick="if (confirm(\'' . $cancelExtra . '\')) this.href+=\'&confirmed=1\'; else return false;"',
275 'title' => ts('Cancel Mailing'),
277 CRM_Core_Action
::PREVIEW
=> [
278 'name' => ts('Continue'),
279 'url' => 'civicrm/mailing/send',
280 'qs' => 'mid=%%mid%%&continue=true&reset=1',
281 'title' => ts('Continue Mailing'),
283 CRM_Core_Action
::DELETE
=> [
284 'name' => ts('Delete'),
285 'url' => 'civicrm/mailing/browse',
286 'qs' => 'action=delete&mid=%%mid%%&reset=1',
287 'extra' => 'onclick="if (confirm(\'' . $deleteExtra . '\')) this.href+=\'&confirmed=1\'; else return false;"',
288 'title' => ts('Delete Mailing'),
290 CRM_Core_Action
::RENEW
=> [
291 'name' => ts('Archive'),
292 'url' => 'civicrm/mailing/browse/archived',
293 'qs' => 'action=renew&mid=%%mid%%&reset=1',
294 'extra' => 'onclick="if (confirm(\'' . $archiveExtra . '\')) this.href+=\'&confirmed=1\'; else return false;"',
295 'title' => ts('Archive Mailing'),
297 CRM_Core_Action
::REOPEN
=> [
298 'name' => ts('Resume'),
299 'url' => 'civicrm/mailing/browse',
300 'qs' => 'action=reopen&mid=%%mid%%&reset=1',
301 'title' => ts('Resume mailing'),
303 CRM_Core_Action
::CLOSE
=> [
304 'name' => ts('Pause'),
305 'url' => 'civicrm/mailing/browse',
306 'qs' => 'action=close&mid=%%mid%%&reset=1',
307 'title' => ts('Pause mailing'),
313 $workFlow = $showApprovalLinks = $showScheduleLinks = $showCreateLinks = FALSE;
314 if (CRM_Mailing_Info
::workflowEnabled()) {
317 // supercedes all permission
318 if (CRM_Core_Permission
::check('access CiviMail')) {
322 if (CRM_Core_Permission
::check('approve mailings')) {
323 $showApprovalLinks = TRUE;
326 if (CRM_Core_Permission
::check('create mailings')) {
327 $showCreateLinks = TRUE;
330 if (CRM_Core_Permission
::check('schedule mailings')) {
331 $showScheduleLinks = TRUE;
334 $mailing = new CRM_Mailing_BAO_Mailing();
338 $whereClause = ' AND ' . $this->whereClause($params);
340 if (empty($params)) {
341 $this->_parent
->assign('isSearch', 0);
344 $this->_parent
->assign('isSearch', 1);
346 $rows = &$mailing->getRows($offset, $rowCount, $sort, $whereClause, $params);
348 // get the search base mailing Ids, CRM-3711.
349 $searchMailings = $mailing->searchMailingIDs();
351 // check for delete CRM-4418
352 $allowToDelete = CRM_Core_Permission
::check('delete in CiviMail');
354 if ($output != CRM_Core_Selector_Controller
::EXPORT
) {
356 // create the appropriate $op to use for hook_civicrm_links
357 $pageTypes = ['view', 'mailing', 'browse'];
358 if ($this->_parent
->_unscheduled
) {
359 $pageTypes[] = 'unscheduled';
361 if ($this->_parent
->_scheduled
) {
362 $pageTypes[] = 'scheduled';
364 if ($this->_parent
->_archived
) {
365 $pageTypes[] = 'archived';
367 $opString = implode('.', $pageTypes);
369 // get languages for later conversion
370 $languages = CRM_Core_I18n
::languages();
372 foreach ($rows as $key => $row) {
374 if ($row['sms_provider_id']) {
375 $actionLinks[CRM_Core_Action
::PREVIEW
]['url'] = 'civicrm/sms/send';
378 if (!($row['status'] == 'Not scheduled') && !$row['sms_provider_id']) {
379 if ($allAccess ||
$showCreateLinks) {
380 $actionMask = CRM_Core_Action
::VIEW
;
383 if (!in_array($row['id'], $searchMailings)) {
384 if ($allAccess ||
$showCreateLinks) {
385 $actionMask |
= CRM_Core_Action
::UPDATE
;
390 if ($allAccess ||
($showCreateLinks ||
$showScheduleLinks)) {
391 $actionMask = CRM_Core_Action
::PREVIEW
;
394 if (in_array($row['status'], [
400 ($showApprovalLinks && $showCreateLinks && $showScheduleLinks)
403 $actionMask |
= CRM_Core_Action
::DISABLE
;
404 if ($row['status'] == "Paused") {
405 $actionMask |
= CRM_Core_Action
::REOPEN
;
408 $actionMask |
= CRM_Core_Action
::CLOSE
;
411 if ($row['status'] == 'Scheduled' &&
412 empty($row['approval_status_id'])
414 if ($workFlow && ($allAccess ||
$showApprovalLinks)) {
415 $actionMask |
= CRM_Core_Action
::ENABLE
;
420 if (in_array($row['status'], ['Complete', 'Canceled']) &&
423 if ($allAccess ||
$showCreateLinks) {
424 $actionMask |
= CRM_Core_Action
::RENEW
;
428 // check for delete permission.
429 if ($allowToDelete) {
430 $actionMask |
= CRM_Core_Action
::DELETE
;
433 if ($actionMask == NULL) {
434 $actionMask = CRM_Core_Action
::ADD
;
436 // get status strings as per locale settings CRM-4411.
437 $rows[$key]['status'] = CRM_Mailing_BAO_MailingJob
::status($row['status']);
439 // get language string
440 $rows[$key]['language'] = (isset($row['language']) ?
$languages[$row['language']] : NULL);
442 $validLinks = $actionLinks;
443 if (($mailingUrl = CRM_Mailing_BAO_Mailing
::getPublicViewUrl($row['id'])) != FALSE) {
444 $validLinks[CRM_Core_Action
::BROWSE
] = [
445 'name' => ts('Public View'),
446 'url' => 'civicrm/mailing/view',
447 'qs' => 'id=%%hashOrMid%%&reset=1',
448 'title' => ts('Public View'),
451 $actionMask |
= CRM_Core_Action
::BROWSE
;
454 $hash = CRM_Mailing_BAO_Mailing
::getMailingHash($row['id']);
455 $rows[$key]['action'] = CRM_Core_Action
::formLink(
460 'hashOrMid' => $hash ?
$hash : $row['id'],
469 // unset($rows[$key]['id']);
470 // if the scheduled date is 0, replace it with an empty string
471 if ($rows[$key]['scheduled_iso'] == '0000-00-00 00:00:00') {
472 $rows[$key]['scheduled'] = '';
474 unset($rows[$key]['scheduled_iso']);
478 // also initialize the AtoZ pager
484 * Name of export file.
486 * @param string $output
492 public function getExportFileName($output = 'csv') {
493 return ts('CiviMail Mailings');
499 public function setParent($parent) {
500 $this->_parent
= $parent;
504 * @param array $params
505 * @param bool $sortBy
509 public function whereClause(&$params, $sortBy = TRUE) {
510 $values = $clauses = [];
511 $isFormSubmitted = $this->_parent
->get('hidden_find_mailings');
513 $title = $this->_parent
->get('mailing_name');
515 $clauses[] = 'name LIKE %1';
516 if (strpos($title, '%') !== FALSE) {
517 $params[1] = [$title, 'String', FALSE];
520 $params[1] = [$title, 'String', TRUE];
524 $dateClause1 = $dateClause2 = [];
525 $from = $this->_parent
->get('mailing_low');
526 if (!CRM_Utils_System
::isNull($from)) {
527 if ($this->_parent
->get('unscheduled')) {
528 $dateClause1[] = 'civicrm_mailing.created_date >= %2';
531 $dateClause1[] = 'civicrm_mailing_job.start_date >= %2';
532 $dateClause2[] = 'civicrm_mailing_job.scheduled_date >= %2';
534 $params[2] = [$from, 'String'];
537 $to = $this->_parent
->get('mailing_high');
538 if (!CRM_Utils_System
::isNull($to)) {
539 if ($this->_parent
->get('unscheduled')) {
540 $dateClause1[] = ' civicrm_mailing.created_date <= %3 ';
543 $dateClause1[] = 'civicrm_mailing_job.start_date <= %3';
544 $dateClause2[] = 'civicrm_mailing_job.scheduled_date <= %3';
546 $params[3] = [$to, 'String'];
550 if (!empty($dateClause1)) {
551 $dateClauses[] = implode(' AND ', $dateClause1);
553 if (!empty($dateClause2)) {
554 $dateClauses[] = implode(' AND ', $dateClause2);
556 $dateClauses = implode(' OR ', $dateClauses);
557 if (!empty($dateClauses)) {
558 $clauses[] = "({$dateClauses})";
561 if ($this->_parent
->get('sms')) {
562 $clauses[] = "civicrm_mailing.sms_provider_id IS NOT NULL";
565 $clauses[] = "civicrm_mailing.sms_provider_id IS NULL";
568 // get values submitted by form
569 $isDraft = $this->_parent
->get('status_unscheduled');
570 $isArchived = $this->_parent
->get('is_archived');
571 $mailingStatus = $this->_parent
->get('mailing_status');
573 if (!$isFormSubmitted && $this->_parent
->get('scheduled')) {
574 // mimic default behavior for scheduled screen
576 $mailingStatus = ['Scheduled' => 1, 'Complete' => 1, 'Running' => 1, 'Paused' => 1, 'Canceled' => 1];
578 if (!$isFormSubmitted && $this->_parent
->get('archived')) {
579 // mimic default behavior for archived screen
582 if (!$isFormSubmitted && $this->_parent
->get('unscheduled')) {
583 // mimic default behavior for draft screen
589 $statusClauses[] = "civicrm_mailing.scheduled_id IS NULL";
591 if (!empty($mailingStatus)) {
592 $statusClauses[] = "civicrm_mailing_job.status IN ('" . implode("', '", array_keys($mailingStatus)) . "')";
594 if (!empty($statusClauses)) {
595 $clauses[] = "(" . implode(' OR ', $statusClauses) . ")";
598 if (isset($isArchived)) {
600 $clauses[] = "civicrm_mailing.is_archived = 1";
603 $clauses[] = "(civicrm_mailing.is_archived IS NULL OR civicrm_mailing.is_archived = 0)";
608 $this->_parent
->_sortByCharacter
!== NULL
610 $clauses[] = "name LIKE '" . strtolower(CRM_Core_DAO
::escapeWildCardString($this->_parent
->_sortByCharacter
)) . "%'";
613 // dont do a the below assignement when doing a
616 if (count($clauses) > 1) {
617 $this->_parent
->assign('isSearch', 1);
620 $this->_parent
->assign('isSearch', 0);
624 $createOrSentBy = $this->_parent
->get('sort_name');
625 if (!CRM_Utils_System
::isNull($createOrSentBy)) {
626 $clauses[] = '(createdContact.sort_name LIKE %4 OR scheduledContact.sort_name LIKE %4)';
627 $params[4] = ['%' . $createOrSentBy . '%', 'String'];
630 $createdId = $this->_parent
->get('createdId');
632 $clauses[] = "(created_id = {$createdId})";
633 $params[5] = [$createdId, 'Integer'];
636 $campainIds = $this->_parent
->get('campaign_id');
637 if (!CRM_Utils_System
::isNull($campainIds)) {
638 if (!is_array($campainIds)) {
639 $campaignIds = [$campaignIds];
641 $clauses[] = '( campaign_id IN ( ' . implode(' , ', array_values($campainIds)) . ' ) )';
644 if ($language = $this->_parent
->get('language')) {
645 $clauses[] = "civicrm_mailing.language = %6";
646 $params[6] = [$language, 'String'];
649 if (empty($clauses)) {
653 return implode(' AND ', $clauses);
656 public function pagerAtoZ() {
659 $whereClause = $this->whereClause($params, FALSE);
662 SELECT DISTINCT UPPER(LEFT(name, 1)) as sort_name
664 LEFT JOIN civicrm_mailing_job ON (civicrm_mailing_job.mailing_id = civicrm_mailing.id)
665 LEFT JOIN civicrm_contact createdContact ON ( civicrm_mailing.created_id = createdContact.id )
666 LEFT JOIN civicrm_contact scheduledContact ON ( civicrm_mailing.scheduled_id = scheduledContact.id )
668 ORDER BY UPPER(LEFT(name, 1))
671 $dao = CRM_Core_DAO
::executeQuery($query, $params);
673 $aToZBar = CRM_Utils_PagerAToZ
::getAToZBar($dao, $this->_parent
->_sortByCharacter
, TRUE);
674 $this->_parent
->assign('aToZ', $aToZBar);