Commit f3438102dd2a8d7b0796e0cfdee57224bab544a3

Authored by Paul Barrett
1 parent c8de4448

Beginnings of link media types and complete atom node data

Story ID:2713417. CMIS working with PHP client

Committed by: Paul Barrett
webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php
... ... @@ -2,7 +2,7 @@
2 2  
3 3 /**
4 4 * Any feed must be a valid atom Feed document and conform to the guidelines below:
5   -1. Updated will be the latest time the folder or its contents was updated. If unknown by the underlying repository, it should be the current time.
  5 +1. Updated will be the latest time the folder or its contents was updated. If unknown by the underlying repository, it MUST be the current time.
6 6 2. Author/name will be the CMIS property createdBy
7 7 3. Title will be the CMIS property name
8 8 4. App:edited will be the CMIS property lastModifiedDate
... ... @@ -145,14 +145,12 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
145 145 $folderId = $this->params[0];
146 146 }
147 147  
148   - if (!empty($this->params[1]) && (($this->params[1] == 'children') || ($this->params[1] == 'descendants')))
149   - {
  148 + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt());
  149 + if (!empty($this->params[1]) && (($this->params[1] == 'children') || ($this->params[1] == 'descendants'))) {
150 150 $NavigationService = new KTNavigationService(KT_cmis_atom_service_helper::getKt());
151   - $feed = $this->getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $folderName, $this->params[1]);
  151 + $feed = $this->getFolderChildrenFeed($NavigationService, $ObjectService, $repositoryId, $folderId, $folderName, $this->params[1]);
152 152 }
153   - else
154   - {
155   - $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt());
  153 + else {
156 154 $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $folderId);
157 155 }
158 156  
... ... @@ -305,6 +303,7 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
305 303 $this->setStatus(self::STATUS_SERVER_ERROR);
306 304  
307 305 $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI);
  306 + // FIXME? this should perhaps use a different status code?
308 307 $feed->newField('title', 'Error: Failed to delete all objects in tree: ' . self::STATUS_SERVER_ERROR, $feed);
309 308  
310 309 foreach($response as $failed)
... ... @@ -337,8 +336,17 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
337 336 * @param string $feedType children or descendants
338 337 * @return string CMIS AtomPub feed
339 338 */
340   - private function getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $folderName, $feedType = 'children')
  339 + private function getFolderChildrenFeed(&$NavigationService, &$ObjectService, $repositoryId, $folderId, $folderName, $feedType = 'children')
341 340 {
  341 + // fetch properties of parent folder for which children/descendants are being retrieved
  342 + try {
  343 + $rootProperties = $ObjectService->getProperties($repositoryId, $folderId);
  344 + }
  345 + catch (Exception $e) {
  346 + $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode($e), $e->getMessage());
  347 + return null;
  348 + }
  349 +
342 350 if ($feedType == 'children') {
343 351 try {
344 352 $entries = $NavigationService->getChildren($repositoryId, $folderId, false, false);
... ... @@ -373,30 +381,39 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
373 381  
374 382 $feed->newField('title', $folderName . ' ' . ucwords($feedType), $feed);
375 383  
376   - // TODO dynamic?
377 384 $feedElement = $feed->newField('author');
378   - $element = $feed->newField('name', 'System', $feedElement);
  385 + $element = $feed->newField('name', $rootProperties['properties']['createdBy']['value'], $feedElement);
379 386 $feed->appendChild($feedElement);
380 387  
381 388 // id
382 389 $feed->newField('id', 'urn:uuid:' . $folderId . '-' . $feedType, $feed);
383 390  
384   - // TODO get actual most recent update time, only use current if no other available
385   - $feed->newField('updated', KT_cmis_atom_service_helper::formatDatestamp(), $feed);
  391 + $updated = null;
  392 + if ($rootProperties['properties']['lastModificationDate']['value'] != '0000-00-00 00:00:00') {
  393 + $updated = $rootProperties['properties']['lastModificationDate']['value'];
  394 + }
  395 + else if ($rootProperties['properties']['creationDate']['value'] != '0000-00-00 00:00:00') {
  396 + $updated = $rootProperties['properties']['creationDate']['value'];
  397 + }
  398 +
  399 + $feed->newField('updated', KT_cmis_atom_service_helper::formatDatestamp($updated), $feed);
386 400  
387 401 $link = $feed->newElement('link');
388 402 $link->appendChild($feed->newAttr('rel', 'self'));
389 403 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId . '/' . $feedType));
390 404 $feed->appendChild($link);
391 405  
  406 + // TODO this link must specify the workspace
