Merge pull request #17033 from seamuslee001/5.25
[civicrm-core.git] / CRM / Core / Selector / Controller.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 * 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
22 * @copyright CiviCRM LLC https://civicrm.org/licensing
23 * $Id$
24 *
25 */
26 class CRM_Core_Selector_Controller {
27
28 /**
29 * Constants to determine if we should store
30 * the output in the session or template
31 * @var int
32 */
33 // move the values from the session to the template
34 const SESSION = 1, TEMPLATE = 2,
35 TRANSFER = 4, EXPORT = 8, SCREEN = 16, PDF = 32;
36
37 /**
38 * A CRM Object that implements CRM_Core_Selector_API.
39 * @var object
40 */
41 protected $_object;
42
43 /**
44 * @var CRM_Utils_Sort
45 */
46 protected $_sort;
47
48 /**
49 * The current column to sort on
50 * @var int
51 */
52 protected $_sortID;
53
54 /**
55 * The sortOrder array
56 * @var array
57 */
58 protected $_sortOrder;
59
60 /**
61 * @var CRM_Utils_Pager
62 */
63 protected $_pager;
64
65 /**
66 * The pageID
67 * @var int
68 */
69 protected $_pageID;
70
71 /**
72 * Offset
73 * @var int
74 */
75 protected $_pagerOffset;
76
77 /**
78 * Number of rows to return
79 * @var int
80 */
81 protected $_pagerRowCount;
82
83 /**
84 * Total number of rows
85 * @var int
86 */
87 protected $_total;
88
89 /**
90 * The objectAction for the WebObject
91 * @var int
92 */
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 *
107 * @var bool
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 *
115 * @var bool
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 /**
141 * Cache the smarty template for efficiency reasons
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
151 */
152 public static $_properties = ['columnHeaders', 'rows', 'rowsEmpty'];
153
154 /**
155 * Should we compute actions dynamically (since they are quite verbose)
156 *
157 * @var bool
158 */
159 protected $_dynamicAction = FALSE;
160
161 /**
162 * Class constructor.
163 *
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.
172 * @param CRM_Core_Page|CRM_Core_Form $store place in session to store some values
173 * @param int $output
174 * What do we so with the output, session/template//both.
175 *
176 * @param null $prefix
177 * @param null $case
178 *
179 * @return \CRM_Core_Selector_Controller
180 */
181 public function __construct($object, $pageID, $sortID, $action, $store = NULL, $output = self::TEMPLATE, $prefix = NULL, $case = NULL) {
182
183 $this->_object = $object;
184 $this->_pageID = $pageID ? $pageID : 1;
185 $this->_sortID = $sortID ? $sortID : NULL;
186 $this->_action = $action;
187 $this->_store = $store;
188 $this->_output = $output;
189 $this->_prefix = $prefix;
190 $this->_case = $case;
191
192 // fix sortID
193 if ($this->_sortID && strpos($this->_sortID, '_') === FALSE) {
194 $this->_sortID .= '_u';
195 }
196
197 $params = [
198 'pageID' => $this->_pageID,
199 ];
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 /*
210 * if we are in transfer mode, do not goto database, use the
211 * session values instead
212 */
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 /*
225 * Set the default values of RowsPerPage
226 */
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 /**
241 * Have the GET vars changed, i.e. pageId or sortId that forces us to recompute the search values
242 *
243 * @param int $reset
244 * Are we being reset.
245 *
246 * @return bool
247 * if the GET params are different from the session params
248 */
249 public function hasChanged($reset) {
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
286 */
287 public function run() {
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.
311 CRM_Core_Error::deprecatedFunctionWarning('This code is believed to be unreachable & to be later removed. Please log how you reached it in gitlab');
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);
328 $reorderedHeaders = [];
329 $noWeightHeaders = [];
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 {
338 $noWeightHeaders[$key] = $columnHeader;
339 }
340 }
341 ksort($reorderedHeaders);
342 // Merge headers not containing weight to ordered headers
343 $finalColumnHeaders = array_merge($reorderedHeaders, $noWeightHeaders);
344
345 $qill = $this->getQill();
346 $summary = $this->getSummary();
347 // if we need to store in session, lets update session
348 if ($this->_output & self::SESSION) {
349 $this->_store->set("{$this->_prefix}columnHeaders", $finalColumnHeaders);
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);
355 $this->_store->set("{$this->_prefix}rowsEmpty", !$rows);
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
363 self::$_template->assign_by_ref("{$this->_prefix}columnHeaders", $finalColumnHeaders);
364 self::$_template->assign_by_ref("{$this->_prefix}rows", $rows);
365 self::$_template->assign("{$this->_prefix}rowsEmpty", !$rows);
366 self::$_template->assign("{$this->_prefix}qill", $qill);
367 self::$_template->assign("{$this->_prefix}summary", $summary);
368 }
369
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 /**
390 * Retrieve rows.
391 *
392 * @param CRM_Core_Form $form
393 *
394 * @return array
395 * Array of rows
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 /**
410 * Default function for qill, if needed to be implemented, we
411 * expect the subclass to do it
412 *
413 * @return string
414 * the status message
415 */
416 public function getQill() {
417 return $this->_object->getQill();
418 }
419
420 /**
421 * @return mixed
422 */
423 public function getSummary() {
424 return $this->_object->getSummary();
425 }
426
427 /**
428 * Getter for pager.
429 *
430 * @return CRM_Utils_Pager
431 */
432 public function getPager() {
433 return $this->_pager;
434 }
435
436 /**
437 * Getter for sort.
438 *
439 * @return CRM_Utils_Sort
440 */
441 public function getSort() {
442 return $this->_sort;
443 }
444
445 /**
446 * Move the variables from the session to the template.
447 *
448 * @return void
449 */
450 public function moveFromSessionToTemplate() {
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
476 self::$_template->assign('tplFile', $this->_object->getHookedTemplateFileName());
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 /**
488 * Setter for embedded.
489 *
490 * @param bool $embedded
491 *
492 * @return void
493 */
494 public function setEmbedded($embedded) {
495 $this->_embedded = $embedded;
496 }
497
498 /**
499 * Getter for embedded.
500 *
501 * @return bool
502 * return the embedded value
503 */
504 public function getEmbedded() {
505 return $this->_embedded;
506 }
507
508 /**
509 * Setter for print.
510 *
511 * @param bool $print
512 *
513 * @return void
514 */
515 public function setPrint($print) {
516 $this->_print = $print;
517 }
518
519 /**
520 * Getter for print.
521 *
522 * @return bool
523 * return the print value
524 */
525 public function getPrint() {
526 return $this->_print;
527 }
528
529 /**
530 * @param $value
531 */
532 public function setDynamicAction($value) {
533 $this->_dynamicAction = $value;
534 }
535
536 /**
537 * @return bool
538 */
539 public function getDynamicAction() {
540 return $this->_dynamicAction;
541 }
542
543 }