Commit f3438102dd2a8d7b0796e0cfdee57224bab544a3
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
Showing
4 changed files
with
122 additions
and
45 deletions
webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php
| @@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
| 2 | 2 | ||
| 3 | /** | 3 | /** |
| 4 | * Any feed must be a valid atom Feed document and conform to the guidelines below: | 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 | 2. Author/name will be the CMIS property createdBy | 6 | 2. Author/name will be the CMIS property createdBy |
| 7 | 3. Title will be the CMIS property name | 7 | 3. Title will be the CMIS property name |
| 8 | 4. App:edited will be the CMIS property lastModifiedDate | 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,14 +145,12 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { | ||
| 145 | $folderId = $this->params[0]; | 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 | $NavigationService = new KTNavigationService(KT_cmis_atom_service_helper::getKt()); | 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 | $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $folderId); | 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,6 +303,7 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { | ||
| 305 | $this->setStatus(self::STATUS_SERVER_ERROR); | 303 | $this->setStatus(self::STATUS_SERVER_ERROR); |
| 306 | 304 | ||
| 307 | $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); | 305 | $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); |
| 306 | + // FIXME? this should perhaps use a different status code? | ||
| 308 | $feed->newField('title', 'Error: Failed to delete all objects in tree: ' . self::STATUS_SERVER_ERROR, $feed); | 307 | $feed->newField('title', 'Error: Failed to delete all objects in tree: ' . self::STATUS_SERVER_ERROR, $feed); |
| 309 | 308 | ||
| 310 | foreach($response as $failed) | 309 | foreach($response as $failed) |
| @@ -337,8 +336,17 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { | @@ -337,8 +336,17 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { | ||
| 337 | * @param string $feedType children or descendants | 336 | * @param string $feedType children or descendants |
| 338 | * @return string CMIS AtomPub feed | 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 | if ($feedType == 'children') { | 350 | if ($feedType == 'children') { |
| 343 | try { | 351 | try { |
| 344 | $entries = $NavigationService->getChildren($repositoryId, $folderId, false, false); | 352 | $entries = $NavigationService->getChildren($repositoryId, $folderId, false, false); |
| @@ -373,30 +381,39 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { | @@ -373,30 +381,39 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { | ||
| 373 | 381 | ||
| 374 | $feed->newField('title', $folderName . ' ' . ucwords($feedType), $feed); | 382 | $feed->newField('title', $folderName . ' ' . ucwords($feedType), $feed); |
| 375 | 383 | ||
| 376 | - // TODO dynamic? | ||
| 377 | $feedElement = $feed->newField('author'); | 384 | $feedElement = $feed->newField('author'); |
| 378 | - $element = $feed->newField('name', 'System', $feedElement); | 385 | + $element = $feed->newField('name', $rootProperties['properties']['createdBy']['value'], $feedElement); |
| 379 | $feed->appendChild($feedElement); | 386 | $feed->appendChild($feedElement); |
| 380 | 387 | ||
| 381 | // id | 388 | // id |
| 382 | $feed->newField('id', 'urn:uuid:' . $folderId . '-' . $feedType, $feed); | 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 | $link = $feed->newElement('link'); | 401 | $link = $feed->newElement('link'); |
| 388 | $link->appendChild($feed->newAttr('rel', 'self')); | 402 | $link->appendChild($feed->newAttr('rel', 'self')); |
| 389 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId . '/' . $feedType)); | 403 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId . '/' . $feedType)); |
| 390 | $feed->appendChild($link); | 404 | $feed->appendChild($link); |
| 391 | 405 | ||
| 406 | + // TODO this link must specify the workspace | ||
| 392 | $link = $feed->newElement('link'); | 407 | $link = $feed->newElement('link'); |
| 393 | $link->appendChild($feed->newAttr('rel', 'service')); | 408 | $link->appendChild($feed->newAttr('rel', 'service')); |
| 394 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . 'servicedocument')); | 409 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . 'servicedocument')); |
| 410 | + $link->appendChild($feed->newAttr('type', 'application/atomsvc+xml')); | ||
| 395 | $feed->appendChild($link); | 411 | $feed->appendChild($link); |
| 396 | 412 | ||
| 397 | $link = $feed->newElement('link'); | 413 | $link = $feed->newElement('link'); |
| 398 | $link->appendChild($feed->newAttr('rel', 'via')); | 414 | $link->appendChild($feed->newAttr('rel', 'via')); |
| 399 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId)); | 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 | $feed->appendChild($link); | 417 | $feed->appendChild($link); |
| 401 | 418 | ||
| 402 | KT_cmis_atom_service_helper::createObjectFeed($feed, $entries, $folderName); | 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,10 +570,10 @@ class KT_cmis_atom_service_pwc extends KT_cmis_atom_service { | ||
| 553 | $content = KT_cmis_atom_service_helper::getCmisContent($this->rawContent); | 570 | $content = KT_cmis_atom_service_helper::getCmisContent($this->rawContent); |
| 554 | // NOTE not sure about the text type, will need testing, most content will be base64 | 571 | // NOTE not sure about the text type, will need testing, most content will be base64 |
| 555 | $cmisContent = (isset($content['cmisra:base64']) | 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 | // if we haven't found it now, the hack begins - retrieve the EXISTING content and submit this as the contentStream | 578 | // if we haven't found it now, the hack begins - retrieve the EXISTING content and submit this as the contentStream |
| 562 | // this is needed because KnowledgeTree will not accept a checkin without a content stream but CMISSpaces (and possibly | 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,14 +635,14 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { | ||
| 618 | 635 | ||
| 619 | $feed->newField('title', 'Checked out Documents', $feed); | 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 | $feedElement = $feed->newField('author'); | 639 | $feedElement = $feed->newField('author'); |
| 623 | - $element = $feed->newField('name', 'admin', $feedElement); | 640 | + $element = $feed->newField('name', 'Administrator', $feedElement); |
| 624 | $feed->appendChild($feedElement); | 641 | $feed->appendChild($feedElement); |
| 625 | 642 | ||
| 626 | $feed->appendChild($feed->newElement('id', 'urn:uuid:checkedout')); | 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 | $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp())); | 646 | $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp())); |
| 630 | 647 | ||
| 631 | $link = $feed->newElement('link'); | 648 | $link = $feed->newElement('link'); |
| @@ -671,8 +688,8 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { | @@ -671,8 +688,8 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { | ||
| 671 | // in the helper code, but I don't feel that throwing an exception is necessary or always wanted; | 688 | // in the helper code, but I don't feel that throwing an exception is necessary or always wanted; |
| 672 | // alternative is to send the name of the Exception but not an instance, and do an is_a check on the other side, | 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 | // but since it will only be needed to this and similar calls, it seems wasteful to do that for every other case | 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 | return null; | 693 | return null; |
| 677 | } | 694 | } |
| 678 | 695 | ||
| @@ -683,7 +700,7 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { | @@ -683,7 +700,7 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { | ||
| 683 | $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode($e), $e->getMessage()); | 700 | $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode($e), $e->getMessage()); |
| 684 | return null; | 701 | return null; |
| 685 | } | 702 | } |
| 686 | - | 703 | + |
| 687 | $this->setStatus(self::STATUS_CREATED); | 704 | $this->setStatus(self::STATUS_CREATED); |
| 688 | $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $cmisObjectProperties['cmis:objectId'], 'POST'); | 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,6 +793,7 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service { | ||
| 776 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . 'type/' . $this->params[0] . '/' . $this->params[1] . '?pageNo=1&pageSize=0')); | 793 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . 'type/' . $this->params[0] . '/' . $this->params[1] . '?pageNo=1&pageSize=0')); |
| 777 | $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=feed')); | 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 | $feed->newField('updated', KT_cmis_atom_service_helper::formatDatestamp(), $feed); | 797 | $feed->newField('updated', KT_cmis_atom_service_helper::formatDatestamp(), $feed); |
| 780 | $feed->newField('cmis:hasMoreItems', 'false', $feed); | 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,6 +54,7 @@ class KT_cmis_atom_service_helper { | ||
| 54 | * @return string CMIS AtomPub feed | 54 | * @return string CMIS AtomPub feed |
| 55 | */ | 55 | */ |
| 56 | // TODO enable this to work on an existing set of object properties if submitted | 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 | static public function getObjectFeed(&$service, $ObjectService, $repositoryId, $objectId, $method = 'GET') | 58 | static public function getObjectFeed(&$service, $ObjectService, $repositoryId, $objectId, $method = 'GET') |
| 58 | { | 59 | { |
| 59 | self::$repositoryId = $repositoryId; | 60 | self::$repositoryId = $repositoryId; |
| @@ -173,9 +174,8 @@ class KT_cmis_atom_service_helper { | @@ -173,9 +174,8 @@ class KT_cmis_atom_service_helper { | ||
| 173 | { | 174 | { |
| 174 | $type = $cmisEntry['properties']['objectTypeId']['value']; | 175 | $type = $cmisEntry['properties']['objectTypeId']['value']; |
| 175 | 176 | ||
| 176 | - // TODO dynamic actual creator name | ||
| 177 | $responseElement = $feed->newField('author'); | 177 | $responseElement = $feed->newField('author'); |
| 178 | - $element = $feed->newField('name', 'admin', $responseElement); | 178 | + $element = $feed->newField('name', $cmisEntry['properties']['createdBy']['value'], $responseElement); |
| 179 | $entry->appendChild($responseElement); | 179 | $entry->appendChild($responseElement); |
| 180 | 180 | ||
| 181 | $typeString = str_replace('cmis:', '', $type); | 181 | $typeString = str_replace('cmis:', '', $type); |
| @@ -206,6 +206,7 @@ class KT_cmis_atom_service_helper { | @@ -206,6 +206,7 @@ class KT_cmis_atom_service_helper { | ||
| 206 | $link->appendChild($feed->newAttr('rel', 'edit')); | 206 | $link->appendChild($feed->newAttr('rel', 'edit')); |
| 207 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString | 207 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString |
| 208 | . '/' . $cmisEntry['properties']['objectId']['value'])); | 208 | . '/' . $cmisEntry['properties']['objectId']['value'])); |
| 209 | + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry')); | ||
| 209 | $entry->appendChild($link); | 210 | $entry->appendChild($link); |
| 210 | 211 | ||
| 211 | if ((strtolower($cmisEntry['properties']['objectTypeId']['value']) == 'cmis:document') | 212 | if ((strtolower($cmisEntry['properties']['objectTypeId']['value']) == 'cmis:document') |
| @@ -218,14 +219,6 @@ class KT_cmis_atom_service_helper { | @@ -218,14 +219,6 @@ class KT_cmis_atom_service_helper { | ||
| 218 | . '/' . $cmisEntry['properties']['objectId']['value'] | 219 | . '/' . $cmisEntry['properties']['objectId']['value'] |
| 219 | . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); | 220 | . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); |
| 220 | $entry->appendChild($link); | 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 | // according to spec this MUST be present, but spec says that links for function which are not supported | 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,6 +248,7 @@ class KT_cmis_atom_service_helper { | ||
| 255 | $link->appendChild($feed->newAttr('rel', 'up')); | 248 | $link->appendChild($feed->newAttr('rel', 'up')); |
| 256 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' | 249 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' |
| 257 | . $cmisEntry['properties']['parentId']['value'])); | 250 | . $cmisEntry['properties']['parentId']['value'])); |
| 251 | + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry')); | ||
| 258 | $entry->appendChild($link); | 252 | $entry->appendChild($link); |
| 259 | } | 253 | } |
| 260 | 254 | ||
| @@ -267,13 +261,16 @@ class KT_cmis_atom_service_helper { | @@ -267,13 +261,16 @@ class KT_cmis_atom_service_helper { | ||
| 267 | . $typeString | 261 | . $typeString |
| 268 | . '/' . $cmisEntry['properties']['objectId']['value'] | 262 | . '/' . $cmisEntry['properties']['objectId']['value'] |
| 269 | . '/children')); | 263 | . '/children')); |
| 264 | + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=feed')); | ||
| 270 | $entry->appendChild($link); | 265 | $entry->appendChild($link); |
| 266 | + | ||
| 271 | $link = $feed->newElement('link'); | 267 | $link = $feed->newElement('link'); |
| 272 | $link->appendChild($feed->newAttr('rel', 'down')); | 268 | $link->appendChild($feed->newAttr('rel', 'down')); |
| 273 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' | 269 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' |
| 274 | . $typeString | 270 | . $typeString |
| 275 | . '/' . $cmisEntry['properties']['objectId']['value'] | 271 | . '/' . $cmisEntry['properties']['objectId']['value'] |
| 276 | . '/descendants')); | 272 | . '/descendants')); |
| 273 | + $link->appendChild($feed->newAttr('type', 'application/cmistree+xml')); | ||
| 277 | $entry->appendChild($link); | 274 | $entry->appendChild($link); |
| 278 | 275 | ||
| 279 | // TODO add folder tree link when we have folder tree implemented | 276 | // TODO add folder tree link when we have folder tree implemented |
| @@ -323,18 +320,33 @@ class KT_cmis_atom_service_helper { | @@ -323,18 +320,33 @@ class KT_cmis_atom_service_helper { | ||
| 323 | $link = $feed->newElement('link'); | 320 | $link = $feed->newElement('link'); |
| 324 | $link->appendChild($feed->newAttr('rel', 'describedby')); | 321 | $link->appendChild($feed->newAttr('rel', 'describedby')); |
| 325 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . $type)); | 322 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . $type)); |
| 323 | + $link->appendChild($feed->newAttr('type', 'application/atomsvc+xml')); | ||
| 326 | $entry->appendChild($link); | 324 | $entry->appendChild($link); |
| 327 | 325 | ||
| 326 | + // TODO this link must specify the workspace | ||
| 328 | $link = $feed->newElement('link'); | 327 | $link = $feed->newElement('link'); |
| 329 | $link->appendChild($feed->newAttr('rel', 'service')); | 328 | $link->appendChild($feed->newAttr('rel', 'service')); |
| 330 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); | 329 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); |
| 330 | + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry')); | ||
| 331 | $entry->appendChild($link); | 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 | $entry->appendChild($feed->newElement('summary', $cmisEntry['properties']['name']['value'])); | 347 | $entry->appendChild($feed->newElement('summary', $cmisEntry['properties']['name']['value'])); |
| 336 | $entry->appendChild($feed->newElement('title', $cmisEntry['properties']['name']['value'])); | 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 | // main CMIS entry | 351 | // main CMIS entry |
| 340 | $objectElement = $feed->newElement('cmisra:object'); | 352 | $objectElement = $feed->newElement('cmisra:object'); |
| @@ -343,7 +355,14 @@ class KT_cmis_atom_service_helper { | @@ -343,7 +355,14 @@ class KT_cmis_atom_service_helper { | ||
| 343 | 355 | ||
| 344 | // TODO check determination of when to add app:edited tag | 356 | // TODO check determination of when to add app:edited tag |
| 345 | // if ($method == 'POST') { | 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 | // TODO pathSegment entry | 368 | // TODO pathSegment entry |
| @@ -424,17 +443,20 @@ class KT_cmis_atom_service_helper { | @@ -424,17 +443,20 @@ class KT_cmis_atom_service_helper { | ||
| 424 | // TODO set page number correctly - to be done when we support paging the the API | 443 | // TODO set page number correctly - to be done when we support paging the the API |
| 425 | 444 | ||
| 426 | // author | 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 | $feedElement = $feed->newField('author'); | 448 | $feedElement = $feed->newField('author'); |
| 429 | - $element = $feed->newField('name', 'admin', $feedElement); | 449 | + $element = $feed->newField('name', 'Administrator', $feedElement); |
| 430 | $feed->appendChild($feedElement); | 450 | $feed->appendChild($feedElement); |
| 431 | 451 | ||
| 432 | foreach($types as $type) | 452 | foreach($types as $type) |
| 433 | { | 453 | { |
| 434 | $entry = $feed->newEntry(); | 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 | $feedElement = $feed->newField('author'); | 458 | $feedElement = $feed->newField('author'); |
| 437 | - $element = $feed->newField('name', 'admin', $feedElement); | 459 | + $element = $feed->newField('name', 'Administrator', $feedElement); |
| 438 | $entry->appendChild($feedElement); | 460 | $entry->appendChild($feedElement); |
| 439 | $feedElement = $feed->newField('content', 'Type definition for ' . $type['baseId']); | 461 | $feedElement = $feed->newField('content', 'Type definition for ' . $type['baseId']); |
| 440 | $entry->appendChild($feedElement); | 462 | $entry->appendChild($feedElement); |
| @@ -449,17 +471,20 @@ class KT_cmis_atom_service_helper { | @@ -449,17 +471,20 @@ class KT_cmis_atom_service_helper { | ||
| 449 | // TODO type link MUST point to base type | 471 | // TODO type link MUST point to base type |
| 450 | // KnowledgeTree currently only supports base types so this is not important | 472 | // KnowledgeTree currently only supports base types so this is not important |
| 451 | // at the present time as it will always point at the base type. | 473 | // at the present time as it will always point at the base type. |
| 474 | + | ||
| 452 | $link = $feed->newElement('link'); | 475 | $link = $feed->newElement('link'); |
| 453 | $link->appendChild($feed->newAttr('rel','type')); | 476 | $link->appendChild($feed->newAttr('rel','type')); |
| 454 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['baseId']))); | 477 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['baseId']))); |
| 455 | $entry->appendChild($link); | 478 | $entry->appendChild($link); |
| 479 | + | ||
| 456 | $link = $feed->newElement('link'); | 480 | $link = $feed->newElement('link'); |
| 457 | $link->appendChild($feed->newAttr('rel','repository')); | 481 | $link->appendChild($feed->newAttr('rel','repository')); |
| 458 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); | 482 | $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); |
| 459 | $entry->appendChild($link); | 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 | $entry->appendChild($feed->newElement('updated', self::formatDatestamp())); | 488 | $entry->appendChild($feed->newElement('updated', self::formatDatestamp())); |
| 464 | 489 | ||
| 465 | $objectElement = $feed->newElement('cmisra:type'); | 490 | $objectElement = $feed->newElement('cmisra:type'); |
| @@ -479,13 +504,14 @@ class KT_cmis_atom_service_helper { | @@ -479,13 +504,14 @@ class KT_cmis_atom_service_helper { | ||
| 479 | // appear to be seen by browser based clients (I am not sure of non-browser clients) | 504 | // appear to be seen by browser based clients (I am not sure of non-browser clients) |
| 480 | // NOTE just added a check and only send back the header containing 404, no text, and this works, we get a 404 header | 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 | // plus a readable AtomPub response | 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 | if (!strstr($status, '404')) { | 508 | if (!strstr($status, '404')) { |
| 483 | $service->setStatus($status); | 509 | $service->setStatus($status); |
| 484 | } | 510 | } |
| 485 | else { | 511 | else { |
| 486 | $service->setStatus('404'); | 512 | $service->setStatus('404'); |
| 487 | } | 513 | } |
| 488 | - | 514 | + |
| 489 | $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); | 515 | $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); |
| 490 | 516 | ||
| 491 | $feed->newField('title', 'Error: ' . $status, $feed); | 517 | $feed->newField('title', 'Error: ' . $status, $feed); |
| @@ -697,13 +723,21 @@ class KT_cmis_atom_service_helper { | @@ -697,13 +723,21 @@ class KT_cmis_atom_service_helper { | ||
| 697 | } | 723 | } |
| 698 | } | 724 | } |
| 699 | } | 725 | } |
| 726 | + | ||
| 700 | return self::$ktapi; | 727 | return self::$ktapi; |
| 701 | } | 728 | } |
| 702 | 729 | ||
| 703 | // TODO adjust for time zones? | 730 | // TODO adjust for time zones? |
| 704 | static public function formatDatestamp($time = null) | 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 | return date('Y-m-d H:i:s', $time); | 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,6 +802,7 @@ class KT_cmis_atom_service_helper { | ||
| 768 | $service->setHeader('Content-type', $response['properties']['contentStreamMimeType']['value'] . ';charset=utf-8'); | 802 | $service->setHeader('Content-type', $response['properties']['contentStreamMimeType']['value'] . ';charset=utf-8'); |
| 769 | } | 803 | } |
| 770 | else { | 804 | else { |
| 805 | + // FIXME what should the default type be? application/unknown? | ||
| 771 | $service->setHeader('Content-type', 'text/plain;charset=utf-8'); | 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,6 +19,21 @@ class KT_cmis_atom_response extends KT_atom_response { | ||
| 19 | { | 19 | { |
| 20 | return $this->workspace; | 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 | // TODO try to get rid of this function | 38 | // TODO try to get rid of this function |
| 24 | function appendChild($element) | 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,14 +22,14 @@ class KT_cmis_atom_server extends KT_atom_server { | ||
| 22 | header('Expires:'); | 22 | header('Expires:'); |
| 23 | header('Pragma:'); | 23 | header('Pragma:'); |
| 24 | 24 | ||
| 25 | - // prevent output of standard text/xml header | 25 | + // prevent output of regular headers for AtomPub responses |
| 26 | $this->headersSet = true; | 26 | $this->headersSet = true; |
| 27 | 27 | ||
| 28 | return false; | 28 | return false; |
| 29 | } | 29 | } |
| 30 | else if ($doc->notModified()) | 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 | $this->headersSet = true; | 33 | $this->headersSet = true; |
| 34 | $this->setNoContent(true); | 34 | $this->setNoContent(true); |
| 35 | 35 | ||
| @@ -114,6 +114,15 @@ class KT_cmis_atom_server extends KT_atom_server { | @@ -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 | // uri templates - getObjectById, getObjectByPath, getTypeById | 126 | // uri templates - getObjectById, getObjectByPath, getTypeById |
| 118 | $ws->appendChild($service->uriTemplate('objectbyid', $workspace)); | 127 | $ws->appendChild($service->uriTemplate('objectbyid', $workspace)); |
| 119 | $ws->appendChild($service->uriTemplate('objectbypath', $workspace)); | 128 | $ws->appendChild($service->uriTemplate('objectbypath', $workspace)); |