392 407 $link = $feed->newElement('link');
393 408 $link->appendChild($feed->newAttr('rel', 'service'));
394 409 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . 'servicedocument'));
  410 + $link->appendChild($feed->newAttr('type', 'application/atomsvc+xml'));
395 411 $feed->appendChild($link);
396 412  
397 413 $link = $feed->newElement('link');
398 414 $link->appendChild($feed->newAttr('rel', 'via'));
399 415 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId));
  416 + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry'));
400 417 $feed->appendChild($link);
401 418  
402 419 KT_cmis_atom_service_helper::createObjectFeed($feed, $entries, $folderName);
... ... @@ -553,10 +570,10 @@ class KT_cmis_atom_service_pwc extends KT_cmis_atom_service {
553 570 $content = KT_cmis_atom_service_helper::getCmisContent($this->rawContent);
554 571 // NOTE not sure about the text type, will need testing, most content will be base64
555 572 $cmisContent = (isset($content['cmisra:base64'])
556   - ? $content['cmisra:base64']
557   - : ((isset($content['cmisra:text']))
558   - ? $content['cmisra:text']
559   - : null));
  573 + ? $content['cmisra:base64']
  574 + : ((isset($content['cmisra:text']))
  575 + ? $content['cmisra:text']
  576 + : null));
560 577  
561 578 // if we haven't found it now, the hack begins - retrieve the EXISTING content and submit this as the contentStream
562 579 // this is needed because KnowledgeTree will not accept a checkin without a content stream but CMISSpaces (and possibly
... ... @@ -618,14 +635,14 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service {
618 635  
619 636 $feed->newField('title', 'Checked out Documents', $feed);
620 637  
621   - // TODO dynamic?
  638 + // Since checked out documents do not necessarily share the same creator, we use a default value
622 639 $feedElement = $feed->newField('author');
623   - $element = $feed->newField('name', 'admin', $feedElement);
  640 + $element = $feed->newField('name', 'Administrator', $feedElement);
624 641 $feed->appendChild($feedElement);
625 642  
626 643 $feed->appendChild($feed->newElement('id', 'urn:uuid:checkedout'));
627 644  
628   - // TODO get actual most recent update time, only use current if no other available
  645 + // Since checked out documents are not necessarily from a single folder, we don't know the time last updated, so we use current
629 646 $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp()));
630 647  
631 648 $link = $feed->newElement('link');
... ... @@ -671,8 +688,8 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service {
671 688 // in the helper code, but I don't feel that throwing an exception is necessary or always wanted;
672 689 // alternative is to send the name of the Exception but not an instance, and do an is_a check on the other side,
673 690 // but since it will only be needed to this and similar calls, it seems wasteful to do that for every other case
674   - $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode(new InvalidArgumentException()),
675   - 'No object was specified for checkout');
  691 + $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode(new InvalidArgumentException()),
  692 + 'No object was specified for checkout');
676 693 return null;
677 694 }
678 695  
... ... @@ -683,7 +700,7 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service {
683 700 $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode($e), $e->getMessage());
684 701 return null;
685 702 }
686   -
  703 +
687 704 $this->setStatus(self::STATUS_CREATED);
688 705 $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $cmisObjectProperties['cmis:objectId'], 'POST');
689 706  
... ... @@ -776,6 +793,7 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service {
776 793 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . 'type/' . $this->params[0] . '/' . $this->params[1] . '?pageNo=1&pageSize=0'));
777 794 $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=feed'));
778 795  
  796 + // Since types do not have associated dates, we don't know the time last updated, so we use current
