diff --git a/ktatompub/services/cmis/NavigationService.inc.php b/ktatompub/services/cmis/NavigationService.inc.php index a0a8c6e..fe47e18 100644 --- a/ktatompub/services/cmis/NavigationService.inc.php +++ b/ktatompub/services/cmis/NavigationService.inc.php @@ -99,6 +99,26 @@ class NavigationService extends KTNavigationService { } } + /** + * Returns a list of checked out documents from the selected repository + * + * @param string $repositoryId + * @param string $folderId The folder for which checked out docs are requested + * @param string $filter + * @param int $maxItems + * @param int $skipCount + * @return array $checkedout The collection of checked out documents + */ + function getCheckedoutDocs($repositoryId, $folderId = null, $filter = '', $maxItems = 0, $skipCount = 0) + { + $checkedout = parent::getObjectParents($repositoryId, $folderId, $filter, $maxItems, $skipCount); + + if ($result['status_code'] == 0) + { + return $result['results']; + } + } + } ?> diff --git a/ktatompub/services/cmis/checkedout.inc.php b/ktatompub/services/cmis/checkedout.inc.php index 4178dd7..308baff 100644 --- a/ktatompub/services/cmis/checkedout.inc.php +++ b/ktatompub/services/cmis/checkedout.inc.php @@ -20,8 +20,28 @@ $NavigationService->startSession($username, $password); $repositories = $RepositoryService->getRepositories(); $repositoryId = $repositories[0]['repositoryId']; +$checkedout = $NavigationService->getCheckedoutDocs($repositoryId); + $feed = new KTCMISAPPFeed(KT_APP_BASE_URI, 'Checked out Documents', null, null, null, 'urn:uuid:checkedout'); +foreach($checkedout as $document) +{ + $entry = $feed->newEntry(); + $objectElement = $feed->newElement('cmis:object'); + $propertiesElement = $feed->newElement('cmis:properties'); + + foreach($cmisEntry['properties'] as $propertyName => $property) + { + $propElement = $feed->newElement('cmis:' . $property['type']); + $propElement->appendChild($feed->newAttr('cmis:name', $propertyName)); + $feed->newField('value', CMISUtil::boolToString($property['value']), $propElement); + $propertiesElement->appendChild($propElement); + } + + $objectElement->appendChild($propertiesElement); + $entry->appendChild($objectElement); +} + $entry = null; $feed->newField('hasMoreItems', 'false', $entry, true); diff --git a/ktatompub/services/cmis/folder.inc.php b/ktatompub/services/cmis/folder.inc.php index 10330db..24ced47 100644 --- a/ktatompub/services/cmis/folder.inc.php +++ b/ktatompub/services/cmis/folder.inc.php @@ -444,6 +444,7 @@ class CMISFolderFeed extends CMISObjectFeed { // false $output = $feed->getAPPdoc(); + $outputs = ' diff --git a/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php b/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php index 8bda862..93eb76a 100644 --- a/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php +++ b/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php @@ -54,8 +54,6 @@ abstract class CMISPropertyCollection { static $LastModifiedBy; static $LastModificationDate; static $ChangeToken; - static $ContentStreamLength; - static $ContentStreamMimeType; // TODO these definitions probably belong elsewhere, but here will do for now static $propertyTypes; diff --git a/lib/api/ktcmis/classes/CMISRepository.inc.php b/lib/api/ktcmis/classes/CMISRepository.inc.php index 2cabc84..a4b34fa 100644 --- a/lib/api/ktcmis/classes/CMISRepository.inc.php +++ b/lib/api/ktcmis/classes/CMISRepository.inc.php @@ -167,44 +167,6 @@ class CMISRepository { return $this->objectTypes; } -// // TODO this function MUST accept the same arguments as the calling functions and respond accordingly -// // TODO consider moving this into the RepositoryService class: -// // anything which requires a repository id seems like it does not belong in the repository class -// function getTypes() -// { -// $objectTypes = $this->objectTypes->getObjectTypes(); -// -// // fetch the attributes for each type -// foreach ($objectTypes as $key => $objectType) -// { -// $objectTypes[$key] = $this->getTypeDefinition($this->repositoryId, $objectType); -// } -// -// return $objectTypes; -// } - -// // TODO consider moving this into the RepositoryService class: -// // anything which requires a repository id seems like it does not belong in the repository class -// function getTypeDefinition($repositoryId, $typeId) -// { -// // TODO is this the best way of doing this? -// switch ($typeId) -// { -// case 'Document': -// require_once(CMIS_DIR . '/objecttypes/CMISDocumentObject.inc.php'); -// $tmpObject = new DocumentObject(); -// $objectAttributes = $tmpObject->getProperties(); -// break; -// case 'Folder': -// require_once(CMIS_DIR . '/objecttypes/CMISFolderObject.inc.php'); -// $tmpObject = new FolderObject(); -// $objectAttributes = $tmpObject->getProperties(); -// break; -// } -// -// return $objectAttributes; -// } - } ?> diff --git a/lib/api/ktcmis/exceptions/ConstraintViolationException.inc.php b/lib/api/ktcmis/exceptions/ConstraintViolationException.inc.php new file mode 100644 index 0000000..1b7a619 --- /dev/null +++ b/lib/api/ktcmis/exceptions/ConstraintViolationException.inc.php @@ -0,0 +1,7 @@ + diff --git a/lib/api/ktcmis/exceptions/InvalidArgumentException.inc.php b/lib/api/ktcmis/exceptions/InvalidArgumentException.inc.php new file mode 100644 index 0000000..53f7390 --- /dev/null +++ b/lib/api/ktcmis/exceptions/InvalidArgumentException.inc.php @@ -0,0 +1,7 @@ + diff --git a/lib/api/ktcmis/exceptions/ObjectNotFoundException.inc.php b/lib/api/ktcmis/exceptions/ObjectNotFoundException.inc.php new file mode 100644 index 0000000..52291eb --- /dev/null +++ b/lib/api/ktcmis/exceptions/ObjectNotFoundException.inc.php @@ -0,0 +1,7 @@ + diff --git a/lib/api/ktcmis/exceptions/StorageException.inc.php b/lib/api/ktcmis/exceptions/StorageException.inc.php new file mode 100644 index 0000000..ba8b912 --- /dev/null +++ b/lib/api/ktcmis/exceptions/StorageException.inc.php @@ -0,0 +1,7 @@ + diff --git a/lib/api/ktcmis/ktcmis.inc.php b/lib/api/ktcmis/ktcmis.inc.php index 6cc8d3a..8b38935 100644 --- a/lib/api/ktcmis/ktcmis.inc.php +++ b/lib/api/ktcmis/ktcmis.inc.php @@ -85,7 +85,7 @@ class KTCMISBase { return $this->session; } - // TODO what about destroying sessions? + // TODO what about destroying sessions? only on logout (which is not offered by the CMIS clients tested so far) } /** @@ -152,8 +152,8 @@ class KTRepositoryService extends KTCMISBase { ); } - // TODO output this manually, the function works but only for some objects so rather avoid it completely - // NOTE the fact that it works for this instance is irrelevant... + // TODO output this manually, the function works but only for some objects so rather avoid it completely? + // NOTE the problems appear to be due to recursive objects return array ( "status_code" => 0, "results" => CMISUtil::objectToArray($repositoryInfo) @@ -168,13 +168,15 @@ class KTRepositoryService extends KTCMISBase { public function getTypes($repositoryId, $typeId = '', $returnPropertyDefinitions = false, $maxItems = 0, $skipCount = 0, &$hasMoreItems = false) { - $repositoryObjectTypeResult = $this->RepositoryService->getTypes($repositoryId, $typeId, $returnPropertyDefinitions, - $maxItems, $skipCount, $hasMoreItems); - if (PEAR::isError($repositoryObjectTypeResult)) + try { + $repositoryObjectTypeResult = $this->RepositoryService->getTypes($repositoryId, $typeId, $returnPropertyDefinitions, + $maxItems, $skipCount, $hasMoreItems); + } + catch (Exception $e) { return array( "status_code" => 1, - "message" => "Failed getting supported object types" + "message" => $e->getMessage() ); } @@ -202,13 +204,14 @@ class KTRepositoryService extends KTCMISBase { */ public function getTypeDefinition($repositoryId, $typeId) { - $typeDefinitionResult = $this->RepositoryService->getTypeDefinition($repositoryId, $typeId); - - if (PEAR::isError($typeDefinitionResult)) + try { + $typeDefinitionResult = $this->RepositoryService->getTypeDefinition($repositoryId, $typeId); + } + catch (Exception $e) { return array( "status_code" => 1, - "message" => "Failed getting object type definition for $typeId" + "message" => $e->getMessage() ); } @@ -380,6 +383,35 @@ class KTNavigationService extends KTCMISBase { ); } + /** + * Returns a list of checked out documents from the selected repository + * + * @param string $repositoryId + * @param string $folderId The folder for which checked out docs are requested + * @param string $filter + * @param int $maxItems + * @param int $skipCount + * @return array $checkedout The collection of checked out documents + */ + function getCheckedoutDocs($repositoryId, $folderId = null, $filter = '', $maxItems = 0, $skipCount = 0) + { + $checkedout = $this->NavigationService->getObjectParents($repositoryId, $objectId, $includeAllowableActions, + $includeRelationships); + + if (PEAR::isError($ancestryResult)) + { + return array( + "status_code" => 1, + "message" => "Failed getting list of checked out documents" + ); + } + + return array( + "status_code" => 0, + "results" => $checkedout + ); + } + } /** @@ -415,13 +447,15 @@ class KTObjectService extends KTCMISBase { public function getProperties($repositoryId, $objectId, $includeAllowableActions, $includeRelationships, $returnVersion = false, $filter = '') { - $propertyCollection = $this->ObjectService->getProperties($repositoryId, $objectId, $includeAllowableActions, $includeRelationships); - - if (PEAR::isError($propertiesResult)) + try { + $propertyCollection = $this->ObjectService->getProperties($repositoryId, $objectId, $includeAllowableActions, + $includeRelationships); + } + catch (Exception $e) { return array( "status_code" => 1, - "message" => "Failed getting properties for object" + "message" => $e->getMessage() ); } @@ -446,13 +480,14 @@ class KTObjectService extends KTCMISBase { { $objectId = null; - $objectId = $this->ObjectService->createFolder($repositoryId, $typeId, $properties, $folderId); - - if (PEAR::isError($propertiesResult)) + try { + $objectId = $this->ObjectService->createFolder($repositoryId, $typeId, $properties, $folderId); + } + catch (Exception $e) { return array( "status_code" => 1, - "message" => "Failed getting properties for object" + "message" => $e->getMessage() ); } diff --git a/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php b/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php index db7eaa0..7f45780 100644 --- a/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php +++ b/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php @@ -54,7 +54,7 @@ class CMISDocumentObject extends CMISBaseObject { private $uri; // TODO some of this should probably come from configuration files as it is repository specific - function CMISDocumentObject(&$ktapi = null, $uri = null) + function __construct($documentId = null, &$ktapi = null, $uri = null) { $this->ktapi = $ktapi; // uri to use for document links @@ -92,12 +92,16 @@ class CMISDocumentObject extends CMISBaseObject { // parent::__construct(); // set document specific property definitions - + + if (!is_null($documentId)) + { + $this->_get($documentId); + } } - function get($objectId) + private function _get($documentId) { - $object = $this->ktapi->get_document_by_id($objectId); + $object = $this->ktapi->get_document_by_id($documentId); // error? if (PEAR::isError($object)) diff --git a/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php b/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php index fe1897a..4c3eb78 100644 --- a/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php +++ b/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php @@ -49,7 +49,7 @@ class CMISFolderObject extends CMISBaseObject { var $ktapi; var $uri; - function CMISFolderObject(&$ktapi = null, $uri = null) + function __construct($folderId = null, &$ktapi = null, $uri = null) { $this->ktapi = $ktapi; $this->uri = $uri; @@ -69,11 +69,16 @@ class CMISFolderObject extends CMISBaseObject { // properties $this->properties = new CMISFolderPropertyCollection(); + + if (!is_null($folderId)) + { + $this->_get($folderId); + } } - function get($objectId) + private function _get($folderId) { - $object = $this->ktapi->get_folder_by_id($objectId); + $object = $this->ktapi->get_folder_by_id($folderId); // error? if (PEAR::isError($object)) diff --git a/lib/api/ktcmis/services/CMISNavigationService.inc.php b/lib/api/ktcmis/services/CMISNavigationService.inc.php index e35da84..b35d512 100644 --- a/lib/api/ktcmis/services/CMISNavigationService.inc.php +++ b/lib/api/ktcmis/services/CMISNavigationService.inc.php @@ -40,7 +40,6 @@ * @version Version 0.1 */ -// really wanted to keep KT code out of here but I don't see how require_once(KT_DIR . '/ktapi/ktapi.inc.php'); require_once(CMIS_DIR . '/util/CMISUtil.inc.php'); @@ -75,7 +74,6 @@ class CMISNavigationService { // NOTE If the Repository supports the optional “VersionSpecificFiling” capability, // then the repository SHALL return the document versions filed in the specified folder or its descendant folders. // Otherwise, the latest version of the documents SHALL be returned. - // TODO FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid function getDescendants($repositoryId, $folderId, $includeAllowableActions, $includeRelationships, $depth = 1, $typeId = 'Any', $filter = '') @@ -156,8 +154,8 @@ class CMISNavigationService { * @return array $ancestry */ // TODO FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid + // TODO If this service method is invoked on the root folder of the Repository, then the Repository SHALL return an empty result set. // NOTE SHOULD always include the “ObjectId” and “ParentId” properties for all objects returned - // NOTE If this service method is invoked on the root folder of the Repository, then the Repository shall return an empty result set. function getFolderParent($repositoryId, $folderId, $includeAllowableActions, $includeRelationships, $returnToRoot, $filter = '') { $ancestry = array(); @@ -217,7 +215,7 @@ class CMISNavigationService { * @return array $parents */ // TODO ConstraintViolationException: The Repository SHALL throw this exception if this method is invoked - // on an object who Object-Type Defintion specifies that it is not fileable. + // on an object who Object-Type Definition specifies that it is not fileable. // FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid. function getObjectParents($repositoryId, $objectId, $includeAllowableActions, $includeRelationships, $filter = '') { @@ -245,6 +243,26 @@ class CMISNavigationService { return $ancestry; } + /** + * Returns a list of checked out documents from the selected repository + * + * @param string $repositoryId + * @param string $folderId The folder for which checked out docs are requested + * @param string $filter + * @param int $maxItems + * @param int $skipCount + * @return array $checkedout The collection of checked out documents + */ + // NOTE NOT YET IMPLEMENTED (this function is just a place holder at the moment :)) + function getCheckedoutDocs($repositoryId, $folderId = null, $filter = '', $maxItems = 0, $skipCount = 0) + { + $checkedout = array(); + + + + return $checkedout(); + } + } ?> diff --git a/lib/api/ktcmis/services/CMISObjectService.inc.php b/lib/api/ktcmis/services/CMISObjectService.inc.php index aa1408f..157d9ab 100644 --- a/lib/api/ktcmis/services/CMISObjectService.inc.php +++ b/lib/api/ktcmis/services/CMISObjectService.inc.php @@ -1,7 +1,10 @@ ktapi, $repository->getRepositoryURI()); + $CMISObject = new CMISDocumentObject($objectId, $this->ktapi, $repository->getRepositoryURI()); break; case 'Folder': - $CMISObject = new CMISFolderObject($this->ktapi, $repository->getRepositoryURI()); + $CMISObject = new CMISFolderObject($objectId, $this->ktapi, $repository->getRepositoryURI()); break; } - $CMISObject->get($objectId); $properties = $CMISObject->getProperties(); return $properties; @@ -70,14 +77,24 @@ class CMISObjectService { * @return string $objectId The id of the created folder object */ // TODO throw ConstraintViolationException if: - // typeID is not an Object-Type whose baseType is “Folder”. // value of any of the properties violates the min/max/required/length constraints // specified in the property definition in the Object-Type. - // typeID is NOT in the list of AllowedChildObjectTypeIds of the parent-folder specified by folderId - // TODO throw storageException under conditions specified in "specific exceptions" section function createFolder($repositoryId, $typeId, $properties, $folderId) { - $objectId = null; + // fetch type definition of supplied type and check for base type "folder", if not true throw exception + $RepositoryService = new CMISRepositoryService(); + try { + $typeDefinition = $RepositoryService->getTypeDefinition($repositoryId, $typeId); + } + catch (Exception $e) + { + throw new ConstraintViolationException('Object is not of base type folder.'); + } + + if ($typeDefinition['attributes']['baseType'] != 'folder') + { + throw new ConstraintViolationException('Object is not of base type folder'); + } // TODO determine whether this is in fact necessary or if we should require decoding in the calling code // Attempt to decode $folderId, use as is if not detected as encoded @@ -85,11 +102,21 @@ class CMISObjectService { $tmpTypeId = CMISUtil::decodeObjectId($objectId); if ($tmpTypeId != 'Unknown') $folderId = $objectId; + + // if parent folder is not allowed to hold this type, throw exception + $CMISFolder = new CMISFolderObject($folderId, $this->ktapi); + $folderProperties = $CMISFolder->getProperties(); + $allowed = $folderProperties->getValue('AllowedChildObjectTypeIds'); + if (!is_array($allowed) || !in_array($typeId, $allowed)) + { + throw new ConstraintViolationException('Parent folder may not hold objects of this type (' . $typeId . ')'); + } $response = $this->ktapi->create_folder($folderId, $properties['name'], $sig_username = '', $sig_password = '', $reason = ''); if ($response['status_code'] != 0) { - // TODO add some error handling here + // throw storageException + throw new StorageException('The repository was unable to create the folder - ' . $response['message']); } else { diff --git a/lib/api/ktcmis/services/CMISRepositoryService.inc.php b/lib/api/ktcmis/services/CMISRepositoryService.inc.php index adfb54f..ea1ffcd 100644 --- a/lib/api/ktcmis/services/CMISRepositoryService.inc.php +++ b/lib/api/ktcmis/services/CMISRepositoryService.inc.php @@ -43,6 +43,7 @@ require_once(CMIS_DIR . '/classes/CMISRepository.inc.php'); require_once(CMIS_DIR . '/classes/CMISObjectTypes.inc.php'); +require_once(CMIS_DIR . '/exceptions/InvalidArgumentException.inc.php'); /** * CMIS Repository Service. @@ -98,10 +99,21 @@ class CMISRepositoryService { * @return array $objectTypes */ // NOTE this code may fit better within the Repository Class + // TODO return for specific type when $typeId is specified + // TODO other optional parameters function getTypes($repositoryId, $typeId = '', $returnPropertyDefinitions = false, $maxItems = 0, $skipCount = 0, &$hasMoreItems = false) { - // TODO throw invalidArgumentException if invalid typeId submitted + if ($typeId != '') + { + try { + $typeDefinition = $this->getTypeDefinition($repositoryId, $typeId); + } + catch (Exception $e) + { + throw new InvalidArgumentException('Type ' . $typeId . ' is not supported'); + } + } $repository = new CMISRepository($repositoryId); $supportedTypes = $repository->getTypes(); @@ -140,15 +152,24 @@ class CMISRepositoryService { * @param string $typeId The ID of the object type requested * @return $array $typeDefinition */ + // NOTE this code may fit better in the Repository Class function getTypeDefinition($repositoryId, $typeId) { + $object = 'CMIS' . $typeId . 'Object'; + + // check whether the object type exists, return error if not + // consider throwing an exception instead (see General Exceptions) + if (!file_exists(CMIS_DIR . '/objecttypes/' . $object . '.inc.php')) + { + throw new InvalidArgumentException('Type ' . $typeId . ' is not supported'); + } + $typeDefinition = array(); - require_once(CMIS_DIR . '/objecttypes/CMIS' . $typeId . 'Object.inc.php'); - $object = 'CMIS' . $typeId . 'Object'; - $tmpObject = new $object; - $typeDefinition['attributes'] = $tmpObject->getAttributes(); - $typeDefinition['properties'] = $tmpObject->getProperties(); + require_once(CMIS_DIR . '/objecttypes/' . $object . '.inc.php'); + $cmisObject = new $object; + $typeDefinition['attributes'] = $cmisObject->getAttributes(); + $typeDefinition['properties'] = $cmisObject->getProperties(); return $typeDefinition; } diff --git a/lib/api/ktcmis/util/CMISUtil.inc.php b/lib/api/ktcmis/util/CMISUtil.inc.php index 0cbfc21..fc9fe02 100644 --- a/lib/api/ktcmis/util/CMISUtil.inc.php +++ b/lib/api/ktcmis/util/CMISUtil.inc.php @@ -133,14 +133,13 @@ class CMISUtil { switch($object['item_type']) { case 'D': - $CMISObject = new CMISDocumentObject($ktapi, $repositoryURI); + $CMISObject = new CMISDocumentObject($object['id'], $ktapi, $repositoryURI); break; case 'F': - $CMISObject = new CMISFolderObject($ktapi, $repositoryURI); + $CMISObject = new CMISFolderObject($object['id'], $ktapi, $repositoryURI); break; } - $CMISObject->get($object['id']); $CMISArray[$count]['object'] = $CMISObject; // if sub-array @@ -184,8 +183,7 @@ class CMISUtil { if (isset($detail['id'])) { - $CMISObject = new CMISFolderObject($ktapi, $repositoryURI); - $CMISObject->get($detail['id']); + $CMISObject = new CMISFolderObject($detail['id'], $ktapi, $repositoryURI); $CMISElement['object'] = $CMISObject; // if more parent elements diff --git a/tests/ktcmis/testCmisApi.php b/tests/ktcmis/testCmisApi.php index bac216d..94fd66d 100644 --- a/tests/ktcmis/testCmisApi.php +++ b/tests/ktcmis/testCmisApi.php @@ -7,6 +7,7 @@ require_once (KT_LIB_DIR . '/api/ktcmis/ktcmis.inc.php'); define (KT_TEST_USER, 'admin'); define (KT_TEST_PASS, 'admin'); +// set to true to print out results define (DEBUG_CMIS, false); /** @@ -339,15 +340,23 @@ class CMISTestCase extends KTUnitTestCase { // TODO test invalid type // TODO test invalid parent folder // TODO other invalid parameters - $created = $ObjectService->createFolder($repositoryId, 'Folder', array('name' => 'My Test Folder ' . mt_rand()), 1); + $created = $ObjectService->createFolder($repositoryId, 'Folder', array('name' => 'My Test Folder ' . mt_rand()), 'F1'); $this->assertNotNull($created['results']); - // delete created folder if (!is_null($created['results'])) { - $folder_id = $created['results']; - CMISUtil::decodeObjectId($folder_id); - $this->ktapi->delete_folder($folder_id, 'Testing API', KT_TEST_USER, KT_TEST_PASS); + $folderId = $created['results']; + + // check that folder object actually exists + $properties = $ObjectService->getProperties($repositoryId, $folderId, false, false); + $this->assertNotNull($properties['results']); + + // test printout + $this->printTable($properties['results'][0], 'Properties for CMIS Created Folder Object ' . $folderId . ' (getProperties())'); + + // delete + CMISUtil::decodeObjectId($folderId); + $this->ktapi->delete_folder($folderId, 'Testing API', KT_TEST_USER, KT_TEST_PASS); } // tear down the folder/doc tree structure with which we were testing