From 3eb55c2b592a0bf92792ab44433df774f4d0e725 Mon Sep 17 00:00:00 2001 From: Paul Barrett Date: Tue, 14 Jul 2009 14:59:20 +0200 Subject: [PATCH] Partial fix for CMISSpaces browsing - now getting right hand side content including display of documents but tree is broken on left hand side. Committing to allow me to go back to code which worked for the lest hand side and see what is different --- lib/api/ktcmis/classes/CMISPropertyCollection.inc.php | 9 +++++++-- lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php | 2 +- lib/api/ktcmis/objecttypes/CMISFolderObject.inc.php | 2 +- lib/api/ktcmis/util/CMISUtil.inc.php | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php | 507 ++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- webservice/atompub/cmis/index.php | 17 ++++++++--------- webservice/classes/atompub/cmis/KT_cmis_atom_responseFeed.inc.php | 39 ++++++++++++++++++++++----------------- webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php | 53 ++++++++++++++++++++++++++++------------------------- webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php | 7 +++++-- webservice/classes/soap/IPPhpDoc.class.php | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------- 11 files changed, 475 insertions(+), 669 deletions(-) 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 -- libgit2 0.21.4