3 * Copyright (C) 2005-2010 Alfresco Software Limited.
5 * This file is part of Alfresco
7 * Alfresco is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * Alfresco is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
21 require_once $CFG->libdir
.'/alfresco/Service/Store.php';
22 require_once $CFG->libdir
.'/alfresco/Service/ChildAssociation.php';
23 require_once $CFG->libdir
.'/alfresco/Service/Association.php';
24 require_once $CFG->libdir
.'/alfresco/Service/NamespaceMap.php';
25 require_once $CFG->libdir
.'/alfresco/Service/ContentData.php';
26 require_once $CFG->libdir
.'/alfresco/Service/VersionHistory.php';
27 require_once $CFG->libdir
.'/alfresco/Service/Version.php';
29 class Node
extends BaseObject
39 private $_primaryParent;
41 private $_associations;
42 private $_versionHistory;
43 private $origionalProperties;
44 private $addedAspects;
45 private $removedAspects;
46 private $addedChildren;
47 private $addedParents;
48 private $addedAssociations;
49 private $removedAssociations;
54 public function __construct($session, $store, $id)
56 $this->_session
= $session;
57 $this->_store
= $store;
59 $this->_isNewNode
= false;
60 $this->addedChildren
= array();
61 $this->addedParents
= array();
62 $this->addedAssociations
= array();
66 * Util method to create a node from a web service node structure.
68 public static function createFromWebServiceData($session, $webServiceNode)
70 $scheme = $webServiceNode->reference
->store
->scheme
;
71 $address = $webServiceNode->reference
->store
->address
;
72 $id = $webServiceNode->reference
->uuid
;
74 $store = $session->getStore($address, $scheme);
75 $node = $session->getNode($store, $id);
76 $node->populateFromWebServiceNode($webServiceNode);
81 public function setPropertyValues($properties)
83 // Check that the properties of the node have been populated
84 $this->populateProperties();
86 // Set the property values
87 foreach ($properties as $name=>$value)
89 $name = $this->_session
->namespaceMap
->getFullName($name);
90 $this->_properties
[$name] = $value;
94 public function updateContent($property, $mimetype, $encoding="UTF-8", $content=null)
96 list($property) = $this->_session
->namespaceMap
->getFullNames(array($property));
97 $contentData = new ContentData($this, $property, $mimetype, $encoding);
100 $contentData->content
= $content;
102 $this->_properties
[$property] = $contentData;
107 public function hasAspect($aspect)
109 list($aspect) = $this->_session
->namespaceMap
->getFullNames(array($aspect));
110 $this->populateProperties();
111 return in_array($aspect, $this->_aspects
);
114 public function addAspect($aspect, $properties = null)
116 list($aspect) = $this->_session
->namespaceMap
->getFullNames(array($aspect));
117 $this->populateProperties();
119 if (in_array($aspect, $this->_aspects
) == false)
121 $this->_aspects
[] = $aspect;
122 if ($properties != null)
124 foreach ($properties as $name=>$value)
126 $name = $this->_session
->namespaceMap
->getFullName($name);
127 $this->_properties
[$name] = $value;
131 $this->remove_array_value($aspect, $this->removedAspects
);
132 $this->addedAspects
[] = $aspect;
136 public function removeAspect($aspect)
138 list($aspect) = $this->_session
->namespaceMap
->getFullNames(array($aspect));
139 $this->populateProperties();
141 if (in_array($aspect, $this->_aspects
) == true)
143 $this->remove_array_value($aspect, $this->_aspects
);
144 $this->remove_array_value($aspect, $this->addedAspects
);
145 $this->removedAspects
[] = $aspect;
149 public function createChild($type, $associationType, $associationName)
151 list($type, $associationType, $associationName) = $this->_session
->namespaceMap
->getFullNames(array($type, $associationType, $associationName));
153 $id = $this->_session
->nextSessionId();
154 $newNode = new Node($this->_session
, $this->_store
, $id);
155 $childAssociation = new ChildAssociation($this, $newNode, $associationType, $associationName, true);
157 $newNode->_isNewNode
= true;
159 $newNode->_properties
= array();
160 $newNode->_aspects
= array();
161 $newNode->_properties
= array();
162 $newNode->_children
= array();
163 $newNode->origionalProperties
= array();
164 $newNode->addedAspects
= array();
165 $newNode->removedAspects
= array();
167 $newNode->_type
= $type;
168 $newNode->_parents
= array();
169 $newNode->addedParents
= array($this->__toString() => $childAssociation);
170 $newNode->_primaryParent
= $this;
172 $this->addedChildren
[$newNode->__toString()] = $childAssociation;
174 $this->_session
->addNode($newNode);
179 public function addChild($node, $associationType, $associationName)
181 list($associationType, $associationName) = $this->_session
->namespaceMap
->getFullNames(array($associationType, $associationName));
183 $childAssociation = new ChildAssociation($this, $node, $associationType, $associationName, false);
184 $this->addedChildren
[$node->__toString()] = $childAssociation;
185 $node->addedParents
[$this->__toString()] = $childAssociation;
188 public function removeChild($childAssociation)
193 public function addAssociation($to, $associationType)
195 list($associationType) = $this->_session
->namespaceMap
->getFullNames(array($associationType));
197 $association = new Association($this, $to, $associationType);
198 $this->addedAssociations
[$to->__toString()] = $association;
201 public function removeAssociation($association)
206 public function createVersion($description=null, $major=false)
208 // We can only create a version if there are no outstanding changes for this node
209 if ($this->isDirty() == true)
211 throw new Exception("You must save any outstanding modifications before a new version can be created.");
214 // TODO implement major flag ...
216 $client = WebServiceFactory
::getAuthoringService($this->_session
->repository
->connectionUrl
, $this->_session
->ticket
);
217 $result = $client->createVersion(
218 array("items" => array("nodes" => $this->__toArray()),
219 "comments" => array("name" => "description", "value" => $description),
220 "versionChildren" => false));
222 // Clear the properties and aspects
223 $this->_properties
= null;
224 $this->_aspects
= null;
226 // Get the version details
227 // TODO get some of the other details too ...
228 $versionId = $result->createVersionReturn
->versions
->id
->uuid
;
229 $versionStoreScheme = $result->createVersionReturn
->versions
->id
->store
->scheme
;
230 $versionStoreAddress = $result->createVersionReturn
->versions
->id
->store
->address
;
232 // Create the version object to return
233 return new Version($this->_session
, new Store($this->_session
, $versionStoreAddress, $versionStoreScheme), $versionId);
236 private function isDirty()
239 if ($this->_isNewNode
== false &&
240 count($this->getModifiedProperties()) == 0 &&
241 ($this->addedAspects
== null ||
count($this->addedAspects
) == 0) &&
242 ($this->removedAssociations
== null ||
count($this->removedAssociations
) == 0) &&
243 ($this->addedChildren
== null ||
count($this->addedChildren
) == 0) &&
244 ($this->addedAssociations
== null ||
count($this->addedAssociations
) == 0))
251 public function __get($name)
253 $fullName = $this->_session
->namespaceMap
->getFullName($name);
254 if ($fullName != $name)
256 $this->populateProperties();
257 if (array_key_exists($fullName, $this->_properties
) == true)
259 return $this->_properties
[$fullName];
268 return parent
::__get($name);
272 public function __set($name, $value)
274 $fullName = $this->_session
->namespaceMap
->getFullName($name);
275 if ($fullName != $name)
277 $this->populateProperties();
278 $this->_properties
[$fullName] = $value;
280 // Ensure that the node and property details are stored on the contentData object
281 if ($value instanceof ContentData
)
283 $value->setPropertyDetails($this, $fullName);
288 parent
::__set($name, $value);
293 * toString method. Returns node as a node reference style string.
295 public function __toString()
297 return Node
::__toNodeRef($this->_store
, $this->id
);
300 public static function __toNodeRef($store, $id)
302 return $store->scheme
. "://" . $store->address
. "/" . $id;
305 public function __toArray()
307 return array("store" => $this->_store
->__toArray(),
308 "uuid" => $this->_id
);
311 public function getSession()
313 return $this->_session
;
316 public function getStore()
318 return $this->_store
;
321 public function getId()
326 public function getIsNewNode()
328 return $this->_isNewNode
;
331 public function getType()
333 $this->populateProperties();
337 public function getAspects()
339 $this->populateProperties();
340 return $this->_aspects
;
343 public function getProperties()
345 $this->populateProperties();
346 return $this->_properties
;
349 public function setProperties($properties)
351 $this->populateProperties();
352 $this->_properties
= $properties;
356 * Accessor for the versionHistory property.
358 * @return VersionHistory the versionHistory for the node, null is none
360 public function getVersionHistory()
362 if ($this->_versionHistory
== null)
364 $this->_versionHistory
= new VersionHistory($this);
366 return $this->_versionHistory
;
369 public function getChildren()
371 if ($this->_children
== null)
373 $this->populateChildren();
375 return $this->_children +
$this->addedChildren
;
378 public function getParents()
380 if ($this->_parents
== null)
382 $this->populateParents();
384 return $this->_parents +
$this->addedParents
;
387 public function getPrimaryParent()
389 if ($this->_primaryParent
== null)
391 $this->populateParents();
393 return $this->_primaryParent
;
396 public function getAssociations()
398 if ($this->_associations
== null)
400 $this->populateAssociations();
402 return $this->_associations +
$this->addedAssociations
;
405 /** Methods used to populate node details from repository */
407 private function populateProperties()
409 if ($this->_isNewNode
== false && $this->_properties
== null)
411 $result = $this->_session
->repositoryService
->get(array (
414 "store" => $this->_store
->__toArray(),
415 "uuid" => $this->_id
))));
417 $this->populateFromWebServiceNode($result->getReturn
);
421 private function populateFromWebServiceNode($webServiceNode)
423 $this->_type
= $webServiceNode->type
;
426 $this->_aspects
= array();
427 $aspects = $webServiceNode->aspects
;
428 if (is_array($aspects) == true)
430 foreach ($aspects as $aspect)
432 $this->_aspects
[] = $aspect;
437 $this->_aspects
[] = $aspects;
440 // Set the property values
441 // NOTE: do we need to be concerned with identifying whether this is an array or not when there is
442 // only one property on a node
443 $this->_properties
= array();
444 foreach ($webServiceNode->properties
as $propertyDetails)
446 $name = $propertyDetails->name
;
447 $isMultiValue = $propertyDetails->isMultiValue
;
449 if ($isMultiValue == false)
451 $value = $propertyDetails->value
;
452 if ($this->isContentData($value) == true)
454 $value = new ContentData($this, $name);
459 $value = $propertyDetails->value
;
461 $this->_properties
[$name] = $value;
465 $this->origionalProperties
= $this->_properties
;
466 $this->addedAspects
= array();
467 $this->removedAspects
= array();
471 private function populateChildren()
473 // TODO should do some sort of limited pull here
474 $result = $this->_session
->repositoryService
->queryChildren(array("node" => $this->__toArray()));
475 $resultSet = $result->queryReturn
->resultSet
;
478 $map = $this->resultSetToMap($resultSet);
479 foreach($map as $value)
481 $id = $value["{http://www.alfresco.org/model/system/1.0}node-uuid"];
482 $store_scheme = $value["{http://www.alfresco.org/model/system/1.0}store-protocol"];
483 $store_address = $value["{http://www.alfresco.org/model/system/1.0}store-identifier"];
484 $assoc_type = $value["associationType"];
485 $assoc_name = $value["associationName"];
486 $isPrimary = $value["isPrimary"];
487 $nthSibling = $value["nthSibling"];
489 $child = $this->_session
->getNode(new Store($this->_session
, $store_address, $store_scheme), $id);
490 $children[$child->__toString()] = new ChildAssociation($this, $child, $assoc_type, $assoc_name, $isPrimary, $nthSibling);
493 $this->_children
= $children;
496 private function populateAssociations()
498 // TODO should do some sort of limited pull here
499 $result = $this->_session
->repositoryService
->queryAssociated(array("node" => $this->__toArray(),
500 "association" => array("associationType" => null,
501 "direction" => null)));
502 $resultSet = $result->queryReturn
->resultSet
;
504 $associations = array();
505 $map = $this->resultSetToMap($resultSet);
506 foreach($map as $value)
508 $id = $value["{http://www.alfresco.org/model/system/1.0}node-uuid"];
509 $store_scheme = $value["{http://www.alfresco.org/model/system/1.0}store-protocol"];
510 $store_address = $value["{http://www.alfresco.org/model/system/1.0}store-identifier"];
511 $assoc_type = $value["associationType"];
513 $to = $this->_session
->getNode(new Store($this->_session
, $store_address, $store_scheme), $id);
514 $associations[$to->__toString()] = new Association($this, $to, $assoc_type);
517 $this->_associations
= $associations;
520 private function populateParents()
522 // TODO should do some sort of limited pull here
523 $result = $this->_session
->repositoryService
->queryParents(array("node" => $this->__toArray()));
524 $resultSet = $result->queryReturn
->resultSet
;
527 $map = $this->resultSetToMap($resultSet);
528 foreach($map as $value)
530 $id = $value["{http://www.alfresco.org/model/system/1.0}node-uuid"];
531 $store_scheme = $value["{http://www.alfresco.org/model/system/1.0}store-protocol"];
532 $store_address = $value["{http://www.alfresco.org/model/system/1.0}store-identifier"];
533 $assoc_type = $value["associationType"];
534 $assoc_name = $value["associationName"];
535 $isPrimary = $value["isPrimary"];
536 $nthSibling = $value["nthSibling"];
538 $parent = $this->_session
->getNode(new Store($this->_session
, $store_address, $store_scheme), $id);
539 if ($isPrimary == "true" or $isPrimary == true)
541 $this->_primaryParent
= $parent;
543 $parents[$parent->__toString()] = new ChildAssociation($parent, $this, $assoc_type, $assoc_name, $isPrimary, $nthSibling);
546 $this->_parents
= $parents;
549 public function onBeforeSave(&$statements)
551 if ($this->_isNewNode
== true)
553 $childAssociation = $this->addedParents
[$this->_primaryParent
->__toString()];
555 $parentArray = array();
556 $parent = $this->_primaryParent
;
557 if ($parent->_isNewNode
== true)
559 $parentArray["parent_id"] = $parent->id
;
560 $parentArray["associationType"] = $childAssociation->type
;
561 $parentArray["childName"] = $childAssociation->name
;
565 $parentArray["parent"] = array(
566 "store" => $this->_store
->__toArray(),
567 "uuid" => $this->_primaryParent
->_id
,
568 "associationType" => $childAssociation->type
,
569 "childName" => $childAssociation->name
);
572 $this->addStatement($statements, "create",
573 array("id" => $this->_id
) +
576 "type" => $this->_type
,
577 "property" => $this->getPropertyArray($this->_properties
)));
581 // Add the update statement for the modified properties
582 $modifiedProperties = $this->getModifiedProperties();
583 if (count($modifiedProperties) != 0)
585 $this->addStatement($statements, "update", array("property" => $this->getPropertyArray($modifiedProperties)) +
$this->getWhereArray());
588 // TODO deal with any deleted properties
591 // Update any modified content properties
592 if ($this->_properties
!= null)
594 foreach($this->_properties
as $name=>$value)
596 if (($value instanceof ContentData
) && $value->isDirty
== true)
598 $value->onBeforeSave($statements, $this->getWhereArray());
603 // Add the addAspect statements
604 if ($this->addedAspects
!= null)
606 foreach($this->addedAspects
as $aspect)
608 $this->addStatement($statements, "addAspect", array("aspect" => $aspect) +
$this->getWhereArray());
612 // Add the removeAspect
613 if ($this->removedAspects
!= null)
615 foreach($this->removedAspects
as $aspect)
617 $this->addStatement($statements, "removeAspect", array("aspect" => $aspect) +
$this->getWhereArray());
621 // Add non primary children
622 foreach($this->addedChildren
as $childAssociation)
624 if ($childAssociation->isPrimary
== false)
627 $assocDetails = array("associationType" => $childAssociation->type
, "childName" => $childAssociation->name
);
630 if ($childAssociation->child
->_isNewNode
== true)
632 $temp["to_id"] = $childAssociation->child
->_id
;
633 $temp = $temp +
$assocDetails;
638 "store" => $this->_store
->__toArray(),
639 "uuid" => $childAssociation->child
->_id
) +
642 $temp = $temp +
$this->getWhereArray();
643 $this->addStatement($statements, "addChild", $temp);
648 foreach($this->addedAssociations
as $association)
650 $temp = array("association" => $association->type
);
651 $temp = $temp +
$this->getPredicateArray("from", $this) +
$this->getPredicateArray("to", $association->to
);
652 $this->addStatement($statements, "createAssociation", $temp);
656 private function addStatement(&$statements, $statement, $body)
659 if (array_key_exists($statement, $statements) == true)
661 $result = $statements[$statement];
664 $statements[$statement] = $result;
667 private function getWhereArray()
669 return $this->getPredicateArray("where", $this);
672 private function getPredicateArray($label, $node)
674 if ($node->_isNewNode
== true)
676 return array($label."_id" => $node->_id
);
682 "nodes" => $node->__toArray()
687 private function getPropertyArray($properties)
690 foreach ($properties as $name=>$value)
692 // Ignore content properties
693 if (($value instanceof ContentData
) == false)
695 // TODO need to support multi values
698 "isMultiValue" => false,
705 private function getModifiedProperties()
707 $modified = $this->_properties
;
708 $origional = $this->origionalProperties
;
710 if ($modified != null)
712 foreach ($modified as $key=>$value)
714 // Ignore content properties
715 if (($value instanceof ContentData
) == false)
717 if (array_key_exists($key, $origional) == true)
719 // Check to see if the value have been modified
720 if ($value != $origional[$key])
722 $result[$key] = $value;
727 $result[$key] = $value;
735 public function onAfterSave($idMap)
737 if (array_key_exists($this->_id
, $idMap ) == true)
739 $uuid = $idMap[$this->_id
];
746 if ($this->_isNewNode
== true)
748 $this->_isNewNode
= false;
750 // Clear the properties and aspect
751 $this->_properties
= null;
752 $this->_aspects
= null;
755 // Update any modified content properties
756 if ($this->_properties
!= null)
758 foreach($this->_properties
as $name=>$value)
760 if (($value instanceof ContentData
) && $value->isDirty
== true)
762 $value->onAfterSave();
767 $this->origionalProperties
= $this->_properties
;
769 if ($this->_aspects
!= null)
771 // Calculate the updated aspect list
772 if ($this->addedAspects
!= null)
774 $this->_aspects
= $this->_aspects +
$this->addedAspects
;
776 if ($this->removedAspects
!= null)
778 foreach ($this->_aspects
as $aspect)
780 if (in_array($aspect, $this->removedAspects
) == true)
782 $this->remove_array_value($aspect, $this->_aspects
);
787 $this->addedAspects
= array();
788 $this->removedAspects
= array();
790 if ($this->_parents
!= null)
792 $this->_parents
= $this->_parents +
$this->addedParents
;
794 $this->addedParents
= array();
796 if ($this->_children
!= null)
798 $this->_children
= $this->_children +
$this->addedChildren
;
800 $this->addedChildren
= array();
802 if ($this->_associations
!= null)
804 $this->_associations
= $this->_associations +
$this->addedAssociations
;
806 $this->addedAssociations
= array();