Open Monograph Press  1.1
 All Classes Namespaces Functions Variables Groups Pages
ListbuilderHandler.inc.php
1 <?php
2 
16 import('lib.pkp.classes.controllers.grid.GridHandler');
17 import('lib.pkp.classes.controllers.listbuilder.ListbuilderGridRow');
18 import('lib.pkp.classes.controllers.listbuilder.ListbuilderGridColumn');
19 import('lib.pkp.classes.controllers.listbuilder.MultilingualListbuilderGridColumn');
20 
21 /* Listbuilder source types: text-based, pulldown, ... */
22 define_exposed('LISTBUILDER_SOURCE_TYPE_TEXT', 0);
23 define_exposed('LISTBUILDER_SOURCE_TYPE_SELECT', 1);
24 
25 /* Listbuilder save types */
26 define('LISTBUILDER_SAVE_TYPE_EXTERNAL', 0); // Outside the listbuilder handler
27 define('LISTBUILDER_SAVE_TYPE_INTERNAL', 1); // Using ListbuilderHandler::save
28 
29 /* String to identify optgroup in the returning options data. If you want to use
30  * optgroup in listbuilder select, return the options data in a multidimensional array
31  * array[columnIndex][optgroupId][selectItemId] and also with
32  * array[columnIndex][LISTBUILDER_OPTGROUP_LABEL][optgroupId] */
33 define_exposed('LISTBUILDER_OPTGROUP_LABEL', 'optGroupLabel');
34 
35 // FIXME: Rather than inheriting from grid handler, common base
36 // functionality might better be factored into a common base handler
37 // class and then both, GridHandler and ListbuilderHandler should
38 // inherit from the common base class. The shared concept of grids
39 // and list builders is that both seem to work with element lists. Maybe
40 // ElementListHandler would be a good name then for a common base
41 // class? I'm not a 100% sure about this but it'll become obvious
42 // once you try. If there's considerable amounts of code in both
43 // the base class and the re-factored grid handler then you know
44 // you're on the right track.
47  var $_sourceType;
48 
50  var $_saveType = LISTBUILDER_SAVE_TYPE_INTERNAL;
51 
53  var $_saveFieldName = null;
54 
58  function ListbuilderHandler() {
59  parent::GridHandler();
60  }
61 
65  function initialize($request, $addItemLink = true) {
66  parent::initialize($request);
67 
68  if ($addItemLink) {
69  import('lib.pkp.classes.linkAction.request.NullAction');
70  $this->addAction($this->getAddItemLinkAction(new NullAction()));
71  }
72  }
73 
74 
75  //
76  // Getters and Setters
77  //
82  function getTemplate() {
83  if (is_null($this->_template)) {
84  $this->setTemplate('controllers/listbuilder/listbuilder.tpl');
85  }
86 
87  return $this->_template;
88  }
89 
94  function setSourceType($sourceType) {
95  $this->_sourceType = $sourceType;
96  }
97 
102  function getSourceType() {
104  }
105 
110  function setSaveType($saveType) {
111  $this->_saveType = $saveType;
112  }
113 
118  function getSaveType() {
120  }
121 
126  function setSaveFieldName($fieldName) {
127  $this->_saveFieldName = $fieldName;
128  }
129 
134  function getSaveFieldName() {
135  assert(isset($this->_saveFieldName));
136  return $this->_saveFieldName;
137  }
138 
144  function getAddItemLinkAction($actionRequest) {
145  return new LinkAction(
146  'addItem',
147  $actionRequest,
148  __('grid.action.addItem'),
149  'add_item'
150  );
151  }
152 
160  function getNewRowId($request) {
161  return $request->getUserVar('newRowId');
162  }
163 
170  function deleteEntry($request, $rowId) {
171  fatalError('ABSTRACT METHOD');
172  }
173 
181  function updateEntry($request, $rowId, $newRowId) {
182  // This may well be overridden by a subclass to modify
183  // an existing entry, e.g. to maintain referential integrity.
184  // If not, we can simply delete and insert.
185  if (!$this->deleteEntry($request, $rowId)) return false;
186  return $this->insertEntry($request, $newRowId);
187  }
188 
194  function insertEntry($request, $newRowId) {
195  fatalError('ABSTRACT METHOD');
196  }
197 
208  function getOptions($request) {
209  fatalError('ABSTRACT METHOD');
210  }
211 
212  //
213  // Publicly (remotely) available listbuilder functions
214  //
220  function fetch($args, $request) {
221  return $this->fetchGrid($args, $request);
222  }
223 
231  function unpack($request, $data, $deletionCallback = null, $insertionCallback = null, $updateCallback = null) {
232  // Set some defaults
233  // N.B. if this class is called statically, then $this is not set to Listbuilder, but to your calling class.
234  if ( !$deletionCallback ) {
235  $deletionCallback = array($this, 'deleteEntry');
236  }
237  if ( !$insertionCallback ) {
238  $insertionCallback = array($this, 'insertEntry');
239  }
240  if ( !$updateCallback ) {
241  $updateCallback = array($this, 'updateEntry');
242  }
243 
244  $data = json_decode($data);
245 
246  // Handle deletions
247  if (isset($data->deletions)) {
248  foreach (explode(' ', trim($data->deletions)) as $rowId) {
249  call_user_func($deletionCallback, $request, $rowId, $data->numberOfRows);
250  }
251  }
252 
253  // Handle changes and insertions
254  if (isset($data->changes)) foreach ($data->changes as $entry) {
255  // Get the row ID, if any, from submitted data
256  if (isset($entry->rowId)) {
257  $rowId = $entry->rowId;
258  unset($entry->rowId);
259  } else {
260  $rowId = null;
261  }
262 
263  // $entry should now contain only submitted modified or new rows.
264  // Go through each and unpack the data in prep for application.
265  $changes = array();
266  foreach ($entry as $key => $value) {
267  // Match the column name and localization data, if any.
268  if (!preg_match('/^newRowId\[([a-zA-Z]+)\](\[([a-z][a-z]_[A-Z][A-Z])\])?$/', $key, $matches)) assert(false);
269 
270  // Get the column name
271  $column = $matches[1];
272 
273  // If this is a multilingual input, fetch $locale; otherwise null
274  $locale = isset($matches[3])?$matches[3]:null;
275 
276  if ($locale) $changes[$column][$locale] = $value;
277  else $changes[$column] = $value;
278  }
279 
280  // $changes should now contain e.g.:
281  // array ('localizedColumnName' => array('en_US' => 'englishValue'),
282  // 'nonLocalizedColumnName' => 'someNonLocalizedValue');
283  if (is_null($rowId)) {
284  call_user_func($insertionCallback, $request, $changes);
285  } else {
286  call_user_func($updateCallback, $request, $rowId, $changes);
287  }
288  }
289  }
290 
296  function save($args, $request) {
297  // The ListbuilderHandler will post a list of changed
298  // data in the "data" post var. Need to go through it
299  // and reconcile the data against this list, adding/
300  // updating/deleting as needed.
301  $data = $request->getUserVar('data');
302  $this->unpack(
303  $request, $data,
304  array($this, 'deleteEntry'),
305  array($this, 'insertEntry'),
306  array($this, 'updateEntry')
307  );
308  }
309 
310 
316  function fetchOptions($args, $request) {
317  $options = $this->getOptions($request);
318  $json = new JSONMessage(true, $options);
319  return $json->getString();
320  }
321 
322  //
323  // Overridden methods from GridHandler
324  //
329  protected function getRowInstance() {
330  // Return a citation row
331  return new ListbuilderGridRow();
332  }
333 }
334 
335 ?>
deleteEntry($request, $rowId)
setTemplate($template)
addAction($action, $position=GRID_ACTION_POSITION_ABOVE)
Class defining basic operations for handling HTML grids.
fetchGrid($args, $request)
Class defining basic operations for handling Listbuilder UI elements.
Class to represent a JSON (Javascript Object Notation) message.
insertEntry($request, $newRowId)
fetchOptions($args, $request)
updateEntry($request, $rowId, $newRowId)
initialize($request, $addItemLink=true)
Handle list builder row requests.
getAddItemLinkAction($actionRequest)
unpack($request, $data, $deletionCallback=null, $insertionCallback=null, $updateCallback=null)
Base class defining an action that can be performed by the user in the user interface.
This action does nothing.