wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.AsyncPagingController.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2014 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id: class.AsyncPagingController.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/presentation/class.Controller.php");
20 require_once(BASE."wcmf/lib/util/class.SessionData.php");
21 require_once(BASE."wcmf/lib/output/class.ArrayOutputStrategy.php");
22 require_once(BASE."wcmf/lib/visitor/class.OutputVisitor.php");
23 require_once(BASE."wcmf/lib/util/class.Obfuscator.php");
24 
25 /**
26  * @class AsyncPagingController
27  * @ingroup Controller
28  * @brief AsyncPagingController is a controller that allows to navigate lists.
29  *
30  * <b>Input actions:</b>
31  * - unspecified: List nodes
32  *
33  * <b>Output actions:</b>
34  * - @em ok In any case
35  *
36  * @param[in] type The entity type to list
37  * @param[in] filter A query passed to ObjectQuery::executeString()
38  * @param[in] limit The page size of the used PagingInfo
39  * @param[in] start The start index used to initialize the used PagingInfo
40  * @param[in] sort The attribute to order the entities by
41  * @param[in] dir The direction to use to order (ASC|DESC)
42  * @param[in] poid The parent object id of the entities if any (this is used to set relation information on the result)
43  * @param[in] renderValues True/False wether to render the values using NodeUtil::renderValues or not
44  * (optional, default: false)
45  * @param[in] completeObjects True/False wether to return all object attributes objects or only the display values
46  * using NodeUtil::removeNonDisplayValues (optional, default: false)
47  * @param[out] totalCount The total number of all entities that match the criteria
48  * @param[out] objects An array of entities of the specified type
49  * Additional properties are 'realSubject', 'realSubjectType' and 'composition' for many-to-many entities
50  * and 'clientOID'
51  *
52  * @author ingo herwig <ingo@wemove.com>
53  */
55 {
56  var $_sortProperties = null;
57 
58  /**
59  * @see Controller::hasView()
60  */
61  function hasView()
62  {
63  return false;
64  }
65  /**
66  * Do processing and assign Node data to View.
67  * @return False in every case.
68  * @see Controller::executeKernel()
69  */
70  function executeKernel()
71  {
72  // unveil the filter value if it is ofuscated
73  $filter = $this->_request->getValue('filter');
74  $unveiled = Obfuscator::unveil($filter);
75  if (strlen($filter) > 0 && strlen($unveiled) > 0) {
76  $filter = $unveiled;
77  }
78  $rightsManager = &RightsManager::getInstance();
79 
80  // get objects using the paging parameters
81  $pagingInfo = new PagingInfo($this->_request->getValue('limit'));
82  $pagingInfo->setIndex($this->_request->getValue('start'));
83  $type = $this->_request->getValue('type');
84 
85  // add sort term
86  $sortArray = null;
87  $sortProperties = $this->getSortingProperties();
88  $sortType = $sortProperties['type'];
89  $sortAttribute = $sortProperties['attribute'];
90  $sortDir = $sortProperties['direction'];
91  if ($sortAttribute != null) {
92  $sortStr = $sortAttribute;
93  if ($sortType != null) {
94  $sortStr = $sortType.'.'.$sortStr;
95  }
96  if ($sortDir != null) {
97  $sortStr .= ' '.$sortDir;
98  }
99  $sortArray = array($sortStr);
100  }
101  // get the object ids
102  $objects = $this->getObjects($type, stripslashes($filter), $sortArray, $pagingInfo);
103 
104  // collect the nodes
105  $nodes = array();
106  for($i=0; $i<sizeof($objects); $i++)
107  {
108  $curObject = &$objects[$i];
109 
110  // check if we can read the object
111  if ($rightsManager->authorize($curObject->getOID(), '', ACTION_READ)) {
112  $nodes[sizeof($nodes)] = &$curObject;
113  }
114  }
115 
116  // translate all nodes to the requested language if requested
117  if ($this->isLocalizedRequest())
118  {
119  $localization = Localization::getInstance();
120  for ($i=0; $i<sizeof($nodes); $i++) {
121  $localization->loadTranslation($nodes[$i], $this->_request->getValue('language'), true, true);
122  }
123  }
124 
125  // allow subclasses to modify the model
126  $this->modifyModel($nodes);
127 
128  // assign response values
129  $this->_response->setValue('totalCount', $pagingInfo->getTotalCount());
130  $this->_response->setValue('objects', $nodes);
131 
132  // success
133  $this->_response->setAction('ok');
134  return false;
135  }
136  /**
137  * Get the object to display. The default implementation uses ObjectQuery::executeString for the
138  * object retrieval. Subclasses may override this. If filter is an empty string, all nodes of the given
139  * type will be selected.
140  * @param type The object type
141  * @param filter The filter query passed from the view (a serialized ObjectQuery).
142  * @param sortArray An array of attributes to order by (with an optional ASC|DESC appended)
143  * @param pagingInfo A reference to the current paging information (Paginginfo instance)
144  * @return An array of object instances
145  */
146  function getObjects($type, $filter, $sortArray, &$pagingInfo)
147  {
148  if(!PersistenceFacade::isKnownType($type)) {
149  return array();
150  }
151  // check if sortArray is valid (if sortproperties type is null,
152  // the sort attribute does not belong to it and will cause an error in ObjectQuery)
153  $sortProperties = $this->getSortingProperties();
154  if ($sortProperties['type'] == null) {
155  $sortArray = null;
156  }
157  // if no filter is given, we select all nodes of the given type
158  if (strlen($filter) == 0) {
159  $filter = NodeUtil::getNodeQuery($type);
160  }
161  $objects = ObjectQuery::executeString($type, $filter, BUILDDEPTH_SINGLE, $sortArray, $pagingInfo);
162  return $objects;
163  }
164  /**
165  * @deprecated
166  */
167  function getOIDs($type, $filter, $sortArray, &$pagingInfo)
168  {
169  WCMFException::throwEx("This method is deprecated. Implement getObjects instead.", __FILE__, __LINE__);
170  }
171  /**
172  * Get the sorting properties from the 'sort', 'dir' request parameters.
173  * The type is guessed from the sort parameter (if it has the form type.attribute
174  * or the attribute belongs to the type given in the 'type' request parameter)
175  * @return Associative array with keys 'type', 'attribute', 'direction'
176  */
178  {
179  if ($this->_sortProperties == null) {
180  $type = null;
181  $attribute = null;
182  $dir = $this->_request->hasValue('dir') ? $this->_request->getValue('dir') : null;
183 
184  $sort = $this->_request->getValue('sort');
185  if (strlen($sort) > 0) {
186  $orderParts = preg_split('/\./', $sort, 2);
187  if (sizeof($orderParts) > 1) {
188  $type = $orderParts[0];
189  $attribute = $orderParts[1];
190  }
191  else {
192  $attribute = $orderParts[0];
193  // check if attribute belongs to type
194  $persistenceFacade = PersistenceFacade::getInstance();
195  $mapper = $persistenceFacade->getMapper($this->_request->getValue('type'));
196  if ($mapper->isAttribute($attribute)) {
197  $type = $this->_request->getValue('type');
198  }
199  }
200  }
201  $this->_sortProperties = array('type' => $type, 'attribute' => $attribute, 'direction' => $dir);
202  }
203  return $this->_sortProperties;
204  }
205  /**
206  * Modify the model passed to the view.
207  * @note subclasses will override this to implement special application requirements.
208  * @param nodes A reference to the array of node references passed to the view
209  */
210  function modifyModel(&$nodes)
211  {
212  // @todo put this into subclass AsyncPagingController
213 
214  // remove all attributes except for display_values
215  if ($this->_request->getBooleanValue('completeObjects', false) == false) {
216  for($i=0; $i<sizeof($nodes); $i++) {
218  }
219  }
220  // render values
221  if ($this->_request->getBooleanValue('renderValues', false) == true) {
222  NodeUtil::renderValues($nodes);
223  }
224  // set sort properties
225  if (strlen($this->_request->getValue('sort')) == 0) {
227  }
228  // if the nodes are loaded as children of a parent, we set additional
229  // properties describing the relation
230  if (PersistenceFacade::isValidOID($this->_request->getValue('poid')))
231  {
232  $parentType = PersistenceFacade::getOIDParameter($this->_request->getValue('poid'), 'type');
233 
234  $persistenceFacade = &PersistenceFacade::getInstance();
235  $parentTemplate = &$persistenceFacade->create($parentType, 1);
236  for ($i=0; $i<sizeof($nodes); $i++)
237  {
238  $curNode = &$nodes[$i];
239 
240  // set the relation properties from childTemplate
241  $childTemplate = &$parentTemplate->getFirstChild($curNode->getType());
242  foreach ($childTemplate->getPropertyNames() as $property)
243  {
244  if ($property != 'childoids' && $property != 'parentoids') {
245  $curNode->setProperty($property, $childTemplate->getProperty($property));
246  }
247  }
248 
249  // as manyToMany objects act as a proxy, we set a property 'realSubject',
250  // which holds to the real subject.
251  // we assume that the proxy connects exactly two objects, the client and the real subject
252  if (in_array('manyToMany', $curNode->getPropertyNames()))
253  {
254  $realSubjectType = NodeUtil::getRealSubjectType($curNode, $parentType);
255  // get the real subject from the parentoids property
256  $realSubject = null;
257  $clientOID = null;
258  foreach($curNode->getProperty('parentoids') as $curParentOID)
259  {
260  if (PersistenceFacade::getOIDParameter($curParentOID, 'type') == $realSubjectType)
261  $realSubject = &$persistenceFacade->load($curParentOID, BUILDDEPTH_SINGLE);
262  else
263  $clientOID = $curParentOID;
264  }
265  $curNode->setProperty('realSubject', $realSubject);
266  $curNode->setProperty('realSubjectType', $realSubjectType);
267  $curNode->setProperty('clientOID', $clientOID);
268  $curNode->setProperty('composition', false);
269  }
270  // for normal nodes we set the clientOID parameter, to know the parent later
271  else
272  {
273  $curNode->setProperty('clientOID', $this->_request->getValue('poid'));
274  }
275  }
276  }
277 
278  // if the node's type is a manyToMany relation type, we need check if
279  // the requested object order is belonging to the real subject
280  if (sizeof($nodes) > 0) {
281  $testNode = $nodes[0];
282  if (in_array('manyToMany', $testNode->getPropertyNames())) {
283  $sortProperties = $this->getSortingProperties();
284  if ($sortProperties['type'] != $testNode->getType()) {
285  // check if attribute belongs to realsubject
286  $realSubject = $testNode->getProperty('realSubject');
287  $persistenceFacade = PersistenceFacade::getInstance();
288  $mapper = $persistenceFacade->getMapper($realSubject->getType());
289  if ($mapper->isAttribute($sortProperties['attribute'])) {
290  // attribute belongs to real subject -> sort
291  Node::sort($nodes, null, false, false, array($this, 'sortByRealSubject'));
292  }
293  }
294  // render realsubject values after sorting, because otherwise
295  // date values may be unsortable
296  if ($this->_request->getValue('renderValues'))
297  {
298  foreach ($nodes as $node) {
299  $realSubject = $node->getProperty('realSubject');
300  if ($realSubject) {
301  $subjectList = array(&$realSubject);
302  NodeUtil::renderValues($subjectList);
303  }
304  else {
305  $realSubject = $persistenceFacade->create($node->getProperty('realSubjectType'), BUILDDEPTH_SINGLE);
306  }
307  $node->setProperty('realSubject', $realSubject);
308  }
309  }
310  }
311  }
312 
313  }
314 
315  function sortByRealSubject($a, $b)
316  {
317  $sortProperties = $this->getSortingProperties();
318  $realSubjectA = $a->getProperty('realSubject');
319  $realSubjectB = $b->getProperty('realSubject');
320  $valueA = strtoupper($realSubjectA->getUnconvertedValue($sortProperties['attribute'], DATATYPE_ATTRIBUTE));
321  $valueB = strtoupper($realSubjectB->getUnconvertedValue($sortProperties['attribute'], DATATYPE_ATTRIBUTE));
322  if ($valueA == $valueB) return 0;
323  if ($sortProperties['direction'] == 'ASC') {
324  return ($valueA > $valueB) ? 1 : -1;
325  }
326  else {
327  return ($valueA < $valueB) ? 1 : -1;
328  }
329  }
330 }
331 ?>
executeString($type, $query, $buildDepth, $orderby=null, &$pagingInfo)
getOIDs($type, $filter, $sortArray, &$pagingInfo)
setSortProperties(&$nodeList)
sort(&$nodeList, $criteria, $recursive=false, $changeSortkey=false, $sortFunction='')
Definition: class.Node.php:356
const DATATYPE_ATTRIBUTE
renderValues(&$nodes, $language=null)
AsyncPagingController is a controller that allows to navigate lists.
throwEx($message, $file='', $line='')
getNodeQuery($nodeType)
Controller is the base class of all controllers. If a Controller has a view it is expected to reside ...
removeNonDisplayValues(&$node)
getOIDParameter($oid, $param, $validate=true)
PagingInfo contains information about a paged list.
getObjects($type, $filter, $sortArray, &$pagingInfo)
const BUILDDEPTH_SINGLE
const ACTION_READ
getRealSubjectType(&$proxy, $parentType)