Merge pull request #22455 from eileenmcnaughton/legg
[civicrm-core.git] / CRM / Campaign / BAO / Campaign.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17 class CRM_Campaign_BAO_Campaign extends CRM_Campaign_DAO_Campaign {
18
19 /**
20 * Takes an associative array and creates a campaign object.
21 *
22 * the function extract all the params it needs to initialize the create a
23 * contact object. the params array could contain additional unused name/value
24 * pairs
25 *
26 * @param array $params
27 * (reference ) an assoc array of name/value pairs.
28 *
29 * @return CRM_Campaign_DAO_Campaign
30 * @throws \CRM_Core_Exception
31 */
32 public static function create(&$params) {
33 if (empty($params)) {
34 return NULL;
35 }
36
37 if (empty($params['id'])) {
38 if (empty($params['created_id'])) {
39 $params['created_id'] = CRM_Core_Session::getLoggedInContactID();
40 }
41
42 if (empty($params['created_date'])) {
43 $params['created_date'] = date('YmdHis');
44 }
45
46 if (empty($params['name'])) {
47 $params['name'] = CRM_Utils_String::titleToVar($params['title'], 64);
48 }
49 }
50
51 /* @var \CRM_Campaign_DAO_Campaign $campaign */
52 $campaign = self::writeRecord($params);
53
54 /* Create the campaign group record */
55 $groupTableName = CRM_Contact_BAO_Group::getTableName();
56
57 if (isset($params['groups']) && !empty($params['groups']['include']) && is_array($params['groups']['include'])) {
58 foreach ($params['groups']['include'] as $entityId) {
59 $dao = new CRM_Campaign_DAO_CampaignGroup();
60 $dao->campaign_id = $campaign->id;
61 $dao->entity_table = $groupTableName;
62 $dao->entity_id = $entityId;
63 $dao->group_type = 'Include';
64 $dao->save();
65 }
66 }
67
68 return $campaign;
69 }
70
71 /**
72 * Delete the campaign.
73 *
74 * @param int $id
75 *
76 * @deprecated
77 * @return bool|int
78 */
79 public static function del($id) {
80 try {
81 self::deleteRecord(['id' => $id]);
82 }
83 catch (CRM_Core_Exception $e) {
84 return FALSE;
85 }
86 return 1;
87 }
88
89 /**
90 * Retrieve DB object based on input parameters.
91 *
92 * It also stores all the retrieved values in the default array.
93 *
94 * @param array $params
95 * (reference ) an assoc array of name/value pairs.
96 * @param array $defaults
97 * (reference ) an assoc array to hold the flattened values.
98 *
99 * @return \CRM_Campaign_DAO_Campaign|null
100 */
101 public static function retrieve(&$params, &$defaults) {
102 $campaign = new CRM_Campaign_DAO_Campaign();
103
104 $campaign->copyValues($params);
105
106 if ($campaign->find(TRUE)) {
107 CRM_Core_DAO::storeValues($campaign, $defaults);
108 return $campaign;
109 }
110 return NULL;
111 }
112
113 /**
114 * Return the all eligible campaigns w/ cache.
115 *
116 * @param int $includeId
117 * Lets include this campaign by force.
118 * @param int $excludeId
119 * Do not include this campaign.
120 * @param bool $onlyActive
121 * Consider only active campaigns.
122 *
123 * @param bool $onlyCurrent
124 * @param bool $appendDatesToTitle
125 * @param bool $forceAll
126 *
127 * @return mixed
128 * $campaigns a set of campaigns.
129 */
130 public static function getCampaigns(
131 $includeId = NULL,
132 $excludeId = NULL,
133 $onlyActive = TRUE,
134 $onlyCurrent = TRUE,
135 $appendDatesToTitle = FALSE,
136 $forceAll = FALSE
137 ) {
138 static $campaigns;
139 $cacheKey = 0;
140 $cacheKeyParams = [
141 'includeId',
142 'excludeId',
143 'onlyActive',
144 'onlyCurrent',
145 'appendDatesToTitle',
146 'forceAll',
147 ];
148 foreach ($cacheKeyParams as $param) {
149 $cacheParam = $$param;
150 if (!$cacheParam) {
151 $cacheParam = 0;
152 }
153 $cacheKey .= '_' . $cacheParam;
154 }
155
156 if (!isset($campaigns[$cacheKey])) {
157 $where = ['( camp.title IS NOT NULL )'];
158 if ($excludeId) {
159 $where[] = "( camp.id != $excludeId )";
160 }
161 if ($onlyActive) {
162 $where[] = '( camp.is_active = 1 )';
163 }
164 if ($onlyCurrent) {
165 $where[] = '( camp.end_date IS NULL OR camp.end_date >= NOW() )';
166 }
167 $whereClause = implode(' AND ', $where);
168 if ($includeId) {
169 $whereClause .= " OR ( camp.id = $includeId )";
170 }
171
172 //lets force all.
173 if ($forceAll) {
174 $whereClause = '( 1 )';
175 }
176
177 $query = "
178 SELECT camp.id,
179 camp.title,
180 camp.start_date,
181 camp.end_date
182 FROM civicrm_campaign camp
183 WHERE {$whereClause}
184 Order By camp.title";
185
186 $campaign = CRM_Core_DAO::executeQuery($query);
187 $campaigns[$cacheKey] = [];
188 $config = CRM_Core_Config::singleton();
189
190 while ($campaign->fetch()) {
191 $title = $campaign->title;
192 if ($appendDatesToTitle) {
193 $dates = [];
194 foreach (['start_date', 'end_date'] as $date) {
195 if ($campaign->$date) {
196 $dates[] = CRM_Utils_Date::customFormat($campaign->$date, $config->dateformatFull);
197 }
198 }
199 if (!empty($dates)) {
200 $title .= ' (' . implode('-', $dates) . ')';
201 }
202 }
203 $campaigns[$cacheKey][$campaign->id] = $title;
204 }
205 }
206
207 return $campaigns[$cacheKey];
208 }
209
210 /**
211 * Wrapper to self::getCampaigns( )
212 * w/ permissions and component check.
213 *
214 * @param int $includeId
215 * @param int $excludeId
216 * @param bool $onlyActive
217 * @param bool $onlyCurrent
218 * @param bool $appendDatesToTitle
219 * @param bool $forceAll
220 * @param bool $doCheckForComponent
221 * @param bool $doCheckForPermissions
222 *
223 * @return mixed
224 */
225 public static function getPermissionedCampaigns(
226 $includeId = NULL,
227 $excludeId = NULL,
228 $onlyActive = TRUE,
229 $onlyCurrent = TRUE,
230 $appendDatesToTitle = FALSE,
231 $forceAll = FALSE,
232 $doCheckForComponent = TRUE,
233 $doCheckForPermissions = TRUE
234 ) {
235 $cacheKey = 0;
236 $cachekeyParams = [
237 'includeId',
238 'excludeId',
239 'onlyActive',
240 'onlyCurrent',
241 'appendDatesToTitle',
242 'doCheckForComponent',
243 'doCheckForPermissions',
244 'forceAll',
245 ];
246 foreach ($cachekeyParams as $param) {
247 $cacheKeyParam = $$param;
248 if (!$cacheKeyParam) {
249 $cacheKeyParam = 0;
250 }
251 $cacheKey .= '_' . $cacheKeyParam;
252 }
253
254 static $validCampaigns;
255 if (!isset($validCampaigns[$cacheKey])) {
256 $isValid = TRUE;
257 $campaigns = [
258 'campaigns' => [],
259 'hasAccessCampaign' => FALSE,
260 'isCampaignEnabled' => FALSE,
261 ];
262
263 //do check for component.
264 if ($doCheckForComponent) {
265 $campaigns['isCampaignEnabled'] = $isValid = self::isCampaignEnable();
266 }
267
268 //do check for permissions.
269 if ($doCheckForPermissions) {
270 $campaigns['hasAccessCampaign'] = $isValid = self::accessCampaign();
271 }
272
273 //finally retrieve campaigns from db.
274 if ($isValid) {
275 $campaigns['campaigns'] = self::getCampaigns($includeId,
276 $excludeId,
277 $onlyActive,
278 $onlyCurrent,
279 $appendDatesToTitle,
280 $forceAll
281 );
282 }
283
284 //store in cache.
285 $validCampaigns[$cacheKey] = $campaigns;
286 }
287
288 return $validCampaigns[$cacheKey];
289 }
290
291 /**
292 * Is CiviCampaign enabled.
293 *
294 * @return bool
295 */
296 public static function isCampaignEnable(): bool {
297 return in_array('CiviCampaign', CRM_Core_Config::singleton()->enableComponents, TRUE);
298 }
299
300 /**
301 * Retrieve campaigns for dashboard.
302 *
303 * @param array $params
304 * @param bool $onlyCount
305 *
306 * @return array|int
307 */
308 public static function getCampaignSummary($params = [], $onlyCount = FALSE) {
309 $campaigns = [];
310
311 //build the limit and order clause.
312 $limitClause = $orderByClause = $lookupTableJoins = NULL;
313 if (!$onlyCount) {
314 $sortParams = [
315 'sort' => 'start_date',
316 'offset' => 0,
317 'rowCount' => 10,
318 'sortOrder' => 'desc',
319 ];
320 foreach ($sortParams as $name => $default) {
321 if (!empty($params[$name])) {
322 $sortParams[$name] = $params[$name];
323 }
324 }
325
326 //need to lookup tables.
327 $orderOnCampaignTable = TRUE;
328 if ($sortParams['sort'] === 'status') {
329 $orderOnCampaignTable = FALSE;
330 $lookupTableJoins = "
331 LEFT JOIN civicrm_option_value status ON ( status.value = campaign.status_id OR campaign.status_id IS NULL )
332 INNER JOIN civicrm_option_group grp ON ( status.option_group_id = grp.id AND grp.name = 'campaign_status' )";
333 $orderByClause = "ORDER BY status.label {$sortParams['sortOrder']}";
334 }
335 elseif ($sortParams['sort'] === 'campaign_type') {
336 $orderOnCampaignTable = FALSE;
337 $lookupTableJoins = "
338 LEFT JOIN civicrm_option_value campaign_type ON ( campaign_type.value = campaign.campaign_type_id
339 OR campaign.campaign_type_id IS NULL )
340 INNER JOIN civicrm_option_group grp ON ( campaign_type.option_group_id = grp.id AND grp.name = 'campaign_type' )";
341 $orderByClause = "ORDER BY campaign_type.label {$sortParams['sortOrder']}";
342 }
343 elseif ($sortParams['sort'] === 'isActive') {
344 $sortParams['sort'] = 'is_active';
345 }
346 if ($orderOnCampaignTable) {
347 $orderByClause = "ORDER BY campaign.{$sortParams['sort']} {$sortParams['sortOrder']}";
348 }
349 $orderByClause = ($orderByClause) ? $orderByClause . ", campaign.id {$sortParams['sortOrder']}" : $orderByClause;
350 $limitClause = "LIMIT {$sortParams['offset']}, {$sortParams['rowCount']}";
351 }
352
353 //build the where clause.
354 $queryParams = $where = [];
355 if (!empty($params['id'])) {
356 $where[] = "( campaign.id = %1 )";
357 $queryParams[1] = [$params['id'], 'Positive'];
358 }
359 if (!empty($params['name'])) {
360 $where[] = "( campaign.name LIKE %2 )";
361 $queryParams[2] = ['%' . trim($params['name']) . '%', 'String'];
362 }
363 if (!empty($params['title'])) {
364 $where[] = "( campaign.title LIKE %3 )";
365 $queryParams[3] = ['%' . trim($params['title']) . '%', 'String'];
366 }
367 if (!empty($params['start_date'])) {
368 $startDate = CRM_Utils_Date::processDate($params['start_date']);
369 $where[] = "( campaign.start_date >= %4 OR campaign.start_date IS NULL )";
370 $queryParams[4] = [$startDate, 'String'];
371 }
372 if (!empty($params['end_date'])) {
373 $endDate = CRM_Utils_Date::processDate($params['end_date'], '235959');
374 $where[] = "( campaign.end_date <= %5 OR campaign.end_date IS NULL )";
375 $queryParams[5] = [$endDate, 'String'];
376 }
377 if (!empty($params['description'])) {
378 $where[] = "( campaign.description LIKE %6 )";
379 $queryParams[6] = ['%' . trim($params['description']) . '%', 'String'];
380 }
381 if (!empty($params['campaign_type_id'])) {
382 $where[] = "( campaign.campaign_type_id IN ( %7 ) )";
383 $queryParams[7] = [implode(',', (array) $params['campaign_type_id']), 'CommaSeparatedIntegers'];
384 }
385 if (!empty($params['status_id'])) {
386 $where[] = "( campaign.status_id IN ( %8 ) )";
387 $queryParams[8] = [implode(',', (array) $params['status_id']), 'CommaSeparatedIntegers'];
388 }
389 if (array_key_exists('is_active', $params)) {
390 $active = "( campaign.is_active = 1 )";
391 if (!empty($params['is_active'])) {
392 $active = "( campaign.is_active = 0 OR campaign.is_active IS NULL )";
393 }
394 $where[] = $active;
395 }
396 $whereClause = NULL;
397 if (!empty($where)) {
398 $whereClause = ' WHERE ' . implode(" \nAND ", $where);
399 }
400
401 $properties = [
402 'id',
403 'name',
404 'title',
405 'start_date',
406 'end_date',
407 'status_id',
408 'is_active',
409 'description',
410 'campaign_type_id',
411 ];
412
413 $selectClause = '
414 SELECT campaign.id as id,
415 campaign.name as name,
416 campaign.title as title,
417 campaign.is_active as is_active,
418 campaign.status_id as status_id,
419 campaign.end_date as end_date,
420 campaign.start_date as start_date,
421 campaign.description as description,
422 campaign.campaign_type_id as campaign_type_id';
423 if ($onlyCount) {
424 $selectClause = 'SELECT COUNT(*)';
425 }
426 $fromClause = 'FROM civicrm_campaign campaign';
427
428 $query = "{$selectClause} {$fromClause} {$lookupTableJoins} {$whereClause} {$orderByClause} {$limitClause}";
429
430 //in case of only count.
431 if ($onlyCount) {
432 return (int) CRM_Core_DAO::singleValueQuery($query, $queryParams);
433 }
434
435 $campaign = CRM_Core_DAO::executeQuery($query, $queryParams);
436 while ($campaign->fetch()) {
437 foreach ($properties as $property) {
438 $campaigns[$campaign->id][$property] = $campaign->$property;
439 }
440 }
441
442 return $campaigns;
443 }
444
445 /**
446 * Get the campaign count.
447 *
448 * @return int
449 */
450 public static function getCampaignCount(): int {
451 return (int) CRM_Core_DAO::singleValueQuery('SELECT COUNT(*) FROM civicrm_campaign');
452 }
453
454 /**
455 * Get Campaigns groups.
456 *
457 * @param int $campaignId
458 * Campaign id.
459 *
460 * @return array
461 */
462 public static function getCampaignGroups($campaignId) {
463 static $campaignGroups;
464 if (!$campaignId) {
465 return [];
466 }
467
468 if (!isset($campaignGroups[$campaignId])) {
469 $campaignGroups[$campaignId] = [];
470
471 $query = "
472 SELECT grp.title, grp.id
473 FROM civicrm_campaign_group campgrp
474 INNER JOIN civicrm_group grp ON ( grp.id = campgrp.entity_id )
475 WHERE campgrp.group_type = 'Include'
476 AND campgrp.entity_table = 'civicrm_group'
477 AND campgrp.campaign_id = %1";
478
479 $groups = CRM_Core_DAO::executeQuery($query, [1 => [$campaignId, 'Positive']]);
480 while ($groups->fetch()) {
481 $campaignGroups[$campaignId][$groups->id] = $groups->title;
482 }
483 }
484
485 return $campaignGroups[$campaignId];
486 }
487
488 /**
489 * Update the is_active flag in the db.
490 *
491 * @param int $id
492 * Id of the database record.
493 * @param bool $is_active
494 * Value we want to set the is_active field.
495 *
496 * @return bool
497 * true if we found and updated the object, else false
498 */
499 public static function setIsActive($id, $is_active) {
500 return CRM_Core_DAO::setFieldValue('CRM_Campaign_DAO_Campaign', $id, 'is_active', $is_active);
501 }
502
503 /**
504 * @return bool
505 */
506 public static function accessCampaign() {
507 static $allow = NULL;
508
509 if (!isset($allow)) {
510 $allow = FALSE;
511 if (CRM_Core_Permission::check('manage campaign') ||
512 CRM_Core_Permission::check('administer CiviCampaign')
513 ) {
514 $allow = TRUE;
515 }
516 }
517
518 return $allow;
519 }
520
521 /**
522 * Add select element for campaign
523 * and assign needful info to templates.
524 *
525 * @param CRM_Core_Form $form
526 * @param int $connectedCampaignId
527 */
528 public static function addCampaign(&$form, $connectedCampaignId = NULL) {
529 //some forms do set default and freeze.
530 $appendDates = TRUE;
531 if ($form->get('action') & CRM_Core_Action::VIEW) {
532 $appendDates = FALSE;
533 }
534
535 $campaignDetails = self::getPermissionedCampaigns($connectedCampaignId, NULL, TRUE, TRUE, $appendDates);
536
537 $campaigns = $campaignDetails['campaigns'] ?? NULL;
538 $hasAccessCampaign = $campaignDetails['hasAccessCampaign'] ?? NULL;
539 $isCampaignEnabled = $campaignDetails['isCampaignEnabled'] ?? NULL;
540
541 $showAddCampaign = FALSE;
542 if ($connectedCampaignId || ($isCampaignEnabled && $hasAccessCampaign)) {
543 $showAddCampaign = TRUE;
544 $campaign = $form->addEntityRef('campaign_id', ts('Campaign'), [
545 'entity' => 'Campaign',
546 'create' => TRUE,
547 'select' => ['minimumInputLength' => 0],
548 ]);
549 //lets freeze when user does not has access or campaign is disabled.
550 if (!$isCampaignEnabled || !$hasAccessCampaign) {
551 $campaign->freeze();
552 }
553 }
554
555 //carry this info to templates.
556 $campaignInfo = [
557 'showAddCampaign' => $showAddCampaign,
558 'hasAccessCampaign' => $hasAccessCampaign,
559 'isCampaignEnabled' => $isCampaignEnabled,
560 ];
561
562 $form->assign('campaignInfo', $campaignInfo);
563 }
564
565 /**
566 * Add campaign in component search.
567 * and assign needful info to templates.
568 *
569 * @param CRM_Core_Form $form
570 * @param string $elementName
571 */
572 public static function addCampaignInComponentSearch(&$form, $elementName = 'campaign_id') {
573 $campaignInfo = [];
574 $campaignDetails = self::getPermissionedCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE);
575 $campaigns = $campaignDetails['campaigns'] ?? NULL;
576 $hasAccessCampaign = $campaignDetails['hasAccessCampaign'] ?? NULL;
577 $isCampaignEnabled = $campaignDetails['isCampaignEnabled'] ?? NULL;
578
579 $showCampaignInSearch = FALSE;
580 if ($isCampaignEnabled && $hasAccessCampaign && !empty($campaigns)) {
581 //get the current campaign only.
582 $currentCampaigns = self::getCampaigns(NULL, NULL, FALSE);
583 $pastCampaigns = array_diff($campaigns, $currentCampaigns);
584 $allCampaigns = [];
585 if (!empty($currentCampaigns)) {
586 $allCampaigns = ['crm_optgroup_current_campaign' => ts('Current Campaigns')] + $currentCampaigns;
587 }
588 if (!empty($pastCampaigns)) {
589 $allCampaigns += ['crm_optgroup_past_campaign' => ts('Past Campaigns')] + $pastCampaigns;
590 }
591
592 $showCampaignInSearch = TRUE;
593 $form->add('select', $elementName, ts('Campaigns'), $allCampaigns, FALSE,
594 ['id' => 'campaigns', 'multiple' => 'multiple', 'class' => 'crm-select2']
595 );
596 }
597
598 $form->assign('campaignElementName', $showCampaignInSearch ? $elementName : '');
599 }
600
601 /**
602 * @return array
603 */
604 public static function getEntityRefFilters() {
605 return [
606 ['key' => 'campaign_type_id', 'value' => ts('Campaign Type')],
607 ['key' => 'status_id', 'value' => ts('Status')],
608 [
609 'key' => 'start_date',
610 'value' => ts('Start Date'),
611 'options' => [
612 ['key' => '{">":"now"}', 'value' => ts('Upcoming')],
613 [
614 'key' => '{"BETWEEN":["now - 3 month","now"]}',
615 'value' => ts('Past 3 Months'),
616 ],
617 [
618 'key' => '{"BETWEEN":["now - 6 month","now"]}',
619 'value' => ts('Past 6 Months'),
620 ],
621 [
622 'key' => '{"BETWEEN":["now - 1 year","now"]}',
623 'value' => ts('Past Year'),
624 ],
625 ],
626 ],
627 [
628 'key' => 'end_date',
629 'value' => ts('End Date'),
630 'options' => [
631 ['key' => '{">":"now"}', 'value' => ts('In the future')],
632 ['key' => '{"<":"now"}', 'value' => ts('In the past')],
633 ['key' => '{"IS NULL":"1"}', 'value' => ts('Not set')],
634 ],
635 ],
636 ];
637 }
638
639 /**
640 * Links to create new campaigns from entityRef widget
641 *
642 * @return array|bool
643 */
644 public static function getEntityRefCreateLinks() {
645 if (CRM_Core_Permission::check([['administer CiviCampaign', 'manage campaign']])) {
646 return [
647 [
648 'label' => ts('New Campaign'),
649 'url' => CRM_Utils_System::url('civicrm/campaign/add', "reset=1",
650 NULL, NULL, FALSE, FALSE, TRUE),
651 'type' => 'Campaign',
652 ],
653 ];
654 }
655 return FALSE;
656 }
657
658 }