779 797 $feed->newField('updated', KT_cmis_atom_service_helper::formatDatestamp(), $feed);
780 798 $feed->newField('cmis:hasMoreItems', 'false', $feed);
781 799  
... ...
webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php
... ... @@ -54,6 +54,7 @@ class KT_cmis_atom_service_helper {
54 54 * @return string CMIS AtomPub feed
55 55 */
56 56 // TODO enable this to work on an existing set of object properties if submitted
  57 + // TODO this might be better as an entry rather than a feed - see Alfresco example
57 58 static public function getObjectFeed(&$service, $ObjectService, $repositoryId, $objectId, $method = 'GET')
58 59 {
59 60 self::$repositoryId = $repositoryId;
... ... @@ -173,9 +174,8 @@ class KT_cmis_atom_service_helper {
173 174 {
174 175 $type = $cmisEntry['properties']['objectTypeId']['value'];
175 176  
176   - // TODO dynamic actual creator name
177 177 $responseElement = $feed->newField('author');
178   - $element = $feed->newField('name', 'admin', $responseElement);
  178 + $element = $feed->newField('name', $cmisEntry['properties']['createdBy']['value'], $responseElement);
179 179 $entry->appendChild($responseElement);
180 180  
181 181 $typeString = str_replace('cmis:', '', $type);
... ... @@ -206,6 +206,7 @@ class KT_cmis_atom_service_helper {
206 206 $link->appendChild($feed->newAttr('rel', 'edit'));
207 207 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString
208 208 . '/' . $cmisEntry['properties']['objectId']['value']));
  209 + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry'));
209 210 $entry->appendChild($link);
210 211  
211 212 if ((strtolower($cmisEntry['properties']['objectTypeId']['value']) == 'cmis:document')
... ... @@ -218,14 +219,6 @@ class KT_cmis_atom_service_helper {
218 219 . '/' . $cmisEntry['properties']['objectId']['value']
219 220 . '/' . $cmisEntry['properties']['contentStreamFilename']['value']));
220 221 $entry->appendChild($link);
221   -
222   - $link = $feed->newElement('link');
223   - $link->appendChild($feed->newAttr('rel', 'enclosure'));
224   - $link->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value']));
225   - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString
226   - . '/' . $cmisEntry['properties']['objectId']['value']
227   - . '/' . $cmisEntry['properties']['contentStreamFilename']['value']));
228   - $entry->appendChild($link);
229 222 }
230 223  
231 224 // according to spec this MUST be present, but spec says that links for function which are not supported
... ... @@ -255,6 +248,7 @@ class KT_cmis_atom_service_helper {
255 248 $link->appendChild($feed->newAttr('rel', 'up'));
256 249 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/'
257 250 . $cmisEntry['properties']['parentId']['value']));
  251 + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry'));
258 252 $entry->appendChild($link);
259 253 }
260 254  
... ... @@ -267,13 +261,16 @@ class KT_cmis_atom_service_helper {
267 261 . $typeString
268 262 . '/' . $cmisEntry['properties']['objectId']['value']
269 263 . '/children'));
  264 + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=feed'));
270 265 $entry->appendChild($link);
  266 +
271 267 $link = $feed->newElement('link');
272 268 $link->appendChild($feed->newAttr('rel', 'down'));
273 269 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/'
274 270 . $typeString
275 271 . '/' . $cmisEntry['properties']['objectId']['value']
276 272 . '/descendants'));
  273 + $link->appendChild($feed->newAttr('type', 'application/cmistree+xml'));
277 274 $entry->appendChild($link);
278 275  
279 276 // TODO add folder tree link when we have folder tree implemented
... ... @@ -323,18 +320,33 @@ class KT_cmis_atom_service_helper {
323 320 $link = $feed->newElement('link');
324 321 $link->appendChild($feed->newAttr('rel', 'describedby'));
325 322 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . $type));
  323 + $link->appendChild($feed->newAttr('type', 'application/atomsvc+xml'));
326 324 $entry->appendChild($link);
327 325  
  326 + // TODO this link must specify the workspace
328 327 $link = $feed->newElement('link');
329 328 $link->appendChild($feed->newAttr('rel', 'service'));
330 329 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument'));
  330 + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry'));
331 331 $entry->appendChild($link);
332 332  
333   - // TODO proper date
334   - $entry->appendChild($feed->newField('published', self::formatDatestamp()));
  333 + $updated = null;
  334 + $published = null;
  335 + if ($cmisEntry['properties']['lastModificationDate']['value'] != '0000-00-00 00:00:00') {
  336 + $updated = $cmisEntry['properties']['lastModificationDate']['value'];
  337 + }
  338 + else if ($cmisEntry['properties']['creationDate']['value'] != '0000-00-00 00:00:00') {
  339 + $updated = $cmisEntry['properties']['creationDate']['value'];
  340 + }
  341 +
  342 + if ($cmisEntry['properties']['creationDate']['value'] != '0000-00-00 00:00:00') {
  343 + $published = $cmisEntry['properties']['creationDate']['value'];
  344 + }
  345 +
  346 + $entry->appendChild($feed->newField('published', self::formatDatestamp($published)));
