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