Open Journal Systems  2.4.3
 All Classes Namespaces Functions Variables Groups Pages
GridHandler.inc.php
1 <?php
2 
16 // Import the base Handler.
17 import('lib.pkp.classes.handler.PKPHandler');
18 
19 // Import action class.
20 import('lib.pkp.classes.linkAction.LinkAction');
21 import('lib.pkp.classes.linkAction.LegacyLinkAction');
22 
23 // Import grid classes.
24 import('lib.pkp.classes.controllers.grid.GridColumn');
25 import('lib.pkp.classes.controllers.grid.GridRow');
26 
27 // Import JSON class for use with all AJAX requests.
28 import('lib.pkp.classes.core.JSONMessage');
29 
30 // Grid specific action positions.
31 define('GRID_ACTION_POSITION_DEFAULT', 'default');
32 define('GRID_ACTION_POSITION_ABOVE', 'above');
33 define('GRID_ACTION_POSITION_LASTCOL', 'lastcol');
34 define('GRID_ACTION_POSITION_BELOW', 'below');
35 
36 class GridHandler extends PKPHandler {
37 
39  var $_title = '';
40 
42  var $_emptyRowText = 'grid.noItems';
43 
45  var $_dataProvider;
46 
52  var $_actions = array(GRID_ACTION_POSITION_DEFAULT => array());
53 
55  var $_columns = array();
56 
58  var $_data;
59 
61  var $_template;
62 
64  var $_urls;
65 
67  var $_features;
68 
69 
77  function GridHandler($dataProvider = null) {
78  $this->_dataProvider =& $dataProvider;
79  parent::PKPHandler();
80  }
81 
82 
83  //
84  // Getters and Setters
85  //
90  function &getDataProvider() {
91  return $this->_dataProvider;
92  }
93 
111  function getRequestArgs() {
112  $dataProvider =& $this->getDataProvider();
113  $requestArgs = array();
114  if (is_a($dataProvider, 'GridDataProvider')) {
115  $requestArgs = $dataProvider->getRequestArgs();
116  }
117  return $requestArgs;
118  }
119 
127  function getRequestArg($key) {
128  $requestArgs = $this->getRequestArgs();
129  assert(isset($requestArgs[$key]));
130  return $requestArgs[$key];
131  }
132 
137  function getTitle() {
138  return $this->_title;
139  }
140 
145  function setTitle($title) {
146  $this->_title = $title;
147  }
148 
152  function getEmptyRowText() {
153  return $this->_emptyRowText;
154  }
155 
159  function setEmptyRowText($emptyRowText) {
160  $this->_emptyRowText = $emptyRowText;
161  }
162 
167  function getInstructions() {
168  return $this->_instructions;
169  }
170 
175  function setInstructions($instructions) {
176  $this->_instructions = $instructions;
177  }
178 
183  function getFootNote() {
184  return $this->_footNote;
185  }
186 
191  function setFootNote($footNote) {
192  $this->_footNote = $footNote;
193  }
194 
200  function getActions($position = GRID_ACTION_POSITION_ABOVE) {
201  if(!isset($this->_actions[$position])) return array();
202  return $this->_actions[$position];
203  }
204 
210  function addAction($action, $position = GRID_ACTION_POSITION_ABOVE) {
211  if (!isset($this->_actions[$position])) $this->_actions[$position] = array();
212  $this->_actions[$position][$action->getId()] = $action;
213  }
214 
219  function &getColumns() {
220  return $this->_columns;
221  }
222 
228  function &getColumn($columnId) {
229  assert(isset($this->_columns[$columnId]));
230  return $this->_columns[$columnId];
231  }
232 
238  function &getColumnsByFlag($flag) {
239  $columns = array();
240  foreach ($this->getColumns() as $column) {
241  if ($column->hasFlag($flag)) {
242  $columns[$column->getId()] = $column;
243  }
244  }
245 
246  return $columns;
247  }
248 
255  function getColumnsCount($flag) {
256  $count = 0;
257  foreach ($this->getColumns() as $column) {
258  if (!$column->hasFlag($flag)) {
259  $count++;
260  }
261  }
262 
263  return $count;
264  }
265 
271  function hasColumn($columnId) {
272  return isset($this->_columns[$columnId]);
273  }
274 
279  function addColumn(&$column) {
280  assert(is_a($column, 'GridColumn'));
281  $this->_columns[$column->getId()] =& $column;
282  }
283 
289  function &getGridDataElements($request) {
290  // Try to load data if it has not yet been loaded.
291  if (is_null($this->_data)) {
292  $filter = $this->getFilterSelectionData($request);
293  $data = $this->loadData($request, $filter);
294 
295  if (is_null($data)) {
296  // Initialize data to an empty array.
297  $data = array();
298  }
299 
300  $this->setGridDataElements($data);
301  }
302 
303  return $this->_data;
304  }
305 
310  function hasGridDataElements($request) {
311  $data =& $this->getGridDataElements($request);
312  assert (is_array($data));
313  return (boolean) count($data);
314  }
315 
320  function setGridDataElements(&$data) {
321  // FIXME: We go to arrays for all types of iterators because
322  // iterators cannot be re-used, see #6498.
323  if (is_array($data)) {
324  $this->_data =& $data;
325  } elseif(is_a($data, 'DAOResultFactory')) {
326  $this->_data = $data->toAssociativeArray();
327  } elseif(is_a($data, 'ItemIterator')) {
328  $this->_data = $data->toArray();
329  } else {
330  assert(false);
331  }
332  }
333 
338  function getTemplate() {
339  if (is_null($this->_template)) {
340  $this->setTemplate('controllers/grid/grid.tpl');
341  }
342 
343  return $this->_template;
344  }
345 
350  function setTemplate($template) {
351  $this->_template = $template;
352  }
353 
359  function getUrls() {
360  return $this->_urls;
361  }
362 
369  function setUrls(&$request, $extraUrls = array()) {
370  $router =& $request->getRouter();
371  $urls = array(
372  'fetchGridUrl' => $router->url($request, null, null, 'fetchGrid', null, $this->getRequestArgs()),
373  'fetchRowUrl' => $router->url($request, null, null, 'fetchRow', null, $this->getRequestArgs())
374  );
375  $this->_urls = array_merge($urls, $extraUrls);
376  }
377 
385  function getIsSubcomponent() {
386  return false;
387  }
388 
393  function getFeatures() {
394  return $this->_features;
395  }
396 
402  return array();
403  }
404 
405  //
406  // Overridden methods from PKPHandler
407  //
411  function authorize(&$request, &$args, $roleAssignments) {
412  $dataProvider =& $this->getDataProvider();
413  $hasDataProvider = is_a($dataProvider, 'GridDataProvider');
414  if ($hasDataProvider) {
415  $this->addPolicy($dataProvider->getAuthorizationPolicy($request, $args, $roleAssignments));
416  }
417 
418  $success = parent::authorize($request, $args, $roleAssignments);
419 
420  if ($hasDataProvider && $success === true) {
421  $dataProvider->setAuthorizedContext($this->getAuthorizedContext());
422  }
423 
424  return $success;
425  }
426 
430  function initialize(&$request, $args = null) {
431  parent::initialize($request, $args);
432 
433  // Load grid-specific translations
434  AppLocale::requireComponents(LOCALE_COMPONENT_PKP_GRID, LOCALE_COMPONENT_APPLICATION_COMMON);
435 
436  // Give a chance to grid add features before calling hooks.
437  // Because we must control when features are added to a grid,
438  // this is the only place that should use the _addFeature() method.
439  $this->_addFeatures($this->initFeatures($request, $args));
440  $this->callFeaturesHook('gridInitialize', array('grid' => &$this));
441  }
442 
443 
444  //
445  // Public handler methods
446  //
454  function fetchGrid($args, &$request) {
455  $this->setUrls($request);
456 
457  // Prepare the template to render the grid.
458  $templateMgr =& TemplateManager::getManager();
459  $templateMgr->assign_by_ref('grid', $this);
460 
461  // Add rendered filter
462  $renderedFilter = $this->renderFilter($request);
463  $templateMgr->assign('gridFilterForm', $renderedFilter);
464 
465  // Add columns.
466  $this->setFirstDataColumn();
467  $columns =& $this->getColumns();
468  $templateMgr->assign_by_ref('columns', $columns);
469 
470  // Do specific actions to fetch this grid.
471  $this->doSpecificFetchGridActions($args, $request, $templateMgr);
472 
473  // Assign additional params for the fetchRow and fetchGrid URLs to use.
474  $templateMgr->assign('gridRequestArgs', $this->getRequestArgs());
475 
476  $this->callFeaturesHook('fetchGrid', array('grid' => &$this, 'request' => &$request));
477 
478  // Assign features.
479  $templateMgr->assign_by_ref('features', $this->getFeatures());
480 
481  // Let the view render the grid.
482  $json = new JSONMessage(true, $templateMgr->fetch($this->getTemplate()));
483  return $json->getString();
484  }
485 
494  function fetchRow(&$args, &$request) {
495  // Instantiate the requested row (includes a
496  // validity check on the row id).
497  $row =& $this->getRequestedRow($request, $args);
498 
499  $json = new JSONMessage(true);
500  if (is_null($row)) {
501  // Inform the client that the row does no longer exist.
502  $json->setAdditionalAttributes(array('elementNotFound' => (int)$args['rowId']));
503  } else {
504  // Render the requested row
505  $this->setFirstDataColumn();
506  $json->setContent($this->renderRowInternally($request, $row));
507  }
508 
509  // Render and return the JSON message.
510  return $json->getString();
511  }
512 
519  function fetchCell(&$args, &$request) {
520  // Check the requested column
521  if(!isset($args['columnId'])) fatalError('Missing column id!');
522  if(!$this->hasColumn($args['columnId'])) fatalError('Invalid column id!');
523  $this->setFirstDataColumn();
524  $column =& $this->getColumn($args['columnId']);
525 
526  // Instantiate the requested row
527  $row =& $this->getRequestedRow($request, $args);
528  if (is_null($row)) fatalError('Row not found!');
529 
530  // Render the cell
531  $json = new JSONMessage(true, $this->_renderCellInternally($request, $row, $column));
532  return $json->getString();
533  }
534 
535 
536  //
537  // Protected methods to be overridden/used by subclasses
538  //
545  function &getRowInstance() {
546  //provide a sensible default row definition
547  $row = new GridRow();
548  return $row;
549  }
550 
555  function getJSHandler() {
556  return '$.pkp.controllers.grid.GridHandler';
557  }
558 
568  function &getDataElementFromRequest(&$request, &$elementId) {
569  fatalError('Grid does not support data element creation!');
570  }
571 
576  function getRangeInfo($rangeName, $contextData = null) {
577  import('lib.pkp.classes.db.DBResultRange');
578  $returner = new DBResultRange(-1, -1);
579  return $returner;
580  }
581 
593  function &getRequestedRow($request, $args) {
594  $isModified = isset($args['modify']);
595  if (isset($args['rowId']) && !$isModified) {
596  // A row ID was specified. Fetch it
597  $elementId = $args['rowId'];
598 
599  // Retrieve row data for the requested row id
600  $dataElement = $this->getRowDataElement($request, $elementId);
601  if (is_null($dataElement)) {
602  // If the row doesn't exist then
603  // return null. It may be that the
604  // row has been deleted in the meantime
605  // and the client does not yet know about this.
606  $nullVar = null;
607  return $nullVar;
608  }
609  } elseif ( $isModified ) {
610  // The row is modified. The client may be asking
611  // for a formatted new entry, to be saved later, or
612  // for a representation of a modified row.
613  $dataElement = $this->getRowDataElement($request, null);
614  if ( isset($args['rowId']) ) {
615  // the rowId holds the elementId being modified
616  $elementId = $args['rowId'];
617  } else {
618  // no rowId means that there is no element being modified.
619  $elementId = null;
620  }
621  }
622 
623  // Instantiate a new row
624  $row =& $this->_getInitializedRowInstance($request, $elementId, $dataElement, $isModified);
625  return $row;
626  }
627 
635  function getRowDataElement($request, $rowId) {
636  $elements =& $this->getGridDataElements($request);
637 
638  assert(is_array($elements));
639  if (!isset($elements[$rowId])) return null;
640 
641  return $elements[$rowId];
642  }
643 
652  function &loadData(&$request, $filter) {
653  $gridData = null;
654  $dataProvider =& $this->getDataProvider();
655  if (is_a($dataProvider, 'GridDataProvider')) {
656  // Populate the grid with data from the
657  // data provider.
658  $gridData =& $dataProvider->loadData();
659  }
660  return $gridData;
661  }
662 
667  function getFilterForm() {
668  return null;
669  }
670 
678  function getFilterSelectionData($request) {
679  return null;
680  }
681 
688  function renderFilter($request, $filterData = array()) {
689  $form = $this->getFilterForm();
690  assert(is_null($form) || is_a($form, 'Form') || is_string($form));
691 
692  $renderedForm = '';
693  switch(true) {
694  case is_a($form, 'Form'):
695  // Only read form data if the clientSubmit flag has been checked
696  $clientSubmit = (boolean) $request->getUserVar('clientSubmit');
697  if($clientSubmit) {
698  $form->readInputData();
699  $form->validate();
700  }
701 
702  $form->initData($filterData, $request);
703  $renderedForm = $form->fetch($request);
704  break;
705  case is_string($form):
706  $templateMgr =& TemplateManager::getManager();
707 
708  // Assign data to the filter.
709  $templateMgr->assign('filterData', $filterData);
710 
711  // Assign current selected filter data.
712  $filterSelectionData = $this->getFilterSelectionData($request);
713  $templateMgr->assign('filterSelectionData', $filterSelectionData);
714 
715  $renderedForm = $templateMgr->fetch($form);
716  break;
717  }
718 
719  return $renderedForm;
720  }
721 
728  $returner = array();
729  $returner[] = array('label' => __('common.noMatches'), 'value' => '');
730 
731  $json = new JSONMessage(true, $returner);
732  return $json->getString();
733  }
734 
740  function saveSequence($args, &$request) {
741  $this->callFeaturesHook('saveSequence', array('request' => &$request, 'grid' => &$this));
742 
743  return DAO::getDataChangedEvent();
744  }
745 
751  function getRowDataElementSequence(&$gridDataElement) {
752  assert(false);
753  }
754 
760  function saveRowDataElementSequence(&$request, $rowId, &$gridDataElement, $newSequence) {
761  assert(false);
762  }
763 
771  function doSpecificFetchGridActions($args, &$request, &$templateMgr) {
772  $this->_fixColumnWidths();
773 
774  // Render the body elements.
775  $gridBodyParts = $this->_renderGridBodyPartsInternally($request);
776  $templateMgr->assign_by_ref('gridBodyParts', $gridBodyParts);
777  }
778 
786  function setFirstDataColumn() {
787  $columns =& $this->getColumns();
788  $firstColumn =& reset($columns);
789  $firstColumn->addFlag('firstColumn', true);
790  }
791 
801  function initFeatures(&$request, $args) {
802  return array();
803  }
804 
810  function callFeaturesHook($hookName, $args) {
811  $features = $this->getFeatures();
812  if (is_array($features)) {
813  foreach ($features as &$feature) {
814  if (is_callable(array($feature, $hookName))) {
815  $feature->$hookName($args);
816  } else {
817  assert(false);
818  }
819  }
820  }
821  }
822 
833  function renderRowInternally(&$request, &$row) {
834  // Iterate through the columns and render the
835  // cells for the given row.
836  $renderedCells = array();
837  $columns = $this->getColumns();
838  foreach ($columns as $column) {
839  assert(is_a($column, 'GridColumn'));
840  $renderedCells[] = $this->_renderCellInternally($request, $row, $column);
841  }
842 
843  // Pass control to the view to render the row
844  $templateMgr =& TemplateManager::getManager();
845  $templateMgr->assign_by_ref('grid', $this);
846  $templateMgr->assign_by_ref('columns', $columns);
847  $templateMgr->assign_by_ref('cells', $renderedCells);
848  $templateMgr->assign_by_ref('row', $row);
849  return $templateMgr->fetch($row->getTemplate());
850  }
851 
852 
853  //
854  // Private helper methods
855  //
864  function &_getInitializedRowInstance(&$request, $elementId, &$element, $isModified = false) {
865  // Instantiate a new row
866  $row =& $this->getRowInstance();
867  $row->setGridId($this->getId());
868  $row->setId($elementId);
869  $row->setData($element);
870  $row->setRequestArgs($this->getRequestArgs());
871  $row->setIsModified($isModified);
872 
873  // Initialize the row before we render it
874  $row->initialize($request);
875  $this->callFeaturesHook('getInitializedRowInstance', array('row' => &$row));
876  return $row;
877  }
878 
884  function _renderGridBodyPartsInternally(&$request) {
885  // Render the rows.
886  $elements = $this->getGridDataElements($request);
887  $renderedRows = $this->_renderRowsInternally($request, $elements);
888 
889  // Render the body part.
890  $templateMgr =& TemplateManager::getManager();
891  $gridBodyParts = array();
892  if ( count($renderedRows) > 0 ) {
893  $templateMgr->assign_by_ref('grid', $this);
894  $templateMgr->assign_by_ref('rows', $renderedRows);
895  $gridBodyParts[] = $templateMgr->fetch('controllers/grid/gridBodyPart.tpl');
896  }
897  return $gridBodyParts;
898  }
899 
906  function _renderRowsInternally(&$request, &$elements) {
907  // Iterate through the rows and render them according
908  // to the row definition.
909  $renderedRows = array();
910  foreach ($elements as $elementId => $element) {
911  // Instantiate a new row.
912  $row =& $this->_getInitializedRowInstance($request, $elementId, $element);
913 
914  // Render the row
915  $renderedRows[] = $this->renderRowInternally($request, $row);
916  unset($element);
917  }
918 
919  return $renderedRows;
920  }
921 
933  function _renderCellInternally(&$request, &$row, &$column) {
934  // If there is no object, then we want to return an empty row.
935  // override the assigned GridCellProvider and provide the default.
936  $element =& $row->getData();
937  if ( is_null($element) && $row->getIsModified() ) {
938  import('lib.pkp.classes.controllers.grid.GridCellProvider');
939  $cellProvider = new GridCellProvider();
940  return $cellProvider->render($request, $row, $column);
941  }
942 
943  // Otherwise, get the cell content.
944  // If row defines a cell provider, use it.
945  $cellProvider =& $row->getCellProvider();
946  if (!is_a($cellProvider, 'GridCellProvider')) {
947  // Remove reference to the row variable.
948  unset($cellProvider);
949  // Get cell provider from column.
950  $cellProvider =& $column->getCellProvider();
951  }
952 
953  return $cellProvider->render($request, $row, $column);
954  }
955 
960  function _fixColumnWidths() {
961  $columns =& $this->getColumns();
962  $width = 0;
963  $noSpecifiedWidthCount = 0;
964  // Find the total width and how many columns do not specify their width.
965  foreach ($columns as $column) {
966  if ($column->hasFlag('width')) {
967  $width += $column->getFlag('width');
968  } else {
969  $noSpecifiedWidthCount++;
970  }
971  }
972 
973  // Four cases: we have to add or remove some width, and either we have wiggle room or not.
974  // We will try just correcting the first case, width less than 100 and some unspecified columns to add it to.
975  if ($width < 100) {
976  if ($noSpecifiedWidthCount > 0) {
977  // We need to add width to columns that did not specify it.
978  foreach ($columns as $column) {
979  if (!$column->hasFlag('width')) {
980  $modifyColumn =& $this->getColumn($column->getId());
981  $modifyColumn->addFlag('width', round((100 - $width)/$noSpecifiedWidthCount));
982  unset($modifyColumn);
983  }
984  }
985  }
986  }
987  }
988 
993  function _addFeatures($features) {
994  assert(is_array($features));
995  foreach ($features as &$feature) {
996  assert(is_a($feature, 'GridFeature'));
997  $this->_features[$feature->getId()] =& $feature;
998  }
999  }
1000 }
1001 ?>
GridHandler($dataProvider=null)
addColumn(&$column)
hasColumn($columnId)
_renderCellInternally(&$request, &$row, &$column)
getActions($position=GRID_ACTION_POSITION_ABOVE)
getRangeInfo($rangeName, $contextData=null)
Base class for a grid column&#39;s cell provider.
getRowDataElement($request, $rowId)
setEmptyRowText($emptyRowText)
setTemplate($template)
& getColumn($columnId)
doSpecificFetchGridActions($args, &$request, &$templateMgr)
& getRequestedRow($request, $args)
fetchRow(&$args, &$request)
addAction($action, $position=GRID_ACTION_POSITION_ABOVE)
fetchCell(&$args, &$request)
Class defining basic operations for handling HTML grids.
addPolicy(&$authorizationPolicy, $addToTop=false)
getColumnsCount($flag)
_addFeatures($features)
Class to represent a JSON (Javascript Object Notation) message.
authorize(&$request, &$args, $roleAssignments)
Class defining basic operations for handling HTML gridRows.
Definition: GridRow.inc.php:25
_renderRowsInternally(&$request, &$elements)
fetchGrid($args, &$request)
& getGridDataElements($request)
Container class for range information when retrieving a result set.
& getDataElementFromRequest(&$request, &$elementId)
saveSequence($args, &$request)
renderFilter($request, $filterData=array())
& _getInitializedRowInstance(&$request, $elementId, &$element, $isModified=false)
getRowDataElementSequence(&$gridDataElement)
setUrls(&$request, $extraUrls=array())
getFilterSelectionData($request)
_renderGridBodyPartsInternally(&$request)
& getAuthorizedContext()
hasGridDataElements($request)
initFeatures(&$request, $args)
setInstructions($instructions)
& loadData(&$request, $filter)
& getColumnsByFlag($flag)
getDataChangedEvent($elementId=null, $parentElementId=null)
Definition: DAO.inc.php:612
setGridDataElements(&$data)
renderRowInternally(&$request, &$row)
callFeaturesHook($hookName, $args)
initialize(&$request, $args=null)
setFootNote($footNote)
saveRowDataElementSequence(&$request, $rowId, &$gridDataElement, $newSequence)