Commit c8f198dd6e2b3acf87dda2771b3f78404e5da9a7

Authored by Paul Barrett
1 parent 3ecc8d59

Update getProperties, deleteObject, deleteTree, getDescendants

Story ID:2295472. Update KT CMIS implementation to 1.0 compliance

Committed by: Paul Barrett
lib/api/ktcmis/ktNavigationService.inc.php
... ... @@ -80,19 +80,20 @@ class KTNavigationService extends KTCMISBase {
80 80 *
81 81 * @param string $repositoryId
82 82 * @param string $folderId
83   - * @param boolean $includeAllowableActions
84   - * @param boolean $includeRelationships
85   - * @param string $typeID
86 83 * @param int $depth
87 84 * @param string $filter
  85 + * @param boolean $includeRelationships
  86 + * @param string $renditionFilter
  87 + * @param boolean $includeAllowableAc
88 88 * @return array $descendants
89 89 */
90   - public function getDescendants($repositoryId, $folderId, $includeAllowableActions, $includeRelationships,
91   - $depth = 1, $typeID = 'Any', $filter = '')
  90 + public function getDescendants($repositoryId, $folderId, $depth = 2, $filter = '', $includeRelationships = false, $renditionFilter = '',
  91 + $includeAllowableActions = false, $includePathSegment = false)
92 92 {
93 93 // TODO optional parameters
94   - $descendantsResult = $this->NavigationService->getDescendants($repositoryId, $folderId, $includeAllowableActions,
95   - $includeRelationships, $depth);
  94 + $descendantsResult = $this->NavigationService->getDescendants($repositoryId, $folderId, $depth, $filter,
  95 + $includeRelationships = false, $renditionFilter = '',
  96 + $includeAllowableActions = false, $includePathSegment = false);
96 97  
97 98 if (PEAR::isError($descendantsResult))
98 99 {
... ... @@ -101,11 +102,11 @@ class KTNavigationService extends KTCMISBase {
101 102 "message" => "Failed getting descendants for folder"
102 103 );
103 104 }
104   -
  105 +
105 106 // format for webservices consumption
106 107 // NOTE this will almost definitely be changing in the future, this is just to get something working
107   - $descendants = CMISUtil::decodeObjectHierarchy($descendantsResult, 'child');
108   -
  108 + $descendants = CMISUtil::decodeObjectHierarchy($descendantsResult, 'children');
  109 +
109 110 return array (
110 111 "status_code" => 0,
111 112 "results" => $descendants
... ... @@ -126,7 +127,7 @@ class KTNavigationService extends KTCMISBase {
126 127 * @return array $descendants
127 128 */
128 129 public function getChildren($repositoryId, $folderId, $includeAllowableActions, $includeRelationships,
129   - $typeID = 'Any', $filter = '', $maxItems = 0, $skipCount = 0)
  130 + $typeID = 'Any', $filter = '', $maxItems = 0, $skipCount = 0)
130 131 {
131 132 // TODO paging
132 133 // TODO optional parameters
... ... @@ -140,7 +141,7 @@ class KTNavigationService extends KTCMISBase {
140 141 );
141 142 }
142 143  
143   - $children = CMISUtil::decodeObjectHierarchy($childrenResult, 'child');
  144 + $children = CMISUtil::decodeObjectHierarchy($childrenResult, 'children');
144 145  
145 146 return array(
146 147 "status_code" => 0,
... ... @@ -180,7 +181,7 @@ class KTNavigationService extends KTCMISBase {
180 181 );
181 182 }
182 183  
183   - $ancestry = CMISUtil::decodeObjectHierarchy($ancestryResult, 'child');
  184 + $ancestry = CMISUtil::decodeObjectHierarchy($ancestryResult, 'children');
184 185  
185 186 return array(
186 187 "status_code" => 0,
... ... @@ -211,7 +212,7 @@ class KTNavigationService extends KTCMISBase {
211 212 );
212 213 }
213 214  
214   - $ancestry = CMISUtil::decodeObjectHierarchy($ancestryResult, 'child');
  215 + $ancestry = CMISUtil::decodeObjectHierarchy($ancestryResult, 'children');
215 216  
216 217 return array(
217 218 "status_code" => 0,
... ...
lib/api/ktcmis/ktObjectService.inc.php
... ... @@ -80,17 +80,13 @@ class KTObjectService extends KTCMISBase {
80 80 *
81 81 * @param string $repositoryId
82 82 * @param string $objectId
83   - * @param boolean $includeAllowableActions
84   - * @param boolean $includeRelationships
85   - * @param string $returnVersion
86 83 * @param string $filter
87 84 * @return properties[]
88 85 */
89   - public function getProperties($repositoryId, $objectId, $includeAllowableActions, $includeRelationships,
90   - $returnVersion = false, $filter = '')
  86 + public function getProperties($repositoryId, $objectId, $filter = '')
91 87 {
92 88 try {
93   - $propertyCollection = $this->ObjectService->getProperties($repositoryId, $objectId, $includeAllowableActions,
  89 + $properties = $this->ObjectService->getProperties($repositoryId, $objectId, $includeAllowableActions,
94 90 $includeRelationships);
95 91 }
96 92 catch (Exception $e)
... ... @@ -101,8 +97,6 @@ class KTObjectService extends KTCMISBase {
101 97 );
102 98 }
103 99  
104   - $properties = CMISUtil::createObjectPropertiesEntry($propertyCollection);
105   -
106 100 return array(
107 101 "status_code" => 0,
108 102 "results" => $properties
... ... @@ -236,15 +230,13 @@ class KTObjectService extends KTCMISBase {
236 230 *
237 231 * @param string $repositoryId
238 232 * @param string $objectId
239   - * @param string $changeToken [optional]
  233 + * @param string $allVersions [optional] If true, delete all versions
240 234 * @return array
241 235 */
242   - // NOTE Invoking this service method on an object SHALL not delete the entire version series for a Document Object.
243   - // To delete an entire version series, use the deleteAllVersions() service
244   - public function deleteObject($repositoryId, $objectId, $changeToken = null)
  236 + public function deleteObject($repositoryId, $objectId, $allVersions = true)
245 237 {
246 238 try {
247   - $this->ObjectService->deleteObject($repositoryId, $objectId, $changeToken);
  239 + $this->ObjectService->deleteObject($repositoryId, $objectId, $allVersions);
248 240 }
249 241 catch (Exception $e)
250 242 {
... ...
lib/api/ktcmis/ktVersioningService.inc.php
... ... @@ -81,32 +81,6 @@ class KTVersioningService extends KTCMISBase {
81 81 }
82 82  
83 83 /**
84   - * Deletes all Document Objects in the specified Version Series, including the Private Working Copy
85   - *
86   - * @param string $repositoryId
87   - * @param string $versionSeriesId
88   - * @return boolean true if successful
89   - */
90   - public function deleteAllVersions($repositoryId, $versionSeriesId)
91   - {
92   - try {
93   - $result = $this->VersioningService->deleteAllVersions($repositoryId, $versionSeriesId);
94   - }
95   - catch (Exception $e)
96   - {
97   - return array(
98   - "status_code" => 1,
99   - "message" => $e->getMessage()
100   - );
101   - }
102   -
103   - return array(
104   - 'status_code' => 0,
105   - 'results' => $result
106   - );
107   - }
108   -
109   - /**
110 84 * Checks out a document and creates the PWC (Private Working Copy) which will represent the checked out document
111 85 *
112 86 * @param string $repositoryId
... ...
lib/api/ktcmis/services/CMISNavigationService.inc.php
... ... @@ -62,85 +62,108 @@ class CMISNavigationService {
62 62 }
63 63  
64 64 /**
65   - * Get descendents of the specified folder, up to the depth indicated
  65 + * Get direct children of the specified folder
66 66 *
67 67 * @param string $repositoryId
68 68 * @param string $folderId
69 69 * @param boolean $includeAllowableActions
70 70 * @param boolean $includeRelationships
71 71 * @param string $typeId
72   - * @param int $depth
73 72 * @param string $filter
  73 + * @param int $maxItems
  74 + * @param int $skipCount
74 75 * @return array $descendants
  76 + * MUST include (unless not requested) for each object:
  77 + * array $properties
  78 + * array $relationships
  79 + * array $renditions
  80 + * $allowableActions
  81 + * string $pathSegment
  82 + * boolean $hasMoreItems
  83 + * int $numItems [optional]
75 84 */
76   -
77   - // NOTE This method does NOT support paging as defined in the paging section
78 85 // NOTE If the Repository supports the optional โ€œVersionSpecificFiling๏ฟฝ๏ฟฝ? capability,
79 86 // then the repository SHALL return the document versions filed in the specified folder or its descendant folders.
80 87 // Otherwise, the latest version of the documents SHALL be returned.
81 88 // TODO FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid
82   - function getDescendants($repositoryId, $folderId, $includeAllowableActions, $includeRelationships,
83   - $depth = 1, $typeId = 'Any', $filter = '')
  89 + function getChildren($repositoryId, $folderId, $includeAllowableActions = null, $includeRelationships = null,
  90 + $typeId = 'Any', $filter = '', $maxItems = 0, $skipCount = 0, $orderBy = '', $renditionFilter = null,
  91 + $includePathSegment = false)
84 92 {
  93 + // TODO paging
85 94 // TODO optional parameters
86   - $descendants = array();
  95 + $children = array();
87 96 $repository = new CMISRepository($repositoryId);
88 97  
89   - // if this is not a folder, cannot get descendants
  98 + // if this is not a folder, cannot get children
90 99 $folderId = CMISUtil::decodeObjectId($folderId, $type);
91   -
92   - if ($type != 'Folder')
93   - {
94   - return $descendants;
  100 +
  101 + if ($type != 'Folder') {
  102 + throw new invalidArgumentException('The specified object is not a folder');
95 103 }
96 104  
97 105 $folder = $this->ktapi->get_folder_by_id($folderId);
98   - $descendants = $folder->get_listing($depth);
  106 + $children = $folder->get_listing();
99 107  
100   - // parse ktapi descendants result into a list of CMIS objects
101   - $descendants = CMISUtil::createChildObjectHierarchy($descendants, $repository->getRepositoryURI, $this->ktapi);
  108 + $children = CMISUtil::createChildObjectHierarchy($children, $repository->getRepositoryURI, $this->ktapi);
102 109  
103   - return $descendants;
  110 + return $children;
104 111 }
105 112  
106 113 /**
107   - * Get direct children of the specified folder
  114 + * Get descendents of the specified folder, up to the depth indicated
108 115 *
109 116 * @param string $repositoryId
110 117 * @param string $folderId
111   - * @param boolean $includeAllowableActions
112   - * @param boolean $includeRelationships
113   - * @param string $typeId
  118 + * @param int $depth
114 119 * @param string $filter
115   - * @param int $maxItems
116   - * @param int $skipCount
  120 + * @param boolean $includeRelationships
  121 + * @param string $renditionFilter
  122 + * @param boolean $includeAllowableActions
  123 + * @param boolean $includePathSegment
117 124 * @return array $descendants
  125 + * MUST include (unless not requested) for each object:
  126 + * array $properties
  127 + * array $relationships
  128 + * array $renditions
  129 + * $allowableActions
  130 + * string $pathSegment
118 131 */
  132 +
  133 + // NOTE This method does NOT support paging as defined in the paging section
119 134 // NOTE If the Repository supports the optional โ€œVersionSpecificFiling๏ฟฝ๏ฟฝ? capability,
120 135 // then the repository SHALL return the document versions filed in the specified folder or its descendant folders.
121 136 // Otherwise, the latest version of the documents SHALL be returned.
  137 + // NOTE If the Repository supports the optional capability capabilityMutlifiling and the same document is encountered
  138 + // multiple times in the hierarchy, then the repository MUST return that document each time is encountered.
  139 + // NOTE The default value for the $depth parameter is repository specific and SHOULD be at least 2 or -1
  140 + // Chosen 2 as the underlying code currently has no concept of digging all the way down
122 141 // TODO FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid
123   - function getChildren($repositoryId, $folderId, $includeAllowableActions = null, $includeRelationships = null,
124   - $typeId = 'Any', $filter = '', $maxItems = 0, $skipCount = 0, $orderBy = '', $renditionFilter = null, $includePathSegment = false)
  142 + function getDescendants($repositoryId, $folderId, $depth = 2, $filter = '', $includeRelationships = false, $renditionFilter = '',
  143 + $includeAllowableActions = false, $includePathSegment = false)
125 144 {
126   - // TODO paging
127   - // TODO optional parameters
128   - $children = array();
129   - $repository = new CMISRepository($repositoryId);
  145 + if ($depth == 0) {
  146 + throw new InvalidArgumentException('Invalid depth argument supplied');
  147 + }
130 148  
131   - // if this is not a folder, cannot get children
  149 + // if this is not a folder, cannot get descendants
132 150 $folderId = CMISUtil::decodeObjectId($folderId, $type);
133   -
  151 +
134 152 if ($type != 'Folder') {
135   - throw new invalidArgumentException('The specified object is not a folder');
  153 + throw new InvalidArgumentException('The supplied object is not a folder, unable to return descendants');
136 154 }
  155 +
  156 + // TODO optional parameters
  157 + $descendants = array();
  158 + $repository = new CMISRepository($repositoryId);
137 159  
138 160 $folder = $this->ktapi->get_folder_by_id($folderId);
139   - $children = $folder->get_listing();
  161 + $descendants = $folder->get_listing($depth);
140 162  
141   - $children = CMISUtil::createChildObjectHierarchy($children, $repository->getRepositoryURI, $this->ktapi);
  163 + // parse ktapi descendants result into a list of CMIS objects
  164 + $descendants = CMISUtil::createChildObjectHierarchy($descendants, $repository->getRepositoryURI, $this->ktapi);
142 165  
143   - return $children;
  166 + return $descendants;
144 167 }
145 168  
146 169 /**
... ...
lib/api/ktcmis/services/CMISObjectService.inc.php
... ... @@ -357,22 +357,15 @@ class CMISObjectService {
357 357 *
358 358 * @param string $repositoryId
359 359 * @param string $objectId
360   - * @param boolean $includeAllowableActions
361   - * @param boolean $includeRelationships
362   - * @param boolean $returnVersion
363 360 * @param string $filter
364 361 * @return object CMIS object properties
365 362 */
366 363 // TODO optional parameter support
367 364 // TODO FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid
368   - public function getProperties($repositoryId, $objectId, $includeAllowableActions, $includeRelationships,
369   - $returnVersion = false, $filter = '')
  365 + public function getProperties($repositoryId, $objectId, $filter = '')
370 366 {
371 367 $repository = new CMISRepository($repositoryId);
372   -
373   - // TODO a better default value?
374 368 $properties = array();
375   -
376 369 $objectId = CMISUtil::decodeObjectId($objectId, $typeId);
377 370  
378 371 if ($typeId == 'Unknown') {
... ... @@ -395,7 +388,8 @@ class CMISObjectService {
395 388 throw new ObjectNotFoundException('The requested object could not be found');
396 389 }
397 390  
398   - $properties = $CMISObject->getProperties();
  391 + $propertyCollection = $CMISObject->getProperties();
  392 + $properties = CMISUtil::createObjectPropertiesEntry($propertyCollection);
399 393  
400 394 return $properties;
401 395 }
... ... @@ -518,12 +512,11 @@ class CMISObjectService {
518 512 *
519 513 * @param string $repositoryId
520 514 * @param string $objectId
521   - * @param string $changeToken [optional]
522   - * @return boolean true on success (exception should be thrown otherwise)
  515 + * @param string $allVersions [optional] If true, delete all versions
523 516 */
524   - // NOTE Invoking this service method on an object SHALL not delete the entire version series for a Document Object.
525   - // To delete an entire version series, use the deleteAllVersions() service
526   - public function deleteObject($repositoryId, $objectId, $changeToken = null)
  517 + // NOTE Invoking this service method on an object SHALL not delete the entire version series for a Document Object
  518 + // if $allVersions is false.
  519 + public function deleteObject($repositoryId, $objectId, $allVersions = true)
527 520 {
528 521 // determine object type and internal id
529 522 $objectId = CMISUtil::decodeObjectId($objectId, $typeId);
... ... @@ -553,7 +546,7 @@ class CMISObjectService {
553 546 if (!$exists) {
554 547 throw new updateConflictException('Unable to delete the object as it cannot be found.');
555 548 }
556   -
  549 + global $default;
557 550 // throw ConstraintViolationException if method is invoked on a Folder object that contains one or more objects
558 551 if ($typeId == 'Folder')
559 552 {
... ... @@ -572,14 +565,17 @@ class CMISObjectService {
572 565 }
573 566 else if ($typeId == 'Document')
574 567 {
  568 + // NOTE KnowledgeTree does not support deleting of individual versions and will always delete all versions
  569 + // Throw an exception instead if individual version requested for delete
575 570 // since we do not allow deleting of only the latest version we must throw an exception when this function is called on any document
576 571 // which has more than one version. Okay to delete if there is only the one version.
577   - $versions = $object->get_version_history();
578   - if (count($versions) > 1)
579   - {
580   - // NOTE possibly may want to just throw a RuntimeException rather than this CMIS specific exception.
581   - throw new ConstraintViolationException('This function may not be used to delete an object which has multiple versions. '
582   - . 'Since the repository does not allow deleting of only the latest version, nothing can be deleted.');
  572 + if (!$allVersions) {
  573 + $versions = $object->get_version_history();
  574 + if (count($versions) > 1)
  575 + {
  576 + // NOTE possibly may want to just throw a RuntimeException rather than this CMIS specific exception.
  577 + throw new ConstraintViolationException('This repository does not allow deleting of only the latest version.');
  578 + }
583 579 }
584 580  
585 581 // do not allow deletion of a checked out document - this is actually handled by the ktapi code,
... ... @@ -605,9 +601,9 @@ class CMISObjectService {
605 601 * Deletes an entire tree including all subfolders and other filed objects
606 602 *
607 603 * @param string $repositoryId
608   - * @param string $objectId
609   - * @param string $changeToken [optional]
610   - * @param boolean $unfileNonfolderObject [optional] - note that since KnowledgeTree does not allow unfiling this will be ignored
  604 + * @param string $folderId
  605 + * @param boolean $unfileObjects [optional] unfile/deletesinglefiles/delete - note that since KnowledgeTree does not
  606 + * allow unfiling this will be ignored
611 607 * @param boolean $continueOnFailure [optional] - note that since KnowledgeTree does not allow continue on failure this will be ignored
612 608 * @return array $failedToDelete A list of identifiers of objects in the folder tree that were not deleted.
613 609 */
... ... @@ -616,57 +612,52 @@ class CMISObjectService {
616 612 // โ€ข This is not transactional.
617 613 // โ€ข However, if DeleteSingleFiled is chosen and some objects fail to delete, then single-filed objects are either deleted or kept,
618 614 // never just unfiled. This is so that a user can call this command again to recover from the error by using the same tree.
619   - public function deleteTree($repositoryId, $objectId, $changeToken = null, $unfileNonfolderObject = 'delete', $continueOnFailure = false)
  615 + // NOTE when $continueOnFailure is false, the repository SHOULD abort this method when it fails to delete a single child- or
  616 + // descendant-object
  617 + public function deleteTree($repositoryId, $folderId, $unfileObjects = 'delete', $continueOnFailure = false)
620 618 {
621 619 // NOTE since we do not currently allow partial deletes this will always be empty
622 620 // (unless there is a failure at the requested folder level - what do we do then? exception or array of all objects?)
623 621 $failedToDelete = array();
624 622  
625 623 // determine object type and internal id
626   - $objectId = CMISUtil::decodeObjectId($objectId, $typeId);
  624 + $folderId = CMISUtil::decodeObjectId($folderId, $typeId);
627 625  
628   - // throw updateConflictException if the operation is attempting to update an object that is no longer current (as determined by the repository).
629   - $exists = true;
  626 + // throw updateConflictException if the operation is attempting to update an object that is no longer current
  627 + // (as determined by the repository)
630 628 if ($typeId == 'Folder') {
631   - $object = $this->ktapi->get_folder_by_id($objectId);
  629 + $object = $this->ktapi->get_folder_by_id($folderId);
632 630 if (PEAR::isError($object)) {
633   - $exists = false;
  631 + throw new updateConflictException('Unable to delete the object as it cannot be found.');
634 632 }
635 633 }
636 634 // if not of type folder then we have a general problem, throw exception
637 635 else {
638 636 throw new RuntimeException('Cannot call deleteTree on a non-folder object.');
639 637 }
640   -
641   - if (!$exists) {
642   - throw new updateConflictException('Unable to delete the object as it cannot be found.');
643   - }
644 638  
645 639 // attempt to delete tree, throw RuntimeException if failed
646 640 // TODO add a default reason
647 641 // TODO add the electronic signature capability
648   - $result = $this->ktapi->delete_folder($objectId, $reason, $sig_username, $sig_password);
649   - // if there was an error performing the delete, throw exception
650   - // TODO list of objects which failed in $failedToDelete array;
651   - // since we do not delete the folder or any contents if anything cannot be deleted, this will contain the entire tree listing
652   - // NOTE once we do this we will need to deal with it externally as well, since we can no longer just catch an exception.
653   - if ($result['status_code'] == 1)
  642 + // TODO support of $continueOnFailure == false - this is not supported by the underlying code and so is left out for now
  643 + $result = $this->ktapi->delete_folder($folderId, $reason, $sig_username, $sig_password);
  644 + // if there was an error performing the delete, list objects not deleted
  645 + if ($result['status_code'] == 1)
654 646 {
655 647 // TODO consider sending back full properties on each object?
656 648 // Not sure yet what this output may be used for by a client, and the current specification (0.61c) says:
657 649 // "A list of identifiers of objects in the folder tree that were not deleted", so let's leave it returning just ids for now.
658   - $failedToDelete[] = CMISUtil::encodeObjectId(FOLDER, $objectId);
  650 + $failedToDelete[] = CMISUtil::encodeObjectId(FOLDER, $folderId);
659 651 $folderContents = $object->get_full_listing();
660 652 foreach($folderContents as $folderObject)
661 653 {
662   - if ($folderObject['item_type'] == 'F') $type = 'Folder';
663   - else if ($folderObject['item_type'] == 'D') $type = 'Document';
664   - // TODO deal with non-folder and non-document content
665   - else continue;
  654 + if ($folderObject['item_type'] == 'F') {
  655 + $type = 'Folder';
  656 + }
  657 + else if ($folderObject['item_type'] == 'D') {
  658 + $type = 'Document';
  659 + }
666 660  
667   - // TODO find out whether this is meant to be a hierarchical list or simply a list.
668   - // for now we are just returning the list in non-hierarchical form
669   - // (seeing as we don't really know how CMIS AtomPub is planning to deal with hierarchies at this time.)
670 661 $failedToDelete[] = CMISUtil::encodeObjectId($type, $folderObject['id']);
671 662 }
672 663 }
... ... @@ -674,14 +665,8 @@ class CMISObjectService {
674 665 return $failedToDelete;
675 666 }
676 667  
677   - // NOTE this function is presently incomplete and untested. Completion deferred to implementation of Checkout/Checkin
678   - // functionality
679   - // NOTE I am not sure yet when this function would ever be called - checkin would be able to update the content stream
680   - // already and the only easy method we have (via KTAPI as it stands) to update the content is on checkin anyway.
681   - // Additionally this function doesn't take a value for the versioning status (major/minor) and so cannot pass it
682   - // on to the ktapi checkin function.
683   - // I imagine this function may be called if we ever allow updating document content independent of checkin,
684   - // or if we change some of the underlying code and call direct to the document functions and not via KTAPI.
  668 + // NOTE this function is presently incomplete and untested. Completion deferred until Knowledgetree supports setting
  669 + // content streams independent of checkin (which may be necessary for proper client interaction with some clients)
685 670 /**
686 671 * Sets the content stream data for an existing document
687 672 *
... ...
lib/api/ktcmis/services/CMISVersioningService.inc.php
... ... @@ -25,49 +25,6 @@ class CMISVersioningService {
25 25 }
26 26  
27 27 /**
28   - * Deletes all Document Objects in the specified Version Series, including the Private Working Copy if it exists
29   - *
30   - * @param string $repositoryId
31   - * @param string $versionSeriesId
32   - * @return boolean true if successful
33   - */
34   - // NOTE For KnowledgeTree the $versionSeriesId should be the latest version, if not it will be taken as implied.
35   - // Should we decide to implement the ability to delete individual versions,
36   - // then an exception may be thrown under certain circumstances (to be determined)
37   - // NOTE I am not really sure how this is going to be handled by CMIS clients.
38   - // Testing with CMISSpaces we have it sending the actual document id, not a version series id.
39   - // This may be due to the data sent back from our code, or it may just be how CMISSpaces does it.
40   - // There is a note in their source code about this.
41   - // Meantime we will try based on document id and adjust as needed later
42   - public function deleteAllVersions($repositoryId, $versionSeriesId)
43   - {
44   - // attempt to delete based on versionSeriesId as document/object id
45   - // determine object type and internal id
46   - $objectId = CMISUtil::decodeObjectId($versionSeriesId, $typeId);
47   -
48   - // if not a versionable object, throw exception
49   - // NOTE that we are assuming only documents are versionable at the moment
50   - if ($typeId != 'Document') {
51   - throw new RuntimeException('The object type is not versionable and cannot be deleted using deleteAllVersions.');
52   - }
53   -
54   - // try to delete
55   - // TODO add a default reason
56   - // TODO add the electronic signature capability
57   - $auth_sig = true;
58   - $result = $this->ktapi->delete_document($objectId, $reason, $auth_sig, $sig_username, $sig_password);
59   -
60   - // TODO delete any PWC which may exist (NOTE added 24 August 2009 - we did not have any PWC functionality when this function was originally created)
61   -
62   - // if there was an error performing the delete, throw exception
63   - if ($result['status_code'] == 1) {
64   - throw new RuntimeException('There was an error deleting the object: ' . $result['message']);
65   - }
66   -
67   - return true;
68   - }
69   -
70   - /**
71 28 * Checks out a document and creates the PWC (Private Working Copy) which will represent the checked out document
72 29 *
73 30 * @param string $repositoryId
... ...
lib/api/ktcmis/util/CMISUtil.inc.php
... ... @@ -182,16 +182,15 @@ class CMISUtil {
182 182 $CMISArray[$count]['object'] = $CMISObject;
183 183  
184 184 // if sub-array
185   - if (count($object['items']) > 0)
186   - {
187   - $CMISArray[$count]['items'] = self::createChildObjectHierarchy($object['items'], $repositoryURI, $ktapi);
  185 + if (count($object['items']) > 0) {
  186 + $CMISArray[$count]['children'] = self::createChildObjectHierarchy($object['items'], $repositoryURI, $ktapi);
188 187 }
189 188 }
190 189 else
191 190 {
192 191 // NOTE why is this necessary? That's what you get for not commenting it at the time
193 192 // TODO comment this properly, once we know why it is happening
194   - $CMISArray[$count] = self::createChildObjectHierarchy($object, $repositoryURI, $ktapi);
  193 +// $CMISArray[$count] = self::createChildObjectHierarchy($object, $repositoryURI, $ktapi);
195 194 }
196 195 }
197 196 }
... ... @@ -226,9 +225,8 @@ class CMISUtil {
226 225 $CMISElement['object'] = $CMISObject;
227 226  
228 227 // if more parent elements
229   - if (count($input) > 0)
230   - {
231   - $CMISElement['items'] = self::createParentObjectHierarchy($input, $repositoryURI, $ktapi);
  228 + if (count($input) > 0) {
  229 + $CMISElement['parents'] = self::createParentObjectHierarchy($input, $repositoryURI, $ktapi);
232 230 }
233 231  
234 232 $CMISArray[] = $CMISElement;
... ... @@ -245,10 +243,10 @@ class CMISUtil {
245 243 * though the output may well be different to what went into that function
246 244 *
247 245 * @param array $input // input hierarchy to decode
248   - * @param string $linkText // 'child' or 'parent' - indicates direction of hierarchy => descending or ascending
  246 + * @param string $linkText // 'children' or 'parents' - indicates direction of hierarchy => descending or ascending
249 247 * @return array $hierarchy
250 248 */
251   - static public function decodeObjectHierarchy($input, $linkText)
  249 + static public function decodeObjectHierarchy($input, $linkText = 'children')
252 250 {
253 251 $hierarchy = array();
254 252  
... ... @@ -257,8 +255,11 @@ class CMISUtil {
257 255 {
258 256 $object = $entry['object'];
259 257 $properties = $object->getProperties();
260   -
261 258 $hierarchy[$key] = self::createObjectPropertiesEntry($properties);
  259 +
  260 + if (isset($entry[$linkText]) && count($entry[$linkText])) {
  261 + $hierarchy[$key][$linkText] = self::decodeObjectHierarchy($entry[$linkText], $linkText);
  262 + }
262 263 }
263 264  
264 265 return $hierarchy;
... ... @@ -288,19 +289,6 @@ class CMISUtil {
288 289 }
289 290 }
290 291  
291   - /* what was this for and is it still needed? */
292   - /*
293   - // if we have found a child/parent with one or more children/parents, recurse into the child/parent object
294   - if (count($entry['items']) > 0) {
295   - $object[$linkText] = self::decodeObjectHierarchy($entry['items'], $linkText);
296   - }
297   - // NOTE may need to set a null value here in case webservices don't like it unset
298   - // so we'll set it just in case...
299   - else {
300   - $object[$linkText] = null;
301   - }
302   - */
303   -
304 292 return $object;
305 293 }
306 294  
... ...
webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php
... ... @@ -84,7 +84,7 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
84 84 $response = $response['results'];
85 85 }
86 86  
87   - $folderName = $response['properties']['Name']['value'];
  87 + $folderName = $response['properties']['name']['value'];
88 88 }
89 89 // NOTE parent changes to parents in later specification
90 90 // TODO update when updating to later specification
... ... @@ -262,13 +262,14 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
262 262 // NOTE due to the way KnowledgeTree works with folders this is always going to call deleteTree.
263 263 // we COULD call deleteObject but when we delete a folder we expect to be trying to delete
264 264 // the folder and all content.
  265 + // TODO determine whether client is requesting deleteObject or deleteTree
265 266  
266 267 $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService);
267 268  
268 269 $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt());
269 270  
270   - // attempt delete
271   - $response = $ObjectService->deleteTree($repositoryId, $this->params[0]);
  271 + // attempt delete - last parameter sets $deleteAllVersions true
  272 + $response = $ObjectService->deleteTree($repositoryId, $this->params[0], 'delete', true);
272 273  
273 274 // error?
274 275 if ($response['status_code'] == 1) {
... ... @@ -300,7 +301,6 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
300 301 $propertiesElement->appendChild($propElement);
301 302 $objectElement->appendChild($propertiesElement);
302 303 $entry->appendChild($objectElement);
303   -// $entry->appendChild($feed->newElement('cmis:terminator'));
304 304 }
305 305  
306 306 $this->responseFeed = $feed;
... ... @@ -326,7 +326,8 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
326 326 $entries = $NavigationService->getChildren($repositoryId, $folderId, false, false);
327 327 }
328 328 else if ($feedType == 'descendants') {
329   - $entries = $NavigationService->getDescendants($repositoryId, $folderId, false, false);
  329 + // TODO how will client request depth?
  330 + $entries = $NavigationService->getDescendants($repositoryId, $folderId);
330 331 }
331 332 else {
332 333 // error, we shouldn't be here, if we are then the wrong service/function was called
... ... @@ -362,9 +363,7 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
362 363 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId));
363 364 $feed->appendChild($link);
364 365  
365   - foreach($entries as $cmisEntry) {
366   - KT_cmis_atom_service_helper::createObjectEntry($feed, $cmisEntry, $folderName);
367   - }
  366 + KT_cmis_atom_service_helper::createObjectFeed($feed, $entries, $folderName);
368 367  
369 368 $feed->newField('cmis:hasMoreItems', 'false', $feed);
370 369  
... ... @@ -435,16 +434,14 @@ class KT_cmis_atom_service_document extends KT_cmis_atom_service {
435 434 * @return 204 on success, 500 on error
436 435 */
437 436 public function DELETE_action()
438   - {
439   - // NOTE due to the way KnowledgeTree works with documents this is always going to call deleteAllVersions.
440   - // we do not have support for deleting only specific versions (this may be added in the future.)
441   -
  437 + {
442 438 $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService);
443 439  
444 440 $VersioningService = new KTVersioningService(KT_cmis_atom_service_helper::getKt());
445   -
  441 + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt());
  442 +
446 443 // attempt delete
447   - $response = $VersioningService->deleteAllVersions($repositoryId, $this->params[0]);
  444 + $response = $ObjectService->deleteObject($repositoryId, $this->params[0]);
448 445  
449 446 if ($response['status_code'] == 1) {
450 447 $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response['message']);
... ...
webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php
... ... @@ -90,13 +90,20 @@ class KT_cmis_atom_service_helper {
90 90  
91 91 return $response;
92 92 }
  93 +
  94 + static public function createObjectFeed(&$feed, $entries, $folderName)
  95 + {
  96 + foreach($entries as $cmisEntry) {
  97 + self::createObjectEntry($feed, $cmisEntry, $folderName);
  98 + }
  99 + }
93 100  
94 101 /**
95 102 * Creates an AtomPub entry for a CMIS entry and adds it to the supplied feed
96 103 *
97 104 * @param object $feed The feed to which we add the entry
98 105 * @param array $cmisEntry The entry data
99   - * @param string $parent The parent folder
  106 + * @param string $parent The parent folder - this appears to be unused, not sure what it was meant for
100 107 * @param boolean $pwc Whether this is a PWC object
101 108 * @param $method The request method used (POST/GET/...)
102 109 */
... ... @@ -118,6 +125,54 @@ class KT_cmis_atom_service_helper {
118 125 $entry->appendChild($response->newAttr('xmlns:cmisra', 'http://docs.oasis-open.org/ns/cmis/restatom/200908/'));
119 126 }
120 127  
  128 + self::createObjectEntryContent($entry, $response, $cmisEntry, $parent, $pwc, $method);
  129 + }
  130 +
  131 + /**
  132 + * Creates an AtomPub child feed for a CMIS entry and adds it to the supplied entry
  133 + *
  134 + * @param $feed The child feed element
  135 + * @param $entries Entried contained within the child feed element
  136 + * @param $folderName The parent folder name (currently unused)
  137 + */
  138 + static public function createObjectChildrenFeed(&$childrenFeed, $entries, $workspace, $feed, $folderName)
  139 + {
  140 + foreach($entries as $cmisEntry) {
  141 + self::createChildObjectEntry($childrenFeed, $cmisEntry, $workspace, $feed, $folderName);
  142 + }
  143 + }
  144 +
  145 + /**
  146 + * Creates an AtomPub child feed for a CMIS entry and adds it to the supplied entry
  147 + *
  148 + * @param object $entry The entry to which we add the child feed
  149 + * @param array $cmisEntry The object entry data
  150 + */
  151 + // NOTE this approach appears to be necessary due to the structure of the underlying atompub code and specification,
  152 + // which does not directly support nesting - attempting to create a new feed and append it within the outer
  153 + // feed resulted in an empty cmisra:children node, so this approach was substituted
  154 + static public function createChildObjectEntry(&$childrenFeed, $cmisEntry, $workspace, $response, $folderNam)
  155 + {
  156 + $type = strtolower($cmisEntry['properties']['objectTypeId']['value']);
  157 +
  158 + // create entry
  159 + $entry = $response->newElement('entry');
  160 + self::createObjectEntryContent($entry, $response, $cmisEntry);//, $parent, $pwc, $method);
  161 + $childrenFeed->appendChild($entry);
  162 + }
  163 +
  164 + /**
  165 + * Creates the actual object entry: this is shared between other functions which require this content
  166 + *
  167 + * @param object $entry The entry object
  168 + * @param object $response The response feed
  169 + * @param array $cmisEntry The CMIS object content
  170 + * @param string $parent The parent folder name
  171 + * @param boolean $pwc Whether this is a PWC object (will be returned slightly differently)
  172 + * @param string $method The calling method (slightly affects the output)
  173 + */
  174 + static public function createObjectEntryContent($entry, &$response, $cmisEntry, $parent = '', $pwc = false, $method = 'GET')
  175 + {
121 176 // TODO dynamic actual creator name
122 177 $responseElement = $response->newField('author');
123 178 $element = $response->newField('name', 'admin', $responseElement);
... ... @@ -303,13 +358,23 @@ class KT_cmis_atom_service_helper {
303 358 $objectElement->appendChild($propertiesElement);
304 359 $entry->appendChild($objectElement);
305 360  
306   - // after every entry, append a cmis:terminator tag
307   -// $entry->appendChild($response->newElement('cmis:terminator'));
308   -
309 361 // TODO check determination of when to add app:edited tag
310 362 // if ($method == 'POST') {
311 363 $entry->appendChild($response->newElement('app:edited', self::formatDatestamp()));
312 364 // }
  365 +
  366 + // TODO pathSegment entry
  367 +
  368 + // deal with child objects
  369 + if (isset($cmisEntry['children'])) {
  370 + // add children node and fill with child entries
  371 + $childrenFeed = $response->newElement('feed');
  372 + self::createObjectChildrenFeed($childrenFeed, $cmisEntry['children'], $workspace, $response, '' /*folderName not passed through*/);
  373 +
  374 + $childrenElement = $response->newElement('cmisra:children');
  375 + $childrenElement->appendChild($childrenFeed);
  376 + $entry->appendChild($childrenElement);
  377 + }
313 378 }
314 379  
315 380 /**
... ...
webservice/classes/atompub/cmis/KT_cmis_atom_responseFeed.inc.php
... ... @@ -38,55 +38,6 @@ class KT_cmis_atom_responseFeed extends KT_atom_responseFeed {
38 38 $this->feed->appendChild($element);
39 39 }
40 40  
41   - // this is ALL going away...adjust all calling code...
42   - /*
43   - protected function constructHeader()
44   - {
45   - if (!is_null($this->id))
46   - {
47   - $this->newId($this->id, $this->feed);
48   - }
49   -
50   - $link = $this->newElement('link');
51   - $link->appendChild($this->newAttr('rel','self'));
52   - $link->appendChild($this->newAttr('href', $this->baseURI . trim($_SERVER['QUERY_STRING'], '/')));
53   - $feed->appendChild($link);
54   -
55   - if (!is_null($this->title))
56   - {
57   - $this->feed->appendChild($this->newElement('title', $this->title));
58   - }
59   -
60   - $this->DOM->appendChild($this->feed);
61   - }
62   -
63   - public function &newId($id, $entry = null)
64   - {
65   - $id = $this->newElement('id', $id);
66   - if(isset($entry))$entry->appendChild($id);
67   - return $id;
68   - }
69   -
70   - public function &newField($name = NULL, $value = NULL, &$entry = NULL)
71   - {
72   - $append = false;
73   -
74   - if(func_num_args() > 3)
75   - {
76   - $append = ((func_get_arg(3) === true) ? true : false);
77   - }
78   -
79   - $field = $this->newElement($name, $value);
80   -
81   - if (isset($entry)) $entry->appendChild($field);
82   - else if ($append) $this->feed->appendChild($field);
83   -
84   - return $field;
85   - }
86   -\
87   - *
88   - */
89   -
90 41 }
91 42  
92 43 class KT_cmis_atom_ResponseFeed_GET extends KT_cmis_atom_responseFeed{}
... ...
webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php
... ... @@ -160,26 +160,15 @@ class KT_cmis_atom_server extends KT_atom_server {
160 160  
161 161 public function render()
162 162 {
163   - ob_end_clean();
164   - if (!$this->headersSet) header('Content-type: text/xml');
165   -
166   - //include('/var/www/atompub_response.xml');
167   -
168   -// if (false && preg_match('/F1\-children/', $this->output)) {
169   -// readfile('C:\Users\Paul\Documents\Downloads\alfresco folder tree atompub response.xml');
170   -// }
171   -// else if (false && preg_match('/urn:uuid:checkedout/', $this->output)) {
172   -// readfile('C:\Users\Paul\Documents\Downloads\alfresco checkedout atompub response.xml');
173   -// }
174   -// else if (false && preg_match('/urn:uuid:types\-all/', $this->output)) {
175   -// readfile('C:\Users\Paul\Documents\Downloads\alfresco types atompub response.xml');
176   -// }
177   -// else if (false && preg_match('/\<service\>/', $this->output)) {
178   -// readfile('C:\Users\Paul\Documents\Downloads\cmis_mod_kt.xml');
179   -// }
180   -// else {
181   - if ($this->renderBody) echo $this->output;
182   -// }
  163 + ob_end_clean();
  164 +
  165 + if (!$this->headersSet) {
  166 + header('Content-type: text/xml');
  167 + }
  168 +
  169 + if ($this->renderBody) {
  170 + echo $this->output;
  171 + }
183 172 }
184 173  
185 174 }
... ...