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