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