From f3438102dd2a8d7b0796e0cfdee57224bab544a3 Mon Sep 17 00:00:00 2001 From: Paul Barrett Date: Wed, 10 Mar 2010 17:25:03 +0200 Subject: [PATCH] Beginnings of link media types and complete atom node data --- webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php | 62 ++++++++++++++++++++++++++++++++++++++++---------------------- webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------- webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php | 15 +++++++++++++++ webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php | 13 +++++++++++-- 4 files changed, 122 insertions(+), 45 deletions(-) diff --git a/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php b/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php index b74991e..eaeca3f 100644 --- a/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php +++ b/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php @@ -2,7 +2,7 @@ /** * Any feed must be a valid atom Feed document and conform to the guidelines below: -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. +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. 2. Author/name will be the CMIS property createdBy 3. Title will be the CMIS property name 4. App:edited will be the CMIS property lastModifiedDate @@ -145,14 +145,12 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { $folderId = $this->params[0]; } - if (!empty($this->params[1]) && (($this->params[1] == 'children') || ($this->params[1] == 'descendants'))) - { + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); + if (!empty($this->params[1]) && (($this->params[1] == 'children') || ($this->params[1] == 'descendants'))) { $NavigationService = new KTNavigationService(KT_cmis_atom_service_helper::getKt()); - $feed = $this->getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $folderName, $this->params[1]); + $feed = $this->getFolderChildrenFeed($NavigationService, $ObjectService, $repositoryId, $folderId, $folderName, $this->params[1]); } - else - { - $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); + else { $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $folderId); } @@ -305,6 +303,7 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { $this->setStatus(self::STATUS_SERVER_ERROR); $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); + // FIXME? this should perhaps use a different status code? $feed->newField('title', 'Error: Failed to delete all objects in tree: ' . self::STATUS_SERVER_ERROR, $feed); foreach($response as $failed) @@ -337,8 +336,17 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { * @param string $feedType children or descendants * @return string CMIS AtomPub feed */ - private function getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $folderName, $feedType = 'children') + private function getFolderChildrenFeed(&$NavigationService, &$ObjectService, $repositoryId, $folderId, $folderName, $feedType = 'children') { + // fetch properties of parent folder for which children/descendants are being retrieved + try { + $rootProperties = $ObjectService->getProperties($repositoryId, $folderId); + } + catch (Exception $e) { + $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode($e), $e->getMessage()); + return null; + } + if ($feedType == 'children') { try { $entries = $NavigationService->getChildren($repositoryId, $folderId, false, false); @@ -373,30 +381,39 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { $feed->newField('title', $folderName . ' ' . ucwords($feedType), $feed); - // TODO dynamic? $feedElement = $feed->newField('author'); - $element = $feed->newField('name', 'System', $feedElement); + $element = $feed->newField('name', $rootProperties['properties']['createdBy']['value'], $feedElement); $feed->appendChild($feedElement); // id $feed->newField('id', 'urn:uuid:' . $folderId . '-' . $feedType, $feed); - // TODO get actual most recent update time, only use current if no other available - $feed->newField('updated', KT_cmis_atom_service_helper::formatDatestamp(), $feed); + $updated = null; + if ($rootProperties['properties']['lastModificationDate']['value'] != '0000-00-00 00:00:00') { + $updated = $rootProperties['properties']['lastModificationDate']['value']; + } + else if ($rootProperties['properties']['creationDate']['value'] != '0000-00-00 00:00:00') { + $updated = $rootProperties['properties']['creationDate']['value']; + } + + $feed->newField('updated', KT_cmis_atom_service_helper::formatDatestamp($updated), $feed); $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel', 'self')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId . '/' . $feedType)); $feed->appendChild($link); + // TODO this link must specify the workspace $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel', 'service')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . 'servicedocument')); + $link->appendChild($feed->newAttr('type', 'application/atomsvc+xml')); $feed->appendChild($link); $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel', 'via')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId)); + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry')); $feed->appendChild($link); KT_cmis_atom_service_helper::createObjectFeed($feed, $entries, $folderName); @@ -553,10 +570,10 @@ class KT_cmis_atom_service_pwc extends KT_cmis_atom_service { $content = KT_cmis_atom_service_helper::getCmisContent($this->rawContent); // NOTE not sure about the text type, will need testing, most content will be base64 $cmisContent = (isset($content['cmisra:base64']) - ? $content['cmisra:base64'] - : ((isset($content['cmisra:text'])) - ? $content['cmisra:text'] - : null)); + ? $content['cmisra:base64'] + : ((isset($content['cmisra:text'])) + ? $content['cmisra:text'] + : null)); // if we haven't found it now, the hack begins - retrieve the EXISTING content and submit this as the contentStream // 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 { $feed->newField('title', 'Checked out Documents', $feed); - // TODO dynamic? + // Since checked out documents do not necessarily share the same creator, we use a default value $feedElement = $feed->newField('author'); - $element = $feed->newField('name', 'admin', $feedElement); + $element = $feed->newField('name', 'Administrator', $feedElement); $feed->appendChild($feedElement); $feed->appendChild($feed->newElement('id', 'urn:uuid:checkedout')); - // TODO get actual most recent update time, only use current if no other available + // Since checked out documents are not necessarily from a single folder, we don't know the time last updated, so we use current $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp())); $link = $feed->newElement('link'); @@ -671,8 +688,8 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { // in the helper code, but I don't feel that throwing an exception is necessary or always wanted; // alternative is to send the name of the Exception but not an instance, and do an is_a check on the other side, // but since it will only be needed to this and similar calls, it seems wasteful to do that for every other case - $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode(new InvalidArgumentException()), - 'No object was specified for checkout'); + $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode(new InvalidArgumentException()), + 'No object was specified for checkout'); return null; } @@ -683,7 +700,7 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { $this->responseFeed = KT_cmis_atom_service_helper::getErrorFeed($this, $this->getStatusCode($e), $e->getMessage()); return null; } - + $this->setStatus(self::STATUS_CREATED); $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $cmisObjectProperties['cmis:objectId'], 'POST'); @@ -776,6 +793,7 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service { $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . 'type/' . $this->params[0] . '/' . $this->params[1] . '?pageNo=1&pageSize=0')); $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=feed')); + // Since types do not have associated dates, we don't know the time last updated, so we use current $feed->newField('updated', KT_cmis_atom_service_helper::formatDatestamp(), $feed); $feed->newField('cmis:hasMoreItems', 'false', $feed); diff --git a/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php b/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php index 3c5a19c..d0d62f8 100644 --- a/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php +++ b/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php @@ -54,6 +54,7 @@ class KT_cmis_atom_service_helper { * @return string CMIS AtomPub feed */ // TODO enable this to work on an existing set of object properties if submitted + // TODO this might be better as an entry rather than a feed - see Alfresco example static public function getObjectFeed(&$service, $ObjectService, $repositoryId, $objectId, $method = 'GET') { self::$repositoryId = $repositoryId; @@ -173,9 +174,8 @@ class KT_cmis_atom_service_helper { { $type = $cmisEntry['properties']['objectTypeId']['value']; - // TODO dynamic actual creator name $responseElement = $feed->newField('author'); - $element = $feed->newField('name', 'admin', $responseElement); + $element = $feed->newField('name', $cmisEntry['properties']['createdBy']['value'], $responseElement); $entry->appendChild($responseElement); $typeString = str_replace('cmis:', '', $type); @@ -206,6 +206,7 @@ class KT_cmis_atom_service_helper { $link->appendChild($feed->newAttr('rel', 'edit')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'])); + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry')); $entry->appendChild($link); if ((strtolower($cmisEntry['properties']['objectTypeId']['value']) == 'cmis:document') @@ -218,14 +219,6 @@ class KT_cmis_atom_service_helper { . '/' . $cmisEntry['properties']['objectId']['value'] . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); - - $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel', 'enclosure')); - $link->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString - . '/' . $cmisEntry['properties']['objectId']['value'] - . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); - $entry->appendChild($link); } // 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 { $link->appendChild($feed->newAttr('rel', 'up')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $cmisEntry['properties']['parentId']['value'])); + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry')); $entry->appendChild($link); } @@ -267,13 +261,16 @@ class KT_cmis_atom_service_helper { . $typeString . '/' . $cmisEntry['properties']['objectId']['value'] . '/children')); + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=feed')); $entry->appendChild($link); + $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel', 'down')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' . $cmisEntry['properties']['objectId']['value'] . '/descendants')); + $link->appendChild($feed->newAttr('type', 'application/cmistree+xml')); $entry->appendChild($link); // TODO add folder tree link when we have folder tree implemented @@ -323,18 +320,33 @@ class KT_cmis_atom_service_helper { $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel', 'describedby')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . $type)); + $link->appendChild($feed->newAttr('type', 'application/atomsvc+xml')); $entry->appendChild($link); + // TODO this link must specify the workspace $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel', 'service')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=entry')); $entry->appendChild($link); - // TODO proper date - $entry->appendChild($feed->newField('published', self::formatDatestamp())); + $updated = null; + $published = null; + if ($cmisEntry['properties']['lastModificationDate']['value'] != '0000-00-00 00:00:00') { + $updated = $cmisEntry['properties']['lastModificationDate']['value']; + } + else if ($cmisEntry['properties']['creationDate']['value'] != '0000-00-00 00:00:00') { + $updated = $cmisEntry['properties']['creationDate']['value']; + } + + if ($cmisEntry['properties']['creationDate']['value'] != '0000-00-00 00:00:00') { + $published = $cmisEntry['properties']['creationDate']['value']; + } + + $entry->appendChild($feed->newField('published', self::formatDatestamp($published))); $entry->appendChild($feed->newElement('summary', $cmisEntry['properties']['name']['value'])); $entry->appendChild($feed->newElement('title', $cmisEntry['properties']['name']['value'])); - $entry->appendChild($feed->newField('updated', self::formatDatestamp())); + $entry->appendChild($feed->newField('updated', self::formatDatestamp($updated))); // main CMIS entry $objectElement = $feed->newElement('cmisra:object'); @@ -343,7 +355,14 @@ class KT_cmis_atom_service_helper { // TODO check determination of when to add app:edited tag // if ($method == 'POST') { - $entry->appendChild($feed->newElement('app:edited', self::formatDatestamp())); + $edited = null; + if ($cmisEntry['properties']['lastModificationDate']['value'] != '0000-00-00 00:00:00') { + $edited = $cmisEntry['properties']['lastModificationDate']['value']; + } + else if ($cmisEntry['properties']['creationDate']['value'] != '0000-00-00 00:00:00') { + $edited = $cmisEntry['properties']['creationDate']['value']; + } + $entry->appendChild($feed->newElement('app:edited', self::formatDatestamp($edited))); // } // TODO pathSegment entry @@ -424,17 +443,20 @@ class KT_cmis_atom_service_helper { // TODO set page number correctly - to be done when we support paging the the API // author - // TODO generate this dynamically (based on???)\ + // 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 + // for a type which is not an instantiated object $feedElement = $feed->newField('author'); - $element = $feed->newField('name', 'admin', $feedElement); + $element = $feed->newField('name', 'Administrator', $feedElement); $feed->appendChild($feedElement); foreach($types as $type) { $entry = $feed->newEntry(); + // 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 + // for a type which is not an instantiated object $feedElement = $feed->newField('author'); - $element = $feed->newField('name', 'admin', $feedElement); + $element = $feed->newField('name', 'Administrator', $feedElement); $entry->appendChild($feedElement); $feedElement = $feed->newField('content', 'Type definition for ' . $type['baseId']); $entry->appendChild($feedElement); @@ -449,17 +471,20 @@ class KT_cmis_atom_service_helper { // TODO type link MUST point to base type // KnowledgeTree currently only supports base types so this is not important // at the present time as it will always point at the base type. + $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel','type')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['baseId']))); $entry->appendChild($link); + $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel','repository')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); $entry->appendChild($link); - $entry->appendChild($feed->newElement('summary', 'Summary for ' . $type['baseId'] . ' type')); - $entry->appendChild($feed->newElement('title', $type['baseId'])); + $entry->appendChild($feed->newElement('summary', 'Summary for ' . $type['displayName'] . ' type')); + $entry->appendChild($feed->newElement('title', $type['displayName'])); + // Since types do not have associated dates, we don't know the time last updated, so we use current $entry->appendChild($feed->newElement('updated', self::formatDatestamp())); $objectElement = $feed->newElement('cmisra:type'); @@ -479,13 +504,14 @@ class KT_cmis_atom_service_helper { // appear to be seen by browser based clients (I am not sure of non-browser clients) // NOTE just added a check and only send back the header containing 404, no text, and this works, we get a 404 header // plus a readable AtomPub response + // THEORY is that Apache is generating the html response when given a '404 Not Found' header, overriding the desired output if (!strstr($status, '404')) { $service->setStatus($status); } else { $service->setStatus('404'); } - + $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); $feed->newField('title', 'Error: ' . $status, $feed); @@ -697,13 +723,21 @@ class KT_cmis_atom_service_helper { } } } + return self::$ktapi; } // TODO adjust for time zones? static public function formatDatestamp($time = null) { - if (is_null($time)) $time = time(); + if (is_null($time)) { + $time = time(); + } + else { + // assumes format is 'yyyy-mm-dd hh:mm:ss' or some other string representation, and not a unix timestamp + $time = strtotime($time); + } + return date('Y-m-d H:i:s', $time); } @@ -768,6 +802,7 @@ class KT_cmis_atom_service_helper { $service->setHeader('Content-type', $response['properties']['contentStreamMimeType']['value'] . ';charset=utf-8'); } else { + // FIXME what should the default type be? application/unknown? $service->setHeader('Content-type', 'text/plain;charset=utf-8'); } diff --git a/webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php b/webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php index 2f3319a..5d4cbce 100644 --- a/webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php +++ b/webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php @@ -19,6 +19,21 @@ class KT_cmis_atom_response extends KT_atom_response { { return $this->workspace; } + + protected function constructFeedHeader(){ + $feed = $this->newElement('feed'); + $feed->appendChild($this->newAttr('xmlns','http://www.w3.org/2005/Atom')); + $this->feed = &$feed; + $this->DOM->appendChild($this->feed); + } + + public function &newEntry() + { + $entry = $this->newElement('atom:entry'); + $this->feed->appendChild($entry); + + return $entry; + } // TODO try to get rid of this function function appendChild($element) diff --git a/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php b/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php index 0e82a4b..c0b919c 100644 --- a/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php +++ b/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php @@ -22,14 +22,14 @@ class KT_cmis_atom_server extends KT_atom_server { header('Expires:'); header('Pragma:'); - // prevent output of standard text/xml header + // prevent output of regular headers for AtomPub responses $this->headersSet = true; return false; } else if ($doc->notModified()) { - // prevent output of standard text/xml header + // prevent output of regular headers for AtomPub responses $this->headersSet = true; $this->setNoContent(true); @@ -114,6 +114,15 @@ class KT_cmis_atom_server extends KT_atom_server { } } + // TODO add other links once their services are supported + // links + $link = $service->newElement('atom:link'); + $link->appendChild($service->newAttr('title', 'root descendants')); + $link->appendChild($service->newAttr('type', 'application/cmistree+xml')); + $link->appendChild($service->newAttr('rel', 'http://docs.oasis-open.org/ns/cmis/link/200908/rootdescendants')); + $link->appendChild($service->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/Root%20Folder/descendants')); + $ws->appendChild($link); + // uri templates - getObjectById, getObjectByPath, getTypeById $ws->appendChild($service->uriTemplate('objectbyid', $workspace)); $ws->appendChild($service->uriTemplate('objectbypath', $workspace)); -- libgit2 0.21.4