Merge pull request #17033 from seamuslee001/5.25
[civicrm-core.git] / CRM / Core / Selector / Controller.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * This class is a generic class to be used when we want to display
15 * a list of rows along with a set of associated actions
16 *
17 * Centralizing this code enables us to write a generic lister and enables
18 * us to automate the export process. To use this class, the object has to
19 * implement the Selector/Api.interface.php class
20 *
21 * @package CRM
ca5cec67 22 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
23 * $Id$
24 *
25 */
26class CRM_Core_Selector_Controller {
27
28 /**
100fef9d 29 * Constants to determine if we should store
6a488035
TO
30 * the output in the session or template
31 * @var int
32 */
33 // move the values from the session to the template
7da04cde 34 const SESSION = 1, TEMPLATE = 2,
353ffa53 35 TRANSFER = 4, EXPORT = 8, SCREEN = 16, PDF = 32;
6a488035
TO
36
37 /**
fe482240 38 * A CRM Object that implements CRM_Core_Selector_API.
6a488035
TO
39 * @var object
40 */
41 protected $_object;
42
c490a46a
CW
43 /**
44 * @var CRM_Utils_Sort
45 */
6a488035
TO
46 protected $_sort;
47
c490a46a 48 /**
100fef9d 49 * The current column to sort on
c490a46a
CW
50 * @var int
51 */
6a488035
TO
52 protected $_sortID;
53
c490a46a 54 /**
100fef9d 55 * The sortOrder array
c490a46a
CW
56 * @var array
57 */
6a488035
TO
58 protected $_sortOrder;
59
c490a46a
CW
60 /**
61 * @var CRM_Utils_Pager
62 */
6a488035
TO
63 protected $_pager;
64
c490a46a 65 /**
100fef9d 66 * The pageID
c490a46a
CW
67 * @var int
68 */
6a488035
TO
69 protected $_pageID;
70
c490a46a 71 /**
100fef9d 72 * Offset
c490a46a
CW
73 * @var int
74 */
6a488035
TO
75 protected $_pagerOffset;
76
77 /**
100fef9d 78 * Number of rows to return
6a488035
TO
79 * @var int
80 */
81 protected $_pagerRowCount;
82
83 /**
100fef9d 84 * Total number of rows
6a488035
TO
85 * @var int
86 */
87 protected $_total;
88
c490a46a 89 /**
100fef9d 90 * The objectAction for the WebObject
518fa0ee 91 * @var int
c490a46a 92 */
6a488035
TO
93 protected $_action;
94
95 /**
96 * This caches the content for the display system
97 *
98 * @var string
99 */
100 protected $_content;
101
102 /**
103 * Is this object being embedded in another object. If
104 * so the display routine needs to not do any work. (The
105 * parent object takes care of the display)
106 *
317fceb4 107 * @var bool
6a488035
TO
108 */
109 protected $_embedded = FALSE;
110
111 /**
112 * Are we in print mode? if so we need to modify the display
113 * functionality to do a minimal display :)
114 *
317fceb4 115 * @var bool
6a488035
TO
116 */
117 protected $_print = FALSE;
118
119 /**
120 * The storage object (typically a form or a page)
121 *
122 * @var Object
123 */
124 protected $_store;
125
126 /**
127 * Output target, session, template or both?
128 *
129 * @var int
130 */
131 protected $_output;
132
133 /**
134 * Prefif for the selector variables
135 *
136 * @var int
137 */
138 protected $_prefix;
139
140 /**
100fef9d 141 * Cache the smarty template for efficiency reasons
6a488035
TO
142 *
143 * @var CRM_Core_Smarty
144 */
145 public static $_template;
146
147 /**
148 * Array of properties that the controller dumps into the output object
149 *
150 * @var array
6a488035 151 */
be2fb01f 152 public static $_properties = ['columnHeaders', 'rows', 'rowsEmpty'];
6a488035
TO
153
154 /**
155 * Should we compute actions dynamically (since they are quite verbose)
156 *
317fceb4 157 * @var bool
6a488035
TO
158 */
159 protected $_dynamicAction = FALSE;
160
161 /**
fe482240 162 * Class constructor.
6a488035 163 *
6a0b768e
TO
164 * @param CRM_Core_Selector_API $object
165 * An object that implements the selector API.
166 * @param int $pageID
167 * Default pageID.
168 * @param int $sortID
169 * Default sortID.
170 * @param int $action
171 * The actions to potentially support.
da6b46f4 172 * @param CRM_Core_Page|CRM_Core_Form $store place in session to store some values
6a0b768e
TO
173 * @param int $output
174 * What do we so with the output, session/template//both.
6a488035 175 *
da6b46f4
EM
176 * @param null $prefix
177 * @param null $case
178 *
179 * @return \CRM_Core_Selector_Controller
da6b46f4 180 */
00be9182 181 public function __construct($object, $pageID, $sortID, $action, $store = NULL, $output = self::TEMPLATE, $prefix = NULL, $case = NULL) {
6a488035
TO
182
183 $this->_object = $object;
184 $this->_pageID = $pageID ? $pageID : 1;
185 $this->_sortID = $sortID ? $sortID : NULL;
186 $this->_action = $action;
353ffa53 187 $this->_store = $store;
6a488035
TO
188 $this->_output = $output;
189 $this->_prefix = $prefix;
353ffa53 190 $this->_case = $case;
6a488035
TO
191
192 // fix sortID
193 if ($this->_sortID && strpos($this->_sortID, '_') === FALSE) {
194 $this->_sortID .= '_u';
195 }
196
be2fb01f 197 $params = [
6a488035 198 'pageID' => $this->_pageID,
be2fb01f 199 ];
6a488035
TO
200
201 // let the constructor initialize this, should happen only once
202 if (!isset(self::$_template)) {
203 self::$_template = CRM_Core_Smarty::singleton();
204 }
205
206 $this->_sortOrder = &$this->_object->getSortOrder($action);
207 $this->_sort = new CRM_Utils_Sort($this->_sortOrder, $this->_sortID);
208
209 /*
f4bff68a
CW
210 * if we are in transfer mode, do not goto database, use the
211 * session values instead
212 */
6a488035
TO
213
214 if ($output == self::TRANSFER) {
215 $params['total'] = $this->_store->get($this->_prefix . 'rowCount');
216 }
217 else {
218 $params['total'] = $this->_object->getTotalCount($action, $this->_case);
219 }
220
221 $this->_total = $params['total'];
222 $this->_object->getPagerParams($action, $params);
223
224 /*
f4bff68a
CW
225 * Set the default values of RowsPerPage
226 */
6a488035
TO
227
228 $storeRowCount = $store->get($this->_prefix . CRM_Utils_Pager::PAGE_ROWCOUNT);
229 if ($storeRowCount) {
230 $params['rowCount'] = $storeRowCount;
231 }
232 elseif (!isset($params['rowCount'])) {
233 $params['rowCount'] = CRM_Utils_Pager::ROWCOUNT;
234 }
235
236 $this->_pager = new CRM_Utils_Pager($params);
237 list($this->_pagerOffset, $this->_pagerRowCount) = $this->_pager->getOffsetAndRowCount();
238 }
239
240 /**
100fef9d 241 * Have the GET vars changed, i.e. pageId or sortId that forces us to recompute the search values
6a488035 242 *
6a0b768e
TO
243 * @param int $reset
244 * Are we being reset.
6a488035 245 *
317fceb4 246 * @return bool
a6c01b45 247 * if the GET params are different from the session params
6a488035 248 */
00be9182 249 public function hasChanged($reset) {
6a488035
TO
250
251 /**
252 * if we are in reset state, i.e the store is cleaned out, we return false
253 * we also return if we dont have a record of the sort id or page id
254 */
255 if ($reset ||
256 $this->_store->get($this->_prefix . CRM_Utils_Pager::PAGE_ID) == NULL ||
257 $this->_store->get($this->_prefix . CRM_Utils_Sort::SORT_ID) == NULL
258 ) {
259 return FALSE;
260 }
261
262 if ($this->_store->get($this->_prefix . CRM_Utils_Pager::PAGE_ID) != $this->_pager->getCurrentPageID() ||
263 $this->_store->get($this->_prefix . CRM_Utils_Sort::SORT_ID) != $this->_sort->getCurrentSortID() ||
264 $this->_store->get($this->_prefix . CRM_Utils_Sort::SORT_DIRECTION) != $this->_sort->getCurrentSortDirection()
265 ) {
266 return TRUE;
267 }
268 return FALSE;
269 }
270
271 /**
272 * Heart of the Controller. This is where all the action takes place
273 *
274 * - The rows are fetched and stored depending on the type of output needed
275 *
276 * - For export/printing all rows are selected.
277 *
278 * - for displaying on screen paging parameters are used to display the
279 * required rows.
280 *
281 * - also depending on output type of session or template rows are appropriately stored in session
282 * or template variables are updated.
283 *
284 *
285 * @return void
6a488035 286 */
00be9182 287 public function run() {
6a488035
TO
288
289 // get the column headers
290 $columnHeaders = &$this->_object->getColumnHeaders($this->_action, $this->_output);
291
292 $contextArray = explode('_', get_class($this->_object));
293
294 $contextName = strtolower($contextArray[1]);
295
296 // fix contribute and member
297 if ($contextName == 'contribute') {
298 $contextName = 'contribution';
299 }
300 elseif ($contextName == 'member') {
301 $contextName = 'membership';
302 }
303
304 // we need to get the rows if we are exporting or printing them
305 if ($this->_output == self::EXPORT || $this->_output == self::SCREEN) {
306 // get rows (without paging criteria)
307 $rows = self::getRows($this);
308 CRM_Utils_Hook::searchColumns($contextName, $columnHeaders, $rows, $this);
309 if ($this->_output == self::EXPORT) {
310 // export the rows.
77b68348 311 CRM_Core_Error::deprecatedFunctionWarning('This code is believed to be unreachable & to be later removed. Please log how you reached it in gitlab');
6a488035
TO
312 CRM_Core_Report_Excel::writeCSVFile($this->_object->getExportFileName(),
313 $columnHeaders,
314 $rows
315 );
316 CRM_Utils_System::civiExit();
317 }
318 else {
319 // assign to template and display them.
320 self::$_template->assign_by_ref('rows', $rows);
321 self::$_template->assign_by_ref('columnHeaders', $columnHeaders);
322 }
323 }
324 else {
325 // output requires paging/sorting capability
326 $rows = self::getRows($this);
327 CRM_Utils_Hook::searchColumns($contextName, $columnHeaders, $rows, $this);
be2fb01f
CW
328 $reorderedHeaders = [];
329 $noWeightHeaders = [];
4fb5fcf3 330 foreach ($columnHeaders as $key => $columnHeader) {
331 // So far only contribution selector sets weight, so just use key if not.
332 // Extension writers will need to fix other getColumnHeaders (or add a wrapper)
333 // to extend.
334 if (isset($columnHeader['weight'])) {
335 $reorderedHeaders[$columnHeader['weight']] = $columnHeader;
336 }
337 else {
db167477 338 $noWeightHeaders[$key] = $columnHeader;
4fb5fcf3 339 }
340 }
0824019e 341 ksort($reorderedHeaders);
db167477 342 // Merge headers not containing weight to ordered headers
343 $finalColumnHeaders = array_merge($reorderedHeaders, $noWeightHeaders);
344
353ffa53
TO
345 $qill = $this->getQill();
346 $summary = $this->getSummary();
6a488035
TO
347 // if we need to store in session, lets update session
348 if ($this->_output & self::SESSION) {
db167477 349 $this->_store->set("{$this->_prefix}columnHeaders", $finalColumnHeaders);
6a488035
TO
350 if ($this->_dynamicAction) {
351 $this->_object->removeActions($rows);
352 }
353 $this->_store->set("{$this->_prefix}rows", $rows);
354 $this->_store->set("{$this->_prefix}rowCount", $this->_total);
91768280 355 $this->_store->set("{$this->_prefix}rowsEmpty", !$rows);
6a488035
TO
356 $this->_store->set("{$this->_prefix}qill", $qill);
357 $this->_store->set("{$this->_prefix}summary", $summary);
358 }
359 else {
360 self::$_template->assign_by_ref("{$this->_prefix}pager", $this->_pager);
361 self::$_template->assign_by_ref("{$this->_prefix}sort", $this->_sort);
362
db167477 363 self::$_template->assign_by_ref("{$this->_prefix}columnHeaders", $finalColumnHeaders);
6a488035 364 self::$_template->assign_by_ref("{$this->_prefix}rows", $rows);
91768280 365 self::$_template->assign("{$this->_prefix}rowsEmpty", !$rows);
6a488035
TO
366 self::$_template->assign("{$this->_prefix}qill", $qill);
367 self::$_template->assign("{$this->_prefix}summary", $summary);
368 }
369
6a488035
TO
370 // always store the current pageID and sortID
371 $this->_store->set($this->_prefix . CRM_Utils_Pager::PAGE_ID,
372 $this->_pager->getCurrentPageID()
373 );
374 $this->_store->set($this->_prefix . CRM_Utils_Sort::SORT_ID,
375 $this->_sort->getCurrentSortID()
376 );
377 $this->_store->set($this->_prefix . CRM_Utils_Sort::SORT_DIRECTION,
378 $this->_sort->getCurrentSortDirection()
379 );
380 $this->_store->set($this->_prefix . CRM_Utils_Sort::SORT_ORDER,
381 $this->_sort->orderBy()
382 );
383 $this->_store->set($this->_prefix . CRM_Utils_Pager::PAGE_ROWCOUNT,
384 $this->_pager->_perPage
385 );
386 }
387 }
388
389 /**
100fef9d 390 * Retrieve rows.
6a488035 391 *
c490a46a 392 * @param CRM_Core_Form $form
2a6da8d7 393 *
a6c01b45 394 * @return array
16b10e64 395 * Array of rows
6a488035
TO
396 */
397 public function getRows($form) {
398 if ($form->_output == self::EXPORT || $form->_output == self::SCREEN) {
399 //get rows (without paging criteria)
400 return $form->_object->getRows($form->_action, 0, 0, $form->_sort, $form->_output);
401 }
402 else {
403 return $form->_object->getRows($form->_action, $form->_pagerOffset, $form->_pagerRowCount,
404 $form->_sort, $form->_output, $form->_case
405 );
406 }
407 }
408
409 /**
100fef9d 410 * Default function for qill, if needed to be implemented, we
6a488035
TO
411 * expect the subclass to do it
412 *
a6c01b45
CW
413 * @return string
414 * the status message
6a488035
TO
415 */
416 public function getQill() {
417 return $this->_object->getQill();
418 }
419
a0ee3941
EM
420 /**
421 * @return mixed
422 */
6a488035
TO
423 public function getSummary() {
424 return $this->_object->getSummary();
425 }
426
427 /**
fe482240 428 * Getter for pager.
6a488035 429 *
c490a46a 430 * @return CRM_Utils_Pager
6a488035 431 */
00be9182 432 public function getPager() {
6a488035
TO
433 return $this->_pager;
434 }
435
436 /**
fe482240 437 * Getter for sort.
6a488035 438 *
c490a46a 439 * @return CRM_Utils_Sort
6a488035 440 */
00be9182 441 public function getSort() {
6a488035
TO
442 return $this->_sort;
443 }
444
445 /**
fe482240 446 * Move the variables from the session to the template.
6a488035
TO
447 *
448 * @return void
6a488035 449 */
00be9182 450 public function moveFromSessionToTemplate() {
6a488035
TO
451 self::$_template->assign_by_ref("{$this->_prefix}pager", $this->_pager);
452
453 $rows = $this->_store->get("{$this->_prefix}rows");
454
455 if ($rows) {
456 if ($this->_dynamicAction) {
457 $this->_object->addActions($rows);
458 }
459
460 self::$_template->assign("{$this->_prefix}aToZ",
461 $this->_store->get("{$this->_prefix}AToZBar")
462 );
463 }
464
465 self::$_template->assign_by_ref("{$this->_prefix}sort", $this->_sort);
466 self::$_template->assign("{$this->_prefix}columnHeaders", $this->_store->get("{$this->_prefix}columnHeaders"));
467 self::$_template->assign("{$this->_prefix}rows", $rows);
468 self::$_template->assign("{$this->_prefix}rowsEmpty", $this->_store->get("{$this->_prefix}rowsEmpty"));
469 self::$_template->assign("{$this->_prefix}qill", $this->_store->get("{$this->_prefix}qill"));
470 self::$_template->assign("{$this->_prefix}summary", $this->_store->get("{$this->_prefix}summary"));
471
472 if ($this->_embedded) {
473 return;
474 }
475
8aac22c8 476 self::$_template->assign('tplFile', $this->_object->getHookedTemplateFileName());
6a488035
TO
477 if ($this->_print) {
478 $content = self::$_template->fetch('CRM/common/print.tpl');
479 }
480 else {
481 $config = CRM_Core_Config::singleton();
482 $content = self::$_template->fetch('CRM/common/' . strtolower($config->userFramework) . '.tpl');
483 }
484 echo CRM_Utils_System::theme($content, $this->_print);
485 }
486
487 /**
fe482240 488 * Setter for embedded.
6a488035 489 *
6a0b768e 490 * @param bool $embedded
6a488035
TO
491 *
492 * @return void
6a488035 493 */
00be9182 494 public function setEmbedded($embedded) {
6a488035
TO
495 $this->_embedded = $embedded;
496 }
497
498 /**
fe482240 499 * Getter for embedded.
6a488035 500 *
317fceb4 501 * @return bool
a6c01b45 502 * return the embedded value
6a488035 503 */
00be9182 504 public function getEmbedded() {
6a488035
TO
505 return $this->_embedded;
506 }
507
508 /**
fe482240 509 * Setter for print.
6a488035 510 *
6a0b768e 511 * @param bool $print
6a488035
TO
512 *
513 * @return void
6a488035 514 */
00be9182 515 public function setPrint($print) {
6a488035
TO
516 $this->_print = $print;
517 }
518
519 /**
fe482240 520 * Getter for print.
6a488035 521 *
317fceb4 522 * @return bool
a6c01b45 523 * return the print value
6a488035 524 */
00be9182 525 public function getPrint() {
6a488035
TO
526 return $this->_print;
527 }
528
a0ee3941
EM
529 /**
530 * @param $value
531 */
00be9182 532 public function setDynamicAction($value) {
6a488035
TO
533 $this->_dynamicAction = $value;
534 }
535
a0ee3941
EM
536 /**
537 * @return bool
538 */
00be9182 539 public function getDynamicAction() {
6a488035
TO
540 return $this->_dynamicAction;
541 }
96025800 542
6a488035 543}