335 347 $entry->appendChild($feed->newElement('summary', $cmisEntry['properties']['name']['value']));
336 348 $entry->appendChild($feed->newElement('title', $cmisEntry['properties']['name']['value']));
337   - $entry->appendChild($feed->newField('updated', self::formatDatestamp()));
  349 + $entry->appendChild($feed->newField('updated', self::formatDatestamp($updated)));
338 350  
339 351 // main CMIS entry
340 352 $objectElement = $feed->newElement('cmisra:object');
... ... @@ -343,7 +355,14 @@ class KT_cmis_atom_service_helper {
343 355  
344 356 // TODO check determination of when to add app:edited tag
345 357 // if ($method == 'POST') {
346   - $entry->appendChild($feed->newElement('app:edited', self::formatDatestamp()));
  358 + $edited = null;
  359 + if ($cmisEntry['properties']['lastModificationDate']['value'] != '0000-00-00 00:00:00') {
  360 + $edited = $cmisEntry['properties']['lastModificationDate']['value'];
  361 + }
  362 + else if ($cmisEntry['properties']['creationDate']['value'] != '0000-00-00 00:00:00') {
  363 + $edited = $cmisEntry['properties']['creationDate']['value'];
  364 + }
  365 + $entry->appendChild($feed->newElement('app:edited', self::formatDatestamp($edited)));
347 366 // }
348 367  
349 368 // TODO pathSegment entry
... ... @@ -424,17 +443,20 @@ class KT_cmis_atom_service_helper {
424 443 // TODO set page number correctly - to be done when we support paging the the API
425 444  
426 445 // author
427   - // TODO generate this dynamically (based on???)\
  446 + // since types are/should be always created by system admin, we use a default value - this may be set later but for now is unset
  447 + // for a type which is not an instantiated object
428 448 $feedElement = $feed->newField('author');
429   - $element = $feed->newField('name', 'admin', $feedElement);
  449 + $element = $feed->newField('name', 'Administrator', $feedElement);
430 450 $feed->appendChild($feedElement);
431 451  
432 452 foreach($types as $type)
433 453 {
434 454 $entry = $feed->newEntry();
435 455  
  456 + // since types are/should be always created by system admin, we use a default value - this may be set later but for now is unset
  457 + // for a type which is not an instantiated object
436 458 $feedElement = $feed->newField('author');
437   - $element = $feed->newField('name', 'admin', $feedElement);
  459 + $element = $feed->newField('name', 'Administrator', $feedElement);
438 460 $entry->appendChild($feedElement);
439 461 $feedElement = $feed->newField('content', 'Type definition for ' . $type['baseId']);
440 462 $entry->appendChild($feedElement);
... ... @@ -449,17 +471,20 @@ class KT_cmis_atom_service_helper {
449 471 // TODO type link MUST point to base type
450 472 // KnowledgeTree currently only supports base types so this is not important
451 473 // at the present time as it will always point at the base type.
  474 +
452 475 $link = $feed->newElement('link');
453 476 $link->appendChild($feed->newAttr('rel','type'));
454 477 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['baseId'])));
455 478 $entry->appendChild($link);
  479 +
456 480 $link = $feed->newElement('link');
457 481 $link->appendChild($feed->newAttr('rel','repository'));
458 482 $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument'));
459 483 $entry->appendChild($link);
460 484  
461   - $entry->appendChild($feed->newElement('summary', 'Summary for ' . $type['baseId'] . ' type'));
462   - $entry->appendChild($feed->newElement('title', $type['baseId']));
  485 + $entry->appendChild($feed->newElement('summary', 'Summary for ' . $type['displayName'] . ' type'));
  486 + $entry->appendChild($feed->newElement('title', $type['displayName']));
  487 + // Since types do not have associated dates, we don't know the time last updated, so we use current
463 488 $entry->appendChild($feed->newElement('updated', self::formatDatestamp()));
464 489  
465 490 $objectElement = $feed->newElement('cmisra:type');
... ... @@ -479,13 +504,14 @@ class KT_cmis_atom_service_helper {
479 504 // appear to be seen by browser based clients (I am not sure of non-browser clients)
480 505 // NOTE just added a check and only send back the header containing 404, no text, and this works, we get a 404 header
481 506 // plus a readable AtomPub response
  507 + // THEORY is that Apache is generating the html response when given a '404 Not Found' header, overriding the desired output
482 508 if (!strstr($status, '404')) {
483 509 $service->setStatus($status);
484 510 }
485 511 else {
486 512 $service->setStatus('404');
487 513 }
488   -
  514 +
489 515 $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI);
490 516  
491 517 $feed->newField('title', 'Error: ' . $status, $feed);
... ... @@ -697,13 +723,21 @@ class KT_cmis_atom_service_helper {
697 723 }
698 724 }
699 725 }
  726 +
