diff --git a/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php b/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php index 93eb76a..d70ab7a 100644 --- a/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php +++ b/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php @@ -47,14 +47,14 @@ abstract class CMISPropertyCollection { static $ObjectId; static $BaseType; - static $URI; + static $Uri; static $ObjectTypeId; static $CreatedBy; static $CreationDate; static $LastModifiedBy; static $LastModificationDate; static $ChangeToken; - // TODO these definitions probably belong elsewhere, but here will do for now + // TODO these definitions belong in their own classe definition (see property type definions,) but here will do for now static $propertyTypes; function __construct() @@ -69,6 +69,11 @@ abstract class CMISPropertyCollection { 'Name' => 'propertyString', 'ContentStreamLength' => 'propertyInteger', 'ContentStreamMimeType' => 'propertyString', + 'Uri' => 'propertyUri', + 'AllowedChildObjectTypeIds' => 'propertyId', + 'CreatedBy' => 'propertyString', + 'CreationDate' => 'propertyDateTime', + 'ChangeToken' => 'propertyString', 'ParentId' => 'propertyId'); } diff --git a/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php b/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php index 6307e30..1ca2af6 100644 --- a/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php +++ b/lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php @@ -121,7 +121,7 @@ class CMISDocumentObject extends CMISBaseObject { . $objectProperties['document_id']); // NOTE what about instead creating a downloadable version with appropriate link? see ktapi::download_document // also ktapidocument::get_download_url - $this->_setPropertyInternal('URI', $uri); + $this->_setPropertyInternal('Uri', $uri); // TODO what is this? Assuming it is the object type id, and not OUR document type? $this->_setPropertyInternal('ObjectTypeId', $this->getAttribute('typeId')); // Needed to distinguish type diff --git a/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php b/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php index 0526893..8e6d41e 100644 --- a/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php +++ b/lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php @@ -99,7 +99,7 @@ class CMISFolderObject extends CMISBaseObject { . '/browse.php?fFolderId=' . $objectProperties['id']); // TODO this url is probably incorrect...needs to be checked - $this->_setPropertyInternal('URI', $uri); + $this->_setPropertyInternal('Uri', $uri); // TODO what is this? Assuming it is the object type id, and not OUR document type? $this->_setPropertyInternal('ObjectTypeId', $this->getAttribute('typeId')); // Needed to distinguish type diff --git a/lib/api/ktcmis/util/CMISUtil.inc.php b/lib/api/ktcmis/util/CMISUtil.inc.php index 8506d54..5e26a41 100644 --- a/lib/api/ktcmis/util/CMISUtil.inc.php +++ b/lib/api/ktcmis/util/CMISUtil.inc.php @@ -82,7 +82,8 @@ class CMISUtil { * The decoded object ID is returned by reference via the argument list * * @param string $objectId - * @return string $typeId + * @param string &$typeId + * @return string $objectId */ static public function decodeObjectId($objectId, &$typeId = null) { @@ -94,6 +95,25 @@ class CMISUtil { $typeId = null; + // NOTE Not sure whether this really belongs here, but probably this is the safest and most reliable place + // If we find that the folderId is in fact the name of the repository root folder, we will not be able to + // decode it, but we still need to return a valid id :). This is because the root folder name is returned + // by the repository configuration rather than the actual CMIS folder id. + // TODO consider just setting F1 as the root in the config? Originally didn't based on Alfresco, but... + $RepositoryService = new CMISRepositoryService(); + $repositories = $RepositoryService->getRepositories(); + $repositoryInfo = $repositories[0]->getRepositoryInfo(); + // the string replace is a hack for the drupal module, yay... + if ($repositoryInfo->getRootFolderId() == urldecode(str_replace('%2520', '%20', $objectId))) { + // NOTE that we may want to check the KnowledgeTree (not CMIS) repository for the root folder id. + // This will be vital if we ever implement a way for people to have multiple roots depending + // on who is logged in or what they select. Obviously the CMIS API in general will need a + // method of doing this. + // meantime this minor hack will get things working for the existing system structure, as the root + // folder should always be id 1. + return '1'; + } + preg_match('/(\D)(\d*)/', $objectId, $matches); $type = $matches[1]; $objectId = $matches[2]; @@ -233,29 +253,59 @@ class CMISUtil { static public function createObjectPropertiesEntry($properties) { + // TODO better dynamic style fetching of object properties into array for output $object = array(); + $object['Author'] = array('value' => $properties->getValue('Author')); + // TODO additional properties to be returned - $object['properties']['ObjectId'] = array('type' => $properties->getFieldType('ObjectId'), - 'value' => $properties->getValue('ObjectId')); $object['properties']['BaseType'] = array('type' => $properties->getFieldType('BaseType'), 'value' => $properties->getValue('BaseType')); + $object['properties']['ObjectId'] = array('type' => $properties->getFieldType('ObjectId'), + 'value' => $properties->getValue('ObjectId')); $object['properties']['ObjectTypeId'] = array('type' => $properties->getFieldType('ObjectTypeId'), 'value' => $properties->getValue('ObjectTypeId')); $object['properties']['Name'] = array('type' => $properties->getFieldType('Name'), 'value' => $properties->getValue('Name')); - $object['Author'] = array('value' => $properties->getValue('Author')); + // TODO ensure format of date is always correct + $object['properties']['LastModificationDate'] = array('type' => $properties->getFieldType('LastModificationDate'), + 'value' => $properties->getValue('LastModificationDate')); + $object['properties']['Uri'] = array('type' => $properties->getFieldType('Uri'), + 'value' => $properties->getValue('Uri')); $object['properties']['ParentId'] = array('type' => $properties->getFieldType('ParentId'), 'value' => CMISUtil::encodeObjectId('Folder', $properties->getValue('ParentId'))); - // TODO should check for content stream data before filling these in + + $object['properties']['AllowedChildObjectTypeIds'] = array('type' => $properties->getFieldType('AllowedChildObjectTypeIds'), + 'value' => $properties->getValue('AllowedChildObjectTypeIds')); + +// $object['properties']['AllowedChildObjectTypeIds'] = array('type' => $properties->getFieldType('AllowedChildObjectTypeIds'), +// 'value' => $properties->getValue('AllowedChildObjectTypeIds')); + + $object['properties']['CreatedBy'] = array('type' => $properties->getFieldType('CreatedBy'), + 'value' => $properties->getValue('CreatedBy')); + + $object['properties']['CreationDate'] = array('type' => $properties->getFieldType('CreationDate'), + 'value' => $properties->getValue('CreationDate')); + + $object['properties']['ChangeToken'] = array('type' => $properties->getFieldType('ChangeToken'), + 'value' => $properties->getValue('ChangeToken')); + if (strtolower($properties->getValue('ObjectTypeId')) == 'document') { - $object['properties']['ContentStreamLength'] = array('type' => $properties->getFieldType('ContentStreamLength'), - 'value' => $properties->getValue('ContentStreamLength')); - $object['properties']['ContentStreamMimeType'] = array('type' => $properties->getFieldType('ContentStreamMimeType'), - 'value' => $properties->getValue('ContentStreamMimeType')); + + $object['properties']['ChangeToken'] = array('type' => $properties->getFieldType('ChangeToken'), + 'value' => $properties->getValue('ChangeToken')); + $contentStreamLength = $properties->getValue('ContentStreamLength'); + if (!empty($contentStreamLength)) + { + $contentStreamLength = $properties->getValue('ContentStreamLength'); + $object['properties']['ContentStreamLength'] = array('type' => $properties->getFieldType('ContentStreamLength'), + 'value' => $properties->getValue('ContentStreamLength')); + $object['properties']['ContentStreamMimeType'] = array('type' => $properties->getFieldType('ContentStreamMimeType'), + 'value' => $properties->getValue('ContentStreamMimeType')); + } } // if we have found a child/parent with one or more children/parents, recurse into the child/parent object 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 0afce8b..8349ff0 100644 --- a/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php +++ b/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php @@ -1,5 +1,35 @@ + * * setup.txt * setup.txt * dGhpcyBiZSBzb21lIHRlc3QgY29udGVudCBmb3IgYSBkb2N1bWVudCwgeWVzPw== @@ -239,7 +269,20 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { // $baseURI=NULL,$title=NULL,$link=NULL,$updated=NULL,$author=NULL,$id=NULL $feed = new KT_cmis_atom_responseFeed(CMIS_APP_BASE_URI, $folderName . ' ' . ucwords($feedType), null, null, null, - 'urn:uuid:' . $folderName . '-' . $feedType); + 'urn:uuid:' . $folderId . '-' . $feedType); + + // TODO dynamic? + $feedElement = $feed->newField('author'); + $element = $feed->newField('name', 'System', $feedElement); + $feed->appendChild($feedElement); + + // TODO get actual most recent update time, only use current if no other available + $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp())); + + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel','source')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/folder/' . $folderId)); + $feed->appendChild($link); foreach($entries as $cmisEntry) { @@ -405,7 +448,7 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service { /* // TODO need to create this dynamically now, will no longer work with static output $output = ' - + urn:uuid:type-' . $type . '-children @@ -526,462 +569,4 @@ class KT_cmis_atom_service_document extends KT_cmis_atom_service { } -$childrenFeed[] = ' - - urn:uuid:28537649-8af2-4c74-aa92-5d8bbecac9ce-children - - Root Folder Children - - urn:uuid:86224486-b7ae-4074-a793-82cd259b0026-folder - - - - - - DroppedDocuments - DroppedDocuments - - - - - F2 - - - Folder - - - DroppedDocuments - - - - - - - urn:uuid:86224486-b7ae-4074-a793-82cd259b0026-folder - - - - - - Test KT Folder - Test KT Folder - - - - F4 - - - - Folder - - - Test KT Folder - - - - - - - admin - urn:uuid:2df9d676-f173-47bb-8ec1-41fa1186b66d - - - - - - - - - - 2009-06-23T09:40:47.889+02:00 - - h4555-cmis-so.pdf - 2009-06-23T09:40:58.524+02:00 - - - - workspace://SpacesStore/2df9d676-f173-47bb-8ec1-41fa1186b66d - document - document - admin - - 2009-06-23T09:40:47.889+02:00 - admin - 2009-06-23T09:40:58.524+02:00 - h4555-cmis-so.pdf - false - - true - false - false - - workspace://SpacesStore/2df9d676-f173-47bb-8ec1-41fa1186b66d - - false - - - - 343084 - application/pdf - h4555-cmis-so.pdf - - http://10.33.4.34:8080/alfresco/service/api/node/workspace/SpacesStore/2df9d676-f173-47bb-8ec1-41fa1186b66d/content.h4555-cmis-so.pdf - - - - - - '; - -$childrenFeed[] = ' - -System -Alfresco (Labs) -http://10.33.4.34:8080/alfresco/images/logo/AlfrescoLogo16.ico -urn:uuid:28537649-8af2-4c74-aa92-5d8bbecac9ce-children - - - - - -Company Home Children -2009-06-18T10:20:29.937+02:00 - -System -e98319fa-76e4-478f-8ce8-a3a0fd683e2c -urn:uuid:e98319fa-76e4-478f-8ce8-a3a0fd683e2c - - - - - - - - - - - -2009-06-18T10:20:37.788+02:00 -Site Collaboration Spaces -Sites -2009-06-18T10:20:37.874+02:00 - - - -workspace://SpacesStore/e98319fa-76e4-478f-8ce8-a3a0fd683e2c -folder -F/st_sites -System - -2009-06-18T10:20:37.788+02:00 -System -2009-06-18T10:20:37.874+02:00 -Sites -workspace://SpacesStore/28537649-8af2-4c74-aa92-5d8bbecac9ce - - - - -2009-06-18T10:20:37.874+02:00 -http://10.33.4.34:8080/alfresco/images/icons/space-icon-default-16.gif - - -System -8c80a0f7-74b4-4bd8-bb76-a2464e4b2d10 -urn:uuid:8c80a0f7-74b4-4bd8-bb76-a2464e4b2d10 - - - - - - - - - - - -2009-06-18T10:20:29.939+02:00 -User managed definitions -Data Dictionary -2009-06-18T10:20:30.004+02:00 - - - -workspace://SpacesStore/8c80a0f7-74b4-4bd8-bb76-a2464e4b2d10 -folder -folder -System - -2009-06-18T10:20:29.939+02:00 -System -2009-06-18T10:20:30.004+02:00 -Data Dictionary -workspace://SpacesStore/28537649-8af2-4c74-aa92-5d8bbecac9ce - - - - -2009-06-18T10:20:30.004+02:00 -http://10.33.4.34:8080/alfresco/images/icons/space-icon-default-16.gif - - -System -ba2524ef-7f3d-4ed4-84a0-8d99b6524737 -urn:uuid:ba2524ef-7f3d-4ed4-84a0-8d99b6524737 - - - - - - - - - - - -2009-06-18T10:20:30.312+02:00 -The guest root space -Guest Home - -2009-06-18T10:20:30.400+02:00 - - -workspace://SpacesStore/ba2524ef-7f3d-4ed4-84a0-8d99b6524737 -folder -folder -System - -2009-06-18T10:20:30.312+02:00 -System -2009-06-18T10:20:30.400+02:00 -Guest Home -workspace://SpacesStore/28537649-8af2-4c74-aa92-5d8bbecac9ce - - - - -2009-06-18T10:20:30.400+02:00 -http://10.33.4.34:8080/alfresco/images/icons/space-icon-default-16.gif - - -System -86224486-b7ae-4074-a793-82cd259b0026 -urn:uuid:86224486-b7ae-4074-a793-82cd259b0026 - - - - - - - - - - - -2009-06-18T10:20:30.402+02:00 -User Homes -User Homes -2009-06-18T10:20:30.428+02:00 - - - -workspace://SpacesStore/86224486-b7ae-4074-a793-82cd259b0026 -folder -folder -System - -2009-06-18T10:20:30.402+02:00 -System -2009-06-18T10:20:30.428+02:00 -User Homes -workspace://SpacesStore/28537649-8af2-4c74-aa92-5d8bbecac9ce - - - - -2009-06-18T10:20:30.428+02:00 -http://10.33.4.34:8080/alfresco/images/icons/space-icon-default-16.gif - - -System -0df9087f-e334-4890-a467-b60e3d6be92c -urn:uuid:0df9087f-e334-4890-a467-b60e3d6be92c - - - - - - - - - - - -2009-06-18T10:20:45.115+02:00 -Web Content Management Spaces -Web Projects -2009-06-18T10:20:45.137+02:00 - - - -workspace://SpacesStore/0df9087f-e334-4890-a467-b60e3d6be92c -folder -folder -System - -2009-06-18T10:20:45.115+02:00 -System -2009-06-18T10:20:45.137+02:00 -Web Projects -workspace://SpacesStore/28537649-8af2-4c74-aa92-5d8bbecac9ce - - - - -2009-06-18T10:20:45.137+02:00 -http://10.33.4.34:8080/alfresco/images/icons/space-icon-default-16.gif - - -admin -urn:uuid:2df9d676-f173-47bb-8ec1-41fa1186b66d - - - - - - - - - -2009-06-23T09:40:47.889+02:00 - -h4555-cmis-so.pdf -2009-06-23T09:40:58.524+02:00 - - - -workspace://SpacesStore/2df9d676-f173-47bb-8ec1-41fa1186b66d -document -document -admin - -2009-06-23T09:40:47.889+02:00 -admin -2009-06-23T09:40:58.524+02:00 -h4555-cmis-so.pdf -false - -true -false -false - -workspace://SpacesStore/2df9d676-f173-47bb-8ec1-41fa1186b66d - -false - - - -343084 -application/pdf -h4555-cmis-so.pdf - -http://10.33.4.34:8080/alfresco/service/api/node/workspace/SpacesStore/2df9d676-f173-47bb-8ec1-41fa1186b66d/content.h4555-cmis-so.pdf - - - -2009-06-23T09:40:58.524+02:00 -http://10.33.4.34:8080/alfresco/images/filetypes/pdf.gif - -false -6 -0 - -0 -'; - -$folderFeed = ' - - -System -28537649-8af2-4c74-aa92-5d8bbecac9ce -urn:uuid:28537649-8af2-4c74-aa92-5d8bbecac9ce - - - - - - - - -2009-06-18T10:20:29.871+02:00 -The company root space -Company Home -2009-06-18T10:20:29.937+02:00 - - -workspace://SpacesStore/28537649-8af2-4c74-aa92-5d8bbecac9ce -folder -folder -System -2009-06-18T10:20:29.871+02:00 -System -2009-06-18T10:20:29.937+02:00 -Company Home - - - - -2009-06-18T10:20:29.937+02:00 -http://127.0.0.1:8080/alfresco/images/icons/space-icon-default-16.gif - -'; - -$docFeed = ' - - -admin -urn:uuid:2df9d676-f173-47bb-8ec1-41fa1186b66d - - - - - - - - -2009-06-23T09:40:47.889+02:00 - -h4555-cmis-so.pdf -2009-06-23T09:40:58.524+02:00 - - -workspace://SpacesStore/2df9d676-f173-47bb-8ec1-41fa1186b66d -document -document -admin -2009-06-23T09:40:47.889+02:00 -admin -2009-06-23T09:40:58.524+02:00 -h4555-cmis-so.pdf -false -true -false -false - -workspace://SpacesStore/2df9d676-f173-47bb-8ec1-41fa1186b66d -false - - - -343084 -application/pdf -h4555-cmis-so.pdf -http://127.0.0.1:8080/alfresco/service/api/node/workspace/SpacesStore/2df9d676-f173-47bb-8ec1-41fa1186b66d/content.h4555-cmis-so.pdf - - - -2009-06-23T09:40:58.524+02:00 -http://127.0.0.1:8080/alfresco/images/filetypes/pdf.gif - -'; ?> \ No newline at end of file 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 edf0699..adcf420 100644 --- a/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php +++ b/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php @@ -28,45 +28,156 @@ class KT_cmis_atom_service_helper { $id = $cmisEntry['properties']['ObjectId']['value']; $entry = $feed->newEntry(); $feed->newField('id', 'urn:uuid:' . $id, $entry); -// print_r($cmisEntry); + + // TODO dynamic actual creator name + $feedElement = $feed->newField('author'); + $element = $feed->newField('name', 'admin', $feedElement); + $entry->appendChild($feedElement); + + $entry->appendChild($feed->newField('content', $id)); + // TODO proper date + $entry->appendChild($feed->newField('published', self::formatDatestamp())); + $entry->appendChild($feed->newField('updated', self::formatDatestamp())); + +// $entry->appendChild($feed->newField('summary', $cmisEntry['properties']['Name']['value'])); + // links + /* + + + + + */ + // + + /* + + +System +2009-07-13T13:59:20.724+02:00 + +System +folder + +workspace://SpacesStore/b30ea6e5-1e3f-4133-82f4-172bc240a9a2 + */ + + $type = strtolower($cmisEntry['properties']['ObjectTypeId']['value']); + + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel','self')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' . $type . '/' . $cmisEntry['properties']['ObjectId']['value'])); + $entry->appendChild($link); + + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel','edit')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' . $type . '/' . $cmisEntry['properties']['ObjectId']['value'])); + $entry->appendChild($link); + // TODO check parent link is correct, fix if needed + // TODO leave out if at root folder + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'parents')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/folder/' + . $cmisEntry['properties']['ObjectId']['value'] . '/parent')); + $entry->appendChild($link); + + // according to spec this MUST be present, but spec says that links for function which are not supported + // do not need to be present, so unsure for the moment $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-parent')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/folder/' . $cmisEntry['properties']['ParentId']['value'])); + $link->appendChild($feed->newAttr('rel', 'allowableactions')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' . $type . '/' + . $cmisEntry['properties']['ObjectId']['value'] . '/permissions')); $entry->appendChild($link); + // Folder/Document specific links if (strtolower($cmisEntry['properties']['ObjectTypeId']['value']) == 'folder') { - // TODO check parent link is correct, fix if needed - $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-folderparent')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/folder/' . $cmisEntry['properties']['ParentId']['value'])); - $entry->appendChild($link); + // no longer valid, remove... +// // TODO check parent link is correct, fix if needed +// $link = $feed->newElement('link'); +// $link->appendChild($feed->newAttr('rel','folderparent')); +// $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/folder/' . $cmisEntry['properties']['ParentId']['value'])); +// $entry->appendChild($link); + $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-children')); + $link->appendChild($feed->newAttr('rel','children')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' - . strtolower($cmisEntry['properties']['ObjectTypeId']['value']) + . $type . '/' . $cmisEntry['properties']['ObjectId']['value'] . '/children')); $entry->appendChild($link); $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-descendants')); + $link->appendChild($feed->newAttr('rel','descendants')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' - . strtolower($cmisEntry['properties']['ObjectTypeId']['value']) + . $type . '/' . $cmisEntry['properties']['ObjectId']['value'] . '/descendants')); $entry->appendChild($link); } + else if (strtolower($cmisEntry['properties']['ObjectTypeId']['value']) == 'document') + { + // according to spec this MUST be present, but spec says that links for function which are not supported + // do not need to be present, so unsure for the moment +// $link = $feed->newElement('link'); +// $link->appendChild($feed->newAttr('rel', 'allversions')); +// $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' . $type . '/' . $cmisEntry['properties']['ParentId']['value'])); +// $entry->appendChild($link); + + // according to spec this MUST be present, but spec says that links for function which are not supported + // do not need to be present, so unsure for the moment +// $link = $feed->newElement('link'); +// $link->appendChild($feed->newAttr('rel', 'latestversion')); +// $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' . $type . '/' . $cmisEntry['properties']['ParentId']['value'])); +// $entry->appendChild($link); + + // if there is a content stream, this link MUST be present + // not sure yet where it must point... + if (!empty($cmisEntry['properties']['ContentStreamLength']['value'])) + { + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'stream')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' . $type . '/' + . $cmisEntry['properties']['ObjectId']['value'])); + $entry->appendChild($link); + } + + // if the document is checked out and this is NOT the PWC, this link MUST be present +// if (!empty($cmisEntry['properties']['ContentStreamLength']['value'])) +// { +// $link = $feed->newElement('link'); +// $link->appendChild($feed->newAttr('rel', 'stream')); +// $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' . $type . '/' . $cmisEntry['properties']['ParentId']['value'])); +// $entry->appendChild($link); +// } + } + + + // according to spec this MUST be present, but spec says that links for function which are not supported + // do not need to be present, so unsure for the moment + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'relationships')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' . $type . '/' + . $cmisEntry['properties']['ObjectId']['value'] . '/rels')); + $entry->appendChild($link); + $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-type')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/type/' . strtolower($cmisEntry['properties']['ObjectTypeId']['value']))); + $link->appendChild($feed->newAttr('rel','type')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/type/' . $type)); $entry->appendChild($link); + $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-repository')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/servicedocument')); + $link->appendChild($feed->newAttr('rel','repository')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); $entry->appendChild($link); + + // according to spec this MUST be present, but spec says that links for function which are not supported + // do not need to be present, so unsure for the moment - policies are being abandoned, or so I thought... +// $link = $feed->newElement('link'); +// $link->appendChild($feed->newAttr('rel', 'policies')); +// $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/' . $type . '/' . $cmisEntry['properties']['ParentId']['value'])); +// $entry->appendChild($link); // end links $entry->appendChild($feed->newElement('summary', $cmisEntry['properties']['Name']['value'])); @@ -86,6 +197,9 @@ class KT_cmis_atom_service_helper { $objectElement->appendChild($propertiesElement); $entry->appendChild($objectElement); + + // after every entry, append a cmis:terminator tag + $entry->appendChild($feed->newElement('cmis:terminator')); } /** @@ -116,35 +230,67 @@ class KT_cmis_atom_service_helper { //Create a new response feed $feed = new KT_cmis_atom_responseFeed(CMIS_APP_BASE_URI, $typesHeading, null, null, null, 'urn:uuid:' . $typesString); + // TODO set page number correctly - to be done when we support paging the the API + + // author + // TODO generate this dynamically (based on???)\ + $feedElement = $feed->newField('author'); + $element = $feed->newField('name', 'admin', $feedElement); + $feed->appendChild($feedElement); + + // NOTE spec says this link MUST be present but is vague on where it points + // as of 0.61c: + // "The source link relation points to the underlying CMIS Type Definition as Atom Entry" + // so what is the underlying CMIS Type Definition for a collection of base types? + // suspect that it only applies when not listing all types, i.e. a base type is asked for + /* + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel','source')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/type/' . strtolower($type['typeId']))); + $feed->appendChild($link); + */ + + // current time: format = 2009-07-13T14:49:27.659+02:00 + $feed->appendChild($feed->newElement('updated', self::formatDatestamp())); + foreach($types as $type) { $entry = $feed->newEntry(); + + $feedElement = $feed->newField('author'); + $element = $feed->newField('name', 'admin', $feedElement); + $entry->appendChild($feedElement); + $feedElement = $feed->newField('content', $type['typeId']); + $entry->appendChild($feedElement); + + // NOTE should this be strtolower? thought so but maybe not always, Alfresco is not consistent... $feed->newId('urn:uuid:type-' . strtolower($type['typeId']), $entry); + // TODO add parents link when not selecting a base type. + // TODO add children link when type has children + // TODO add descendants link when type has children + // NOTE KnowledgeTree currently only supports base types so these are not important at the present time. + // links +// $link = $feed->newElement('link'); +// $link->appendChild($feed->newAttr('rel','self')); +// $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/type/' . strtolower($type['typeId']))); +// $entry->appendChild($link); + // 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','self')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/type/' . strtolower($type['typeId']))); - $entry->appendChild($link); - $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-type')); + $link->appendChild($feed->newAttr('rel','type')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/type/' . strtolower($type['typeId']))); $entry->appendChild($link); $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-children')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/type/' . strtolower($type['typeId']) . '/children')); - $entry->appendChild($link); - $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-descendants')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/type/' . strtolower($type['typeId']) . '/descendants')); - $entry->appendChild($link); - $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','cmis-repository')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $feed->workspace . '/servicedocument')); + $link->appendChild($feed->newAttr('rel','repository')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); $entry->appendChild($link); $entry->appendChild($feed->newElement('summary', $type['typeId'] . ' Type')); $entry->appendChild($feed->newElement('title', $type['typeId'])); + $entry->appendChild($feed->newElement('updated', self::formatDatestamp())); // main CMIS entry $feedElement = $feed->newElement('cmis:' . strtolower($type['typeId']) . 'Type'); @@ -154,6 +300,9 @@ class KT_cmis_atom_service_helper { } $entry->appendChild($feedElement); + + // after every entry, append a cmis:terminator tag + $entry->appendChild($feed->newElement('cmis:terminator')); } return $feed; @@ -234,7 +383,7 @@ class KT_cmis_atom_service_helper { * @param string $ip * @return object Containing the status_code of the login and session id */ - function login($username, $password, $ip=null){ + static public function login($username, $password, $ip=null){ $kt = self::getKt(); $session = $kt->start_session($username,$password, $ip); @@ -256,7 +405,7 @@ class KT_cmis_atom_service_helper { * @param string $session_id * @return object Containing the status_code of the logout attempt */ - function logout($session_id){ + static public function logout($session_id){ $kt = self::getKt(); $session = $kt->get_active_session($session_id, null); @@ -284,6 +433,13 @@ class KT_cmis_atom_service_helper { return self::$kt; } + // TODO adjust for time zones? + static public function formatDatestamp($time = null) + { + if (is_null($time)) $time = time(); + return date('Y-m-d H:i:s', $time); + } + } ?> diff --git a/webservice/atompub/cmis/index.php b/webservice/atompub/cmis/index.php index e71db98..71d6f5b 100644 --- a/webservice/atompub/cmis/index.php +++ b/webservice/atompub/cmis/index.php @@ -93,22 +93,21 @@ if ($workspace == 'servicedocument') */ // TODO consider a registerServices function which will, dependant on what is requested, register the appropriate services, keep the logic out of the index file $APP->registerService('dms', 'folder', 'KT_cmis_atom_service_folder', 'Root Folder Children Collection', - array(rawurlencode($APP->repositoryInfo['rootFolderId']), 'children'), 'root-children'); + array(rawurlencode($APP->repositoryInfo['rootFolderId']), 'children'), 'rootchildren'); $APP->registerService('dms', 'folder', 'KT_cmis_atom_service_folder', 'Root Folder Children Collection', - array(rawurlencode($APP->repositoryInfo['rootFolderId']), 'descendants'), 'root-descendants'); -$APP->registerService('dms', 'checkedout', 'KT_cmis_atom_service_checkedout', 'Checked Out Document Collection', null, 'checkedout'); -$APP->registerService('dms', 'types', 'KT_cmis_atom_service_types', 'Object Type Collection', null, 'types-children'); -$APP->registerService('dms', 'types', 'KT_cmis_atom_service_types', 'Object Type Collection', null, 'types-descendants'); + array(rawurlencode($APP->repositoryInfo['rootFolderId']), 'descendants'), 'rootdescendants'); +$APP->registerService('dms', 'checkedout', 'KT_cmis_atom_service_checkedout', 'Checked Out Document Collection', null, + 'checkedout', 'application/atom+xml;type=entry'); +$APP->registerService('dms', 'types', 'KT_cmis_atom_service_types', 'Object Type Collection', null, 'typeschildren'); +$APP->registerService('dms', 'types', 'KT_cmis_atom_service_types', 'Object Type Collection', null, 'typesdescendants'); -// FIXME HACK! this should not happen every time, ONLY on a specific request, should NOT appear in service document as this is not definable at that time; -// SHOULD be appearing in types listing feed // NOTE $requestParams is meaningless if not actually requesting this service, so not a good way to register the service really if ($workspace != 'servicedocument') { // should check this per workspace??? - $APP->registerService('dms', 'type', 'KT_cmis_atom_service_type', 'Object Type Collection', explode('/', $requestParams), 'types-descendants'); + $APP->registerService('dms', 'type', 'KT_cmis_atom_service_type', 'Object Type Collection', explode('/', $requestParams), 'typesdescendants'); // FIXME HACK! see above, this one for documents - $APP->registerService('dms', 'document', 'KT_cmis_atom_service_document', 'Object Type Collection', explode('/', $requestParams), 'types-descendants'); + $APP->registerService('dms', 'document', 'KT_cmis_atom_service_document', 'Object Type Collection', explode('/', $requestParams), 'typesdescendants'); } //Execute the current url/header request diff --git a/webservice/classes/atompub/cmis/KT_cmis_atom_responseFeed.inc.php b/webservice/classes/atompub/cmis/KT_cmis_atom_responseFeed.inc.php index 1aa8902..e8006c4 100644 --- a/webservice/classes/atompub/cmis/KT_cmis_atom_responseFeed.inc.php +++ b/webservice/classes/atompub/cmis/KT_cmis_atom_responseFeed.inc.php @@ -24,7 +24,7 @@ class KT_cmis_atom_responseFeed extends KT_atom_responseFeed { // $feed->appendChild($this->newAttr('xmlns','http://www.w3.org/2007/app')); // $feed->appendChild($this->newAttr('xmlns:atom','http://www.w3.org/2005/Atom')); $feed->appendChild($this->newAttr('xmlns','http://www.w3.org/2005/Atom')); - $feed->appendChild($this->newAttr('xmlns:cmis','http://www.cmis.org/2008/05')); + $feed->appendChild($this->newAttr('xmlns:cmis','http://docs.oasis-open.org/ns/cmis/core/200901')); $this->feed = &$feed; if (!is_null($this->id)) @@ -52,22 +52,27 @@ class KT_cmis_atom_responseFeed extends KT_atom_responseFeed { return $id; } -// public function &newField($name = NULL, $value = NULL, &$entry = NULL) -// { -// $append = false; -// -// if(func_num_args() > 3) -// { -// $append = ((func_get_arg(3) === true) ? true : false); -// } -// -// $field = $this->newElement('cmis:' . $name, $value); -// -// if (isset($entry)) $entry->appendChild($field); -// else if ($append) $this->feed->appendChild($field); -// -// return $field; -// } + public function &newField($name = NULL, $value = NULL, &$entry = NULL) + { + $append = false; + + if(func_num_args() > 3) + { + $append = ((func_get_arg(3) === true) ? true : false); + } + + $field = $this->newElement($name, $value); + + if (isset($entry)) $entry->appendChild($field); + else if ($append) $this->feed->appendChild($field); + + return $field; + } + + function appendChild($element) + { + $this->feed->appendChild($element); + } /* public function &newEntry() 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 20218c6..34664bb 100644 --- a/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php +++ b/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php @@ -77,13 +77,13 @@ class KT_cmis_atom_server extends KT_atom_server { } $ws->appendChild($element); - foreach($collection as $serviceName => $serviceInstance) + foreach($collection as $serviceName => $serviceInstance) { foreach($serviceInstance as $instance) { $collectionStr = CMIS_APP_BASE_URI . $workspace . '/' . $serviceName . '/' . (is_array($instance['parameters']) ? implode('/', $instance['parameters']).'/' : ''); - $col = $service->newCollection($collectionStr, $instance['title'], $instance['collectionType'], $ws); + $col = $service->newCollection($collectionStr, $instance['title'], $instance['collectionType'], $instance['accept'], $ws); } } } @@ -92,17 +92,18 @@ class KT_cmis_atom_server extends KT_atom_server { } public function registerService($workspace = NULL, $serviceName = NULL, $serviceClass = NULL, $title = NULL, - $serviceParameters = NULL, $collectionType = NULL) + $serviceParameters = NULL, $collectionType = NULL, $accept = null) { $workspace = strtolower(trim($workspace)); $serviceName = strtolower(trim($serviceName)); $serviceRecord = array( - 'fileName' => $fileName, - 'serviceClass' => $serviceClass, - 'title' => $title, - 'parameters' => $serviceParameters, - 'collectionType' => $collectionType + 'fileName' => $fileName, + 'serviceClass' => $serviceClass, + 'title' => $title, + 'parameters' => $serviceParameters, + 'collectionType' => $collectionType, + 'accept' => $accept ); $this->services[$workspace][$serviceName][] = $serviceRecord; @@ -119,23 +120,25 @@ class KT_cmis_atom_server extends KT_atom_server { return false; } - // TODO we probably want this version in the base class for auth purposes - public function render() - { - ob_end_clean(); - // possible additional headers, e.g. basic auth request - // FIXME this won't work with the service document as no service object exists - if (!is_null($this->serviceObject)) - { - $headers = $this->serviceObject->getHeaders(); - foreach ($headers as $header) - { - header($header); - } - } - header('Content-type: text/xml'); - echo $this->output; - } +// // TODO we probably want this version in the base class for auth purposes +// public function render() +// { +// ob_end_clean(); +//// // possible additional headers, e.g. basic auth request +//// // FIXME this won't work with the service document as no service object exists +//// if (!is_null($this->serviceObject)) +//// { +//// $headers = $this->serviceObject->getHeaders(); +//// foreach ($headers as $header) +//// { +//// header($header); +//// } +//// } +// +// // TODO header: application/atom+xml for service doc? is how Alfresco does it but is it needed? +// header('Content-type: text/xml'); +// echo $this->output; +// } } diff --git a/webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php b/webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php index 432af97..1b28d86 100644 --- a/webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php +++ b/webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php @@ -75,17 +75,20 @@ class KT_cmis_atom_serviceDoc extends KT_atom_serviceDoc { $service = $this->newElement('service'); $service->appendChild($this->newAttr('xmlns', 'http://www.w3.org/2007/app')); $service->appendChild($this->newAttr('xmlns:atom', 'http://www.w3.org/2005/Atom')); - $service->appendChild($this->newAttr('xmlns:cmis', 'http://www.cmis.org/2008/05')); + $service->appendChild($this->newAttr('xmlns:cmis', 'http://docs.oasis-open.org/ns/cmis/core/200901')); $this->service =& $service; $this->DOM->appendChild($this->service); } - public function &newCollection($url = NULL, $title = NULL, $cmisCollectionType = NULL, &$ws = NULL) + public function &newCollection($url = NULL, $title = NULL, $cmisCollectionType = NULL, $accept = null, &$ws = NULL) { $collection=$this->newElement('collection'); $collection->appendChild($this->newAttr('href', $url)); $collection->appendChild($this->newAttr('cmis:collectionType', $cmisCollectionType)); $collection->appendChild($this->newElement('atom:title', $title)); + if (!is_null($accept)) { + $collection->appendChild($this->newElement('accept', $accept)); + } if(isset($ws))$ws->appendChild($collection); return $collection; } diff --git a/webservice/classes/soap/IPPhpDoc.class.php b/webservice/classes/soap/IPPhpDoc.class.php index 35e49b4..6ace12c 100755 --- a/webservice/classes/soap/IPPhpDoc.class.php +++ b/webservice/classes/soap/IPPhpDoc.class.php @@ -1,110 +1,110 @@ -getClasses(); - } - - /** Sets the current class - * @param string The class name - * @return void - */ - public function setClass($class) { - $this->class = new IPReflectionClass($class); - } - /** - * Get all classes - * - * @return IPReflectionClass[] - */ - function getClasses() { - $ar = get_declared_classes(); - foreach($ar as $class){ - $c = new reflectionClass($class); - if($c->isUserDefined()){//add only when class is user-defined - $this->classes[$class] = new IPReflectionClass($class); - } - } - ksort($this->classes); - return $this->classes; - } - /** - * Generates the documentation page with all classes, methods etc. - * @TODO FIXME: use the new template class - * @param string Template file (optional) - * @return string - */ - public function getDocumentation($template="classes/soap/templates/docclass.xsl") { - if(!is_file($template)) - throw new WSException("Could not find the template file: '$template'"); - $xtpl = new IPXSLTemplate($template); - $documentation = Array(); - $documentation['menu'] = Array(); - //loop menu items - $documentation['menu'] = $this->getClasses(); - - if($this->class){ - if($this->class->isUserDefined()) { - $this->class->properties = $this->class->getProperties(false, false); - $this->class->methods = $this->class->getMethods(false, false); - foreach((array)$this->class->methods as $method) { - $method->params = $method->getParameters(); - } - } else { - $documentation['fault'] = "Native class"; - } - $documentation['class'] = $this->class; - } - echo $xtpl->execute($documentation); - } - - - /** - * - * @param $comment String The doccomment - * @param $annotationName String the annotation name - * @param $annotationClass String the annotation class - * @return void - */ - public static function getAnnotation($comment, $annotationName, $annotationClass = null){ - if(!$annotationClass){ - $annotationClass = $annotationName; - } - $start = 0; - if($start = stripos($comment, "@".$annotationName)){ - $obi = new $annotationClass(); - $start = strpos($comment, "(", $start); - $end = strpos($comment, ")", $start); - $propString = substr($comment, $start, ($end-$start) + 1); - $eval = "return Array$propString;"; - $arr = @eval($eval); - if($arr === false) throw new Exception("Error parsing annotation: $propString"); - - foreach ((Array)$arr as $name => $value){ - $obi->$name= $value; - } - return $obi; - } - throw new Exception("Cannot find annotation @$annotationName ($start, $end): {$this->comment} "); - } - -} +getClasses(); + } + + /** Sets the current class + * @param string The class name + * @return void + */ + public function setClass($class) { + $this->class = new IPReflectionClass($class); + } + /** + * Get all classes + * + * @return IPReflectionClass[] + */ + function getClasses() { + $ar = get_declared_classes(); + foreach($ar as $class){ + $c = new reflectionClass($class); + if($c->isUserDefined()){//add only when class is user-defined + $this->classes[$class] = new IPReflectionClass($class); + } + } + ksort($this->classes); + return $this->classes; + } + /** + * Generates the documentation page with all classes, methods etc. + * @TODO FIXME: use the new template class + * @param string Template file (optional) + * @return string + */ + public function getDocumentation($template="classes/soap/templates/docclass.xsl") { + if(!is_file($template)) + throw new WSException("Could not find the template file: '$template'"); + $xtpl = new IPXSLTemplate($template); + $documentation = Array(); + $documentation['menu'] = Array(); + //loop menu items + $documentation['menu'] = $this->getClasses(); + + if($this->class){ + if($this->class->isUserDefined()) { + $this->class->properties = $this->class->getProperties(false, false); + $this->class->methods = $this->class->getMethods(false, false); + foreach((array)$this->class->methods as $method) { + $method->params = $method->getParameters(); + } + } else { + $documentation['fault'] = "Native class"; + } + $documentation['class'] = $this->class; + } + echo $xtpl->execute($documentation); + } + + + /** + * + * @param $comment String The doccomment + * @param $annotationName String the annotation name + * @param $annotationClass String the annotation class + * @return void + */ + public static function getAnnotation($comment, $annotationName, $annotationClass = null){ + if(!$annotationClass){ + $annotationClass = $annotationName; + } + $start = 0; + if($start = stripos($comment, "@".$annotationName)){ + $obi = new $annotationClass(); + $start = strpos($comment, "(", $start); + $end = strpos($comment, ")", $start); + $propString = substr($comment, $start, ($end-$start) + 1); + $eval = "return Array$propString;"; + $arr = @eval($eval); + if($arr === false) throw new Exception("Error parsing annotation: $propString"); + + foreach ((Array)$arr as $name => $value){ + $obi->$name= $value; + } + return $obi; + } + throw new Exception("Cannot find annotation @$annotationName ($start, $end): {$this->comment} "); + } + +} ?> \ No newline at end of file