Commit b23610486f3d9dbaf85c4362f208b43ca46cfd17

Authored by Paul Barrett
1 parent 5625bfc6

Added new CMIS exceptions.

Added initial createDocument function

Added skeleton of setContentStream function

Committed by: Paul Barrett
lib/api/ktcmis/exceptions/ContentAlreadyExistsException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class ContentAlreadyExistsException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
... ...
lib/api/ktcmis/exceptions/RuntimeException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class RuntimeException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
... ...
lib/api/ktcmis/exceptions/StreamNotSupportedException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class StreamNotSupportedException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
... ...
lib/api/ktcmis/exceptions/UpdateConflictException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class UpdateConflictException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
... ...
lib/api/ktcmis/exceptions/VersioningException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class VersioningException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
... ...
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
... ...