wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.NodeUtil.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.NodeUtil.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/core/class.WCMFException.php");
20 require_once(BASE."wcmf/lib/util/class.Log.php");
21 require_once(BASE."wcmf/lib/util/class.StringUtil.php");
22 require_once(BASE."wcmf/lib/util/class.FormUtil.php");
23 require_once(BASE."wcmf/lib/model/class.Node.php");
24 require_once(BASE."wcmf/lib/model/class.NodeIterator.php");
25 require_once(BASE."wcmf/lib/model/class.NodeProcessor.php");
26 require_once(BASE."wcmf/lib/persistence/class.PersistenceFacade.php");
27 
28 /**
29  * Globals .
30  */
31 // create only one instance of ValueRenderer
33 
34 /**
35  * @class NodeUtil
36  * @ingroup Model
37  * @brief NodeUtil provides services for the Node class. All methods are static.
38  *
39  * @author ingo herwig <ingo@wemove.com>
40  */
41 class NodeUtil
42 {
43  var $_formUtil = null;
44 
45  /**
46  * Constructor
47  */
48  public function NodeUtil()
49  {
50  $this->_formUtil = new FormUtil();
51  }
52 
53  /**
54  * Get the path to a given Node (from a root node).
55  * @param node The Node to find the path for
56  * @return An array containing the nodes in the path
57  */
58  function getPath(&$node)
59  {
60  $path = array();
61  $persistenceFacade = &PersistenceFacade::getInstance();
62  $parentOIDs = $node->getProperty('parentoids');
63  $parentOID = $parentOIDs[0];
64  while ($parentOID != '' && PersistenceFacade::isValidOID($parentOID))
65  {
66  $nodeInPath = &$persistenceFacade->load($parentOID, BUILDDEPTH_SINGLE);
67  if ($nodeInPath)
68  {
69  array_push($path, $nodeInPath);
70  $parentOIDs = $nodeInPath->getProperty('parentoids');
71  $parentOID = $parentOIDs[0];
72  }
73  else {
74  $parentOID = '';
75  }
76  }
77  return $path;
78  }
79  /**
80  * Get the node types that connect a type to a ancestor type.
81  * @param tplNode The node to start from
82  * @param ancestorType The type to connect to
83  * @param nodes Internal use only
84  * @return An array of connecting nodes or null (if no connection exists)
85  */
86  function getConnectionToAncestor(&$tplNode, $ancestorType, $nodes=null)
87  {
88  if ($nodes == null) {
89  $nodes = array();
90  }
91  $parentOIDs = $tplNode->getProperty('parentoids');
92  if (sizeof($parentOIDs) > 0)
93  {
94  $persistenceFacade = &PersistenceFacade::getInstance();
95  $parents = array();
96  // check parents
97  foreach ($parentOIDs as $oid)
98  {
99  $parent = &$persistenceFacade->create(PersistenceFacade::getOIDParameter($oid, 'type'), 1);
100  if ($parent->getType() == $ancestorType)
101  {
102  $nodes[sizeof($nodes)] = &$parent;
103  return $nodes;
104  }
105  $parents[sizeof($parents)] = &$parent;
106  }
107  // nothing found -> proceed with grandparents
108  foreach ($parents as $parent)
109  {
110  $parentOIDs = $parent->getProperty('parentoids');
111  if (sizeof($parentOIDs) > 0)
112  {
113  $nodes[sizeof($nodes)] = &$parent;
114  NodeUtil::getConnectionToAncestor($parent, $ancestorType, $nodes);
115  }
116  }
117  }
118  return $nodes;
119  }
120  /**
121  * Get the node types that connect a type to a descendant type.
122  * @note tplNode must be loaded/created with at least BUILDDEPTH = 1
123  * @param tplNode The node to start from
124  * @param descendantType The type to connect to
125  * @param nodes Internal use only
126  * @return An array of connecting nodes or null (if no connection exists)
127  */
128  function getConnectionToDescendant(&$tplNode, $descendantType, $nodes=null)
129  {
130  if ($nodes == null) {
131  $nodes = array();
132  }
133  $childOIDs = $tplNode->getProperty('childoids');
134  if (sizeof($childOIDs) > 0)
135  {
136  $persistenceFacade = &PersistenceFacade::getInstance();
137  $children = array();
138  // check children
139  foreach ($childOIDs as $oid)
140  {
141  $child = &$persistenceFacade->create(PersistenceFacade::getOIDParameter($oid, 'type'), 1);
142  if ($child->getType() == $descendantType)
143  {
144  $nodes[sizeof($nodes)] = &$child;
145  return $nodes;
146  }
147  $children[sizeof($children)] = &$child;
148  }
149  // nothing found -> proceed with grandparents
150  foreach ($children as $child)
151  {
152  $childOIDs = $child->getProperty('childoids');
153  if (sizeof($childOIDs) > 0)
154  {
155  $nodes[sizeof($nodes)] = &$child;
156  NodeUtil::getConnectionToDescendant($child, $descendantType, $nodes);
157  }
158  }
159  }
160  return $nodes;
161  }
162  /**
163  * Get the query used to select all Nodes of a type.
164  * @param nodeType The Node type
165  * @return The serialized query string to be used with ObjectQuery::executeString.
166  */
167  function getNodeQuery($nodeType)
168  {
169  $query = &PersistenceFacade::createObjectQuery($nodeType);
170  return $query->toString();
171  }
172  /**
173  * Get the query used to select a special Node.
174  * @param nodeType The Node type
175  * @param oid The object id of the node
176  * @return The serialized query string to be used with ObjectQuery::executeString.
177  */
178  function getSelfQuery($nodeType, $oid)
179  {
180  $persistenceFacade = &PersistenceFacade::getInstance();
181  $query = &PersistenceFacade::createObjectQuery($nodeType);
182  $tpl = &$query->getObjectTemplate($nodeType);
183  $mapper = &$tpl->getMapper();
184  $oidParts = PersistenceFacade::decomposeOID($oid);
185  $i = 0;
186  foreach ($mapper->getPkNames() as $pkName => $pkValueType) {
187  $tpl->setValue($pkName, '= '.$oidParts['id'][$i++], $pkValueType);
188  }
189  return $query->toString();
190  }
191  /**
192  * Get the query used to select all parent Nodes of a given type.
193  * @param parentType The parent type
194  * @param childNode The Node to select the parents for
195  * @return The serialized query string to be used with ObjectQuery::executeString.
196  */
197  function getParentQuery($parentType, &$childNode)
198  {
199  $persistenceFacade = &PersistenceFacade::getInstance();
200  $query = &PersistenceFacade::createObjectQuery($parentType);
201  $tpl = &$query->getObjectTemplate($parentType);
202  // prepare the child: use a new one and set the primary key values
203  $cTpl = &$query->getObjectTemplate($childNode->getType());
204  $mapper = &$childNode->getMapper();
205  foreach ($mapper->getPkNames() as $pkName => $pkValueType) {
206  $cTpl->setValue($pkName, '= '.$childNode->getValue($pkName, $pkValueType), $pkValueType);
207  }
208  $tpl->addChild($cTpl);
209  return $query->toString();
210  }
211  /**
212  * Get the query used to select all child Nodes of a given type.
213  * @param parentNode The Node to select the children for
214  * @param childType The child type
215  * @return The serialized query string to be used with ObjectQuery::executeString.
216  */
217  function getChildQuery(&$parentNode, $childType)
218  {
219  $query = &PersistenceFacade::createObjectQuery($childType);
220  $tpl = &$query->getObjectTemplate($childType);
221  // prepare the parent: use a new one and set the primary key values
222  // always start from base type
223  $pTpl = &$query->getObjectTemplate($parentNode->getBaseType());
224  $mapper = &$parentNode->getMapper();
225  foreach ($mapper->getPkNames() as $pkName => $pkValueType) {
226  $pTpl->setValue($pkName, '= '.$parentNode->getValue($pkName, $pkValueType), $pkValueType);
227  }
228  $pTpl->addChild($tpl);
229  return $query->toString();
230  }
231  /**
232  * Get allowed parent types for a Node by comparing the existing parents of realNode with the possible parents of tplNode.
233  * @param realNode A reference to the Node that defines the existing parents (property parentoids must be given)
234  * @param tplNode A reference to the Node that defines the possible parents (property parentoids must be given)
235  * @return An associative array with the parent type as key and a template of the parent as values.
236  * @note: The template has the following extra properties (use Node::getProperty()):
237  * - parentOID gives the object id of the parent object if an instance already assigned to that parent type
238  * (per definition only one instance is assignable to a parent type)
239  * - canAssociate (boolean) indicating if an instance may be associated (depends on the navigability from the parent)
240  * - composition (boolean) indicating if the parent child relation is a composition
241  */
242  function getPossibleParents(&$realNode, &$tplNode)
243  {
244  $allowedParentTypes = array();
245  if ($tplNode != null && $realNode != null)
246  {
247  $persistenceFacade = PersistenceFacade::getInstance();
248  $possibleParentOIDs = $tplNode->getProperty('parentoids');
249  for ($i=0; $i<sizeof($possibleParentOIDs); $i++)
250  {
251  $type = PersistenceFacade::getOIDParameter($possibleParentOIDs[$i], 'type');
252  $possibleParent = &$persistenceFacade->create($type, 1);
253 
254  // check if the parent itself knows the type as children and if the relation is a composition
255  $children = $possibleParent->getChildrenEx(null, $realNode->getType(), null, null);
256  if (sizeof($children) > 0)
257  {
258  $possibleParent->setProperty('canAssociate', true);
259  $possibleParent->setProperty('composition', $children[0]->getProperty('composition'));
260  }
261  else
262  {
263  $possibleParent->setProperty('canAssociate', false);
264  $possibleParent->setProperty('composition', true);
265  }
266 
267  // get the already assigned parent, if existing
268  foreach ($realNode->getProperty('parentoids') as $parentOID)
269  {
270  if (PersistenceFacade::getOIDParameter($parentOID, 'type') == $type)
271  {
272  $possibleParent->setProperty('parentOID', $parentOID);
273  break;
274  }
275  }
276 
277  $allowedParentTypes[$type] = &$possibleParent;
278  }
279  }
280  return $allowedParentTypes;
281  }
282  /**
283  * Get allowed child types for a Node by comparing the existing children of realNode with the possible children of tplNode.
284  * @param realNode A reference to the Node that defines the existing children (property childoids must be given)
285  * @param tplNode A reference to the Node that defines the possible children (children must be included already)
286  * @param resolveManyToMany True/False wether for all many to many children the real subject types should be returned [default: true]
287  * @return An associative array with the child type as key and a template of the child as values.
288  * @note: The template has the following extra properties (use Node::getProperty()):
289  * - canCreate (boolean) indicating if an instance may be created (depends on the multiplicity)
290  * - realSubjectType the type of the real subject if the template is acting as proxy (many to many instance)
291  */
292  function getPossibleChildren(&$realNode, &$tplNode, $resolveManyToMany=true)
293  {
294  $allowedChildTypes = array();
295  if ($tplNode != null && $realNode != null && $tplNode->getNumChildren() > 0)
296  {
297  $persistenceFacade = PersistenceFacade::getInstance();
298  $possibleChildren = $tplNode->getChildren();
299  for ($i=0; $i<sizeof($possibleChildren); $i++)
300  {
301  $possibleChild = &$possibleChildren[$i];
302 
303  // get the number of existing children
304  $occurs = 0;
305  $childOIDs = $realNode->getProperty('childoids');
306  foreach($childOIDs as $childOID)
307  {
308  if (PersistenceFacade::getOIDParameter($childOID, 'type') == $possibleChild->getType()) {
309  $occurs++;
310  }
311  }
312  if ($possibleChild->getProperty('maxOccurs') == 'unbounded' || ($occurs < $possibleChild->getProperty('maxOccurs'))) {
313  $possibleChild->setProperty('canCreate', true);
314  }
315  else {
316  $possibleChild->setProperty('canCreate', false);
317  }
318  // check if we have an association object
319  // if yes we set the composition property to false (instances are deleted with the parent)
320  // and get the display name from the associated type
321  $type = $possibleChild->getType();
322  $realSubjectType = NodeUtil::getRealSubjectType($possibleChild, $realNode->getType());
323  if ($realSubjectType != null)
324  {
325  $associatedNode = &$persistenceFacade->create($realSubjectType, BUILDTYPE_SINGLE);
326  $possibleChild->setProperty('composition', false);
327  $possibleChild->setProperty('realSubject', $associatedNode);
328  }
329 
330  if ($realSubjectType != null && $resolveManyToMany) {
331  $type = $realSubjectType;
332  }
333  else {
334  $type = $possibleChild->getType();
335  }
336  $allowedChildTypes[$type] = &$possibleChild;
337  }
338  }
339  return $allowedChildTypes;
340  }
341  /**
342  * Get the real subject type for a proxy node, that is a many to many instance. A many to many instance
343  * serves as proxy between a client and a real subject, where the client is the parent node in this case
344  * and the proxy is the child node.
345  * @param proxy The (many to many) proxy node
346  * @param parentType The parent type
347  * @return The type
348  */
349  function getRealSubjectType(&$proxy, $parentType)
350  {
351  $manyToMany = $proxy->getProperty('manyToMany');
352  if (is_array($manyToMany))
353  {
354  if (sizeof($manyToMany) == 2)
355  {
356  // get the type of the real subject from the manyToMany property
357  foreach($proxy->getProperty('manyToMany') as $curParentType)
358  {
359  if ($curParentType != $parentType) {
360  return $curParentType;
361  }
362  }
363  }
364  else
365  {
366  // try to call custom implementation in Mapper class
367  $mapper = $proxy->getMapper();
368  if (method_exists($mapper, 'getRealSubjectType')) {
369  return $mapper->getRealSubjectType($proxy, $parentType);
370  }
371  }
372  }
373  return null;
374  }
375  /**
376  * Get the display value names of a Node.
377  * @param node The Node instance
378  * @return An array of value names
379  */
380  function getDisplayValueNames(&$node)
381  {
382  $displayValueStr = $node->getProperty('display_value');
383  if (!strPos($displayValueStr, '|')) {
384  $displayValues = array($displayValueStr);
385  }
386  else {
387  $displayValues = split('\|', $displayValueStr);
388  }
389  return $displayValues;
390  }
391  /**
392  * Get the display value for a Node defined by the 'display_value' property that may reference values of subnodes.
393  * If the 'display_value' is an array ('|' separated strings) the pieces will be put together with ' - '.
394  * If search for 'display_value' gives no result the function returns an empty string.
395  * Example: 'name|Comment/text' shows the name of the Node together with the text of the first Comment child
396  * @note If display_value is ambiguous because a parent has more than one children of a given type than the first child of
397  * that type will be chosen for display.
398  * @param node A reference to the Node to display
399  * @param useDisplayType True/False wether to use the display types that are associated with the values which the display value contains [default: false]
400  * @param language The lanugage if values should be localized. Optional, default is Localization::getDefaultLanguage()
401  * @param values An assoziative array holding key value pairs that the display node's values should match [maybe null].
402  * @note The display type is configured via the display_type property of a value. It describes how the value should be displayed.
403  * The description is of the form @code type @endcode or @code type[attributes] @endcode
404  * - type: text|image
405  * - attributes: a string of attributes used in the HTML definition (e.g. 'height="50"')
406  * @return The display string
407  * @see DefaultValueRenderer::renderValue
408  */
409  function getDisplayValue(&$node, $useDisplayType=false, $language=null, $values=null)
410  {
411  return join(' - ', array_values(NodeUtil::getDisplayValues($node, $useDisplayType, $language, $values)));
412  }
413  /**
414  * Does the same as DefaultValueRenderer::getDisplayValue() but returns the display value as associative array
415  * @param node A reference to the Node to display
416  * @param useDisplayType True/False wether to use the display types that are associated with the values which the display value contains [default: false]
417  * @param language The lanugage if values should be localized. Optional, default is Localization::getDefaultLanguage()
418  * @param values An assoziative array holding key value pairs that the display node's values should match [maybe null].
419  * @return The display array
420  */
421  function getDisplayValues(&$node, $useDisplayType=false, $language=null, $values=null)
422  {
423  // localize node if requested
424  $localization = &Localization::getInstance();
425  if ($language != null) {
426  $localization->loadTranslation($node, $language);
427  }
428 
429  $displayArray = array();
430  $persistenceFacade = &PersistenceFacade::getInstance();
431  $formUtil = new FormUtil($language);
432  $pathToShow = $node->getProperty('display_value');
433  if (!strPos($pathToShow, '|')) {
434  $pathToShowPieces = array($pathToShow);
435  }
436  else {
437  $pathToShowPieces = split('\|', $pathToShow);
438  }
439  foreach($pathToShowPieces as $pathToShowPiece)
440  {
441  $tmpDisplay = '';
442  $inputType = ''; // needed for the translation of a list value
443  if ($pathToShowPiece != '')
444  {
445  $curNode = $node;
446  $pieces = split('/', $pathToShowPiece);
447  foreach ($pieces as $curPiece)
448  {
449  if (in_array($curPiece, $curNode->getValueNames()))
450  {
451  // we found a matching attribute/element
452  $tmpDisplay = $curNode->getValue($curPiece);
453  $properties = $curNode->getValueProperties($curPiece);
454  $inputType = $properties['input_type'];
455  $displayType = $properties['display_type'];
456  break;
457  }
458  else
459  {
460  // see if a child type matches
461  // see if the $value is valid for the child to look for
462  if ($values != null)
463  {
464  if (PersistenceFacade::isKnownType($curPiece))
465  {
466  $template = &$persistenceFacade->create($curPiece, BUILDDEPTH_SINGLE);
467  $possibleValues = $template->getValueNames();
468  foreach ($values as $key => $value) {
469  if (!in_array($key, $possibleValues)) {
470  unset($values[$key]);
471  }
472  }
473  if (sizeOf($values) == 0) {
474  $values = null;
475  }
476  }
477  }
478 
479  // the child is loaded -> take it
480  $curNodeArray = $curNode->getChildrenEx(null, $curPiece, $values, null);
481  if (sizeOf($curNodeArray) > 0) {
482  $curNode = $curNodeArray[0];
483  }
484  // the child is not loaded -> try to load it
485  else
486  {
487  $loaded = false;
488  if (PersistenceFacade::isKnownType($curPiece))
489  {
490  $nodesOfTypePiece = $persistenceFacade->getOIDs($curPiece);
491  foreach($curNode->getProperty('childoids') as $childOID)
492  {
493  if (in_array($childOID, $nodesOfTypePiece))
494  {
495  $curNode = &$persistenceFacade->load($childOID, BUILDDEPTH_SINGLE);
496  // localize node if requested
497  if ($language != null) {
498  $localization->loadTranslation($curNode, $language);
499  }
500  $matches = true;
501  if ($values != null)
502  {
503  // check values
504  foreach($values as $key => $value)
505  {
506  if ($curNode->getValue($key) != $value)
507  {
508  $matches = false;
509  break;
510  }
511  }
512  }
513  if ($matches)
514  {
515  $loaded = true;
516  break;
517  }
518  }
519  }
520  }
521  if (!$loaded) {
522  break;
523  }
524  }
525  }
526  }
527  }
528  $tmpDisplay = $formUtil->translateValue($tmpDisplay, $inputType);
529  if (strlen($tmpDisplay) == 0) {
530  $tmpDisplay = $node->getOID();
531  }
532  if ($useDisplayType)
533  {
534  // apply display type if desired
535  if (!is_object($GLOBALS['gValueRenderer']))
536  {
537  $objectFactory = &ObjectFactory::getInstance();
538  $GLOBALS['gValueRenderer'] = &$objectFactory->createInstanceFromConfig('implementation', 'ValueRenderer');
539  if ($GLOBALS['gValueRenderer'] == null) {
540  WCMFException::throwEx('ValueRenderer not defined in section implementation.', __FILE__, __LINE__);
541  }
542  }
543 
544  // get type and attributes from definition
545  preg_match_all("/[\w][^\[\]]+/", $displayType, $matches);
546  if (sizeOf($matches[0]) > 0) {
547  list($type, $attributes) = $matches[0];
548  }
549  if (!$type || $type == '') {
550  $type = 'text';
551  }
552  $tmpDisplay = $GLOBALS['gValueRenderer']->renderValue($type, $tmpDisplay, $attributes);
553  }
554 
555  $displayArray[$pathToShowPiece] = $tmpDisplay;
556  }
557  return $displayArray;
558  }
559  /**
560  * Get the display name for a Node type.
561  * @param type The name of the type
562  * @return The display string
563  */
564  function getDisplayNameFromType($type)
565  {
566  $persistenceFacade = &PersistenceFacade::getInstance();
567  $typeNode = $persistenceFacade->create($type, BUILDDEPTH_SINGLE);
568  return $typeNode->getObjectDisplayName();
569  }
570  /**
571  * Get the description for a Node type.
572  * @param type The name of the type
573  * @return The description
574  */
575  function getDescriptionFromType($type)
576  {
577  $persistenceFacade = &PersistenceFacade::getInstance();
578  $typeNode = $persistenceFacade->create($type, BUILDDEPTH_SINGLE);
579  return $typeNode->getObjectDescription();
580  }
581  /**
582  * Get a HTML input control for a given node value. The control is defined by the
583  * 'input_type' property of the value. The property 'is_editable' is used to determine
584  * wether the control should be enabled or not.
585  * @param node A reference to the Node which contains the value
586  * @param name The name of the value to construct the control for
587  * @param dataType The type of the value [optional]
588  * (if type is omitted the first value of any type that matches will be used)
589  * @param templateNode A Node which contains the value definition
590  * (if not given the definition will be taken from the node parameter) [optional]
591  * @param addEmptyValue True/False if this is set true, an additional empty value is added to the input control
592  * (if not given no empty value will be added) [optional]
593  * @return The HTML control string (see FormUtil::getInputControl())
594  */
595  function getInputControl(&$node, $name, $dataType=null, $templateNode=null, $addEmptyValue=false)
596  {
597  // set the datatype if not given (to the fist one found)
598  if ($dataType == null)
599  {
600  $dataTypes = $node->getValueTypes($name);
601  if (sizeof($dataTypes) > 0) {
602  $dataType = $dataTypes[0];
603  }
604  }
605  $controlName = NodeUtil::getInputControlName($node, $name, $dataType);
606  if ($templateNode != null) {
607  $properties = $templateNode->getValueProperties($name, $dataType);
608  }
609  else {
610  $properties = $node->getValueProperties($name, $dataType);
611  if (!$properties)
612  {
613  $persistenceFacade = &PersistenceFacade::getInstance();
614  $templateNode = $persistenceFacade->create($node->getType(), BUILDDEPTH_SINGLE);
615  $properties = $templateNode->getValueProperties($name, $dataType);
616  }
617  }
618  $value = $node->getValue($name, $dataType);
619  // use member formUtil, because some controls may change the state while being rendered one after another
620  return $this->_formUtil->getInputControl($controlName, $properties['input_type'], $value, $properties['is_editable'], $addEmptyValue);
621  }
622  /**
623  * Get a HTML input control name for a given node value (see FormUtil::getInputControl()).
624  * @param node A reference to the Node which contains the value
625  * @param name The name of the value to construct the control for
626  * @param dataType The type of the value [optional]
627  * (if type is omitted the first value of any type that matches will be used)
628  * @return The HTML control name string in the form value-<datatype>-<name>-<oid>
629  */
630  function getInputControlName(&$node, $name, $dataType=null)
631  {
632  $fieldDelimiter = FormUtil::getInputFieldDelimiter();
633  return 'value'.$fieldDelimiter.$dataType.$fieldDelimiter.$name.$fieldDelimiter.$node->getOID();
634  }
635  /**
636  * Get the node value definition from a HTML input control name.
637  * @param name The name of input control in the format defined by getInputControlName
638  * @return An associative array with keys 'oid', 'name', 'dataType' or null if the name is not valid
639  * If the dataType is empty, it defaults to DATATYPE_ATTRIBUTE
640  */
642  {
643  if (!(strpos($name, 'value') === 0)) {
644  return null;
645  }
646  $def = array();
647  $fieldDelimiter = FormUtil::getInputFieldDelimiter();
648  $pieces = split($fieldDelimiter, $name);
649  if (!sizeof($pieces) == 4) {
650  return null;
651  }
652  $forget = array_shift($pieces);
653  $dataType = array_shift($pieces);
654  if (strlen($dataType) > 0) {
655  $def['dataType'] = intval($dataType);
656  }
657  else {
658  $def['dataType'] = DATATYPE_ATTRIBUTE;
659  }
660  $def['name'] = array_shift($pieces);
661  $def['oid'] = array_shift($pieces);
662 
663  return $def;
664  }
665  /**
666  * Add the missing attributes to a Node. The state remains the same.
667  * @param node A reference to the Node to complete
668  */
669  function completeNode(&$node)
670  {
671  // remember old state
672  $oldState = $node->getState();
673  // construct a template node and add the missing attributes to the node to save
674  $persistenceFacade = &PersistenceFacade::getInstance();
675  $tmpNode = &$persistenceFacade->create($node->getType(), BUILDDEPTH_SINGLE);
676  foreach ($tmpNode->getValueNames(DATATYPE_ATTRIBUTE) as $attribute)
677  if($node->getValue($attribute, DATATYPE_ATTRIBUTE) == null)
678  {
679  $node->setValue($attribute, '', DATATYPE_ATTRIBUTE, true);
680  $node->setValueProperties($attribute, $tmpNode->getValueProperties($attribute, DATATYPE_ATTRIBUTE), DATATYPE_ATTRIBUTE);
681  }
682  // reset old state
683  $node->setState($oldState, false);
684  }
685  /**
686  * Sort a list of Nodes and set the sort properties on Nodes of a given list. The two attributes (DATATPE_IGNORE)
687  * 'hasSortUp', 'hasSortDown' (values (false,true)) will be added to each Node depending on
688  * its list position. If applicable the attributes (DATATPE_IGNORE) 'prevoid'
689  * and 'nextoid' resp. will be added to denote the neighboured Nodes.
690  * The attributes will only be added if a Node has a sortkey value (DATATPE_IGNORE).
691  * @param nodeList A reference to the list of Nodes
692  */
693  function setSortProperties(&$nodeList)
694  {
695  if(sizeof($nodeList) > 0 && $nodeList[0]->hasValue('sortkey', DATATYPE_IGNORE)) {
696  Node::sort($nodeList, 'sortkey');
697  }
698  for ($i=0; $i<sizeof($nodeList); $i++)
699  {
700  if ($nodeList[$i]->hasValue('sortkey', DATATYPE_IGNORE))
701  {
702  $nodeList[$i]->setValue('hasSortUp', true, DATATYPE_IGNORE);
703  $nodeList[$i]->setValue('hasSortDown', true, DATATYPE_IGNORE);
704 
705  if ($i == 0) {
706  $nodeList[$i]->setValue('hasSortUp', false, DATATYPE_IGNORE);
707  }
708  else {
709  $nodeList[$i]->setValue('prevoid', $nodeList[$i-1]->getOID(), DATATYPE_IGNORE);
710  }
711  if ($i == sizeof($nodeList)-1) {
712  $nodeList[$i]->setValue('hasSortDown', false, DATATYPE_IGNORE);
713  }
714  else {
715  $nodeList[$i]->setValue('nextoid', $nodeList[$i+1]->getOID(), DATATYPE_IGNORE);
716  }
717  }
718  }
719  }
720  /**
721  * Make all urls matching a given base url in a Node relative.
722  * @param node A reference to the Node the holds the value
723  * @param baseUrl The baseUrl to which matching urls will be made relative
724  * @param recursive True/False wether to recurse into child Nodes or not (default: true)
725  */
726  function makeNodeUrlsRelative(&$node, $baseUrl, $recursive=true)
727  {
728  // use NodeProcessor to iterate over all Node values
729  // and call the global convert function on each
730  $processor = new NodeProcessor('makeValueUrlsRelative', array($baseUrl), new NodeUtil());
731  $processor->run($node, $recursive);
732  }
733  /**
734  * Make the urls matching a given base url in a Node value relative.
735  * @param node A reference to the Node the holds the value
736  * @param valueName The name of the value
737  * @param dataType The dataType of the value
738  * @param baseUrl The baseUrl to which matching urls will be made relative
739  */
740  function makeValueUrlsRelative(&$node, $valueName, $dataType, $baseUrl)
741  {
742  $value = $node->getValue($valueName, $dataType);
743 
744  // find urls in texts
745  $urls = StringUtil::getUrls($value);
746  // find direct attribute urls
747  if (strpos($value, 'http://') === 0 || strpos($value, 'https://') === 0) {
748  array_push($urls, $value);
749  }
750  // process urls
751  foreach ($urls as $url)
752  {
753  // convert absolute urls matching baseUrl
754  $urlConv = $url;
755  if (strpos($url, $baseUrl) === 0) {
756  $urlConv = str_replace($baseUrl, '', $url);
757  }
758  // replace url
759  $value = str_replace($url, $urlConv, $value);
760  }
761  $node->setValue($valueName, $value, $dataType);
762  }
763  /**
764  * Render all values in a list of Nodes using the DefaultValueRenderer.
765  * @note Values will be translated before rendering using FormUtil::translateValue
766  * @param nodes A reference to the array of Nodes
767  * @param language The language code, if the translated values should be localized.
768  * Optional, default is Localization::getDefaultLanguage()
769  */
770  function renderValues(&$nodes, $language=null)
771  {
772  // render the node values
773  $nodeUtil = new NodeUtil();
774  $formUtil = new FormUtil($language);
775  $processor = new NodeProcessor('renderValue', array($formUtil), $nodeUtil);
776  for($i=0; $i<sizeof($nodes); $i++)
777  {
778  // render values
779  $processor->run($nodes[$i], false);
780  }
781  }
782  /**
783  * Callback to render a Node value
784  * @see NodeProcessor
785  * @note This method is used internally only
786  */
787  function renderValue(&$node, $valueName, $dataType, $formUtil)
788  {
789  if ($dataType == DATATYPE_ATTRIBUTE)
790  {
791  $value = $node->getValue($valueName, $dataType);
792  // translate list values
793  $value = $formUtil->translateValue($value, $node->getValueProperty($valueName, 'input_type', $dataType), true);
794  // render the value to html
795  $displayType = $node->getValueProperty($valueName, 'display_type', $dataType);
796  if (strlen($displayType) == 0) {
797  $displayType = 'text';
798  }
799  $renderer = new DefaultValueRenderer();
800  $value = $renderer->renderValue($displayType, $value, $renderAttribs);
801  // force set (the rendered value may not be satisfy validation rules)
802  $node->setValue($valueName, $value, $dataType, true);
803  }
804  }
805  /**
806  * Translate all values in a list of Nodes using the DefaultValueRenderer.
807  * @note Translation in this case refers to mapping list values from the key to the value
808  * and should not be confused with localization, although values maybe localized using the
809  * language parameter.
810  * @param nodes A reference to the array of Nodes
811  * @param language The language code, if the translated values should be localized.
812  * Optional, default is Localization::getDefaultLanguage()
813  */
814  function translateValues(&$nodes, $language=null)
815  {
816  // translate the node values
817  $nodeUtil = new NodeUtil();
818  $formUtil = new FormUtil($language);
819  $processor = new NodeProcessor('translateValue', array($formUtil), $nodeUtil);
820  for($i=0; $i<sizeof($nodes); $i++)
821  {
822  // render values
823  $processor->run($nodes[$i], false);
824  }
825  }
826  /**
827  * Callback to translate a Node value
828  * @see NodeProcessor
829  * @note This method is used internally only
830  */
831  function translateValue(&$node, $valueName, $dataType, $formUtil)
832  {
833  if ($dataType == DATATYPE_ATTRIBUTE)
834  {
835  $value = $node->getValue($valueName, $dataType);
836 
837  // translate list values
838  $value = $formUtil->translateValue($value, $node->getValueProperty($valueName, 'input_type', $dataType), true, $node->getBaseOid());
839  // force set (the rendered value may not be satisfy validation rules)
840  $node->setValue($valueName, $value, $dataType, true);
841  }
842  }
843  /**
844  * Remove all values from a Node that are not a display value and don't have DATATYPE_IGNORE.
845  * @param node The Node instance
846  */
847  function removeNonDisplayValues(&$node)
848  {
849  $displayValues = NodeUtil::getDisplayValueNames($node);
850  $valueNames = $node->getValueNames();
851  foreach($valueNames as $name) {
852  if (!in_array($name, $displayValues)) {
853  $node->removeValue($name);
854  }
855  }
856  }
857 }
858 ?>
getSelfQuery($nodeType, $oid)
getDescriptionFromType($type)
getInputControlName(&$node, $name, $dataType=null)
getChildQuery(&$parentNode, $childType)
$gValueRenderer
setSortProperties(&$nodeList)
FormUtil provides basic support for HTML forms. It's mainly for creating input controls from definiti...
getParentQuery($parentType, &$childNode)
getDisplayValueNames(&$node)
makeValueUrlsRelative(&$node, $valueName, $dataType, $baseUrl)
sort(&$nodeList, $criteria, $recursive=false, $changeSortkey=false, $sortFunction='')
Definition: class.Node.php:356
const DATATYPE_ATTRIBUTE
getDisplayValues(&$node, $useDisplayType=false, $language=null, $values=null)
getPossibleChildren(&$realNode, &$tplNode, $resolveManyToMany=true)
getDisplayNameFromType($type)
NodeProcessor is used to iterate over all values of a Node and apply a given callback function...
NodeUtil provides services for the Node class. All methods are static.
renderValues(&$nodes, $language=null)
throwEx($message, $file='', $line='')
makeNodeUrlsRelative(&$node, $baseUrl, $recursive=true)
getNodeQuery($nodeType)
getConnectionToAncestor(&$tplNode, $ancestorType, $nodes=null)
translateValues(&$nodes, $language=null)
const DATATYPE_IGNORE
decomposeOID($oid, $validate=true)
getPath(&$node)
renderValue(&$node, $valueName, $dataType, $formUtil)
removeNonDisplayValues(&$node)
translateValue(&$node, $valueName, $dataType, $formUtil)
getUrls($string)
getOIDParameter($oid, $param, $validate=true)
getInputControl(&$node, $name, $dataType=null, $templateNode=null, $addEmptyValue=false)
static getInputFieldDelimiter()
getPossibleParents(&$realNode, &$tplNode)
$GLOBALS['gJSONData']
getDisplayValue(&$node, $useDisplayType=false, $language=null, $values=null)
DefaultValueRenderer is responsible for rendering (Node) values of a given display type...
getConnectionToDescendant(&$tplNode, $descendantType, $nodes=null)
const BUILDDEPTH_SINGLE
getValueDefFromInputControlName($name)
completeNode(&$node)
getRealSubjectType(&$proxy, $parentType)