Commit b23610486f3d9dbaf85c4362f208b43ca46cfd17
1 parent
5625bfc6
Added new CMIS exceptions.
Added initial createDocument function Added skeleton of setContentStream function Committed by: Paul Barrett
Showing
9 changed files
with
304 additions
and
15 deletions
lib/api/ktcmis/exceptions/ContentAlreadyExistsException.inc.php
0 → 100644
lib/api/ktcmis/exceptions/RuntimeException.inc.php
0 → 100644
lib/api/ktcmis/exceptions/StreamNotSupportedException.inc.php
0 → 100644
lib/api/ktcmis/exceptions/UpdateConflictException.inc.php
0 → 100644
lib/api/ktcmis/exceptions/VersioningException.inc.php
0 → 100644
lib/api/ktcmis/ktcmis.inc.php
| ... | ... | @@ -468,7 +468,42 @@ class KTObjectService extends KTCMISBase { |
| 468 | 468 | } |
| 469 | 469 | |
| 470 | 470 | /** |
| 471 | - * Function to create a folder | |
| 471 | + * Creates a new document within the repository | |
| 472 | + * | |
| 473 | + * @param string $repositoryId The repository to which the document must be added | |
| 474 | + * @param string $typeId Object Type id for the document object being created | |
| 475 | + * @param array $properties Array of properties which must be applied to the created document object | |
| 476 | + * @param string $folderId The id of the folder which will be the parent of the created document object | |
| 477 | + * This parameter is optional IF unfilingCapability is supported | |
| 478 | + * @param contentStream $contentStream optional content stream data | |
| 479 | + * @param string $versioningState optional version state value: checkedout/major/minor | |
| 480 | + * @return string $objectId The id of the created folder object | |
| 481 | + */ | |
| 482 | + function createDocument($repositoryId, $typeId, $properties, $folderId = null, | |
| 483 | + $contentStream = null, $versioningState = 'Major') | |
| 484 | + { | |
| 485 | + $objectId = null; | |
| 486 | + | |
| 487 | + try { | |
| 488 | + $objectId = $this->ObjectService->createDocument($repositoryId, $typeId, $properties, $folderId, | |
| 489 | + $contentStream, $versioningState); | |
| 490 | + } | |
| 491 | + catch (Exception $e) | |
| 492 | + { | |
| 493 | + return array( | |
| 494 | + "status_code" => 1, | |
| 495 | + "message" => $e->getMessage() | |
| 496 | + ); | |
| 497 | + } | |
| 498 | + | |
| 499 | + return array( | |
| 500 | + 'status_code' => 0, | |
| 501 | + 'results' => $objectId | |
| 502 | + ); | |
| 503 | + } | |
| 504 | + | |
| 505 | + /** | |
| 506 | + * Creates a new folder within the repository | |
| 472 | 507 | * |
| 473 | 508 | * @param string $repositoryId The repository to which the folder must be added |
| 474 | 509 | * @param string $typeId Object Type id for the folder object being created |
| ... | ... | @@ -497,6 +532,41 @@ class KTObjectService extends KTCMISBase { |
| 497 | 532 | ); |
| 498 | 533 | } |
| 499 | 534 | |
| 535 | + /** | |
| 536 | + * Sets the content stream data for an existing document | |
| 537 | + * | |
| 538 | + * if $overwriteFlag = TRUE, the new content stream is applied whether or not the document has an existing content stream | |
| 539 | + * if $overwriteFlag = FALSE, the new content stream is applied only if the document does not have an existing content stream | |
| 540 | + * | |
| 541 | + * NOTE A Repository MAY automatically create new Document versions as part of this service method. | |
| 542 | + * Therefore, the documentId output NEED NOT be identical to the documentId input. | |
| 543 | + * | |
| 544 | + * @param string $repositoryId | |
| 545 | + * @param string $documentId | |
| 546 | + * @param boolean $overwriteFlag | |
| 547 | + * @param string $contentStream | |
| 548 | + * @param string $changeToken | |
| 549 | + * @return string $documentId | |
| 550 | + */ | |
| 551 | + function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null) | |
| 552 | + { | |
| 553 | + try { | |
| 554 | + $objectId = $this->ObjectService->setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken); | |
| 555 | + } | |
| 556 | + catch (Exception $e) | |
| 557 | + { | |
| 558 | + return array( | |
| 559 | + "status_code" => 1, | |
| 560 | + "message" => $e->getMessage() | |
| 561 | + ); | |
| 562 | + } | |
| 563 | + | |
| 564 | + return array( | |
| 565 | + 'status_code' => 0, | |
| 566 | + 'results' => $documentId | |
| 567 | + ); | |
| 568 | + } | |
| 569 | + | |
| 500 | 570 | } |
| 501 | 571 | |
| 502 | 572 | ?> | ... | ... |
lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php
| ... | ... | @@ -81,10 +81,9 @@ class CMISDocumentObject extends CMISBaseObject { |
| 81 | 81 | $this->includedInSupertypeQuery = true; // |
| 82 | 82 | // TODO determine what these next 3 should be |
| 83 | 83 | $this->controllable = false; // <repository-specific> |
| 84 | - $this->versionable = false; // <repository-specific> | |
| 85 | - $this->contentStreamAllowed = false; // <repository-specific> | |
| 86 | - $this->contentStreamLength = 0; | |
| 87 | - $this->contentStreamMimeType = ''; | |
| 84 | + $this->versionable = true; // <repository-specific> | |
| 85 | + $this->contentStreamAllowed = 'required'; // <repository-specific> notAllowed/allowed/required | |
| 86 | + | |
| 88 | 87 | // properties |
| 89 | 88 | $this->properties = new CMISDocumentPropertyCollection(); |
| 90 | 89 | ... | ... |
lib/api/ktcmis/services/CMISObjectService.inc.php
| ... | ... | @@ -2,8 +2,12 @@ |
| 2 | 2 | |
| 3 | 3 | require_once(KT_DIR . '/ktapi/ktapi.inc.php'); |
| 4 | 4 | require_once(CMIS_DIR . '/exceptions/ConstraintViolationException.inc.php'); |
| 5 | +require_once(CMIS_DIR . '/exceptions/ContentAlreadyExistsException.inc.php'); | |
| 5 | 6 | require_once(CMIS_DIR . '/exceptions/ObjectNotFoundException.inc.php'); |
| 6 | 7 | require_once(CMIS_DIR . '/exceptions/StorageException.inc.php'); |
| 8 | +require_once(CMIS_DIR . '/exceptions/StreamNotSupportedException.inc.php'); | |
| 9 | +require_once(CMIS_DIR . '/exceptions/UpdateConflictException.inc.php'); | |
| 10 | +require_once(CMIS_DIR . '/exceptions/VersioningException.inc.php'); | |
| 7 | 11 | require_once(CMIS_DIR . '/services/CMISRepositoryService.inc.php'); |
| 8 | 12 | require_once(CMIS_DIR . '/objecttypes/CMISDocumentObject.inc.php'); |
| 9 | 13 | require_once(CMIS_DIR . '/objecttypes/CMISFolderObject.inc.php'); |
| ... | ... | @@ -68,7 +72,128 @@ class CMISObjectService { |
| 68 | 72 | } |
| 69 | 73 | |
| 70 | 74 | /** |
| 71 | - * Function to create a folder | |
| 75 | + * Creates a new document within the repository | |
| 76 | + * | |
| 77 | + * @param string $repositoryId The repository to which the document must be added | |
| 78 | + * @param string $typeId Object Type id for the document object being created | |
| 79 | + * @param array $properties Array of properties which must be applied to the created document object | |
| 80 | + * @param string $folderId The id of the folder which will be the parent of the created document object | |
| 81 | + * This parameter is optional IF unfilingCapability is supported | |
| 82 | + * @param contentStream $contentStream optional content stream data | |
| 83 | + * @param string $versioningState optional version state value: checkedout/major/minor | |
| 84 | + * @return string $objectId The id of the created folder object | |
| 85 | + */ | |
| 86 | + // TODO throw ConstraintViolationException if: | |
| 87 | + // value of any of the properties violates the min/max/required/length constraints | |
| 88 | + // 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, | |
| 96 | + $contentStream = null, $versioningState = 'Major') | |
| 97 | + { | |
| 98 | + $objectId = null; | |
| 99 | + | |
| 100 | + // fetch type definition of supplied type and check for base type "document", if not true throw exception | |
| 101 | + $RepositoryService = new CMISRepositoryService(); | |
| 102 | + try { | |
| 103 | + $typeDefinition = $RepositoryService->getTypeDefinition($repositoryId, $typeId); | |
| 104 | + } | |
| 105 | + // NOTE Not sure that we should throw this specific exception, maybe just let the underlying | |
| 106 | + // exception propogate upward... | |
| 107 | + // Alternatively: throw new exception with original exception message appended | |
| 108 | + // NOTE The latter method has been adopted for the moment | |
| 109 | + catch (Exception $e) | |
| 110 | + { | |
| 111 | + throw new ConstraintViolationException('Object is not of base type document. ' . $e->getMessage()); | |
| 112 | + } | |
| 113 | + | |
| 114 | + if ($typeDefinition['attributes']['baseType'] != 'document') | |
| 115 | + { | |
| 116 | + throw new ConstraintViolationException('Object is not of base type document'); | |
| 117 | + } | |
| 118 | + | |
| 119 | + // if no $folderId submitted and repository does not support "unfiling" throw exception | |
| 120 | + if (empty($folderId)) | |
| 121 | + { | |
| 122 | + $repositoryInfo = $RepositoryService->getRepositoryInfo($repositoryId); | |
| 123 | + $capabilities = $repositoryInfo->getCapabilities(); | |
| 124 | + if (!$capabilities->hasCapabilityUnfiling()) | |
| 125 | + { | |
| 126 | + throw new ConstraintViolationException('Repository does not support the Unfiling capability and no folder id was supplied'); | |
| 127 | + } | |
| 128 | + } | |
| 129 | + | |
| 130 | + // Attempt to decode $folderId, use as is if not detected as encoded | |
| 131 | + $tmpObjectId = $folderId; | |
| 132 | + $tmpTypeId = CMISUtil::decodeObjectId($tmpObjectId); | |
| 133 | + if ($tmpTypeId != 'Unknown') | |
| 134 | + $folderId = $tmpObjectId; | |
| 135 | + | |
| 136 | + // if parent folder is not allowed to hold this type, throw exception | |
| 137 | + $CMISFolder = new CMISFolderObject($folderId, $this->ktapi); | |
| 138 | + $folderProperties = $CMISFolder->getProperties(); | |
| 139 | + $allowed = $folderProperties->getValue('AllowedChildObjectTypeIds'); | |
| 140 | + if (!is_array($allowed) || !in_array($typeId, $allowed)) | |
| 141 | + { | |
| 142 | + throw new ConstraintViolationException('Parent folder may not hold objects of this type (' . $typeId . ')'); | |
| 143 | + } | |
| 144 | + | |
| 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 | |
| 161 | + if (($typeDefinition['attributes']['contentStreamAllowed'] == 'required') && empty($contentStream)) | |
| 162 | + { | |
| 163 | + throw new ConstraintViolationException('The Knowledgetree Repository requires a content stream for document creation. ' | |
| 164 | + . 'Refusing to create an empty document'); | |
| 165 | + } | |
| 166 | + else if (($typeDefinition['attributes']['contentStreamAllowed'] == 'notAllowed') && !empty($contentStream)) | |
| 167 | + { | |
| 168 | + throw new StreamNotSupportedException('Content Streams are not supported'); | |
| 169 | + } | |
| 170 | + | |
| 171 | + // TODO deal with $versioningState when supplied | |
| 172 | + | |
| 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) | |
| 176 | + { | |
| 177 | + throw new StorageException('The repository was unable to create the document - ' . $response['message']); | |
| 178 | + } | |
| 179 | + else | |
| 180 | + { | |
| 181 | + $objectId = CMISUtil::encodeObjectId('Document', $response['results']['id']); | |
| 182 | + } | |
| 183 | + | |
| 184 | + // now that the document object exists, create the content stream from the supplied data | |
| 185 | + if (!empty($contentStream)) | |
| 186 | + { | |
| 187 | + // TODO changeToken support | |
| 188 | + $changeToken = null; | |
| 189 | + $this->setContentStream($repositoryId, $objectId, false, $contentStream, $changeToken); | |
| 190 | + } | |
| 191 | + | |
| 192 | + return $objectId; | |
| 193 | + } | |
| 194 | + | |
| 195 | + /** | |
| 196 | + * Creates a new folder within the repository | |
| 72 | 197 | * |
| 73 | 198 | * @param string $repositoryId The repository to which the folder must be added |
| 74 | 199 | * @param string $typeId Object Type id for the folder object being created |
| ... | ... | @@ -81,14 +206,20 @@ class CMISObjectService { |
| 81 | 206 | // specified in the property definition in the Object-Type. |
| 82 | 207 | function createFolder($repositoryId, $typeId, $properties, $folderId) |
| 83 | 208 | { |
| 209 | + $objectId = null; | |
| 210 | + | |
| 84 | 211 | // fetch type definition of supplied type and check for base type "folder", if not true throw exception |
| 85 | 212 | $RepositoryService = new CMISRepositoryService(); |
| 86 | 213 | try { |
| 87 | 214 | $typeDefinition = $RepositoryService->getTypeDefinition($repositoryId, $typeId); |
| 88 | 215 | } |
| 216 | + // NOTE Not sure that we should throw this specific exception, maybe just let the underlying | |
| 217 | + // exception propogate upward... | |
| 218 | + // Alternatively: throw new exception with original exception message appended | |
| 219 | + // NOTE The latter method has been adopted for the moment | |
| 89 | 220 | catch (Exception $e) |
| 90 | 221 | { |
| 91 | - throw new ConstraintViolationException('Object is not of base type folder.'); | |
| 222 | + throw new ConstraintViolationException('Object is not of base type folder. ' . $e->getMessage()); | |
| 92 | 223 | } |
| 93 | 224 | |
| 94 | 225 | if ($typeDefinition['attributes']['baseType'] != 'folder') |
| ... | ... | @@ -96,12 +227,11 @@ class CMISObjectService { |
| 96 | 227 | throw new ConstraintViolationException('Object is not of base type folder'); |
| 97 | 228 | } |
| 98 | 229 | |
| 99 | - // TODO determine whether this is in fact necessary or if we should require decoding in the calling code | |
| 100 | 230 | // Attempt to decode $folderId, use as is if not detected as encoded |
| 101 | - $objectId = $folderId; | |
| 102 | - $tmpTypeId = CMISUtil::decodeObjectId($objectId); | |
| 231 | + $tmpObjectId = $folderId; | |
| 232 | + $tmpTypeId = CMISUtil::decodeObjectId($tmpObjectId); | |
| 103 | 233 | if ($tmpTypeId != 'Unknown') |
| 104 | - $folderId = $objectId; | |
| 234 | + $folderId = $tmpObjectId; | |
| 105 | 235 | |
| 106 | 236 | // if parent folder is not allowed to hold this type, throw exception |
| 107 | 237 | $CMISFolder = new CMISFolderObject($folderId, $this->ktapi); |
| ... | ... | @@ -115,7 +245,6 @@ class CMISObjectService { |
| 115 | 245 | $response = $this->ktapi->create_folder($folderId, $properties['name'], $sig_username = '', $sig_password = '', $reason = ''); |
| 116 | 246 | if ($response['status_code'] != 0) |
| 117 | 247 | { |
| 118 | - // throw storageException | |
| 119 | 248 | throw new StorageException('The repository was unable to create the folder - ' . $response['message']); |
| 120 | 249 | } |
| 121 | 250 | else |
| ... | ... | @@ -126,6 +255,62 @@ class CMISObjectService { |
| 126 | 255 | return $objectId; |
| 127 | 256 | } |
| 128 | 257 | |
| 258 | + /** | |
| 259 | + * Sets the content stream data for an existing document | |
| 260 | + * | |
| 261 | + * if $overwriteFlag = TRUE, the new content stream is applied whether or not the document has an existing content stream | |
| 262 | + * if $overwriteFlag = FALSE, the new content stream is applied only if the document does not have an existing content stream | |
| 263 | + * | |
| 264 | + * NOTE A Repository MAY automatically create new Document versions as part of this service method. | |
| 265 | + * Therefore, the documentId output NEED NOT be identical to the documentId input. | |
| 266 | + * | |
| 267 | + * @param string $repositoryId | |
| 268 | + * @param string $documentId | |
| 269 | + * @param boolean $overwriteFlag | |
| 270 | + * @param string $contentStream | |
| 271 | + * @param string $changeToken | |
| 272 | + * @return string $documentId | |
| 273 | + */ | |
| 274 | + // TODO exceptions: | |
| 275 | + // updateConflictException: The operation is attempting to update an object that is no longer current | |
| 276 | + // (as determined by the repository). | |
| 277 | + // 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) | |
| 279 | + { | |
| 280 | + // fetch type definition of supplied document | |
| 281 | + $CMISDocument = new CMISDocumentObject($documentId, $this->ktapi); | |
| 282 | + | |
| 283 | + // if content stream is not allowed for this object type definition, throw a ConstraintViolationException | |
| 284 | + if (($CMISDocument->getAttribute('contentStreamAllowed') == 'notAllowed')) | |
| 285 | + { | |
| 286 | + // NOTE spec version 0.61c specifies both a ConstraintViolationException and a StreamNotSupportedException | |
| 287 | + // for this case. Choosing to throw StreamNotSupportedException until the specification is clarified | |
| 288 | + // as it is a more specific exception | |
| 289 | + throw new StreamNotSupportedException('Content Streams are not allowed for this object type'); | |
| 290 | + } | |
| 291 | + | |
| 292 | + $properties = $CMISDocument->getProperties(); | |
| 293 | + if (!empty($properties->getValue('ContentStreamFilename')) && (!$overwriteFlag)) | |
| 294 | + { | |
| 295 | + throw new ContentAlreadyExistsException('Unable to overwrite existing content stream'); | |
| 296 | + } | |
| 297 | + | |
| 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) | |
| 307 | + { | |
| 308 | + throw new StorageException('Unable to update the content stream'); | |
| 309 | + } | |
| 310 | + | |
| 311 | + return $documentId; | |
| 312 | + } | |
| 313 | + | |
| 129 | 314 | } |
| 130 | 315 | |
| 131 | 316 | ?> | ... | ... |
tests/ktcmis/testCmisApi.php
| ... | ... | @@ -592,14 +592,14 @@ class CMISTestCase extends KTUnitTestCase { |
| 592 | 592 | foreach($results as $key => $value) |
| 593 | 593 | { |
| 594 | 594 | ?><tr><td colspan="<?php echo 1 + $depth; ?>"><div style="padding: 8px; background-color: lightblue; color: black;"><?php |
| 595 | - echo $value['properties']['name']; | |
| 595 | + echo $value['properties']['name']['value']; | |
| 596 | 596 | ?></div></td></tr><?php |
| 597 | 597 | // properties first |
| 598 | 598 | foreach ($value['properties'] as $propkey => $propval) |
| 599 | 599 | { |
| 600 | - if ($propval == '') continue; | |
| 600 | + if ($propval['value'] == '') continue; | |
| 601 | 601 | |
| 602 | - $this->printRow($propkey, $propval); | |
| 602 | + $this->printRow($propkey, $propval['value']); | |
| 603 | 603 | } |
| 604 | 604 | |
| 605 | 605 | // now any children | ... | ... |