Commit 30d7fae36b0a8905c3ffa0193a3740e3fa79144a
1 parent
e6428228
Updated CMIS API with Document Creation functionality
Removed conflicting exceptions Updated CMIS API Unit Tests Committed by: Paul Barrett Reviewed by: Jarrett Jordaan
Showing
12 changed files
with
212 additions
and
97 deletions
ktwebservice/KTUploadManager.inc.php
| @@ -110,6 +110,7 @@ class KTUploadManager | @@ -110,6 +110,7 @@ class KTUploadManager | ||
| 110 | return ($tempdir == $this->temp_dir); | 110 | return ($tempdir == $this->temp_dir); |
| 111 | */ | 111 | */ |
| 112 | } | 112 | } |
| 113 | + | ||
| 113 | function store_base64_file($base64, $prefix= 'sa_') | 114 | function store_base64_file($base64, $prefix= 'sa_') |
| 114 | { | 115 | { |
| 115 | $tempfilename = $this->get_temp_filename($prefix); | 116 | $tempfilename = $this->get_temp_filename($prefix); |
lib/api/ktcmis/classes/CMISBaseObject.inc.php
| @@ -62,7 +62,7 @@ abstract class CMISBaseObject { | @@ -62,7 +62,7 @@ abstract class CMISBaseObject { | ||
| 62 | // TODO all we have here so far is getAttributes & getProperties | 62 | // TODO all we have here so far is getAttributes & getProperties |
| 63 | // add all the other methods as we go along | 63 | // add all the other methods as we go along |
| 64 | 64 | ||
| 65 | - function __construct() | 65 | + public function __construct() |
| 66 | { | 66 | { |
| 67 | // $propertyDef = new PropertyDefinition(); | 67 | // $propertyDef = new PropertyDefinition(); |
| 68 | // $this->properties[] = $propertyDef; | 68 | // $this->properties[] = $propertyDef; |
| @@ -73,7 +73,7 @@ abstract class CMISBaseObject { | @@ -73,7 +73,7 @@ abstract class CMISBaseObject { | ||
| 73 | * | 73 | * |
| 74 | * @return array $attributes | 74 | * @return array $attributes |
| 75 | */ | 75 | */ |
| 76 | - function getAttributes() | 76 | + public function getAttributes() |
| 77 | { | 77 | { |
| 78 | $attributes = array(); | 78 | $attributes = array(); |
| 79 | 79 | ||
| @@ -95,7 +95,7 @@ abstract class CMISBaseObject { | @@ -95,7 +95,7 @@ abstract class CMISBaseObject { | ||
| 95 | return $attributes; | 95 | return $attributes; |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | - function getAttribute($field) | 98 | + public function getAttribute($field) |
| 99 | { | 99 | { |
| 100 | return $this->{$field}; | 100 | return $this->{$field}; |
| 101 | } | 101 | } |
| @@ -104,7 +104,7 @@ abstract class CMISBaseObject { | @@ -104,7 +104,7 @@ abstract class CMISBaseObject { | ||
| 104 | * Sets properties for this type | 104 | * Sets properties for this type |
| 105 | * Obeys the rules as specified in the property definitions (once implemented) | 105 | * Obeys the rules as specified in the property definitions (once implemented) |
| 106 | */ | 106 | */ |
| 107 | - function setProperty($field, $value) | 107 | + public function setProperty($field, $value) |
| 108 | { | 108 | { |
| 109 | $this->properties->setValue($field, $value); | 109 | $this->properties->setValue($field, $value); |
| 110 | } | 110 | } |
| @@ -123,11 +123,26 @@ abstract class CMISBaseObject { | @@ -123,11 +123,26 @@ abstract class CMISBaseObject { | ||
| 123 | /** | 123 | /** |
| 124 | * Fetches properties for this object type | 124 | * Fetches properties for this object type |
| 125 | */ | 125 | */ |
| 126 | - function getProperties() | 126 | + public function getProperties() |
| 127 | { | 127 | { |
| 128 | return $this->properties; | 128 | return $this->properties; |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | + public function getProperty($property) | ||
| 132 | + { | ||
| 133 | + return $this->properties->getValue($property); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + public function reload($documentId) | ||
| 137 | + { | ||
| 138 | + $this->_get($documentId); | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + private function _get($documentId) | ||
| 142 | + { | ||
| 143 | + // override in child classes | ||
| 144 | + } | ||
| 145 | + | ||
| 131 | } | 146 | } |
| 132 | 147 | ||
| 133 | ?> | 148 | ?> |
lib/api/ktcmis/exceptions/InvalidArgumentException.inc.php deleted
lib/api/ktcmis/exceptions/RuntimeException.inc.php deleted
lib/api/ktcmis/ktcmis.inc.php
| @@ -480,7 +480,7 @@ class KTObjectService extends KTCMISBase { | @@ -480,7 +480,7 @@ class KTObjectService extends KTCMISBase { | ||
| 480 | * @return string $objectId The id of the created folder object | 480 | * @return string $objectId The id of the created folder object |
| 481 | */ | 481 | */ |
| 482 | function createDocument($repositoryId, $typeId, $properties, $folderId = null, | 482 | function createDocument($repositoryId, $typeId, $properties, $folderId = null, |
| 483 | - $contentStream = null, $versioningState = 'Major') | 483 | + $contentStream = null, $versioningState = null) |
| 484 | { | 484 | { |
| 485 | $objectId = null; | 485 | $objectId = null; |
| 486 | 486 |
lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php
| @@ -48,8 +48,8 @@ require_once(CMIS_DIR . '/util/CMISUtil.inc.php'); | @@ -48,8 +48,8 @@ require_once(CMIS_DIR . '/util/CMISUtil.inc.php'); | ||
| 48 | 48 | ||
| 49 | class CMISDocumentObject extends CMISBaseObject { | 49 | class CMISDocumentObject extends CMISBaseObject { |
| 50 | 50 | ||
| 51 | - private $versionable; | ||
| 52 | - private $contentStreamAllowed; | 51 | + protected $versionable; |
| 52 | + protected $contentStreamAllowed; | ||
| 53 | private $ktapi; | 53 | private $ktapi; |
| 54 | private $uri; | 54 | private $uri; |
| 55 | 55 | ||
| @@ -100,7 +100,7 @@ class CMISDocumentObject extends CMISBaseObject { | @@ -100,7 +100,7 @@ class CMISDocumentObject extends CMISBaseObject { | ||
| 100 | 100 | ||
| 101 | private function _get($documentId) | 101 | private function _get($documentId) |
| 102 | { | 102 | { |
| 103 | - $object = $this->ktapi->get_document_by_id($documentId); | 103 | + $object = $this->ktapi->get_document_by_id((int)$documentId); |
| 104 | 104 | ||
| 105 | // error? | 105 | // error? |
| 106 | if (PEAR::isError($object)) | 106 | if (PEAR::isError($object)) |
lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php
| @@ -46,10 +46,10 @@ require_once(CMIS_DIR . '/util/CMISUtil.inc.php'); | @@ -46,10 +46,10 @@ require_once(CMIS_DIR . '/util/CMISUtil.inc.php'); | ||
| 46 | 46 | ||
| 47 | class CMISFolderObject extends CMISBaseObject { | 47 | class CMISFolderObject extends CMISBaseObject { |
| 48 | 48 | ||
| 49 | - var $ktapi; | ||
| 50 | - var $uri; | 49 | + private $ktapi; |
| 50 | + private $uri; | ||
| 51 | 51 | ||
| 52 | - function __construct($folderId = null, &$ktapi = null, $uri = null) | 52 | + public function __construct($folderId = null, &$ktapi = null, $uri = null) |
| 53 | { | 53 | { |
| 54 | $this->ktapi = $ktapi; | 54 | $this->ktapi = $ktapi; |
| 55 | $this->uri = $uri; | 55 | $this->uri = $uri; |
| @@ -78,7 +78,7 @@ class CMISFolderObject extends CMISBaseObject { | @@ -78,7 +78,7 @@ class CMISFolderObject extends CMISBaseObject { | ||
| 78 | 78 | ||
| 79 | private function _get($folderId) | 79 | private function _get($folderId) |
| 80 | { | 80 | { |
| 81 | - $object = $this->ktapi->get_folder_by_id($folderId); | 81 | + $object = $this->ktapi->get_folder_by_id((int)$folderId); |
| 82 | 82 | ||
| 83 | // error? | 83 | // error? |
| 84 | if (PEAR::isError($object)) | 84 | if (PEAR::isError($object)) |
lib/api/ktcmis/services/CMISNavigationService.inc.php
| @@ -83,7 +83,7 @@ class CMISNavigationService { | @@ -83,7 +83,7 @@ class CMISNavigationService { | ||
| 83 | $repository = new CMISRepository($repositoryId); | 83 | $repository = new CMISRepository($repositoryId); |
| 84 | 84 | ||
| 85 | // if this is not a folder, cannot get descendants | 85 | // if this is not a folder, cannot get descendants |
| 86 | - $type = CMISUtil::decodeObjectId($folderId); | 86 | + $folderId = CMISUtil::decodeObjectId($folderId, $type); |
| 87 | 87 | ||
| 88 | if ($type != 'Folder') | 88 | if ($type != 'Folder') |
| 89 | { | 89 | { |
| @@ -125,7 +125,7 @@ class CMISNavigationService { | @@ -125,7 +125,7 @@ class CMISNavigationService { | ||
| 125 | $repository = new CMISRepository($repositoryId); | 125 | $repository = new CMISRepository($repositoryId); |
| 126 | 126 | ||
| 127 | // if this is not a folder, cannot get children | 127 | // if this is not a folder, cannot get children |
| 128 | - $type = CMISUtil::decodeObjectId($folderId); | 128 | + $folderId = CMISUtil::decodeObjectId($folderId, $type); |
| 129 | // NOTE this will quite possibly break the webservices | 129 | // NOTE this will quite possibly break the webservices |
| 130 | if ($type != 'Folder') | 130 | if ($type != 'Folder') |
| 131 | { | 131 | { |
| @@ -162,7 +162,7 @@ class CMISNavigationService { | @@ -162,7 +162,7 @@ class CMISNavigationService { | ||
| 162 | $repository = new CMISRepository($repositoryId); | 162 | $repository = new CMISRepository($repositoryId); |
| 163 | 163 | ||
| 164 | // if this is not a folder, cannot get folder parent :) | 164 | // if this is not a folder, cannot get folder parent :) |
| 165 | - $type = CMISUtil::decodeObjectId($folderId); | 165 | + $folderId = CMISUtil::decodeObjectId($folderId, $type); |
| 166 | // NOTE this will quite possibly break the webservices | 166 | // NOTE this will quite possibly break the webservices |
| 167 | if ($type != 'Folder') | 167 | if ($type != 'Folder') |
| 168 | { | 168 | { |
| @@ -221,7 +221,7 @@ class CMISNavigationService { | @@ -221,7 +221,7 @@ class CMISNavigationService { | ||
| 221 | { | 221 | { |
| 222 | $ancestry = array(); | 222 | $ancestry = array(); |
| 223 | 223 | ||
| 224 | - $typeId = CMISUtil::decodeObjectId($objectId); | 224 | + $objectId = CMISUtil::decodeObjectId($objectId, $typeId); |
| 225 | 225 | ||
| 226 | // TODO - what about other types? only implementing folders and documents at the moment so ignore for now | 226 | // TODO - what about other types? only implementing folders and documents at the moment so ignore for now |
| 227 | switch($typeId) | 227 | switch($typeId) |
lib/api/ktcmis/services/CMISObjectService.inc.php
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | require_once(KT_DIR . '/ktapi/ktapi.inc.php'); | 3 | require_once(KT_DIR . '/ktapi/ktapi.inc.php'); |
| 4 | +require_once(KT_DIR . '/ktwebservice/KTUploadManager.inc.php'); | ||
| 4 | require_once(CMIS_DIR . '/exceptions/ConstraintViolationException.inc.php'); | 5 | require_once(CMIS_DIR . '/exceptions/ConstraintViolationException.inc.php'); |
| 5 | require_once(CMIS_DIR . '/exceptions/ContentAlreadyExistsException.inc.php'); | 6 | require_once(CMIS_DIR . '/exceptions/ContentAlreadyExistsException.inc.php'); |
| 6 | require_once(CMIS_DIR . '/exceptions/ObjectNotFoundException.inc.php'); | 7 | require_once(CMIS_DIR . '/exceptions/ObjectNotFoundException.inc.php'); |
| @@ -8,6 +9,7 @@ require_once(CMIS_DIR . '/exceptions/StorageException.inc.php'); | @@ -8,6 +9,7 @@ require_once(CMIS_DIR . '/exceptions/StorageException.inc.php'); | ||
| 8 | require_once(CMIS_DIR . '/exceptions/StreamNotSupportedException.inc.php'); | 9 | require_once(CMIS_DIR . '/exceptions/StreamNotSupportedException.inc.php'); |
| 9 | require_once(CMIS_DIR . '/exceptions/UpdateConflictException.inc.php'); | 10 | require_once(CMIS_DIR . '/exceptions/UpdateConflictException.inc.php'); |
| 10 | require_once(CMIS_DIR . '/exceptions/VersioningException.inc.php'); | 11 | require_once(CMIS_DIR . '/exceptions/VersioningException.inc.php'); |
| 12 | +require_once(CMIS_DIR . '/services/CMISNavigationService.inc.php'); | ||
| 11 | require_once(CMIS_DIR . '/services/CMISRepositoryService.inc.php'); | 13 | require_once(CMIS_DIR . '/services/CMISRepositoryService.inc.php'); |
| 12 | require_once(CMIS_DIR . '/objecttypes/CMISDocumentObject.inc.php'); | 14 | require_once(CMIS_DIR . '/objecttypes/CMISDocumentObject.inc.php'); |
| 13 | require_once(CMIS_DIR . '/objecttypes/CMISFolderObject.inc.php'); | 15 | require_once(CMIS_DIR . '/objecttypes/CMISFolderObject.inc.php'); |
| @@ -49,7 +51,7 @@ class CMISObjectService { | @@ -49,7 +51,7 @@ class CMISObjectService { | ||
| 49 | // TODO a better default value? | 51 | // TODO a better default value? |
| 50 | $properties = array(); | 52 | $properties = array(); |
| 51 | 53 | ||
| 52 | - $typeId = CMISUtil::decodeObjectId($objectId); | 54 | + $objectId = CMISUtil::decodeObjectId($objectId, $typeId); |
| 53 | 55 | ||
| 54 | if ($typeId == 'Unknown') | 56 | if ($typeId == 'Unknown') |
| 55 | { | 57 | { |
| @@ -86,14 +88,8 @@ class CMISObjectService { | @@ -86,14 +88,8 @@ class CMISObjectService { | ||
| 86 | // TODO throw ConstraintViolationException if: | 88 | // TODO throw ConstraintViolationException if: |
| 87 | // value of any of the properties violates the min/max/required/length constraints | 89 | // value of any of the properties violates the min/max/required/length constraints |
| 88 | // specified in the property definition in the Object-Type. | 90 | // specified in the property definition in the Object-Type. |
| 89 | - // TODO throw ConstraintViolationException if: | ||
| 90 | - // The Object-Type definition specified by the typeId parameter's "contentStreamAllowed" attribute | ||
| 91 | - // is set to "required" and no contentStream input parameter is provided | ||
| 92 | - // TODO throw ConstraintViolationException if: | ||
| 93 | - // The Object-Type definition specified by the typeId parameter's "versionable" attribute | ||
| 94 | - // is set to "false" and a value for the versioningState input parameter is provided | ||
| 95 | function createDocument($repositoryId, $typeId, $properties, $folderId = null, | 91 | function createDocument($repositoryId, $typeId, $properties, $folderId = null, |
| 96 | - $contentStream = null, $versioningState = 'Major') | 92 | + $contentStream = null, $versioningState = null) |
| 97 | { | 93 | { |
| 98 | $objectId = null; | 94 | $objectId = null; |
| 99 | 95 | ||
| @@ -129,38 +125,22 @@ class CMISObjectService { | @@ -129,38 +125,22 @@ class CMISObjectService { | ||
| 129 | 125 | ||
| 130 | // Attempt to decode $folderId, use as is if not detected as encoded | 126 | // Attempt to decode $folderId, use as is if not detected as encoded |
| 131 | $tmpObjectId = $folderId; | 127 | $tmpObjectId = $folderId; |
| 132 | - $tmpTypeId = CMISUtil::decodeObjectId($tmpObjectId); | 128 | + $tmpObjectId = CMISUtil::decodeObjectId($tmpObjectId, $tmpTypeId); |
| 133 | if ($tmpTypeId != 'Unknown') | 129 | if ($tmpTypeId != 'Unknown') |
| 134 | $folderId = $tmpObjectId; | 130 | $folderId = $tmpObjectId; |
| 135 | 131 | ||
| 136 | // if parent folder is not allowed to hold this type, throw exception | 132 | // if parent folder is not allowed to hold this type, throw exception |
| 137 | $CMISFolder = new CMISFolderObject($folderId, $this->ktapi); | 133 | $CMISFolder = new CMISFolderObject($folderId, $this->ktapi); |
| 138 | - $folderProperties = $CMISFolder->getProperties(); | ||
| 139 | - $allowed = $folderProperties->getValue('AllowedChildObjectTypeIds'); | 134 | + $allowed = $CMISFolder->getProperty('AllowedChildObjectTypeIds'); |
| 140 | if (!is_array($allowed) || !in_array($typeId, $allowed)) | 135 | if (!is_array($allowed) || !in_array($typeId, $allowed)) |
| 141 | { | 136 | { |
| 142 | throw new ConstraintViolationException('Parent folder may not hold objects of this type (' . $typeId . ')'); | 137 | throw new ConstraintViolationException('Parent folder may not hold objects of this type (' . $typeId . ')'); |
| 143 | } | 138 | } |
| 144 | 139 | ||
| 145 | - // set title and name identical if only one submitted | ||
| 146 | - if ($properties['title'] == '') | ||
| 147 | - { | ||
| 148 | - $properties['title'] = $properties['name']; | ||
| 149 | - } | ||
| 150 | - else if ($properties['name'] == '') | ||
| 151 | - { | ||
| 152 | - $properties['name'] = $properties['title']; | ||
| 153 | - } | ||
| 154 | - | ||
| 155 | - if ($properties['type'] == '') | ||
| 156 | - { | ||
| 157 | - $properties['type'] = $properties['Default']; | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | // if content stream is required and no content stream is supplied, throw a ConstraintViolationException | 140 | // if content stream is required and no content stream is supplied, throw a ConstraintViolationException |
| 161 | if (($typeDefinition['attributes']['contentStreamAllowed'] == 'required') && empty($contentStream)) | 141 | if (($typeDefinition['attributes']['contentStreamAllowed'] == 'required') && empty($contentStream)) |
| 162 | { | 142 | { |
| 163 | - throw new ConstraintViolationException('The Knowledgetree Repository requires a content stream for document creation. ' | 143 | + throw new ConstraintViolationException('This repository requires a content stream for document creation. ' |
| 164 | . 'Refusing to create an empty document'); | 144 | . 'Refusing to create an empty document'); |
| 165 | } | 145 | } |
| 166 | else if (($typeDefinition['attributes']['contentStreamAllowed'] == 'notAllowed') && !empty($contentStream)) | 146 | else if (($typeDefinition['attributes']['contentStreamAllowed'] == 'notAllowed') && !empty($contentStream)) |
| @@ -168,25 +148,66 @@ class CMISObjectService { | @@ -168,25 +148,66 @@ class CMISObjectService { | ||
| 168 | throw new StreamNotSupportedException('Content Streams are not supported'); | 148 | throw new StreamNotSupportedException('Content Streams are not supported'); |
| 169 | } | 149 | } |
| 170 | 150 | ||
| 151 | + // if versionable attribute is set to false and versioningState is supplied, throw a ConstraintViolationException | ||
| 152 | + if (!$typeDefinition['attributes']['versionable'] && !empty($versioningState)) | ||
| 153 | + { | ||
| 154 | + throw new ConstraintViolationException('This repository does not support versioning'); | ||
| 155 | + } | ||
| 156 | + | ||
| 171 | // TODO deal with $versioningState when supplied | 157 | // TODO deal with $versioningState when supplied |
| 172 | 158 | ||
| 173 | - // TODO Use add_document_with_metadata instead if metadata content submitted | ||
| 174 | - $response = $this->ktapi->add_document($folderId, $properties['title'], $properties['name'], $properties['type'], $uploadedFile); | ||
| 175 | - if ($response['status_code'] != 0) | 159 | + // set title and name identical if only one submitted |
| 160 | + if ($properties['title'] == '') | ||
| 176 | { | 161 | { |
| 177 | - throw new StorageException('The repository was unable to create the document - ' . $response['message']); | 162 | + $properties['title'] = $properties['name']; |
| 178 | } | 163 | } |
| 179 | - else | 164 | + else if ($properties['name'] == '') |
| 180 | { | 165 | { |
| 181 | - $objectId = CMISUtil::encodeObjectId('Document', $response['results']['id']); | 166 | + $properties['name'] = $properties['title']; |
| 182 | } | 167 | } |
| 183 | 168 | ||
| 184 | - // now that the document object exists, create the content stream from the supplied data | 169 | + // TODO also set to Default if a non-supported type is submitted |
| 170 | + if ($properties['type'] == '') | ||
| 171 | + { | ||
| 172 | + $properties['type'] = 'Default'; | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + // create the content stream from the supplied data | ||
| 176 | + // NOTE since the repository is set to require a content stream and we don't currently have another way to get the document data | ||
| 177 | + // this check isn't strictly necessary; however it is needed for a repository which does not support content streams | ||
| 185 | if (!empty($contentStream)) | 178 | if (!empty($contentStream)) |
| 186 | { | 179 | { |
| 187 | - // TODO changeToken support | ||
| 188 | - $changeToken = null; | ||
| 189 | - $this->setContentStream($repositoryId, $objectId, false, $contentStream, $changeToken); | 180 | + // NOTE There is a function in CMISUtil to do this but since KTUploadManager exists and has more functionality |
| 181 | + // which could come in useful at some point I decided to go with that instead (did not know it existed when | ||
| 182 | + // I wrote the CMISUtil function) | ||
| 183 | + $uploadManager = new KTUploadManager(); | ||
| 184 | + $file = $uploadManager->store_base64_file($contentStream, 'cmis_'); | ||
| 185 | + // create the document from this temporary file as per usual | ||
| 186 | + // TODO Use add_document_with_metadata instead if metadata content submitted || update metadata separately? | ||
| 187 | + $response = $this->ktapi->add_document((int)$folderId, $properties['title'], $properties['name'], | ||
| 188 | + $properties['type'], $file); | ||
| 189 | + if ($response['status_code'] != 0) | ||
| 190 | + { | ||
| 191 | + throw new StorageException('The repository was unable to create the document. ' . $response['message']); | ||
| 192 | + } | ||
| 193 | + else | ||
| 194 | + { | ||
| 195 | + $objectId = CMISUtil::encodeObjectId('Document', $response['results']['content_id']); | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + // remove temporary file | ||
| 199 | + @unlink($file); | ||
| 200 | + } | ||
| 201 | + // else create the document object in the database but don't actually create any content since none was supplied | ||
| 202 | + // NOTE perhaps this content could be supplied in the $properties array? | ||
| 203 | + else | ||
| 204 | + { | ||
| 205 | + // TODO creation of document without content. leaving this for now as we require content streams and any code | ||
| 206 | + // here will therefore never be executed; if we implement some form of template based document creation | ||
| 207 | + // then we may have something else to do here; | ||
| 208 | + // for now we just throw a general RuntimeException, since we should not | ||
| 209 | + // actually reach this code unless something is wrong; this may be removed or replaced later | ||
| 210 | + throw new RuntimeException('Cannot create empty document'); | ||
| 190 | } | 211 | } |
| 191 | 212 | ||
| 192 | return $objectId; | 213 | return $objectId; |
| @@ -229,20 +250,19 @@ class CMISObjectService { | @@ -229,20 +250,19 @@ class CMISObjectService { | ||
| 229 | 250 | ||
| 230 | // Attempt to decode $folderId, use as is if not detected as encoded | 251 | // Attempt to decode $folderId, use as is if not detected as encoded |
| 231 | $tmpObjectId = $folderId; | 252 | $tmpObjectId = $folderId; |
| 232 | - $tmpTypeId = CMISUtil::decodeObjectId($tmpObjectId); | 253 | + $tmpObjectId = CMISUtil::decodeObjectId($tmpObjectId, $tmpTypeId); |
| 233 | if ($tmpTypeId != 'Unknown') | 254 | if ($tmpTypeId != 'Unknown') |
| 234 | $folderId = $tmpObjectId; | 255 | $folderId = $tmpObjectId; |
| 235 | 256 | ||
| 236 | // if parent folder is not allowed to hold this type, throw exception | 257 | // if parent folder is not allowed to hold this type, throw exception |
| 237 | $CMISFolder = new CMISFolderObject($folderId, $this->ktapi); | 258 | $CMISFolder = new CMISFolderObject($folderId, $this->ktapi); |
| 238 | - $folderProperties = $CMISFolder->getProperties(); | ||
| 239 | - $allowed = $folderProperties->getValue('AllowedChildObjectTypeIds'); | 259 | + $allowed = $CMISFolder->getProperty('AllowedChildObjectTypeIds'); |
| 240 | if (!is_array($allowed) || !in_array($typeId, $allowed)) | 260 | if (!is_array($allowed) || !in_array($typeId, $allowed)) |
| 241 | { | 261 | { |
| 242 | throw new ConstraintViolationException('Parent folder may not hold objects of this type (' . $typeId . ')'); | 262 | throw new ConstraintViolationException('Parent folder may not hold objects of this type (' . $typeId . ')'); |
| 243 | } | 263 | } |
| 244 | 264 | ||
| 245 | - $response = $this->ktapi->create_folder($folderId, $properties['name'], $sig_username = '', $sig_password = '', $reason = ''); | 265 | + $response = $this->ktapi->create_folder((int)$folderId, $properties['name'], $sig_username = '', $sig_password = '', $reason = ''); |
| 246 | if ($response['status_code'] != 0) | 266 | if ($response['status_code'] != 0) |
| 247 | { | 267 | { |
| 248 | throw new StorageException('The repository was unable to create the folder - ' . $response['message']); | 268 | throw new StorageException('The repository was unable to create the folder - ' . $response['message']); |
| @@ -255,6 +275,14 @@ class CMISObjectService { | @@ -255,6 +275,14 @@ class CMISObjectService { | ||
| 255 | return $objectId; | 275 | return $objectId; |
| 256 | } | 276 | } |
| 257 | 277 | ||
| 278 | + // NOTE this function is presently incomplete and untested. Completion deferred to implementation of Checkout/Checkin | ||
| 279 | + // functionality | ||
| 280 | + // NOTE I am not sure yet when this function would ever be called - checkin would be able to update the content stream | ||
| 281 | + // already and the only easy method we have (via KTAPI as it stands) to update the content is on checkin anyway. | ||
| 282 | + // Additionally this function doesn't take a value for the versioning status (major/minor) and so cannot pass it | ||
| 283 | + // on to the ktapi checkin function. | ||
| 284 | + // I imagine this function may be called if we ever allow updating document content independent of checkin, | ||
| 285 | + // or if we change some of the underlying code and call direct to the document functions and not via KTAPI. | ||
| 258 | /** | 286 | /** |
| 259 | * Sets the content stream data for an existing document | 287 | * Sets the content stream data for an existing document |
| 260 | * | 288 | * |
| @@ -277,6 +305,17 @@ class CMISObjectService { | @@ -277,6 +305,17 @@ class CMISObjectService { | ||
| 277 | // versioningException: The repository MAY throw this exception if the object is a non-current Document Version. | 305 | // versioningException: The repository MAY throw this exception if the object is a non-current Document Version. |
| 278 | function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null) | 306 | function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null) |
| 279 | { | 307 | { |
| 308 | + // if no document id was supplied, we are going to create the underlying physical document | ||
| 309 | + // NOTE while it might have been nice to keep this out of here, KTAPI has no method for creating a document without | ||
| 310 | + // a physical upload, so we cannot create the document first and then add the upload as a content stream, the | ||
| 311 | + // entire creation step needs to happen here. | ||
| 312 | + | ||
| 313 | + // Attempt to decode $documentId, use as is if not detected as encoded | ||
| 314 | + $tmpObjectId = $documentId; | ||
| 315 | + $tmpObjectId = CMISUtil::decodeObjectId($tmpObjectId, $tmpTypeId); | ||
| 316 | + if ($tmpTypeId != 'Unknown') | ||
| 317 | + $documentId = $tmpObjectId; | ||
| 318 | + | ||
| 280 | // fetch type definition of supplied document | 319 | // fetch type definition of supplied document |
| 281 | $CMISDocument = new CMISDocumentObject($documentId, $this->ktapi); | 320 | $CMISDocument = new CMISDocumentObject($documentId, $this->ktapi); |
| 282 | 321 | ||
| @@ -289,26 +328,34 @@ class CMISObjectService { | @@ -289,26 +328,34 @@ class CMISObjectService { | ||
| 289 | throw new StreamNotSupportedException('Content Streams are not allowed for this object type'); | 328 | throw new StreamNotSupportedException('Content Streams are not allowed for this object type'); |
| 290 | } | 329 | } |
| 291 | 330 | ||
| 292 | - $properties = $CMISDocument->getProperties(); | ||
| 293 | - if (!empty($properties->getValue('ContentStreamFilename')) && (!$overwriteFlag)) | 331 | + $csFileName = $CMISDocument->getProperty('ContentStreamFilename'); |
| 332 | + if (!empty($csFileName) && (!$overwriteFlag)) | ||
| 294 | { | 333 | { |
| 295 | throw new ContentAlreadyExistsException('Unable to overwrite existing content stream'); | 334 | throw new ContentAlreadyExistsException('Unable to overwrite existing content stream'); |
| 296 | } | 335 | } |
| 297 | 336 | ||
| 298 | - // in order to set the stream we need to do the following: | ||
| 299 | - // 1. decode the stream from the supplied base64 encoding | ||
| 300 | - // 2. create a temporary file as if it were uploaded via a file upload dialog | ||
| 301 | - // 3. create the document content as per usual | ||
| 302 | - // 4. link to the created document object? this perhaps only happens on read anyway | ||
| 303 | - | ||
| 304 | - // if there is any problem updating the content stream, throw StorageException | ||
| 305 | - // TODO real test parameter instead of hard-coded FALSE | ||
| 306 | - if (false) | 337 | + // NOTE There is a function in CMISUtil to do this but since KTUploadManager exists and has more functionality |
| 338 | + // which could come in useful at some point I decided to go with that instead (did not know it existed when | ||
| 339 | + // I wrote the CMISUtil function) | ||
| 340 | + $uploadManager = new KTUploadManager(); | ||
| 341 | + $file = $uploadManager->store_base64_file($contentStream, 'cmis_'); | ||
| 342 | + // update the document content from this temporary file as per usual | ||
| 343 | + // TODO Use checkin_document_with_metadata instead if metadata content submitted || update metadata separately? | ||
| 344 | + $response = $this->ktapi->checkin_document($documentId, $csFileName, 'CMIS setContentStream action', $file, false); | ||
| 345 | + if ($response['status_code'] != 0) | ||
| 307 | { | 346 | { |
| 308 | - throw new StorageException('Unable to update the content stream'); | 347 | + throw new StorageException('Unable to update the content stream. ' . $response['message']); |
| 309 | } | 348 | } |
| 349 | +// else | ||
| 350 | +// { | ||
| 351 | +// $objectId = CMISUtil::encodeObjectId('Document', $response['results']['id']); | ||
| 352 | +// } | ||
| 353 | + | ||
| 354 | + @unlink($csFile); | ||
| 355 | + // update the CMIS document object with the content stream information | ||
| 356 | +// $CMISDocument->reload($document['result']['document_id']); | ||
| 310 | 357 | ||
| 311 | - return $documentId; | 358 | + return $CMISDocument->getProperty('ObjectId'); |
| 312 | } | 359 | } |
| 313 | 360 | ||
| 314 | } | 361 | } |
lib/api/ktcmis/services/CMISRepositoryService.inc.php
| @@ -43,7 +43,6 @@ | @@ -43,7 +43,6 @@ | ||
| 43 | 43 | ||
| 44 | require_once(CMIS_DIR . '/classes/CMISRepository.inc.php'); | 44 | require_once(CMIS_DIR . '/classes/CMISRepository.inc.php'); |
| 45 | require_once(CMIS_DIR . '/classes/CMISObjectTypes.inc.php'); | 45 | require_once(CMIS_DIR . '/classes/CMISObjectTypes.inc.php'); |
| 46 | -require_once(CMIS_DIR . '/exceptions/InvalidArgumentException.inc.php'); | ||
| 47 | 46 | ||
| 48 | /** | 47 | /** |
| 49 | * CMIS Repository Service. | 48 | * CMIS Repository Service. |
lib/api/ktcmis/util/CMISUtil.inc.php
| @@ -53,7 +53,7 @@ class CMISUtil { | @@ -53,7 +53,7 @@ class CMISUtil { | ||
| 53 | * @param string $objectId | 53 | * @param string $objectId |
| 54 | * @return string $encoded | 54 | * @return string $encoded |
| 55 | */ | 55 | */ |
| 56 | - static function encodeObjectId($typeId, $objectId) | 56 | + static public function encodeObjectId($typeId, $objectId) |
| 57 | { | 57 | { |
| 58 | $encoded = null; | 58 | $encoded = null; |
| 59 | 59 | ||
| @@ -84,7 +84,7 @@ class CMISUtil { | @@ -84,7 +84,7 @@ class CMISUtil { | ||
| 84 | * @param string $objectId | 84 | * @param string $objectId |
| 85 | * @return string $typeId | 85 | * @return string $typeId |
| 86 | */ | 86 | */ |
| 87 | - static function decodeObjectId(&$objectId) | 87 | + static public function decodeObjectId($objectId, &$typeId = null) |
| 88 | { | 88 | { |
| 89 | $typeId = null; | 89 | $typeId = null; |
| 90 | 90 | ||
| @@ -105,7 +105,7 @@ class CMISUtil { | @@ -105,7 +105,7 @@ class CMISUtil { | ||
| 105 | break; | 105 | break; |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | - return $typeId; | 108 | + return $objectId; |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | /** | 111 | /** |
| @@ -118,7 +118,7 @@ class CMISUtil { | @@ -118,7 +118,7 @@ class CMISUtil { | ||
| 118 | * @param object $ktapi // reference to ktapi instance | 118 | * @param object $ktapi // reference to ktapi instance |
| 119 | * @return array $CMISArray | 119 | * @return array $CMISArray |
| 120 | */ | 120 | */ |
| 121 | - static function createChildObjectHierarchy($input, $repositoryURI, &$ktapi) | 121 | + static public function createChildObjectHierarchy($input, $repositoryURI, &$ktapi) |
| 122 | { | 122 | { |
| 123 | $CMISArray = array(); | 123 | $CMISArray = array(); |
| 124 | 124 | ||
| @@ -172,7 +172,7 @@ class CMISUtil { | @@ -172,7 +172,7 @@ class CMISUtil { | ||
| 172 | * @return array $CMISArray | 172 | * @return array $CMISArray |
| 173 | */ | 173 | */ |
| 174 | // NOTE this will have to change if we implement multi-filing | 174 | // NOTE this will have to change if we implement multi-filing |
| 175 | - static function createParentObjectHierarchy($input, $repositoryURI, &$ktapi) | 175 | + static public function createParentObjectHierarchy($input, $repositoryURI, &$ktapi) |
| 176 | { | 176 | { |
| 177 | $CMISArray = array(); | 177 | $CMISArray = array(); |
| 178 | 178 | ||
| @@ -209,7 +209,7 @@ class CMISUtil { | @@ -209,7 +209,7 @@ class CMISUtil { | ||
| 209 | * @param string $linkText // 'child' or 'parent' - indicates direction of hierarchy => descending or ascending | 209 | * @param string $linkText // 'child' or 'parent' - indicates direction of hierarchy => descending or ascending |
| 210 | * @return array $hierarchy | 210 | * @return array $hierarchy |
| 211 | */ | 211 | */ |
| 212 | - static function decodeObjectHierarchy($input, $linkText) | 212 | + static public function decodeObjectHierarchy($input, $linkText) |
| 213 | { | 213 | { |
| 214 | $hierarchy = array(); | 214 | $hierarchy = array(); |
| 215 | 215 | ||
| @@ -225,7 +225,7 @@ class CMISUtil { | @@ -225,7 +225,7 @@ class CMISUtil { | ||
| 225 | return $hierarchy; | 225 | return $hierarchy; |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | - static function createObjectPropertiesEntry($properties) | 228 | + static public function createObjectPropertiesEntry($properties) |
| 229 | { | 229 | { |
| 230 | $object = array(); | 230 | $object = array(); |
| 231 | 231 | ||
| @@ -281,7 +281,7 @@ class CMISUtil { | @@ -281,7 +281,7 @@ class CMISUtil { | ||
| 281 | * @param object $data | 281 | * @param object $data |
| 282 | * @return array $array | 282 | * @return array $array |
| 283 | */ | 283 | */ |
| 284 | - static function objectToArray($data) | 284 | + static public function objectToArray($data) |
| 285 | { | 285 | { |
| 286 | $array = array(); | 286 | $array = array(); |
| 287 | 287 | ||
| @@ -309,11 +309,35 @@ class CMISUtil { | @@ -309,11 +309,35 @@ class CMISUtil { | ||
| 309 | * @param boolean/other $input | 309 | * @param boolean/other $input |
| 310 | * @return string | 310 | * @return string |
| 311 | */ | 311 | */ |
| 312 | - function boolToString($input) | 312 | + static public function boolToString($input) |
| 313 | { | 313 | { |
| 314 | return (($input === true) ? 'true' : (($input === false) ? 'false' : $input)); | 314 | return (($input === true) ? 'true' : (($input === false) ? 'false' : $input)); |
| 315 | } | 315 | } |
| 316 | 316 | ||
| 317 | + /** | ||
| 318 | + * Creates a temporary file | ||
| 319 | + * Cleanup is the responsibility of the calling code | ||
| 320 | + * | ||
| 321 | + * @param string|binary $content The content to be written to the file. | ||
| 322 | + * @param string $uploadDir Optional upload directory. Will use the KnowledgeTree system tmp directory if not supplied. | ||
| 323 | + * @return string The path to the created file (for reference and cleanup.) | ||
| 324 | + */ | ||
| 325 | + static public function createTemporaryFile($content, $encoding = null, $uploadDir = null) | ||
| 326 | + { | ||
| 327 | + if(is_null($uploadDir)) | ||
| 328 | + { | ||
| 329 | + $oKTConfig =& KTConfig::getSingleton(); | ||
| 330 | + $uploadDir = $oKTConfig->get('webservice/uploadDirectory'); | ||
| 331 | + } | ||
| 332 | + | ||
| 333 | + $temp = tempnam($uploadDir, 'myfile'); | ||
| 334 | + $fp = fopen($temp, 'wb'); | ||
| 335 | + fwrite($fp, ($encoding == 'base64' ? base64_decode($content) : $content)); | ||
| 336 | + fclose($fp); | ||
| 337 | + | ||
| 338 | + return $temp; | ||
| 339 | + } | ||
| 340 | + | ||
| 317 | } | 341 | } |
| 318 | 342 | ||
| 319 | ?> | 343 | ?> |
tests/ktcmis/testCmisApi.php
| @@ -355,10 +355,53 @@ class CMISTestCase extends KTUnitTestCase { | @@ -355,10 +355,53 @@ class CMISTestCase extends KTUnitTestCase { | ||
| 355 | $this->printTable($properties['results'][0], 'Properties for CMIS Created Folder Object ' . $folderId . ' (getProperties())'); | 355 | $this->printTable($properties['results'][0], 'Properties for CMIS Created Folder Object ' . $folderId . ' (getProperties())'); |
| 356 | 356 | ||
| 357 | // delete | 357 | // delete |
| 358 | - CMISUtil::decodeObjectId($folderId); | ||
| 359 | - $this->ktapi->delete_folder($folderId, 'Testing API', KT_TEST_USER, KT_TEST_PASS); | 358 | + $this->ktapi->delete_folder(CMISUtil::decodeObjectId($folderId), 'Testing API', KT_TEST_USER, KT_TEST_PASS); |
| 360 | } | 359 | } |
| 361 | 360 | ||
| 361 | + // TEST 3 | ||
| 362 | + // test creation of document | ||
| 363 | + $folderId = 'F'.$this->folders[0]; | ||
| 364 | + $folderId = 'F1'; | ||
| 365 | + $properties = array('name' => 'Test CMIS Document 1', 'title' => 'test_cmis_doc_' . mt_rand() . '.txt'); | ||
| 366 | + $contentStream = base64_encode('Some arbitrary text content'); | ||
| 367 | + $created = $ObjectService->createDocument($repositoryId, 'Document', $properties, $folderId, $contentStream); | ||
| 368 | + | ||
| 369 | + $this->assertNotNull($created['results']); | ||
| 370 | + | ||
| 371 | +// echo '<pre>'.print_r($created, true).'</pre>'; | ||
| 372 | + | ||
| 373 | + if (!is_null($created['results'])) | ||
| 374 | + { | ||
| 375 | + $documentId = $created['results']; | ||
| 376 | + | ||
| 377 | + // check that document object actually exists | ||
| 378 | + $properties = $ObjectService->getProperties($repositoryId, $documentId, false, false); | ||
| 379 | + $this->assertNotNull($properties['results']); | ||
| 380 | + | ||
| 381 | + // test printout | ||
| 382 | + $this->printTable($properties['results'][0], 'Properties for CMIS Created Document Object ' . $documentId . ' (getProperties())'); | ||
| 383 | + } | ||
| 384 | + | ||
| 385 | +// // TEST 5 | ||
| 386 | +// // test updating content stream for existing document | ||
| 387 | +// $contentStream = base64_encode('Some updated text content for the content stream'); | ||
| 388 | +// $updated = $ObjectService->setContentStream($repositoryId, $documentId, true, $contentStream); | ||
| 389 | +// | ||
| 390 | +// $this->assertNotNull($updated['results']); | ||
| 391 | +// | ||
| 392 | +//// echo '<pre>'.print_r($created, true).'</pre>'; | ||
| 393 | +// | ||
| 394 | +// if (!is_null($updated['results'])) | ||
| 395 | +// { | ||
| 396 | +//// $documentId = $updated['results']; | ||
| 397 | +// | ||
| 398 | +// // TODO test getContentStream here when we have it | ||
| 399 | +// | ||
| 400 | +// } | ||
| 401 | + | ||
| 402 | + // delete created document | ||
| 403 | +// $this->ktapi->delete_document(CMISUtil::decodeObjectId($documentId), 'Testing API', false); | ||
| 404 | + | ||
| 362 | // tear down the folder/doc tree structure with which we were testing | 405 | // tear down the folder/doc tree structure with which we were testing |
| 363 | $this->cleanupFolderDocStructure(); | 406 | $this->cleanupFolderDocStructure(); |
| 364 | 407 |