Merge pull request #20251 from larssandergreen/change-registration-button-text
[civicrm-core.git] / CRM / Contribute / Selector / Search.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 * @package CRM
14 * @copyright CiviCRM LLC https://civicrm.org/licensing
15 */
16
17 /**
18 * Class to render contribution search results.
19 */
20 class CRM_Contribute_Selector_Search extends CRM_Core_Selector_Base implements CRM_Core_Selector_API {
21
22 /**
23 * Array of action links.
24 *
25 * @var array
26 */
27 public static $_links = NULL;
28
29 /**
30 * We use desc to remind us what that column is, name is used in the tpl
31 *
32 * @var array
33 */
34 public static $_columnHeaders;
35
36 /**
37 * Properties of contact we're interested in displaying
38 * @var array
39 */
40 public static $_properties = [
41 'contact_id',
42 'contribution_id',
43 'contact_type',
44 'sort_name',
45 'amount_level',
46 'total_amount',
47 'financial_type',
48 'contribution_source',
49 'receive_date',
50 'thankyou_date',
51 'contribution_status_id',
52 'contribution_status',
53 'contribution_cancel_date',
54 'product_name',
55 'is_test',
56 'is_template',
57 'contribution_recur_id',
58 'receipt_date',
59 'membership_id',
60 'currency',
61 'contribution_campaign_id',
62 'contribution_soft_credit_name',
63 'contribution_soft_credit_contact_id',
64 'contribution_soft_credit_amount',
65 'contribution_soft_credit_type',
66 ];
67
68 /**
69 * Are we restricting ourselves to a single contact
70 *
71 * @var bool
72 */
73 protected $_single = FALSE;
74
75 /**
76 * Are we restricting ourselves to a single contact
77 *
78 * @var bool
79 */
80 protected $_limit = NULL;
81
82 /**
83 * What context are we being invoked from
84 *
85 * @var string
86 */
87 protected $_context = NULL;
88
89 /**
90 * What component context are we being invoked from
91 *
92 * @var string
93 */
94 protected $_compContext = NULL;
95
96 /**
97 * QueryParams is the array returned by exportValues called on
98 * the HTML_QuickForm_Controller for that page.
99 *
100 * @var array
101 */
102 public $_queryParams;
103
104 /**
105 * Represent the type of selector
106 *
107 * @var int
108 */
109 protected $_action;
110
111 /**
112 * The additional clause that we restrict the search with
113 *
114 * @var string
115 */
116 protected $_contributionClause = NULL;
117
118 /**
119 * The query object
120 *
121 * @var CRM_Contact_BAO_Query
122 */
123 protected $_query;
124
125 protected $_includeSoftCredits = FALSE;
126
127 /**
128 * Class constructor.
129 *
130 * @param array $queryParams
131 * Array of parameters for query.
132 * @param \const|int $action - action of search basic or advanced.
133 * @param string $contributionClause
134 * If the caller wants to further restrict the search (used in contributions).
135 * @param bool $single
136 * Are we dealing only with one contact?.
137 * @param int $limit
138 * How many contributions do we want returned.
139 *
140 * @param string $context
141 * @param null $compContext
142 *
143 * @return CRM_Contribute_Selector_Search
144 */
145 public function __construct(
146 &$queryParams,
147 $action = CRM_Core_Action::NONE,
148 $contributionClause = NULL,
149 $single = FALSE,
150 $limit = NULL,
151 $context = 'search',
152 $compContext = NULL
153 ) {
154
155 // submitted form values
156 $this->_queryParams = &$queryParams;
157
158 $this->_single = $single;
159 $this->_limit = $limit;
160 $this->_context = $context;
161 $this->_compContext = $compContext;
162
163 $this->_contributionClause = $contributionClause;
164
165 // type of selector
166 $this->_action = $action;
167 $returnProperties = CRM_Contribute_BAO_Query::selectorReturnProperties($this->_queryParams);
168 $this->_includeSoftCredits = CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled($this->_queryParams);
169 $this->_queryParams[] = ['contribution_id', '!=', 0, 0, 0];
170 $this->_query = new CRM_Contact_BAO_Query(
171 $this->_queryParams,
172 $returnProperties,
173 NULL, FALSE, FALSE,
174 CRM_Contact_BAO_Query::MODE_CONTRIBUTE
175 );
176 // @todo the function CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled should handle this
177 // can we remove? if not why not?
178 if ($this->_includeSoftCredits) {
179 $this->_query->_rowCountClause = " count(civicrm_contribution.id)";
180 $this->_query->_groupByComponentClause = " GROUP BY contribution_search_scredit_combined.id, contribution_search_scredit_combined.contact_id, contribution_search_scredit_combined.scredit_id ";
181 }
182 else {
183 $this->_query->_distinctComponentClause = " civicrm_contribution.id";
184 $this->_query->_groupByComponentClause = " GROUP BY civicrm_contribution.id ";
185 }
186 }
187
188 /**
189 * This method returns the links that are given for each search row.
190 * currently the links added for each row are
191 *
192 * - View
193 * - Edit
194 *
195 * @param int $componentId
196 * @param null $componentAction
197 * @param null $key
198 * @param null $compContext
199 *
200 * @return array
201 */
202 public static function &links($componentId = NULL, $componentAction = NULL, $key = NULL, $compContext = NULL) {
203 $extraParams = NULL;
204 if ($componentId) {
205 $extraParams = "&compId={$componentId}&compAction={$componentAction}";
206 }
207 if ($compContext) {
208 $extraParams .= "&compContext={$compContext}";
209 }
210 if ($key) {
211 $extraParams .= "&key={$key}";
212 }
213
214 if (!(self::$_links)) {
215 self::$_links = [
216 CRM_Core_Action::VIEW => [
217 'name' => ts('View'),
218 'url' => 'civicrm/contact/view/contribution',
219 'qs' => "reset=1&id=%%id%%&cid=%%cid%%&action=view&context=%%cxt%%&selectedChild=contribute{$extraParams}",
220 'title' => ts('View Contribution'),
221 ],
222 CRM_Core_Action::UPDATE => [
223 'name' => ts('Edit'),
224 'url' => 'civicrm/contact/view/contribution',
225 'qs' => "reset=1&action=update&id=%%id%%&cid=%%cid%%&context=%%cxt%%{$extraParams}",
226 'title' => ts('Edit Contribution'),
227 ],
228 CRM_Core_Action::DELETE => [
229 'name' => ts('Delete'),
230 'url' => 'civicrm/contact/view/contribution',
231 'qs' => "reset=1&action=delete&id=%%id%%&cid=%%cid%%&context=%%cxt%%{$extraParams}",
232 'title' => ts('Delete Contribution'),
233 ],
234 ];
235 }
236 return self::$_links;
237 }
238
239 /**
240 * Getter for array of the parameters required for creating pager.
241 *
242 * @param $action
243 * @param array $params
244 */
245 public function getPagerParams($action, &$params) {
246 $params['status'] = ts('Contribution') . ' %%StatusMessage%%';
247 $params['csvString'] = NULL;
248 if ($this->_limit) {
249 $params['rowCount'] = $this->_limit;
250 }
251 else {
252 $params['rowCount'] = Civi::settings()->get('default_pager_size');
253 }
254
255 $params['buttonTop'] = 'PagerTopButton';
256 $params['buttonBottom'] = 'PagerBottomButton';
257 }
258
259 /**
260 * Returns total number of rows for the query.
261 *
262 * @param string $action
263 *
264 * @return int
265 * Total number of rows
266 */
267 public function getTotalCount($action) {
268 return $this->_query->searchQuery(0, 0, NULL,
269 TRUE, FALSE,
270 FALSE, FALSE,
271 FALSE,
272 $this->_contributionClause
273 );
274 }
275
276 /**
277 * Returns all the rows in the given offset and rowCount.
278 *
279 * @param string $action
280 * The action being performed.
281 * @param int $offset
282 * The row number to start from.
283 * @param int $rowCount
284 * The number of rows to return.
285 * @param string $sort
286 * The sql string that describes the sort order.
287 * @param string $output
288 * What should the result set include (web/email/csv).
289 *
290 * @return int
291 * the total number of rows for this action
292 */
293 public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) {
294 if ($this->_includeSoftCredits) {
295 // especial sort order when rows include soft credits
296 $sort = $sort->orderBy() . ", civicrm_contribution.id, civicrm_contribution_soft.id";
297 }
298 $result = $this->_query->searchQuery($offset, $rowCount, $sort,
299 FALSE, FALSE,
300 FALSE, FALSE,
301 FALSE,
302 $this->_contributionClause
303 );
304 // process the result of the query
305 $rows = [];
306
307 //CRM-4418 check for view/edit/delete
308 $permissions = [CRM_Core_Permission::VIEW];
309 if (CRM_Core_Permission::check('edit contributions')) {
310 $permissions[] = CRM_Core_Permission::EDIT;
311 }
312 if (CRM_Core_Permission::check('delete in CiviContribute')) {
313 $permissions[] = CRM_Core_Permission::DELETE;
314 }
315 $mask = CRM_Core_Action::mask($permissions);
316
317 $qfKey = $this->_key;
318 $componentId = $componentContext = NULL;
319 if ($this->_context != 'contribute') {
320 // @todo explain the significance of context & why we do not get these i that context.
321 $qfKey = CRM_Utils_Request::retrieve('key', 'String');
322 $componentId = CRM_Utils_Request::retrieve('id', 'Positive');
323 $componentAction = CRM_Utils_Request::retrieve('action', 'String');
324 $componentContext = CRM_Utils_Request::retrieve('compContext', 'String');
325
326 if (!$componentContext &&
327 $this->_compContext
328 ) {
329 // @todo explain when this condition might occur.
330 $componentContext = $this->_compContext;
331 $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', CRM_Core_DAO::$_nullObject, NULL, FALSE, 'REQUEST');
332 }
333 // CRM-17628 for some reason qfKey is not always set when searching from contribution search.
334 // as a result if the edit link is opened using right-click + open in new tab
335 // then the browser is not returned to the search results on save.
336 // This is an effort to getting the qfKey without, sadly, understanding the intent of those who came before me.
337 if (empty($qfKey)) {
338 $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', CRM_Core_DAO::$_nullObject, NULL, FALSE, 'REQUEST');
339 }
340 }
341
342 // get all contribution status
343 $contributionStatuses = CRM_Core_OptionGroup::values('contribution_status',
344 FALSE, FALSE, FALSE, NULL, 'name', FALSE
345 );
346
347 //get all campaigns.
348 $allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE);
349
350 while ($result->fetch()) {
351 $this->_query->convertToPseudoNames($result);
352 $links = self::links($componentId,
353 $componentAction,
354 $qfKey,
355 $componentContext
356 );
357
358 $checkLineItem = FALSE;
359 // Set defaults to empty to prevent e-notices.
360 $row = ['amount_level' => ''];
361 // Now check for lineItems
362 if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) {
363 $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($result->id);
364 foreach ($lineItems as $items) {
365 if (!CRM_Core_Permission::check('view contributions of type ' . CRM_Contribute_PseudoConstant::financialType($items['financial_type_id']))) {
366 $checkLineItem = TRUE;
367 break;
368 }
369 if (!CRM_Core_Permission::check('edit contributions of type ' . CRM_Contribute_PseudoConstant::financialType($items['financial_type_id']))) {
370 unset($links[CRM_Core_Action::UPDATE]);
371 }
372 if (!CRM_Core_Permission::check('delete contributions of type ' . CRM_Contribute_PseudoConstant::financialType($items['financial_type_id']))) {
373 unset($links[CRM_Core_Action::DELETE]);
374 }
375 }
376 if ($checkLineItem) {
377 continue;
378 }
379 if (!CRM_Core_Permission::check('edit contributions of type ' . CRM_Contribute_PseudoConstant::financialType($result->financial_type_id))) {
380 unset($links[CRM_Core_Action::UPDATE]);
381 }
382 if (!CRM_Core_Permission::check('delete contributions of type ' . CRM_Contribute_PseudoConstant::financialType($result->financial_type_id))) {
383 unset($links[CRM_Core_Action::DELETE]);
384 }
385 }
386 // the columns we are interested in
387 foreach (self::$_properties as $property) {
388 if (property_exists($result, $property)) {
389 $row[$property] = $result->$property;
390 }
391 }
392
393 //carry campaign on selectors.
394 // @todo - I can't find any evidence that 'carrying' the campaign on selectors actually
395 // results in it being displayed anywhere so why do we do this???
396 $row['campaign'] = $allCampaigns[$result->contribution_campaign_id] ?? NULL;
397 $row['campaign_id'] = $result->contribution_campaign_id;
398
399 // add contribution status name
400 $row['contribution_status_name'] = CRM_Utils_Array::value($row['contribution_status_id'],
401 $contributionStatuses
402 );
403
404 $isPayLater = FALSE;
405 if ($result->is_pay_later && CRM_Utils_Array::value('contribution_status_name', $row) == 'Pending') {
406 $isPayLater = TRUE;
407 $row['contribution_status'] .= ' (' . ts('Pay Later') . ')';
408 $links[CRM_Core_Action::ADD] = [
409 'name' => ts('Pay with Credit Card'),
410 'url' => 'civicrm/contact/view/contribution',
411 'qs' => 'reset=1&action=update&id=%%id%%&cid=%%cid%%&context=%%cxt%%&mode=live',
412 'title' => ts('Pay with Credit Card'),
413 ];
414 }
415 elseif (CRM_Utils_Array::value('contribution_status_name', $row) == 'Pending') {
416 $row['contribution_status'] .= ' (' . ts('Incomplete Transaction') . ')';
417 }
418
419 if ($row['is_test']) {
420 $row['financial_type'] = $row['financial_type'] . ' (' . ts('test') . ')';
421 }
422
423 $row['checkbox'] = CRM_Core_Form::CB_PREFIX . $result->contribution_id;
424
425 $actions = [
426 'id' => $result->contribution_id,
427 'cid' => $result->contact_id,
428 'cxt' => $this->_context,
429 ];
430
431 if (in_array($row['contribution_status_name'], ['Partially paid', 'Pending refund']) || $isPayLater) {
432 $buttonName = ts('Record Payment');
433 if ($row['contribution_status_name'] == 'Pending refund') {
434 $buttonName = ts('Record Refund');
435 }
436 elseif (CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) {
437 $links[CRM_Core_Action::BASIC] = [
438 'name' => ts('Submit Credit Card payment'),
439 'url' => 'civicrm/payment/add',
440 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=add&component=contribution&mode=live',
441 'title' => ts('Submit Credit Card payment'),
442 ];
443 }
444 $links[CRM_Core_Action::ADD] = [
445 'name' => $buttonName,
446 'url' => 'civicrm/payment',
447 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=add&component=contribution',
448 'title' => $buttonName,
449 ];
450 }
451 $links = $links + CRM_Contribute_Task::getContextualLinks($row);
452
453 $row['action'] = CRM_Core_Action::formLink(
454 $links,
455 $mask, $actions,
456 ts('more'),
457 FALSE,
458 'contribution.selector.row',
459 'Contribution',
460 $result->contribution_id
461 );
462
463 $row['contact_type'] = CRM_Contact_BAO_Contact_Utils::getImage($result->contact_sub_type ? $result->contact_sub_type : $result->contact_type, FALSE, $result->contact_id
464 );
465
466 if (!empty($row['amount_level'])) {
467 CRM_Event_BAO_Participant::fixEventLevel($row['amount_level']);
468 }
469
470 $rows[] = $row;
471 }
472
473 return $rows;
474 }
475
476 /**
477 * @inheritDoc
478 */
479 public function getQILL() {
480 return $this->_query->qill();
481 }
482
483 /**
484 * Returns the column headers as an array of tuples:
485 * (name, sortName (key to the sort array))
486 *
487 * @param string $action
488 * The action being performed.
489 * @param string $output
490 * What should the result set include (web/email/csv).
491 *
492 * @return array
493 * the column headers that need to be displayed
494 */
495 public function &getColumnHeaders($action = NULL, $output = NULL) {
496 $pre = [];
497 self::$_columnHeaders = [
498 [
499 'name' => $this->_includeSoftCredits ? ts('Contribution Amount') : ts('Amount'),
500 'sort' => 'total_amount',
501 'direction' => CRM_Utils_Sort::DONTCARE,
502 'field_name' => 'total_amount',
503 'type' => '',
504 ],
505 ];
506 if ($this->_includeSoftCredits) {
507 self::$_columnHeaders
508 = array_merge(
509 self::$_columnHeaders,
510 [
511 [
512 'name' => ts('Soft Credit Amount'),
513 'sort' => 'contribution_soft_credit_amount',
514 'field_name' => 'contribution_soft_credit_amount',
515 'direction' => CRM_Utils_Sort::DONTCARE,
516 'type' => '',
517 ],
518 ]
519 );
520 }
521 self::$_columnHeaders
522 = array_merge(
523 self::$_columnHeaders,
524 [
525 [
526 'name' => ts('Type'),
527 'sort' => 'financial_type',
528 'field_name' => 'financial_type',
529 'direction' => CRM_Utils_Sort::DONTCARE,
530 'type' => '',
531 ],
532 [
533 'name' => ts('Source'),
534 'sort' => 'contribution_source',
535 'field_name' => 'contribution_source',
536 'direction' => CRM_Utils_Sort::DONTCARE,
537 'type' => '',
538 ],
539 [
540 'name' => ts('Received'),
541 'sort' => 'receive_date',
542 'field_name' => 'receive_date',
543 'type' => 'date',
544 'direction' => CRM_Utils_Sort::DESCENDING,
545 ],
546 [
547 'name' => ts('Thank-you Sent'),
548 'sort' => 'thankyou_date',
549 'field_name' => 'thankyou_date',
550 'type' => 'date',
551 'direction' => CRM_Utils_Sort::DONTCARE,
552 ],
553 [
554 'name' => ts('Status'),
555 'sort' => 'contribution_status',
556 'field_name' => 'contribution_status',
557 'direction' => CRM_Utils_Sort::DONTCARE,
558 'type' => '',
559 ],
560 ]
561 );
562 if (CRM_Contribute_BAO_Query::isSiteHasProducts()) {
563 self::$_columnHeaders[] = [
564 'name' => ts('Premium'),
565 'sort' => 'product_name',
566 'field_name' => 'product_name',
567 'direction' => CRM_Utils_Sort::DONTCARE,
568 'type' => '',
569 ];
570 }
571 if (!$this->_single) {
572 $pre = [
573 [
574 'name' => ts('Name'),
575 'sort' => 'sort_name',
576 'field_name' => '',
577 'direction' => CRM_Utils_Sort::DONTCARE,
578 'type' => '',
579 ],
580 ];
581 }
582 self::$_columnHeaders = array_merge($pre, self::$_columnHeaders);
583 if ($this->_includeSoftCredits) {
584 self::$_columnHeaders = array_merge(
585 self::$_columnHeaders,
586 [
587 [
588 'name' => ts('Soft Credit For'),
589 'sort' => 'contribution_soft_credit_name',
590 'direction' => CRM_Utils_Sort::DONTCARE,
591 'field_name' => '',
592 ],
593 [
594 'name' => ts('Soft Credit Type'),
595 'sort' => 'contribution_soft_credit_type',
596 'direction' => CRM_Utils_Sort::ASCENDING,
597 'field_name' => '',
598 ],
599 ]
600 );
601 }
602 self::$_columnHeaders
603 = array_merge(
604 self::$_columnHeaders, [
605 ['desc' => ts('Actions'), 'type' => 'actions', 'field_name' => ''],
606 ]
607 );
608 foreach (array_keys(self::$_columnHeaders) as $index) {
609 // Add weight & space it out a bit to allow headers to be inserted.
610 self::$_columnHeaders[$index]['weight'] = $index * 10;
611 }
612
613 CRM_Core_Smarty::singleton()->assign('softCreditColumns', $this->_includeSoftCredits);
614 return self::$_columnHeaders;
615 }
616
617 /**
618 * @return mixed
619 */
620 public function alphabetQuery() {
621 return $this->_query->alphabetQuery();
622 }
623
624 /**
625 * @return CRM_Contact_BAO_Query
626 */
627 public function &getQuery() {
628 return $this->_query;
629 }
630
631 /**
632 * Name of export file.
633 *
634 * @param string $output
635 * Type of output.
636 *
637 * @return string
638 * name of the file
639 */
640 public function getExportFileName($output = 'csv') {
641 return ts('CiviCRM Contribution Search');
642 }
643
644 /**
645 * @return mixed
646 */
647 public function getSummary() {
648 return $this->_query->summaryContribution($this->_context);
649 }
650
651 }