Commit 30d7fae36b0a8905c3ffa0193a3740e3fa79144a

Authored by Paul Barrett
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
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
1 -<?php  
2 -  
3 -class IllegalArgumentException extends Exception {  
4 -  
5 -}  
6 -  
7 -?>  
lib/api/ktcmis/exceptions/RuntimeException.inc.php deleted
1 -<?php  
2 -  
3 -class RuntimeException extends Exception {  
4 -  
5 -}  
6 -  
7 -?>  
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 . &#39;/util/CMISUtil.inc.php&#39;); @@ -48,8 +48,8 @@ require_once(CMIS_DIR . &#39;/util/CMISUtil.inc.php&#39;);
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 . &#39;/util/CMISUtil.inc.php&#39;); @@ -46,10 +46,10 @@ require_once(CMIS_DIR . &#39;/util/CMISUtil.inc.php&#39;);
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 . &#39;/exceptions/StorageException.inc.php&#39;); @@ -8,6 +9,7 @@ require_once(CMIS_DIR . &#39;/exceptions/StorageException.inc.php&#39;);
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