diff --git a/lib/api/ktcmis/classes/CMISObject.inc.php b/lib/api/ktcmis/classes/CMISObject.inc.php index 1942663..63d2874 100644 --- a/lib/api/ktcmis/classes/CMISObject.inc.php +++ b/lib/api/ktcmis/classes/CMISObject.inc.php @@ -78,6 +78,9 @@ abstract class CMISObject { public function __construct() { + // set properties shared by all objects of this type + $this->_setSharedProperties(); + // $propertyDef = new PropertyDefinition(); // $this->properties[] = $propertyDef; } @@ -159,6 +162,16 @@ abstract class CMISObject { { // override in child classes } + + /** + * Sets properties which are shared between all objects of this type + */ + protected function _setSharedProperties() + { + $this->_setPropertyInternal('objectTypeId', strtolower($this->getAttribute('id'))); + // Needed to distinguish type + $this->_setPropertyInternal('baseTypeId', strtolower($this->getAttribute('id'))); + } } diff --git a/lib/api/ktcmis/classes/AbstractEnum.inc.php b/lib/api/ktcmis/classes/Enum.inc.php index 7892ee2..c22abb9 100644 --- a/lib/api/ktcmis/classes/AbstractEnum.inc.php +++ b/lib/api/ktcmis/classes/Enum.inc.php @@ -6,8 +6,9 @@ // TODO enable creation of enum instances on the fly - this will most likely be done in an extending class -abstract class AbstractEnum { +abstract class Enum { + // actual implementation of these will be in child classes static private $values; static private $value; static private $name; diff --git a/lib/api/ktcmis/enums/EnumCapabilityACL.inc.php b/lib/api/ktcmis/enums/EnumCapabilityACL.inc.php index 571f44c..64e5b82 100644 --- a/lib/api/ktcmis/enums/EnumCapabilityACL.inc.php +++ b/lib/api/ktcmis/enums/EnumCapabilityACL.inc.php @@ -7,9 +7,9 @@ require_once(realpath(dirname(__FILE__) . '/../../../../config/dmsDefaults.php')); define ('CMIS_DIR', KT_LIB_DIR . '/api/ktcmis'); -require_once(CMIS_DIR . '/classes/AbstractEnum.inc.php'); +require_once(CMIS_DIR . '/classes/Enum.inc.php'); -class EnumCapabilityACL extends AbstractEnum { +class EnumCapabilityACL extends Enum { static private $values = array('none', 'discover', 'manage'); static private $name = 'capabilityACL'; diff --git a/lib/api/ktcmis/enums/EnumCapabilityChanges.inc.php b/lib/api/ktcmis/enums/EnumCapabilityChanges.inc.php index c0e6700..7e3c565 100644 --- a/lib/api/ktcmis/enums/EnumCapabilityChanges.inc.php +++ b/lib/api/ktcmis/enums/EnumCapabilityChanges.inc.php @@ -7,9 +7,9 @@ require_once(realpath(dirname(__FILE__) . '/../../../../config/dmsDefaults.php')); define ('CMIS_DIR', KT_LIB_DIR . '/api/ktcmis'); -require_once(CMIS_DIR . '/classes/AbstractEnum.inc.php'); +require_once(CMIS_DIR . '/classes/Enum.inc.php'); -class EnumCapabilityChanges extends AbstractEnum { +class EnumCapabilityChanges extends Enum { static private $values = array('none', 'objectidsonly', 'properties', 'all'); static private $name = 'capabilityChanges'; diff --git a/lib/api/ktcmis/enums/EnumCapabilityContentStreamUpdatability.inc.php b/lib/api/ktcmis/enums/EnumCapabilityContentStreamUpdatability.inc.php index aaa0896..bc84a52 100644 --- a/lib/api/ktcmis/enums/EnumCapabilityContentStreamUpdatability.inc.php +++ b/lib/api/ktcmis/enums/EnumCapabilityContentStreamUpdatability.inc.php @@ -7,9 +7,9 @@ require_once(realpath(dirname(__FILE__) . '/../../../../config/dmsDefaults.php')); define ('CMIS_DIR', KT_LIB_DIR . '/api/ktcmis'); -require_once(CMIS_DIR . '/classes/AbstractEnum.inc.php'); +require_once(CMIS_DIR . '/classes/Enum.inc.php'); -class EnumCapabilityContentStreamUpdatability extends AbstractEnum { +class EnumCapabilityContentStreamUpdatability extends Enum { static private $values = array('none', 'anytime', 'pwconly'); static private $name = 'capabilityContentStreamUpdatability'; diff --git a/lib/api/ktcmis/enums/EnumCapabilityJoin.inc.php b/lib/api/ktcmis/enums/EnumCapabilityJoin.inc.php index 8bd6bf4..61c8f00 100644 --- a/lib/api/ktcmis/enums/EnumCapabilityJoin.inc.php +++ b/lib/api/ktcmis/enums/EnumCapabilityJoin.inc.php @@ -7,9 +7,9 @@ require_once(realpath(dirname(__FILE__) . '/../../../../config/dmsDefaults.php')); define ('CMIS_DIR', KT_LIB_DIR . '/api/ktcmis'); -require_once(CMIS_DIR . '/classes/AbstractEnum.inc.php'); +require_once(CMIS_DIR . '/classes/Enum.inc.php'); -class EnumCapabilityJoin extends AbstractEnum { +class EnumCapabilityJoin extends Enum { static private $values = array('none', 'inneronly', 'innerandouter'); static private $name = 'capabilityJoin'; diff --git a/lib/api/ktcmis/enums/EnumCapabilityQuery.inc.php b/lib/api/ktcmis/enums/EnumCapabilityQuery.inc.php index 27ee15d..08c9360 100644 --- a/lib/api/ktcmis/enums/EnumCapabilityQuery.inc.php +++ b/lib/api/ktcmis/enums/EnumCapabilityQuery.inc.php @@ -7,9 +7,9 @@ require_once(realpath(dirname(__FILE__) . '/../../../../config/dmsDefaults.php')); define ('CMIS_DIR', KT_LIB_DIR . '/api/ktcmis'); -require_once(CMIS_DIR . '/classes/AbstractEnum.inc.php'); +require_once(CMIS_DIR . '/classes/Enum.inc.php'); -class EnumCapabilityQuery extends AbstractEnum { +class EnumCapabilityQuery extends Enum { static private $values = array('none', 'metadataonly', 'fulltextonly', 'bothseparate', 'bothcombined'); static private $name = 'capabilityQuery'; diff --git a/lib/api/ktcmis/enums/EnumCapabilityRenditions.inc.php b/lib/api/ktcmis/enums/EnumCapabilityRenditions.inc.php index 07dc57b..406bde3 100644 --- a/lib/api/ktcmis/enums/EnumCapabilityRenditions.inc.php +++ b/lib/api/ktcmis/enums/EnumCapabilityRenditions.inc.php @@ -7,9 +7,9 @@ require_once(realpath(dirname(__FILE__) . '/../../../../config/dmsDefaults.php')); define ('CMIS_DIR', KT_LIB_DIR . '/api/ktcmis'); -require_once(CMIS_DIR . '/classes/AbstractEnum.inc.php'); +require_once(CMIS_DIR . '/classes/Enum.inc.php'); -class EnumCapabilityRenditions extends AbstractEnum { +class EnumCapabilityRenditions extends Enum { static private $values = array('none', 'objectidsonly', 'properties', 'all'); static private $name = 'capabilityRenditions'; diff --git a/lib/api/ktcmis/ktNavigationService.inc.php b/lib/api/ktcmis/ktNavigationService.inc.php index 5ddb97c..1cf65ea 100644 --- a/lib/api/ktcmis/ktNavigationService.inc.php +++ b/lib/api/ktcmis/ktNavigationService.inc.php @@ -137,7 +137,7 @@ class KTNavigationService extends KTCMISBase { { return array( "status_code" => 1, - "message" => "Failed getting descendants for folder" + "message" => "Failed getting children for folder" ); } @@ -154,38 +154,32 @@ class KTNavigationService extends KTCMISBase { * * @param string $repositoryId * @param string $folderId - * @param boolean $includeAllowableActions - * @param boolean $includeRelationships - * @param boolean $returnToRoot * @param string $filter - * @return ancestry[] + * @return parent[] */ - public function getFolderParent($repositoryId, $folderId, $includeAllowableActions, $includeRelationships, $returnToRoot, $filter = '') + public function getFolderParent($repositoryId, $folderId, $filter = '') { try { - $ancestryResult = $this->NavigationService->getFolderParent($repositoryId, $folderId, $includeAllowableActions, - $includeRelationships, $returnToRoot); + $parent = $this->NavigationService->getFolderParent($repositoryId, $folderId, $filter); } catch (Exception $e) { return array( "status_code" => 1, - "message" => "Failed getting ancestry for folder: " . $e->getMessage() + "message" => "Failed getting folder parent: " . $e->getMessage() ); } - if (PEAR::isError($ancestryResult)) + if (PEAR::isError($parent)) { return array( "status_code" => 1, - "message" => "Failed getting ancestry for folder" + "message" => "Failed getting folder parent" ); } - $ancestry = CMISUtil::decodeObjectHierarchy($ancestryResult, 'children'); - return array( "status_code" => 0, - "results" => $ancestry + "results" => CMISUtil::createObjectPropertiesEntry($parent->getProperties()) ); } @@ -201,10 +195,18 @@ class KTNavigationService extends KTCMISBase { */ function getObjectParents($repositoryId, $objectId, $includeAllowableActions, $includeRelationships, $filter = '') { - $ancestryResult = $this->NavigationService->getObjectParents($repositoryId, $objectId, $includeAllowableActions, - $includeRelationships); + try { + $ancestry = $this->NavigationService->getObjectParents($repositoryId, $objectId, $includeAllowableActions, + $includeRelationships); + } + catch (Exception $e) { + return array( + "status_code" => 1, + "message" => $e->getMessage() + ); + } - if (PEAR::isError($ancestryResult)) + if (PEAR::isError($ancestry)) { return array( "status_code" => 1, @@ -212,8 +214,6 @@ class KTNavigationService extends KTCMISBase { ); } - $ancestry = CMISUtil::decodeObjectHierarchy($ancestryResult, 'children'); - return array( "status_code" => 0, "results" => $ancestry @@ -225,18 +225,27 @@ class KTNavigationService extends KTCMISBase { * * @param string $repositoryId * @param string $folderId The folder for which checked out docs are requested - * @param string $filter - * @param boolean $includeAllowableActions - * @param boolean $includeRelationships * @param int $maxItems * @param int $skipCount - * @return array $checkedout The collection of checked out documents + * @param string $filter + * @param enum $includeRelationships + * @param boolean $includeAllowableActions + * @param string $renditionFilter + * @return array $checkedout The collection of checked out document objects + * MUST include (unless not requested) for each object: + * array $properties + * array $relationships + * array $renditions + * $allowableActions + * @return boolean $hasMoreItems + * @return int $numItems [optional] */ - function getCheckedOutDocs($repositoryId, $includeAllowableActions, $includeRelationships, $folderId = null, $filter = '', - $maxItems = 0, $skipCount = 0) + function getCheckedOutDocs($repositoryId, $folderId = null, $maxItems = 0, $skipCount = 0, $orderBy = '', + $filter = '', $includeRelationships = null, $includeAllowableActions = false, $renditionFilter = '') { - $checkedout = $this->NavigationService->getCheckedOutDocs($repositoryId, $includeAllowableActions, $includeRelationships, - $folderId, $filter, $maxItems, $skipCount); + $checkedout = $this->NavigationService->getCheckedOutDocs($repositoryId, $folderId = null, $maxItems = 0, $skipCount = 0, + $orderBy, $filter, $includeRelationships, $includeAllowableActions, + $renditionFilter); if (PEAR::isError($checkedout)) { diff --git a/lib/api/ktcmis/ktRepositoryService.inc.php b/lib/api/ktcmis/ktRepositoryService.inc.php index cf08f74..c4ea186 100644 --- a/lib/api/ktcmis/ktRepositoryService.inc.php +++ b/lib/api/ktcmis/ktRepositoryService.inc.php @@ -165,25 +165,14 @@ class KTRepositoryService extends KTCMISBase { public function getTypeDefinition($repositoryId, $typeId) { try { - $typeDefinitionResult = $this->RepositoryService->getTypeDefinition($repositoryId, $typeId); + $typeDefinition = $this->RepositoryService->getTypeDefinition($repositoryId, $typeId); } - catch (Exception $e) - { - return array( - "status_code" => 1, - "message" => $e->getMessage() - ); + catch (Exception $e) { + // propogate upward + throw $e; } - - // format as array style output - // NOTE only concerned with attributes at this time - // TODO add support for properties - $typeDefinition = $typeDefinitionResult['attributes']; - - return array ( - "status_code" => 0, - "results" => $typeDefinition - ); + + return $typeDefinition; } } diff --git a/lib/api/ktcmis/ktService.inc.php b/lib/api/ktcmis/ktService.inc.php index fab6e86..f804957 100644 --- a/lib/api/ktcmis/ktService.inc.php +++ b/lib/api/ktcmis/ktService.inc.php @@ -48,7 +48,6 @@ require_once(realpath(dirname(__FILE__) . '/../../../config/dmsDefaults.php')); require_once(KT_DIR . '/ktapi/ktapi.inc.php'); define ('CMIS_DIR', KT_LIB_DIR . '/api/ktcmis'); -require_once(CMIS_DIR . '/exceptions/PermissionDeniedException.inc.php'); require_once(CMIS_DIR . '/util/CMISUtil.inc.php'); /** diff --git a/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php b/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php index df559a2..39c2aac 100644 --- a/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php +++ b/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php @@ -98,7 +98,7 @@ class CMISDocumentObject extends CMISObject { } } - // TODO throw exception if unable to create? + parent::__construct(); } // TODO abstract shared stuff to base class where possible @@ -124,10 +124,6 @@ class CMISDocumentObject extends CMISObject { // also ktapidocument::get_download_url // $this->_setPropertyInternal('uri', $uri); $this->_setPropertyInternal('uri', ''); - // TODO what is this? Assuming it is the object type id, and not OUR document type? - $this->_setPropertyInternal('objectTypeId', strtolower($this->getAttribute('id'))); - // Needed to distinguish type - $this->_setPropertyInternal('baseTypeId', strtolower($this->getAttribute('id'))); $this->_setPropertyInternal('createdBy', $objectProperties['created_by']); $this->_setPropertyInternal('creationDate', $objectProperties['created_date']); $this->_setPropertyInternal('lastModifiedBy', $objectProperties['modified_by']); diff --git a/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php b/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php index 442f01b..e354157 100644 --- a/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php +++ b/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php @@ -85,6 +85,8 @@ class CMISFolderObject extends CMISObject { throw new ObjectNotFoundException($e->getMessage()); } } + + parent::__construct(); } // TODO abstract shared stuff to base class where possible @@ -111,10 +113,6 @@ class CMISFolderObject extends CMISObject { // TODO this url is probably incorrect...needs to be checked // $this->_setPropertyInternal('uri', $uri); $this->_setPropertyInternal('uri', ''); - // TODO what is this? Assuming it is the object type id, and not OUR document type? - $this->_setPropertyInternal('objectTypeId', strtolower($this->getAttribute('id'))); - // Needed to distinguish type - $this->_setPropertyInternal('baseTypeId', strtolower($this->getAttribute('id'))); $this->_setPropertyInternal('createdBy', $objectProperties['created_by']); // TODO cannot currently retrieve via ktapi or regular folder code - add as with created by $this->_setPropertyInternal('creationDate', $objectProperties['created_date']); @@ -125,9 +123,17 @@ class CMISFolderObject extends CMISObject { $this->_setPropertyInternal('changeToken', null); $this->_setPropertyInternal('name', $objectProperties['folder_name']); $this->_setPropertyInternal('parentId', CMISUtil::encodeObjectId(FOLDER, $objectProperties['parent_id'])); - $this->_setPropertyInternal('allowedChildObjectTypeIds', array('cmis:document', 'cmis:folder')); $this->_setPropertyInternal('author', $objectProperties['created_by']); } + + /** + * Sets properties shared between all objects of this type + */ + protected function _setSharedProperties() + { + parent::_setSharedProperties(); + $this->_setPropertyInternal('allowedChildObjectTypeIds', array('cmis:document', 'cmis:folder')); + } } diff --git a/lib/api/ktcmis/services/CMISNavigationService.inc.php b/lib/api/ktcmis/services/CMISNavigationService.inc.php index aaaf406..7fe2d21 100644 --- a/lib/api/ktcmis/services/CMISNavigationService.inc.php +++ b/lib/api/ktcmis/services/CMISNavigationService.inc.php @@ -87,8 +87,8 @@ class CMISNavigationService { // 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 getChildren($repositoryId, $folderId, $includeAllowableActions = null, $includeRelationships = null, - $typeId = 'Any', $filter = '', $maxItems = 0, $skipCount = 0, $orderBy = '', $renditionFilter = null, - $includePathSegment = false) + $typeId = 'Any', $filter = '', $maxItems = 0, $skipCount = 0, $orderBy = '', $renditionFilter = null, + $includePathSegment = false) { // TODO paging // TODO optional parameters @@ -134,13 +134,13 @@ 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. - // NOTE If the Repository supports the optional capability capabilityMutlifiling and the same document is encountered + // NOTE If the Repository supports the optional capability capabilityMutlifiling and the same document is encountered // multiple times in the hierarchy, then the repository MUST return that document each time is encountered. // NOTE The default value for the $depth parameter is repository specific and SHOULD be at least 2 or -1 // Chosen 2 as the underlying code currently has no concept of digging all the way down // TODO FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid - function getDescendants($repositoryId, $folderId, $depth = 2, $filter = '', $includeRelationships = false, $renditionFilter = '', - $includeAllowableActions = false, $includePathSegment = false) + function getDescendants($repositoryId, $folderId, $depth = 2, $filter = '', $includeRelationships = false, $renditionFilter = '', + $includeAllowableActions = false, $includePathSegment = false) { if ($depth == 0) { throw new InvalidArgumentException('Invalid depth argument supplied'); @@ -148,11 +148,11 @@ class CMISNavigationService { // if this is not a folder, cannot get descendants $folderId = CMISUtil::decodeObjectId($folderId, $type); - + if ($type != 'cmis:folder') { throw new InvalidArgumentException('The supplied object is not a folder, unable to return descendants'); } - + // TODO optional parameters $descendants = array(); $repository = new CMISRepository($repositoryId); @@ -171,105 +171,102 @@ class CMISNavigationService { * * @param string $repositoryId * @param string $folderId - * @param boolean $includeAllowableActions - * @param boolean $includeRelationships - * @param boolean $returnToRoot If TRUE, then the repository SHALL return all folder objects - * that are ancestors of the specified folder. - * If FALSE, the repository SHALL return only the parent folder of the specified folder. * @param string $filter - * @return array $ancestry + * @return object $parent The parent folder object */ // 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 - function getFolderParent($repositoryId, $folderId, $includeAllowableActions, $includeRelationships, $returnToRoot, $filter = '') + function getFolderParent($repositoryId, $folderId, $filter = '') { // NOTE the root folder obviously has no parent, throw an ObjectNotFoundException here if this is the root folder if (CMISUtil::isRootFolder($repositoryId, $folderId, $this->ktapi)) { - throw new ObjectNotFoundException('Root folder has no parent'); + throw new InvalidArgumentException('Root folder has no parent'); } - - $ancestry = array(); - $repository = new CMISRepository($repositoryId); + + $parent = null; // if this is not a folder, cannot get folder parent :) $folderId = CMISUtil::decodeObjectId($folderId, $type); - // NOTE this will quite possibly break the webservices - if ($type != 'cmis:folder') - { - return $ancestry; + // this exception is not indicated in the CMIS Specification, but it just makes sense and so we include it here + if ($type != 'cmis:folder') { + throw new InvalidArgumentException('The specified object is not a folder'); } $ktapiFolder = $this->ktapi->get_folder_by_id($folderId); - - if ($returnToRoot) - { - $folder = $ktapiFolder->get_folder(); - $parents = $folder->generateFolderIDs($folderId); - // remove the id of the requesting folder and convert to array - $ancestry = explode(',', str_replace(','.$folderId, '', $parents)); - // reverse to get bottom up listing? don't think so with the current implementation - // specifying that objectTypes may have children but do not have parents listed. -// $ancestry = array_reverse($ancestry); - } - else - { - $parent = $ktapiFolder->get_parent_folder_id(); - $ancestry[] = $parent; + if (PEAR::isError($ktapiFolder)) { + throw new RuntimeException($ktapiFolder->getMessage()); } - // need some info about the parent(s) in order to correctly create the hierarchy - $tmpArray = array(); - foreach ($ancestry as $key => $ancestor) - { - $tmpArray[$key] = $this->ktapi->get_folder_by_id($ancestor); - } - $ancestry = $tmpArray; - unset($tmpArray); - - $ancestry = CMISUtil::createParentObjectHierarchy($ancestry, $repository->getRepositoryURI, $this->ktapi); - - return $ancestry; + $parentId = $ktapiFolder->get_parent_folder_id(); + $parent = new CMISFolderObject(CMISUtil::encodeObjectId($parentId, FOLDER), $this->ktapi); + + return $parent; } /** - * Fetches the parent(s) of the specified object + * Gets the parent folder(s) for the specified non-folder, fileable object. * Multiple parents may exist if a repository supports multi-filing * It is also possible that linked documents/folders may qualify as having multiple parents * as they are essentially the same object * * @param string $repositoryId * @param string $objectId - * @param boolean $includeAllowableActions - * @param boolean $includeRelationships - * @param string $filter - * @return array $parents + * @param string $filter [optional] + * @param enum $includeRelationships [optional] + * @param string $renditionFilter [optional] + * @param boolean $includeAllowableActions [optional] + * @param boolean $includeRelativePathSegment [optional] + * @return array $parents - empty for unfiled objects or the root folder + * MUST include (unless not requested) for each object: + * array $properties + * array $relationships + * array $renditions + * $allowableActions + * string $relativePathSegment */ - // TODO ConstraintViolationException: The Repository SHALL throw this exception if this method is invoked - // 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 = '') + // TODO FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid. + function getObjectParents($repositoryId, $objectId, $filter = '', $includeRelationships = null, $renditionFilter = '', + $includeAllowableActions = false, $includeRelativePathSegment = false) { $ancestry = array(); $objectId = CMISUtil::decodeObjectId($objectId, $typeId); + + // if type is a folder, this function does not apply + if ($typeId == 'cmis:folder') { + throw new InvalidArgumentException('Cannot call this function for a folder object'); + } + + $objectTypeId = ucwords(str_replace('cmis:', '', $typeId)); + $object = 'CMIS' . $objectTypeId . 'Object'; + + if (!file_exists(CMIS_DIR . '/objecttypes/' . $object . '.inc.php')) { + throw new InvalidArgumentException('Type ' . $typeId . ' is not supported'); + } + + require_once(CMIS_DIR . '/objecttypes/' . $object . '.inc.php'); + $cmisObject = new $object; + + if (!$cmisObject->getAttribute('fileable')) { + throw new ConstraintViolationException('Unable to get parents of non-filable object'); + } // TODO - what about other types? only implementing folders and documents at the moment so ignore for now + // NOTE this will change if we implement multi-filing and/or unfiling switch($typeId) { case 'cmis:document': $document = $this->ktapi->get_document_by_id($objectId); - $parent = $document->ktapi_folder; - $ancestry[] = $parent; - break; - case 'cmis:folder': - $folder = $this->ktapi->get_folder_by_id($objectId); - $parent = $this->ktapi->get_folder_by_id($folder->get_parent_folder_id()); - $ancestry[] = $parent; - break; + if ($document->is_deleted()) { + throw new InvalidArgumentException('The requested object has been deleted'); + } + $ancestry[] = $document->ktapi_folder->get_folderid(); + break; + } + + foreach ($ancestry as $key => $parentId) { + $CMISObject = new CMISFolderObject($parentId, $this->ktapi, $repositoryURI); + $ancestry[$key] = CMISUtil::createObjectPropertiesEntry($CMISObject->getProperties()); } - - $ancestry = CMISUtil::createParentObjectHierarchy($ancestry, $repository->getRepositoryURI, $this->ktapi); return $ancestry; } @@ -279,18 +276,25 @@ class CMISNavigationService { * * @param string $repositoryId * @param string $folderId The folder for which checked out docs are requested - * @param string $filter - * @param boolean $includeAllowableActions - * @param boolean $includeRelationships * @param int $maxItems * @param int $skipCount + * @param string $filter + * @param enum $includeRelationships + * @param boolean $includeAllowableActions + * @param string $renditionFilter * @return array $checkedout The collection of checked out document objects + * MUST include (unless not requested) for each object: + * array $properties + * array $relationships + * array $renditions + * $allowableActions + * @return boolean $hasMoreItems + * @return int $numItems [optional] */ // TODO exceptions: • FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid. - // TODO filter by folder id // TODO $filter and paging - function getCheckedOutDocs($repositoryId, $folderId = null, $filter = '', $includeAllowableActions = false, $includeRelationships = null, - $maxItems = 0, $skipCount = 0, $orderBy = '') + function getCheckedOutDocs($repositoryId, $folderId = null, $maxItems = 0, $skipCount = 0, $orderBy = '', + $filter = '', $includeRelationships = null, $includeAllowableActions = false, $renditionFilter = '') { $checkedout = array(); diff --git a/lib/api/ktcmis/services/CMISRepositoryService.inc.php b/lib/api/ktcmis/services/CMISRepositoryService.inc.php index 9f0f15c..4c4e820 100644 --- a/lib/api/ktcmis/services/CMISRepositoryService.inc.php +++ b/lib/api/ktcmis/services/CMISRepositoryService.inc.php @@ -101,9 +101,12 @@ class CMISRepositoryService { * @param boolean $hasMoreItems TRUE if there are more items to return than were requested * @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 + // This code is superseded by getTypeChildren and getTypeDescendants - when implementing those, check + // whether it is possible to entirely remove this function or if it is to remain and be shared by the + // other two functions (when no type is specified they will return base types [children] amd all types + // [descendants] respectively public function getTypes($repositoryId, $typeId = '', $returnPropertyDefinitions = false, $maxItems = 0, $skipCount = 0, &$hasMoreItems = false) { @@ -160,13 +163,15 @@ class CMISRepositoryService { // NOTE this code may fit better in the Repository Class function getTypeDefinition($repositoryId, $typeId) { + global $default; + $default->log->debug(); + $default->log->info(str_replace('cmis:', '', $typeId)); $typeId = ucwords(str_replace('cmis:', '', $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')) - { + if (!file_exists(CMIS_DIR . '/objecttypes/' . $object . '.inc.php')) { throw new InvalidArgumentException('Type ' . $typeId . ' is not supported'); } @@ -174,8 +179,11 @@ class CMISRepositoryService { require_once(CMIS_DIR . '/objecttypes/' . $object . '.inc.php'); $cmisObject = new $object; - $typeDefinition['attributes'] = $cmisObject->getAttributes(); - $typeDefinition['properties'] = $cmisObject->getProperties(); + + // NOTE The specification is ambigous here: it states that this function must return the type properties, but + // the atompub example shows the type attributes, not properties; since most properties are only populated + // on creation of an instance of an object-type, we choose to go with the attributes and not the properties + $typeDefinition = $cmisObject->getAttributes(); return $typeDefinition; } diff --git a/lib/api/ktcmis/util/CMISUtil.inc.php b/lib/api/ktcmis/util/CMISUtil.inc.php index e3e977c..8008bab 100644 --- a/lib/api/ktcmis/util/CMISUtil.inc.php +++ b/lib/api/ktcmis/util/CMISUtil.inc.php @@ -104,7 +104,7 @@ class CMISUtil { $typeId = 'unknown'; return null; } - + $typeId = null; // NOTE Not sure whether this really belongs here, but probably this is the safest and most reliable place @@ -124,6 +124,7 @@ class CMISUtil { // meantime this minor hack will get things working for the existing system structure, as the root // folder should always be id 1. $typeId = 'cmis:folder'; + $className = 'Folder'; return '1'; } @@ -180,9 +181,9 @@ class CMISUtil { $CMISObject = new CMISFolderObject($object['id'], $ktapi, $repositoryURI); break; } - + $CMISArray[$count]['object'] = $CMISObject; - + // if sub-array if (count($object['items']) > 0) { $CMISArray[$count]['children'] = self::createChildObjectHierarchy($object['items'], $repositoryURI, $ktapi); @@ -192,7 +193,7 @@ class CMISUtil { { // NOTE why is this necessary? That's what you get for not commenting it at the time // TODO comment this properly, once we know why it is happening -// $CMISArray[$count] = self::createChildObjectHierarchy($object, $repositoryURI, $ktapi); + // $CMISArray[$count] = self::createChildObjectHierarchy($object, $repositoryURI, $ktapi); } } } @@ -212,6 +213,7 @@ class CMISUtil { * @return array $CMISArray */ // NOTE this will have to change if we implement multi-filing + // NOTE this function probably serves no purpose, the parents are to be returned as a flat array static public function createParentObjectHierarchy($input, $repositoryURI, &$ktapi) { $CMISArray = array(); @@ -251,14 +253,14 @@ class CMISUtil { static public function decodeObjectHierarchy($input, $linkText = 'children') { $hierarchy = array(); - + // first, run through the base array to get the initial children foreach ($input as $key => $entry) { $object = $entry['object']; $properties = $object->getProperties(); $hierarchy[$key] = self::createObjectPropertiesEntry($properties); - + if (isset($entry[$linkText]) && count($entry[$linkText])) { $hierarchy[$key][$linkText] = self::decodeObjectHierarchy($entry[$linkText], $linkText); } @@ -279,10 +281,10 @@ class CMISUtil { static public function createObjectPropertiesEntry($properties) { $object = array(); - + foreach(CMISPropertyCollection::$propertyTypes as $property => $type) { - // hack for Author property + // author property does not work the same as the others if ($property == 'author') { $object[$property] = array('value' => $properties->getValue($property)); } @@ -316,9 +318,9 @@ class CMISUtil { // clean up ", )" - NOTE this may not be necessary, but is included for safety $stringdata = preg_replace('/, *\r?\n? *\)/', ')', $stringdata); - // NOTE is this while loop even needed? - while (preg_match('/\b[\w]*::__set_state\(/', $stringdata, $matches)) - { + // NOTE is this while loop even needed, or can we just run a preg_replace without the while? + // TODO find out... + while (preg_match('/\b[\w]*::__set_state\(/', $stringdata, $matches)) { $stringdata = preg_replace('/\b[\w]*::__set_state\(/', $matches[1], $stringdata); } @@ -342,7 +344,7 @@ class CMISUtil { } // TODO more robust base64 encoding detection, if possible - + /** * Checks the contentStream and ensures that it is a correct base64 string; * This is purely for clients such as CMISSpaces breaking the content into @@ -363,23 +365,23 @@ class CMISUtil { { // always trim content, just in case, as the AtomPub specification says content may be padded with whitespace at the start and end. $contentStream = trim($contentStream); - + // check whether the content is encoded first, return as is if not // A–Z, a–z, 0–9, +, / // NOTE this makes the (fairly reasonable) assumption that text content contains at least one space or punctuation character. // of course this may fail should something be sent in plain text such as a passwords file containing sha1 or md5 hashes only. if (preg_match('/[^\w\/\+=\n]+/', $content)) return $contentStream; - + $decoded = ''; - + // split the content stream on ={1,2} $parts = preg_split('/(={1,2})/', $contentStream, null, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); foreach($parts as $part) - { + { if (preg_match('/={1,2}/', $part)) { continue; } - + // lookahead for delimiter, because we may need it back. // NOTE that decoding appears to work fine without this, so this is just an "in case". // NOTE that even with this it seems the present function works faster than the alternative below. @@ -388,14 +390,14 @@ class CMISUtil { $part .= $parts[$key+1]; } } - + // decode, append to output $decoded .= base64_decode($part); } return $decoded; } - + /** * Checks the contentStream and ensures that it is a correct base64 string; * This is purely for clients such as CMISSpaces breaking the content into @@ -405,7 +407,7 @@ class CMISUtil { * If it is not chunked it is decoded as is and sent back as a single stream. * * NOTE this function and the above need to be checked for efficiency. - * The current one appears to be miles better (1/0/3 vs 14/4/57 on respective test files) + * The other one appears to be miles better (1/0/3 vs 14/4/57 on respective test files) * * @param object $contentStream * @return string decoded @@ -414,13 +416,13 @@ class CMISUtil { { // always trim content, just in case, as the AtomPub specification says content may be padded with whitespace at the start and end. $contentStream = trim($contentStream); - + // check whether the content is encoded first, return as is if not // A–Z, a–z, 0–9, +, / // NOTE this makes the (fairly reasonable) assumption that text content contains at least one space or punctuation character. // of course this may fail should something be sent in plain text such as a passwords file containing sha1 or md5 hashes only. if (preg_match('/[^\w\/\+=\n]+/', $content)) return $contentStream; - + // check the content stream for any lines of unusual length (except the last line, which can be any length) $count = -1; $length = 0; @@ -435,11 +437,11 @@ class CMISUtil { foreach ($splitStream as $line) { $curlen = strlen($line); - + if ($length == 0) { $length = $curlen; } - + // if we find one we know that we must split the line here and end the previous base64 string if ($curlen > $length) { @@ -457,7 +459,7 @@ class CMISUtil { } $decode[++$count] = $b64String . $lastChunk; - + $b64String = $nextChunk . "\n"; $length = strlen($nextChunk); @@ -488,7 +490,7 @@ class CMISUtil { return $decoded; } - + /** * Function to check whether a specified object exists within the KnowledgeTree system * @@ -518,10 +520,10 @@ class CMISUtil { else { $exists = false; } - + return $exists; } - + /** * Creates a temporary file * Cleanup is the responsibility of the calling code @@ -533,22 +535,22 @@ class CMISUtil { { // if contentStream is empty, cannot create file if (empty($contentStream)) return null; - + // TODO consider checking whether content is encoded (currently we expect encoded) // TODO choose between this and the alternative decode function (see CMISUtil class) // this will require some basic benchmarking $contentStream = self::decodeChunkedContentStream($contentStream); - + // NOTE There is a function in CMISUtil to do this, written for the unit tests but since KTUploadManager exists // and has more functionality which could come in useful at some point I decided to go with that instead // (did not know this existed when I wrote the CMISUtil function) $uploadManager = new KTUploadManager(); // assumes already decoded from base64, should use store_base64_file if not $tempfilename = $uploadManager->store_file($contentStream, 'cmis_'); - + return $tempfilename; } - + /** * attempts to fetch the folder id from a name * @@ -562,10 +564,10 @@ class CMISUtil { static public function getIdFromName($name, &$ktapi) { $folder = $ktapi->get_folder_by_name($name); - + return self::encodeObjectId(FOLDER, $folder->get_folderid()); } - + /** * Checks for the root folder * @@ -578,12 +580,12 @@ class CMISUtil { { $repository = new CMISRepository($repositoryId); $repositoryInfo = $repository->getRepositoryInfo(); - + // NOTE this call is required to accomodate the definition of the root folder id in the config as required by the drupal module // we should try to update the drupal module to not require this, but this way is just easier at the moment, and most of // the code accomodates it without any serious hacks $rootFolder = self::getIdFromName($repositoryInfo->getRootFolderId(), $ktapi); - + return $folderId == $rootFolder; } diff --git a/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php b/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php index d72e693..88628d5 100644 --- a/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php +++ b/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php @@ -91,8 +91,6 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { // TODO this only returns one parent, need to implement returnToRoot also else if ($this->params[1] == 'parent') { - // abstract this to be used also by the document service (and the PWC service?) ??? - // alternatively use getFolderParent here makes sense and use getObjectParents when document service? $folderId = $this->params[0]; $NavigationService = new KTNavigationService(KT_cmis_atom_service_helper::getKt()); $response = $NavigationService->getFolderParent($repositoryId, $folderId, false, false, false); @@ -403,9 +401,7 @@ class KT_cmis_atom_service_document extends KT_cmis_atom_service { // update accordingly when updating to newer specification if ($this->params[1] == 'parent') { - // abstract this to be used also by the document service (and the PWC service?) ??? - // alternatively use getFolderParent here makes sense and use getObjectParents when document service? - $NavigationService = new NavigationService(KT_cmis_atom_service_helper::getKt()); + $NavigationService = new KTNavigationService(KT_cmis_atom_service_helper::getKt()); $response = $NavigationService->getObjectParents($repositoryId, $objectId, false, false); if ($response['status_code'] == 1) { @@ -701,28 +697,20 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service { { $RepositoryService = new KTRepositoryService(); $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); + $type = $this->params[0]; - if (!isset($this->params[1])) { - // For easier return in the wanted format, we call getTypes instead of getTypeDefinition. - // Calling this with a single type specified returns an array containing the definition of - // just the requested type. - // NOTE could maybe be more efficient to call getTypeDefinition direct and then place in - // an array on this side? or directly expose the individual entry response code and - // call directly from here rather than via getTypeFeed. - $type = ucwords($this->params[0]); - $types = $RepositoryService->getTypes($repositoryId, $type); - - // hack for removing one level of access - $types = $types['results']; - - $feed = KT_cmis_atom_service_helper::getTypeFeed($type, $types); + try { + $typeDefinition = $RepositoryService->getTypeDefinition($repositoryId, $type); } - else { - // TODO dynamic dates, as needed everywhere - // NOTE children of types not yet implemented and we don't support any non-basic types at this time - $feed = $this->getTypeChildrenFeed($this->params[1]); + catch (Exception $e) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $e->getMessage()); + // Expose the responseFeed + $this->responseFeed = $feed; + return null; } + $feed = KT_cmis_atom_service_helper::getTypeFeed($type, array($typeDefinition)); + // Expose the responseFeed $this->responseFeed=$feed; } diff --git a/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php b/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php index b5c92a5..8821614 100644 --- a/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php +++ b/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php @@ -107,25 +107,25 @@ class KT_cmis_atom_service_helper { * @param boolean $pwc Whether this is a PWC object * @param $method The request method used (POST/GET/...) */ - static public function createObjectEntry(&$response, $cmisEntry, $parent, $pwc = false, $method = 'GET') + static public function createObjectEntry(&$feed, $cmisEntry, $parent, $pwc = false, $method = 'GET') { - $workspace = $response->getWorkspace(); + $workspace = $feed->getWorkspace(); $type = strtolower($cmisEntry['properties']['objectTypeId']['value']); // create entry - $entry = $response->newEntry(); + $entry = $feed->newEntry(); // When request is a POST we will be returning only an object entry, not a full feed, and so this belongs here if (($method == 'POST') || $pwc) { // append attributes - $entry->appendChild($response->newAttr('xmlns', 'http://www.w3.org/2005/Atom')); - $entry->appendChild($response->newAttr('xmlns:app', 'http://www.w3.org/2007/app')); - $entry->appendChild($response->newAttr('xmlns:cmis', 'http://docs.oasis-open.org/ns/cmis/core/200908/')); - $entry->appendChild($response->newAttr('xmlns:cmisra', 'http://docs.oasis-open.org/ns/cmis/restatom/200908/')); + $entry->appendChild($feed->newAttr('xmlns', 'http://www.w3.org/2005/Atom')); + $entry->appendChild($feed->newAttr('xmlns:app', 'http://www.w3.org/2007/app')); + $entry->appendChild($feed->newAttr('xmlns:cmis', 'http://docs.oasis-open.org/ns/cmis/core/200908/')); + $entry->appendChild($feed->newAttr('xmlns:cmisra', 'http://docs.oasis-open.org/ns/cmis/restatom/200908/')); } - self::createObjectEntryContent($entry, $response, $cmisEntry, $parent, $pwc, $method); + self::createObjectEntryContent($entry, $feed, $cmisEntry, $parent, $pwc, $method); } /** @@ -151,13 +151,13 @@ class KT_cmis_atom_service_helper { // NOTE this approach appears to be necessary due to the structure of the underlying atompub code and specification, // which does not directly support nesting - attempting to create a new feed and append it within the outer // feed resulted in an empty cmisra:children node, so this approach was substituted - static public function createChildObjectEntry(&$childrenFeed, $cmisEntry, $workspace, $response, $folderNam) + static public function createChildObjectEntry(&$childrenFeed, $cmisEntry, $workspace, $feed, $folderNam) { $type = strtolower($cmisEntry['properties']['objectTypeId']['value']); // create entry - $entry = $response->newElement('entry'); - self::createObjectEntryContent($entry, $response, $cmisEntry);//, $parent, $pwc, $method); + $entry = $feed->newElement('entry'); + self::createObjectEntryContent($entry, $feed, $cmisEntry);//, $parent, $pwc, $method); $childrenFeed->appendChild($entry); } @@ -165,26 +165,26 @@ class KT_cmis_atom_service_helper { * Creates the actual object entry: this is shared between other functions which require this content * * @param object $entry The entry object - * @param object $response The response feed + * @param object $feed The response feed * @param array $cmisEntry The CMIS object content * @param string $parent The parent folder name * @param boolean $pwc Whether this is a PWC object (will be returned slightly differently) * @param string $method The calling method (slightly affects the output) */ - static public function createObjectEntryContent($entry, &$response, $cmisEntry, $parent = '', $pwc = false, $method = 'GET') + static public function createObjectEntryContent($entry, &$feed, $cmisEntry, $parent = '', $pwc = false, $method = 'GET') { // TODO dynamic actual creator name - $responseElement = $response->newField('author'); - $element = $response->newField('name', 'admin', $responseElement); + $responseElement = $feed->newField('author'); + $element = $feed->newField('name', 'admin', $responseElement); $entry->appendChild($responseElement); $typeString = str_replace('cmis:', '', $type); if (!empty($cmisEntry['properties']['contentStreamLength']['value'])) { - $field = $response->newElement('content'); - $field->appendChild($response->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); - $field->appendChild($response->newAttr('src', CMIS_APP_BASE_URI . $workspace . '/' . $typeString + $field = $feed->newElement('content'); + $field->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); + $field->appendChild($feed->newAttr('src', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'] . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($field); @@ -192,37 +192,37 @@ class KT_cmis_atom_service_helper { // content & id tags $id = $cmisEntry['properties']['objectId']['value']; - $response->newField('id', 'urn:uuid:' . $id, $entry); + $feed->newField('id', 'urn:uuid:' . $id, $entry); // links - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'self')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'self')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . (!$pwc ? $typeString : 'pwc') . '/' . $cmisEntry['properties']['objectId']['value'])); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'edit')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'edit')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'])); $entry->appendChild($link); if ((strtolower($cmisEntry['properties']['objectTypeId']['value']) == 'cmis:document') && (!empty($cmisEntry['properties']['contentStreamLength']['value']))) { - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'edit-media')); - $link->appendChild($response->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'edit-media')); + $link->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'] . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'enclosure')); - $link->appendChild($response->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'enclosure')); + $link->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'] . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); @@ -230,17 +230,17 @@ class KT_cmis_atom_service_helper { // according to spec this MUST be present, but spec says that links for function which are not supported // do not need to be present, so unsure for the moment - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'] . '/allowableactions')); $entry->appendChild($link); // according to spec this MUST be present, but spec says that links for function which are not supported // do not need to be present, so unsure for the moment - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'http://docs.oasis-open.org/ns/cmis/link/200908/relationships')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'http://docs.oasis-open.org/ns/cmis/link/200908/relationships')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'] . '/rels')); $entry->appendChild($link); @@ -251,9 +251,9 @@ class KT_cmis_atom_service_helper { if (!CMISUtil::isRootFolder(self::$repositoryId, $cmisEntry['properties']['objectId']['value'], self::$ktapi)) { // TODO check parent link is correct, fix if needed - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'up')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'up')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $cmisEntry['properties']['parentId']['value'])); $entry->appendChild($link); } @@ -261,16 +261,16 @@ class KT_cmis_atom_service_helper { // Folder/Document specific links if (strtolower($cmisEntry['properties']['objectTypeId']['value']) == 'cmis:folder') { - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'down')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'down')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'] . '/children')); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'down')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'down')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'] . '/descendants')); @@ -285,10 +285,10 @@ class KT_cmis_atom_service_helper { // not sure yet where it must point... if (!empty($cmisEntry['properties']['contentStreamLength']['value'])) { - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'stream')); - $link->appendChild($response->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'stream')); + $link->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' . $cmisEntry['properties']['objectId']['value'] . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); @@ -299,68 +299,51 @@ class KT_cmis_atom_service_helper { // TODO separated code for PWC and actual document object if (!empty($cmisEntry['properties']['versionSeriesCheckedOutId']['value'])) { - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'pwc')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'pwc')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' . $cmisEntry['properties']['objectId']['value'] . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'source')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'source')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' . $cmisEntry['properties']['objectId']['value'] . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); } -// $link = $response->newElement('link'); -// $link->appendChild($response->newAttr('rel', 'stream')); -// $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type +// $link = $feed->newElement('link'); +// $link->appendChild($feed->newAttr('rel', 'stream')); +// $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type // . '/' . $cmisEntry['properties']['objectId']['value'] // . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); } - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'describedby')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . $type)); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'describedby')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . $type)); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'service')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'service')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); $entry->appendChild($link); // TODO proper date - $entry->appendChild($response->newField('published', self::formatDatestamp())); - $entry->appendChild($response->newElement('summary', $cmisEntry['properties']['name']['value'])); - $entry->appendChild($response->newElement('title', $cmisEntry['properties']['name']['value'])); - $entry->appendChild($response->newField('updated', self::formatDatestamp())); + $entry->appendChild($feed->newField('published', self::formatDatestamp())); + $entry->appendChild($feed->newElement('summary', $cmisEntry['properties']['name']['value'])); + $entry->appendChild($feed->newElement('title', $cmisEntry['properties']['name']['value'])); + $entry->appendChild($feed->newField('updated', self::formatDatestamp())); // main CMIS entry - $objectElement = $response->newElement('cmisra:object'); - $propertiesElement = $response->newElement('cmis:properties'); - - foreach($cmisEntry['properties'] as $propertyName => $property) - { - $propElement = $response->newElement('cmis:' . $property['type']); -// $propElement->appendChild($response->newAttr('localName', 'rep-cmis:' . $propertyName)); - $propElement->appendChild($response->newAttr('propertyDefinitionId', 'cmis:' . $propertyName)); - if (!empty($property['value'])) - { - if ($propertyName == 'contentStreamUri') { - $property['value'] = CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' .$property['value']; - } - $response->newField('cmis:value', CMISUtil::boolToString($property['value']), $propElement); - } - $propertiesElement->appendChild($propElement); - } - - $objectElement->appendChild($propertiesElement); + $objectElement = $feed->newElement('cmisra:object'); + $objectElement->appendChild(self::createEntryPropertiesElement($feed, $cmisEntry['properties'])); $entry->appendChild($objectElement); // TODO check determination of when to add app:edited tag // if ($method == 'POST') { - $entry->appendChild($response->newElement('app:edited', self::formatDatestamp())); + $entry->appendChild($feed->newElement('app:edited', self::formatDatestamp())); // } // TODO pathSegment entry @@ -368,14 +351,43 @@ class KT_cmis_atom_service_helper { // deal with child objects if (isset($cmisEntry['children'])) { // add children node and fill with child entries - $childrenFeed = $response->newElement('feed'); - self::createObjectChildrenFeed($childrenFeed, $cmisEntry['children'], $workspace, $response, '' /*folderName not passed through*/); + $childrenFeed = $feed->newElement('feed'); + self::createObjectChildrenFeed($childrenFeed, $cmisEntry['children'], $workspace, $feed, '' /*folderName not passed through*/); - $childrenElement = $response->newElement('cmisra:children'); + $childrenElement = $feed->newElement('cmisra:children'); $childrenElement->appendChild($childrenFeed); $entry->appendChild($childrenElement); } } + + /** + * Shared function for creating an object properties node + * + * @param object $feed AtomPub response feed + * @param array $properties CMIS object properties + * @return object $propertiesElement AtomPub node + */ + // TODO leave out unset properties? + static public function createEntryPropertiesElement(&$feed, $properties) + { + $propertiesElement = $feed->newElement('cmis:properties'); + foreach($properties as $propertyName => $property) + { + $propElement = $feed->newElement('cmis:' . $property['type']); +// $propElement->appendChild($feed->newAttr('localName', 'rep-cmis:' . $propertyName)); + $propElement->appendChild($feed->newAttr('propertyDefinitionId', 'cmis:' . $propertyName)); + if (!empty($property['value'])) + { + if ($propertyName == 'contentStreamUri') { + $property['value'] = CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' .$property['value']; + } + $feed->newField('cmis:value', CMISUtil::boolToString($property['value']), $propElement); + } + $propertiesElement->appendChild($propElement); + } + + return $propertiesElement; + } /** * Retrieves the list of types|type definition as a CMIS AtomPub feed @@ -386,6 +398,10 @@ class KT_cmis_atom_service_helper { */ static public function getTypeFeed($typeDef, $types) { + global $default; +// $default->log->info(print_r($types)); +// $default->log->info(print_r($types, true)); + $default->log->info($typeDef); $typesString = ''; $typesHeading = ''; switch($typeDef) @@ -417,21 +433,6 @@ class KT_cmis_atom_service_helper { $element = $feed->newField('name', 'admin', $feedElement); $feed->appendChild($feedElement); - // NOTE spec says this link MUST be present but is vague on where it points - // as of 0.61c: - // "The source link relation points to the underlying CMIS Type Definition as Atom Entry" - // so what is the underlying CMIS Type Definition for a collection of base types? - // suspect that it only applies when not listing all types, i.e. a base type is asked for - /* - $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','source')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['typeId']))); - $feed->appendChild($link); - */ - - // current time: format = 2009-07-13T14:49:27.659+02:00 - $feed->appendChild($feed->newElement('updated', self::formatDatestamp())); - foreach($types as $type) { $entry = $feed->newEntry(); @@ -439,48 +440,37 @@ class KT_cmis_atom_service_helper { $feedElement = $feed->newField('author'); $element = $feed->newField('name', 'admin', $feedElement); $entry->appendChild($feedElement); - $feedElement = $feed->newField('content', $type['typeId']); + $feedElement = $feed->newField('content', 'Type definition for ' . $type['baseId']); $entry->appendChild($feedElement); - $feed->newField('id', 'urn:uuid:type-' . $type['typeId'], $feed); - - // TODO add parents link when not selecting a base type. - // TODO add children link when type has children - // TODO add descendants link when type has children - // NOTE KnowledgeTree currently only supports base types so these are not important at the present time. - + $feed->newField('id', 'urn:uuid:type-' . $type['baseId'], $feed); + // links $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel','self')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['typeId']))); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['baseId']))); $entry->appendChild($link); // TODO type link MUST point to base type // KnowledgeTree currently only supports base types so this is not important // at the present time as it will always point at the base type. $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel','type')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['typeId']))); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['baseId']))); $entry->appendChild($link); $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel','repository')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); $entry->appendChild($link); - - $entry->appendChild($feed->newElement('summary', $type['typeId'] . ' Type')); - $entry->appendChild($feed->newElement('title', $type['typeId'])); + + $entry->appendChild($feed->newElement('summary', 'Summary for ' . $type['baseId'] . ' type')); + $entry->appendChild($feed->newElement('title', $type['baseId'])); $entry->appendChild($feed->newElement('updated', self::formatDatestamp())); - - // main CMIS entry - $feedElement = $feed->newElement('cmis:' . strtolower($type['typeId']) . 'Type'); - foreach($type as $property => $value) - { - $feed->newField('cmis:' . $property, CMISUtil::boolToString($value), $feedElement); - } - - $entry->appendChild($feedElement); - // after every entry, append a cmis:terminator tag -// $entry->appendChild($feed->newElement('cmis:terminator')); + $objectElement = $feed->newElement('cmisra:type'); + foreach($type as $property => $value) { + $feed->newField('cmis:' . $property, CMISUtil::boolToString($value), $objectElement); + } + $entry->appendChild($objectElement); } return $feed; diff --git a/webservice/clienttools/services/0.9.1/.htaccess b/webservice/clienttools/services/0.9.1/.htaccess index cab070d..f7c094c 100644 --- a/webservice/clienttools/services/0.9.1/.htaccess +++ b/webservice/clienttools/services/0.9.1/.htaccess @@ -1,118 +1,118 @@ -# This file sets up the necessary PHP settings to run KnowledgeTree -# optimally. -# -# It is best that KnowledgeTree be allowed to manage its PHP and access -# permissions using these .htaccess files. This prevents direct access -# to libraries, scripts, and documents that should not be available via -# the web. -# -# By default, to facilitate quick testing of functionality, -# KnowledgeTree places the Documents directory within the KnowledgeTree -# web root. This, by default, would allow people to browse the -# documents in the DMS, bypassing the security permissions. The -# default .htaccess settings would prevent this. -# -# KnowledgeTree itself is able to deal with most PHP configurations, -# excepting "safe mode" currently, but is unable to handle certain -# inherent configuration options (for example, setting the upload size -# very low). Check the setup checklists (as described in the -# documentation) to see how your configuration is handled. -# -# The default .htaccess settings ensure that no workarounds occur in the -# PHP code, leading to better performance and robustness. -# - -php_value default_mimetype text/html -php_value auto_prepend_file none -php_value auto_append_file none -php_flag display_startup_errors ON -php_flag display_errors ON -php_flag file_uploads ON -php_flag magic_quotes_gpc OFF -php_flag magic_quotes_runtime OFF -php_flag register_globals OFF -php_flag output_buffering OFF -php_flag session.auto_start OFF - - - -php_value default_mimetype text/html -php_value auto_prepend_file none -php_value auto_append_file none -php_flag display_startup_errors ON -php_flag display_errors ON -php_flag file_uploads ON -php_flag magic_quotes_gpc OFF -php_flag magic_quotes_runtime OFF -php_flag register_globals OFF -php_flag output_buffering OFF -php_flag session.auto_start OFF - - - -SetEnv kt_htaccess_worked yes -SetEnvIf Authorization (.*) kt_auth=$1 - -LimitRequestBody 0 -DirectoryIndex index.html index.php -Options none -Options +ExecCGI -Options +SymLinksIfOwnerMatch -#AcceptPathInfo On - -AddOutputFilter DEFLATE text/html text/plain text/xml text/javascript application/x-javascript text/css -# The following seems to breaking things on ZendServer - kgf -#ExpiresActive ON -#ExpiresByType text/html "access plus 1 day" -#ExpiresByType text/css "access plus 1 day" -#ExpiresByType text/javascript "access plus 7 days" -#ExpiresByType application/x-javascript "access plus 7 days" -#ExpiresByType image/gif "access plus 1 month" -#ExpiresByType image/jpg "access plus 1 month" -#ExpiresByType image/png "access plus 1 month" -#ExpiresByType image/x-icon "access plus 1 month" - -# -# If you are having uploading larger documents, adjust the 16M examples -# below to increase the maximum file size. This is set to a reasonable -# size for testing and most usage patterns, as increased sizes may allow -# malicious users to use up resources. -# - -php_value upload_max_filesize -1 -php_value post_max_size 2000M -php_value memory_limit -1 -php_value max_input_time -1 -php_value max_execution_time 0 -php_value error_reporting 5 - - -php_value upload_max_filesize -1 -php_value post_max_size 2000M -php_value memory_limit -1 -php_value max_input_time -1 -php_value max_execution_time 0 -php_value error_reporting 5 - - -# Workaround for mod_auth when running php cgi - -RewriteEngine on -RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] - - -# -#RewriteEngine On -#RewriteBase / -## Set RewriteBase to the base directory that KnowledgeTree is viewable -## from via the web. So http://foo.org/ is /, and -## http://bar.com/knowledgeTree/ is /knowledgeTree -##RewriteBase /knowledgeTree -#RewriteCond %{REQUEST_FILENAME} !-f -#RewriteCond %{REQUEST_FILENAME}.php -f -#RewriteRule !^[^/]*.php - [C] -#RewriteRule ^([^/]*)([/].+)? $1.php [E=kt_path_info:$2,E=kt_orig_file:$1.php] -# -#SetEnv kt_no_extensions yes -# -# +# This file sets up the necessary PHP settings to run KnowledgeTree +# optimally. +# +# It is best that KnowledgeTree be allowed to manage its PHP and access +# permissions using these .htaccess files. This prevents direct access +# to libraries, scripts, and documents that should not be available via +# the web. +# +# By default, to facilitate quick testing of functionality, +# KnowledgeTree places the Documents directory within the KnowledgeTree +# web root. This, by default, would allow people to browse the +# documents in the DMS, bypassing the security permissions. The +# default .htaccess settings would prevent this. +# +# KnowledgeTree itself is able to deal with most PHP configurations, +# excepting "safe mode" currently, but is unable to handle certain +# inherent configuration options (for example, setting the upload size +# very low). Check the setup checklists (as described in the +# documentation) to see how your configuration is handled. +# +# The default .htaccess settings ensure that no workarounds occur in the +# PHP code, leading to better performance and robustness. +# + +php_value default_mimetype text/html +php_value auto_prepend_file none +php_value auto_append_file none +php_flag display_startup_errors ON +php_flag display_errors ON +php_flag file_uploads ON +php_flag magic_quotes_gpc OFF +php_flag magic_quotes_runtime OFF +php_flag register_globals OFF +php_flag output_buffering OFF +php_flag session.auto_start OFF + + + +php_value default_mimetype text/html +php_value auto_prepend_file none +php_value auto_append_file none +php_flag display_startup_errors ON +php_flag display_errors ON +php_flag file_uploads ON +php_flag magic_quotes_gpc OFF +php_flag magic_quotes_runtime OFF +php_flag register_globals OFF +php_flag output_buffering OFF +php_flag session.auto_start OFF + + + +SetEnv kt_htaccess_worked yes +SetEnvIf Authorization (.*) kt_auth=$1 + +LimitRequestBody 0 +DirectoryIndex index.html index.php +Options none +Options +ExecCGI +Options +SymLinksIfOwnerMatch +#AcceptPathInfo On + +AddOutputFilter DEFLATE text/html text/plain text/xml text/javascript application/x-javascript text/css +# The following seems to breaking things on ZendServer - kgf +#ExpiresActive ON +#ExpiresByType text/html "access plus 1 day" +#ExpiresByType text/css "access plus 1 day" +#ExpiresByType text/javascript "access plus 7 days" +#ExpiresByType application/x-javascript "access plus 7 days" +#ExpiresByType image/gif "access plus 1 month" +#ExpiresByType image/jpg "access plus 1 month" +#ExpiresByType image/png "access plus 1 month" +#ExpiresByType image/x-icon "access plus 1 month" + +# +# If you are having uploading larger documents, adjust the 16M examples +# below to increase the maximum file size. This is set to a reasonable +# size for testing and most usage patterns, as increased sizes may allow +# malicious users to use up resources. +# + +php_value upload_max_filesize -1 +php_value post_max_size 2000M +php_value memory_limit -1 +php_value max_input_time -1 +php_value max_execution_time 0 +php_value error_reporting 5 + + +php_value upload_max_filesize -1 +php_value post_max_size 2000M +php_value memory_limit -1 +php_value max_input_time -1 +php_value max_execution_time 0 +php_value error_reporting 5 + + +# Workaround for mod_auth when running php cgi + +RewriteEngine on +RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] + + +# +#RewriteEngine On +#RewriteBase / +## Set RewriteBase to the base directory that KnowledgeTree is viewable +## from via the web. So http://foo.org/ is /, and +## http://bar.com/knowledgeTree/ is /knowledgeTree +##RewriteBase /knowledgeTree +#RewriteCond %{REQUEST_FILENAME} !-f +#RewriteCond %{REQUEST_FILENAME}.php -f +#RewriteRule !^[^/]*.php - [C] +#RewriteRule ^([^/]*)([/].+)? $1.php [E=kt_path_info:$2,E=kt_orig_file:$1.php] +# +#SetEnv kt_no_extensions yes +# +#