Merge pull request #16192 from eileenmcnaughton/mem_add
[civicrm-core.git] / CRM / Member / 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 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * This class is used to retrieve and display a range of
20 * contacts that match the given criteria (specifically for
21 * results of advanced search options.
22 */
23 class CRM_Member_Selector_Search extends CRM_Core_Selector_Base implements CRM_Core_Selector_API {
24
25 /**
26 * This defines two actions- View and Edit.
27 *
28 * @var array
29 */
30 public static $_links = NULL;
31
32 /**
33 * We use desc to remind us what that column is, name is used in the tpl
34 *
35 * @var array
36 */
37 public static $_columnHeaders;
38
39 /**
40 * Properties of contact we're interested in displaying
41 * @var array
42 */
43 public static $_properties = [
44 'contact_id',
45 'membership_id',
46 'contact_type',
47 'sort_name',
48 'membership_type',
49 'membership_join_date',
50 'membership_start_date',
51 'membership_end_date',
52 'membership_source',
53 'status_id',
54 'member_is_test',
55 'owner_membership_id',
56 'membership_status',
57 'member_campaign_id',
58 ];
59
60 /**
61 * Are we restricting ourselves to a single contact
62 *
63 * @var bool
64 */
65 protected $_single = FALSE;
66
67 /**
68 * Are we restricting ourselves to a single contact
69 *
70 * @var bool
71 */
72 protected $_limit = NULL;
73
74 /**
75 * What context are we being invoked from
76 *
77 * @var string
78 */
79 protected $_context = NULL;
80
81 /**
82 * QueryParams is the array returned by exportValues called on
83 * the HTML_QuickForm_Controller for that page.
84 *
85 * @var array
86 */
87 public $_queryParams;
88
89 /**
90 * Represent the type of selector.
91 *
92 * @var int
93 */
94 protected $_action;
95
96 /**
97 * The additional clause that we restrict the search with.
98 *
99 * @var string
100 */
101 protected $_memberClause = NULL;
102
103 /**
104 * The query object.
105 *
106 * @var string
107 */
108 protected $_query;
109
110 /**
111 * Class constructor.
112 *
113 * @param array $queryParams
114 * Array of parameters for query.
115 * @param \const|int $action - action of search basic or advanced.
116 * @param string $memberClause
117 * If the caller wants to further restrict the search (used in memberships).
118 * @param bool $single
119 * Are we dealing only with one contact?.
120 * @param int $limit
121 * How many memberships do we want returned.
122 *
123 * @param string $context
124 *
125 * @return \CRM_Member_Selector_Search
126 */
127 public function __construct(
128 &$queryParams,
129 $action = CRM_Core_Action::NONE,
130 $memberClause = NULL,
131 $single = FALSE,
132 $limit = NULL,
133 $context = 'search'
134 ) {
135 // submitted form values
136 $this->_queryParams = &$queryParams;
137
138 $this->_single = $single;
139 $this->_limit = $limit;
140 $this->_context = $context;
141
142 $this->_memberClause = $memberClause;
143
144 // type of selector
145 $this->_action = $action;
146
147 $this->_query = new CRM_Contact_BAO_Query($this->_queryParams,
148 CRM_Member_BAO_Query::defaultReturnProperties(CRM_Contact_BAO_Query::MODE_MEMBER,
149 FALSE
150 ),
151 NULL, FALSE, FALSE,
152 CRM_Contact_BAO_Query::MODE_MEMBER
153 );
154 $this->_query->_distinctComponentClause = " civicrm_membership.id";
155 $this->_query->_groupByComponentClause = " GROUP BY civicrm_membership.id ";
156 }
157
158 /**
159 * This method returns the links that are given for each search row.
160 *
161 * Currently the links added for each row are
162 *
163 * - View
164 * - Edit
165 *
166 * @param string $status
167 * @param bool $isPaymentProcessor
168 * @param null $accessContribution
169 * @param null $qfKey
170 * @param null $context
171 * @param bool $isCancelSupported
172 *
173 * @return array
174 */
175 public static function &links(
176 $status = 'all',
177 $isPaymentProcessor = NULL,
178 $accessContribution = NULL,
179 $qfKey = NULL,
180 $context = NULL,
181 $isCancelSupported = FALSE
182 ) {
183 $extraParams = NULL;
184 if ($context == 'search') {
185 $extraParams .= '&compContext=membership';
186 }
187 if ($qfKey) {
188 $extraParams .= "&key={$qfKey}";
189 }
190
191 if (!self::$_links['view']) {
192 self::$_links['view'] = [
193 CRM_Core_Action::VIEW => [
194 'name' => ts('View'),
195 'url' => 'civicrm/contact/view/membership',
196 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=view&context=%%cxt%%&selectedChild=member' . $extraParams,
197 'title' => ts('View Membership'),
198 ],
199 ];
200 }
201 if (!isset(self::$_links['all']) || !self::$_links['all']) {
202 $extraLinks = [
203 CRM_Core_Action::UPDATE => [
204 'name' => ts('Edit'),
205 'url' => 'civicrm/contact/view/membership',
206 'qs' => 'reset=1&action=update&id=%%id%%&cid=%%cid%%&context=%%cxt%%' . $extraParams,
207 'title' => ts('Edit Membership'),
208 ],
209 CRM_Core_Action::DELETE => [
210 'name' => ts('Delete'),
211 'url' => 'civicrm/contact/view/membership',
212 'qs' => 'reset=1&action=delete&id=%%id%%&cid=%%cid%%&context=%%cxt%%' . $extraParams,
213 'title' => ts('Delete Membership'),
214 ],
215 CRM_Core_Action::RENEW => [
216 'name' => ts('Renew'),
217 'url' => 'civicrm/contact/view/membership',
218 'qs' => 'reset=1&action=renew&id=%%id%%&cid=%%cid%%&context=%%cxt%%' . $extraParams,
219 'title' => ts('Renew Membership'),
220 ],
221 CRM_Core_Action::FOLLOWUP => [
222 'name' => ts('Renew-Credit Card'),
223 'url' => 'civicrm/contact/view/membership',
224 'qs' => 'action=renew&reset=1&cid=%%cid%%&id=%%id%%&context=%%cxt%%&mode=live' . $extraParams,
225 'title' => ts('Renew Membership Using Credit Card'),
226 ],
227 ];
228 if (!$isPaymentProcessor || !$accessContribution) {
229 //unset the renew with credit card when payment
230 //processor is not available or user not permitted to make contributions
231 unset($extraLinks[CRM_Core_Action::FOLLOWUP]);
232 }
233
234 self::$_links['all'] = self::$_links['view'] + $extraLinks;
235 }
236
237 if ($isCancelSupported) {
238 self::$_links['all'][CRM_Core_Action::DISABLE] = [
239 'name' => ts('Cancel Auto-renewal'),
240 'url' => 'civicrm/contribute/unsubscribe',
241 'qs' => 'reset=1&mid=%%id%%&context=%%cxt%%' . $extraParams,
242 'title' => ts('Cancel Auto Renew Subscription'),
243 ];
244 }
245 elseif (isset(self::$_links['all'][CRM_Core_Action::DISABLE])) {
246 unset(self::$_links['all'][CRM_Core_Action::DISABLE]);
247 }
248
249 return self::$_links[$status];
250 }
251
252 /**
253 * Getter for array of the parameters required for creating pager.
254 *
255 * @param int $action
256 * @param array $params
257 */
258 public function getPagerParams($action, &$params) {
259 $params['status'] = ts('Member') . ' %%StatusMessage%%';
260 $params['csvString'] = NULL;
261 if ($this->_limit) {
262 $params['rowCount'] = $this->_limit;
263 }
264 else {
265 $params['rowCount'] = CRM_Utils_Pager::ROWCOUNT;
266 }
267
268 $params['buttonTop'] = 'PagerTopButton';
269 $params['buttonBottom'] = 'PagerBottomButton';
270 }
271
272 /**
273 * Returns total number of rows for the query.
274 *
275 * @param int $action
276 *
277 * @return int
278 * Total number of rows
279 */
280 public function getTotalCount($action) {
281 return $this->_query->searchQuery(0, 0, NULL,
282 TRUE, FALSE,
283 FALSE, FALSE,
284 FALSE,
285 $this->_memberClause
286 );
287 }
288
289 /**
290 * Returns all the rows in the given offset and rowCount.
291 *
292 * @param string $action
293 * The action being performed.
294 * @param int $offset
295 * The row number to start from.
296 * @param int $rowCount
297 * The number of rows to return.
298 * @param string $sort
299 * The sql string that describes the sort order.
300 * @param string $output
301 * What should the result set include (web/email/csv).
302 *
303 * @return array
304 * the total number of rows for this action
305 */
306 public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) {
307 // check if we can process credit card registration
308 $processors = CRM_Core_PseudoConstant::paymentProcessor(FALSE, FALSE,
309 "billing_mode IN ( 1, 3 )"
310 );
311 if (count($processors) > 0) {
312 $this->_isPaymentProcessor = TRUE;
313 }
314 else {
315 $this->_isPaymentProcessor = FALSE;
316 }
317
318 // Only show credit card membership signup and renewal if user has CiviContribute permission
319 if (CRM_Core_Permission::access('CiviContribute')) {
320 $this->_accessContribution = TRUE;
321 }
322 else {
323 $this->_accessContribution = FALSE;
324 }
325
326 //get all campaigns.
327 $allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE);
328
329 $result = $this->_query->searchQuery($offset, $rowCount, $sort,
330 FALSE, FALSE,
331 FALSE, FALSE,
332 FALSE,
333 $this->_memberClause
334 );
335
336 // process the result of the query
337 $rows = [];
338
339 //CRM-4418 check for view, edit, delete
340 $permissions = [CRM_Core_Permission::VIEW];
341 if (CRM_Core_Permission::check('edit memberships')) {
342 $permissions[] = CRM_Core_Permission::EDIT;
343 }
344 if (CRM_Core_Permission::check('delete in CiviMember')) {
345 $permissions[] = CRM_Core_Permission::DELETE;
346 }
347 $mask = CRM_Core_Action::mask($permissions);
348
349 while ($result->fetch()) {
350 $row = [];
351
352 // the columns we are interested in
353 foreach (self::$_properties as $property) {
354 if (property_exists($result, $property)) {
355 $row[$property] = $result->$property;
356 }
357 }
358
359 //carry campaign on selectors.
360 $row['campaign'] = CRM_Utils_Array::value($result->member_campaign_id, $allCampaigns);
361 $row['campaign_id'] = $result->member_campaign_id;
362
363 if (!empty($row['member_is_test'])) {
364 $row['membership_type'] = CRM_Core_TestEntity::appendTestText($row['membership_type']);
365 }
366
367 $row['checkbox'] = CRM_Core_Form::CB_PREFIX . $result->membership_id;
368
369 if (!isset($result->owner_membership_id)) {
370 // unset renew and followup link for deceased membership
371 $currentMask = $mask;
372 if ($result->membership_status == 'Deceased') {
373 $currentMask = $currentMask & ~CRM_Core_Action::RENEW & ~CRM_Core_Action::FOLLOWUP;
374 }
375
376 $isCancelSupported = CRM_Member_BAO_Membership::isCancelSubscriptionSupported($row['membership_id']);
377 $links = self::links('all',
378 $this->_isPaymentProcessor,
379 $this->_accessContribution,
380 $this->_key,
381 $this->_context,
382 $isCancelSupported
383 );
384
385 // check permissions
386 $finTypeId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $result->membership_type_id, 'financial_type_id');
387 $finType = CRM_Contribute_PseudoConstant::financialType($finTypeId);
388 if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()
389 && !CRM_Core_Permission::check('edit contributions of type ' . $finType)
390 ) {
391 unset($links[CRM_Core_Action::UPDATE]);
392 }
393 if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() &&
394 !CRM_Core_Permission::check('delete contributions of type ' . $finType)
395 ) {
396 unset($links[CRM_Core_Action::DELETE]);
397 }
398 $row['action'] = CRM_Core_Action::formLink($links,
399 $currentMask,
400 [
401 'id' => $result->membership_id,
402 'cid' => $result->contact_id,
403 'cxt' => $this->_context,
404 ],
405 ts('Renew') . '...',
406 FALSE,
407 'membership.selector.row',
408 'Membership',
409 $result->membership_id
410 );
411 }
412 else {
413 $links = self::links('view');
414 $row['action'] = CRM_Core_Action::formLink($links, $mask,
415 [
416 'id' => $result->membership_id,
417 'cid' => $result->contact_id,
418 'cxt' => $this->_context,
419 ],
420 ts('more'),
421 FALSE,
422 'membership.selector.row',
423 'Membership',
424 $result->membership_id
425 );
426 }
427
428 // Display Auto-renew status on page (0=disabled, 1=enabled, 2=enabled, but error
429 if (!empty($result->membership_recur_id)) {
430 if (CRM_Member_BAO_Membership::isSubscriptionCancelled($row['membership_id'])) {
431 $row['auto_renew'] = 2;
432 }
433 else {
434 $row['auto_renew'] = 1;
435 }
436 }
437 else {
438 $row['auto_renew'] = 0;
439 }
440
441 $row['contact_type'] = CRM_Contact_BAO_Contact_Utils::getImage($result->contact_sub_type ? $result->contact_sub_type : $result->contact_type, FALSE, $result->contact_id
442 );
443
444 $rows[] = $row;
445 }
446
447 return $rows;
448 }
449
450 /**
451 * @inheritDoc
452 */
453 public function getQILL() {
454 return $this->_query->qill();
455 }
456
457 /**
458 * Returns the column headers as an array of tuples.
459 *
460 * (name, sortName (key to the sort array))
461 *
462 * @param string $action
463 * The action being performed.
464 * @param string $output
465 * What should the result set include (web/email/csv).
466 *
467 * @return array
468 * the column headers that need to be displayed
469 */
470 public function &getColumnHeaders($action = NULL, $output = NULL) {
471 if (!isset(self::$_columnHeaders)) {
472 self::$_columnHeaders = [
473 [
474 'name' => ts('Type'),
475 'sort' => 'membership_type',
476 'direction' => CRM_Utils_Sort::DONTCARE,
477 ],
478 [
479 'name' => ts('Member Since'),
480 'sort' => 'membership_join_date',
481 'direction' => CRM_Utils_Sort::DESCENDING,
482 ],
483 [
484 'name' => ts('Start Date'),
485 'sort' => 'membership_start_date',
486 'direction' => CRM_Utils_Sort::DONTCARE,
487 ],
488 [
489 'name' => ts('End Date'),
490 'sort' => 'membership_end_date',
491 'direction' => CRM_Utils_Sort::DONTCARE,
492 ],
493 [
494 'name' => ts('Source'),
495 'sort' => 'membership_source',
496 'direction' => CRM_Utils_Sort::DONTCARE,
497 ],
498 [
499 'name' => ts('Status'),
500 'sort' => 'membership_status',
501 'direction' => CRM_Utils_Sort::DONTCARE,
502 ],
503 [
504 'name' => ts('Auto-renew?'),
505 ],
506 ['desc' => ts('Actions')],
507 ];
508
509 if (!$this->_single) {
510 $pre = [
511 ['desc' => ts('Contact Type')],
512 [
513 'name' => ts('Name'),
514 'sort' => 'sort_name',
515 'direction' => CRM_Utils_Sort::DONTCARE,
516 ],
517 ];
518 self::$_columnHeaders = array_merge($pre, self::$_columnHeaders);
519 }
520 }
521 return self::$_columnHeaders;
522 }
523
524 /**
525 * Alphabet query.
526 *
527 * @return mixed
528 */
529 public function alphabetQuery() {
530 return $this->_query->alphabetQuery();
531 }
532
533 /**
534 * Get query.
535 *
536 * @return string
537 */
538 public function &getQuery() {
539 return $this->_query;
540 }
541
542 /**
543 * Name of export file.
544 *
545 * @param string $output
546 * Type of output.
547 *
548 * @return string
549 * name of the file
550 */
551 public function getExportFileName($output = 'csv') {
552 return ts('CiviCRM Member Search');
553 }
554
555 }