700 727 return self::$ktapi;
701 728 }
702 729  
703 730 // TODO adjust for time zones?
704 731 static public function formatDatestamp($time = null)
705 732 {
706   - if (is_null($time)) $time = time();
  733 + if (is_null($time)) {
  734 + $time = time();
  735 + }
  736 + else {
  737 + // assumes format is 'yyyy-mm-dd hh:mm:ss' or some other string representation, and not a unix timestamp
  738 + $time = strtotime($time);
  739 + }
  740 +
707 741 return date('Y-m-d H:i:s', $time);
708 742 }
709 743  
... ... @@ -768,6 +802,7 @@ class KT_cmis_atom_service_helper {
768 802 $service->setHeader('Content-type', $response['properties']['contentStreamMimeType']['value'] . ';charset=utf-8');
769 803 }
770 804 else {
  805 + // FIXME what should the default type be? application/unknown?
771 806 $service->setHeader('Content-type', 'text/plain;charset=utf-8');
772 807 }
773 808  
... ...
webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php
... ... @@ -19,6 +19,21 @@ class KT_cmis_atom_response extends KT_atom_response {
19 19 {
20 20 return $this->workspace;
21 21 }
  22 +
  23 + protected function constructFeedHeader(){
  24 + $feed = $this->newElement('feed');
  25 + $feed->appendChild($this->newAttr('xmlns','http://www.w3.org/2005/Atom'));
  26 + $this->feed = &$feed;
  27 + $this->DOM->appendChild($this->feed);
  28 + }
  29 +
  30 + public function &newEntry()
  31 + {
  32 + $entry = $this->newElement('atom:entry');
  33 + $this->feed->appendChild($entry);
  34 +
  35 + return $entry;
  36 + }
22 37  
23 38 // TODO try to get rid of this function
24 39 function appendChild($element)
... ...
webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php
... ... @@ -22,14 +22,14 @@ class KT_cmis_atom_server extends KT_atom_server {
22 22 header('Expires:');
23 23 header('Pragma:');
24 24  
25   - // prevent output of standard text/xml header
  25 + // prevent output of regular headers for AtomPub responses
26 26 $this->headersSet = true;
27 27  
28 28 return false;
29 29 }
30 30 else if ($doc->notModified())
31 31 {
32   - // prevent output of standard text/xml header
  32 + // prevent output of regular headers for AtomPub responses
33 33 $this->headersSet = true;
34 34 $this->setNoContent(true);
35 35  
... ... @@ -114,6 +114,15 @@ class KT_cmis_atom_server extends KT_atom_server {
114 114 }
115 115 }
116 116  
  117 + // TODO add other links once their services are supported
  118 + // links
  119 + $link = $service->newElement('atom:link');
  120 + $link->appendChild($service->newAttr('title', 'root descendants'));
  121 + $link->appendChild($service->newAttr('type', 'application/cmistree+xml'));
  122 + $link->appendChild($service->newAttr('rel', 'http://docs.oasis-open.org/ns/cmis/link/200908/rootdescendants'));
  123 + $link->appendChild($service->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/Root%20Folder/descendants'));
  124 + $ws->appendChild($link);
  125 +
117 126 // uri templates - getObjectById, getObjectByPath, getTypeById
118 127 $ws->appendChild($service->uriTemplate('objectbyid', $workspace));
119 128 $ws->appendChild($service->uriTemplate('objectbypath', $workspace));
... ...