Commit 05f46cc5dcedd62c57897a4a06ca64d5f5ecc4cd
1 parent
f6bfb3ed
Initial CMIS AtomPub service document built on new code base
Committed by: Paul Barrett Reviewed by: Mark Holtzhausen
Showing
11 changed files
with
870 additions
and
0 deletions
webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | +/** | ||
| 3 | + * AtomPub Service: children | ||
| 4 | + * | ||
| 5 | + * Returns a child tree listing starting at the root document, one level only | ||
| 6 | + * Tree structure obtained by referencing parent id | ||
| 7 | + * | ||
| 8 | + */ | ||
| 9 | +class KT_cmis_atom_service_children extends KT_cmis_atom_service { | ||
| 10 | + public function GET_action(){ | ||
| 11 | +// //Create a new response feed | ||
| 12 | + $feed=new KT_atom_ResponseFeed_GET(KT_APP_BASE_URI); | ||
| 13 | +// | ||
| 14 | +// //Invoke the KtAPI to get detail about the referenced document | ||
| 15 | +// $tree=KT_atom_service_helper::getFullTree(); | ||
| 16 | +// | ||
| 17 | +// //Create the atom response feed | ||
| 18 | +// foreach($tree as $item){ | ||
| 19 | +// $id=$item['id']; | ||
| 20 | +// $entry=$feed->newEntry(); | ||
| 21 | +// $feed->newField('id',$id,$entry); | ||
| 22 | +// foreach($item as $property=>$value){ | ||
| 23 | +// $feed->newField($property,$value,$entry); | ||
| 24 | +// } | ||
| 25 | +// } | ||
| 26 | +// //Expose the responseFeed | ||
| 27 | + $feed->newField('bla','bleh',$feed); | ||
| 28 | + $this->responseFeed=$feed; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + public function DELETE_action(){ | ||
| 32 | +// $feed = new KT_atom_ResponseFeed_DELETE(); | ||
| 33 | +// $this->responseFeed=$feed; | ||
| 34 | + } | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + | ||
| 40 | +/** | ||
| 41 | + * AtomPub Service: folder | ||
| 42 | + * | ||
| 43 | + * Returns detail on a particular folder | ||
| 44 | + * | ||
| 45 | + */ | ||
| 46 | +class KT_atom_service_folder extends KT_atom_service { | ||
| 47 | + public function GET_action(){ | ||
| 48 | + //Create a new response feed | ||
| 49 | + $feed=new KT_atom_responseFeed(KT_APP_BASE_URI); | ||
| 50 | + | ||
| 51 | + //Invoke the KtAPI to get detail about the referenced document | ||
| 52 | + $folderDetail=KT_atom_service_helper::getFolderDetail($this->params[0]?$this->params[0]:1); | ||
| 53 | + | ||
| 54 | + //Create the atom response feed | ||
| 55 | + $entry=$feed->newEntry(); | ||
| 56 | + foreach($folderDetail as $property=>$value){ | ||
| 57 | + $feed->newField($property,$value,$entry); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + //Expose the responseFeed | ||
| 61 | + $this->responseFeed=$feed; | ||
| 62 | + } | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | + | ||
| 66 | + | ||
| 67 | + | ||
| 68 | +/** | ||
| 69 | + * AtomPub Service: document | ||
| 70 | + * | ||
| 71 | + * Returns detail on a particular document | ||
| 72 | + * | ||
| 73 | + */ | ||
| 74 | +class KT_atom_service_document extends KT_atom_service { | ||
| 75 | + public function GET_action(){ | ||
| 76 | + //Create a new response feed | ||
| 77 | + $feed=new KT_atom_responseFeed(KT_APP_BASE_URI); | ||
| 78 | + | ||
| 79 | + //Invoke the KtAPI to get detail about the referenced document | ||
| 80 | + $docDetail=KT_atom_service_helper::getDocumentDetail($this->params[0]); | ||
| 81 | + | ||
| 82 | + //Create the atom response feed | ||
| 83 | + $entry=$feed->newEntry(); | ||
| 84 | + foreach($docDetail['results'] as $property=>$value){ | ||
| 85 | + $feed->newField($property,$value,$entry); | ||
| 86 | + } | ||
| 87 | + //Add a downloaduri field manually | ||
| 88 | + $feed->newField('downloaduri',urlencode(KT_APP_SYSTEM_URI.'/action.php?kt_path_info=ktcore.actions.document.view&fDocumentId='.$docDetail['results']['document_id']),$entry); | ||
| 89 | + | ||
| 90 | + //Expose the responseFeed | ||
| 91 | + $this->responseFeed=$feed; | ||
| 92 | + } | ||
| 93 | +} | ||
| 94 | +?> | ||
| 0 | \ No newline at end of file | 95 | \ No newline at end of file |
webservice/atompub/cmis/NavigationService.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +require_once KT_LIB_DIR . '/api/ktcmis/ktcmis.inc.php'; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * CMIS Service class which hooks into the KnowledgeTree interface | ||
| 7 | + * for processing of CMIS queries and responses via atompub/webservices | ||
| 8 | + */ | ||
| 9 | + | ||
| 10 | +class NavigationService extends KTNavigationService { | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * Get descendents of the specified folder, up to the depth indicated | ||
| 14 | + * | ||
| 15 | + * @param string $repositoryId | ||
| 16 | + * @param string $folderId | ||
| 17 | + * @param boolean $includeAllowableActions | ||
| 18 | + * @param boolean $includeRelationships | ||
| 19 | + * @param string $typeID | ||
| 20 | + * @param int $depth | ||
| 21 | + * @param string $filter | ||
| 22 | + * @return cmisObjectType[] | ||
| 23 | + */ | ||
| 24 | + public function getDescendants($repositoryId, $folderId, $includeAllowableActions, $includeRelationships, | ||
| 25 | + $depth = 1, $typeID = 'Any', $filter = '') | ||
| 26 | + { | ||
| 27 | + $result = parent::getDescendants($repositoryId, $folderId, $includeAllowableActions, | ||
| 28 | + $includeRelationships, $depth, $typeID, $filter); | ||
| 29 | + | ||
| 30 | + if ($result['status_code'] == 0) | ||
| 31 | + { | ||
| 32 | + return $result['results']; | ||
| 33 | + } | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * Get direct children of the specified folder | ||
| 38 | + * | ||
| 39 | + * @param string $repositoryId | ||
| 40 | + * @param string $folderId | ||
| 41 | + * @param boolean $includeAllowableActions | ||
| 42 | + * @param boolean $includeRelationships | ||
| 43 | + * @param string $typeID | ||
| 44 | + * @param string $filter | ||
| 45 | + * @param int $maxItems | ||
| 46 | + * @param int $skipCount | ||
| 47 | + * @return cmisObjectType[] | ||
| 48 | + */ | ||
| 49 | + public function getChildren($repositoryId, $folderId, $includeAllowableActions, $includeRelationships, | ||
| 50 | + $typeID = 'Any', $filter = '', $maxItems = 0, $skipCount = 0) | ||
| 51 | + { | ||
| 52 | + $result = parent::getChildren($repositoryId, $folderId, $includeAllowableActions, $includeRelationships, | ||
| 53 | + $typeID, $filter, $maxItems, $skipCount); | ||
| 54 | + | ||
| 55 | + if ($result['status_code'] == 0) | ||
| 56 | + { | ||
| 57 | + return $result['results']; | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * Gets the parent of the selected folder | ||
| 63 | + * | ||
| 64 | + * @param string $repositoryId | ||
| 65 | + * @param string $folderId | ||
| 66 | + * @param boolean $includeAllowableActions | ||
| 67 | + * @param boolean $includeRelationships | ||
| 68 | + * @param boolean $returnToRoot | ||
| 69 | + * @param string $filter | ||
| 70 | + * @return cmisObjectType[] | ||
| 71 | + */ | ||
| 72 | + public function getFolderParent($repositoryId, $folderId, $includeAllowableActions, $includeRelationships, $returnToRoot, $filter = '') | ||
| 73 | + { | ||
| 74 | + $result = parent::getFolderParent($repositoryId, $folderId, $includeAllowableActions, $includeRelationships, $returnToRoot, $filter); | ||
| 75 | + | ||
| 76 | + if ($result['status_code'] == 0) | ||
| 77 | + { | ||
| 78 | + return $result['results']; | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + /** | ||
| 83 | + * Gets the parents for the selected object | ||
| 84 | + * | ||
| 85 | + * @param string $repositoryId | ||
| 86 | + * @param string $folderId | ||
| 87 | + * @param boolean $includeAllowableActions | ||
| 88 | + * @param boolean $includeRelationships | ||
| 89 | + * @param string $filter | ||
| 90 | + * @return cmisObjectType[] | ||
| 91 | + */ | ||
| 92 | + public function getObjectParents($repositoryId, $objectId, $includeAllowableActions, $includeRelationships, $filter = '') | ||
| 93 | + { | ||
| 94 | + $result = parent::getObjectParents($repositoryId, $objectId, $includeAllowableActions, $includeRelationships, $filter); | ||
| 95 | + | ||
| 96 | + if ($result['status_code'] == 0) | ||
| 97 | + { | ||
| 98 | + return $result['results']; | ||
| 99 | + } | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + /** | ||
| 103 | + * Returns a list of checked out documents from the selected repository | ||
| 104 | + * | ||
| 105 | + * @param string $repositoryId | ||
| 106 | + * @param string $folderId The folder for which checked out docs are requested | ||
| 107 | + * @param string $filter | ||
| 108 | + * @param int $maxItems | ||
| 109 | + * @param int $skipCount | ||
| 110 | + * @return array $checkedout The collection of checked out documents | ||
| 111 | + */ | ||
| 112 | + function getCheckedoutDocs($repositoryId, $folderId = null, $filter = '', $maxItems = 0, $skipCount = 0) | ||
| 113 | + { | ||
| 114 | + $checkedout = parent::getObjectParents($repositoryId, $folderId, $filter, $maxItems, $skipCount); | ||
| 115 | + | ||
| 116 | + if ($result['status_code'] == 0) | ||
| 117 | + { | ||
| 118 | + return $result['results']; | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +?> |
webservice/atompub/cmis/ObjectFeed.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +class CMISObjectFeed { | ||
| 4 | + | ||
| 5 | + /** | ||
| 6 | + * Creates an AtomPub entry for a CMIS entry and adds it to the supplied feed | ||
| 7 | + * | ||
| 8 | + * @param object $feed The feed to which we add the entry | ||
| 9 | + * @param array $cmisEntry The entry data | ||
| 10 | + * @param string $parent The parent folder | ||
| 11 | + */ | ||
| 12 | + static public function createEntry(&$feed, $cmisEntry, $parent, $path) | ||
| 13 | + { | ||
| 14 | + preg_match('/^\/?cmis\/folder\/(.*)\/[^\/]*\/?$/', trim($_SERVER['QUERY_STRING'], '/'), $matches); | ||
| 15 | + $path = $matches[1]; | ||
| 16 | + $parent = preg_replace('/\/[^\/]*$/', '', $path); | ||
| 17 | + | ||
| 18 | + $entry = $feed->newEntry(); | ||
| 19 | + $feed->newId('urn:uuid:' . $cmisEntry['properties']['Name']['value'] . '-' | ||
| 20 | + . strtolower($cmisEntry['properties']['ObjectTypeId']['value']), $entry); | ||
| 21 | + | ||
| 22 | + /* | ||
| 23 | +<link rel="edit" href="http://10.33.4.34:8080/alfresco/service/api/node/workspace/SpacesStore/e98319fa-76e4-478f-8ce8-a3a0fd683e2c"/> | ||
| 24 | +<link rel="cmis-allowableactions" href="http://10.33.4.34:8080/alfresco/service/api/node/workspace/SpacesStore/e98319fa-76e4-478f-8ce8-a3a0fd683e2c/permissions"/> | ||
| 25 | +<link rel="cmis-relationships" href="http://10.33.4.34:8080/alfresco/service/api/node/workspace/SpacesStore/e98319fa-76e4-478f-8ce8-a3a0fd683e2c/associations"/> | ||
| 26 | + */ | ||
| 27 | + | ||
| 28 | + // links | ||
| 29 | +// $link = $feed->newElement('link'); | ||
| 30 | +// $link->appendChild($feed->newAttr('rel','self')); | ||
| 31 | +// $link->appendChild($feed->newAttr('href', CMIS_BASE_URI . strtolower($cmisEntry['properties']['ObjectTypeId']['value']) | ||
| 32 | +// . '/' . $cmisEntry['properties']['ObjectId']['value'])); | ||
| 33 | +// $entry->appendChild($link); | ||
| 34 | + $link = $feed->newElement('link'); | ||
| 35 | + $link->appendChild($feed->newAttr('rel','cmis-parent')); | ||
| 36 | + $link->appendChild($feed->newAttr('href', CMIS_BASE_URI . 'folder/' . $path)); | ||
| 37 | + $entry->appendChild($link); | ||
| 38 | + | ||
| 39 | + if (strtolower($cmisEntry['properties']['ObjectTypeId']['value']) == 'folder') | ||
| 40 | + { | ||
| 41 | + $link = $feed->newElement('link'); | ||
| 42 | + $link->appendChild($feed->newAttr('rel','cmis-folderparent')); | ||
| 43 | + $link->appendChild($feed->newAttr('href', CMIS_BASE_URI . 'folder/' . $path)); | ||
| 44 | + $entry->appendChild($link); | ||
| 45 | + $link = $feed->newElement('link'); | ||
| 46 | + $link->appendChild($feed->newAttr('rel','cmis-children')); | ||
| 47 | + $link->appendChild($feed->newAttr('href', CMIS_BASE_URI | ||
| 48 | + . strtolower($cmisEntry['properties']['ObjectTypeId']['value']) | ||
| 49 | + . '/' . $path . '/' . urlencode($cmisEntry['properties']['Name']['value']) | ||
| 50 | + . '/children')); | ||
| 51 | + $entry->appendChild($link); | ||
| 52 | + $link = $feed->newElement('link'); | ||
| 53 | + $link->appendChild($feed->newAttr('rel','cmis-descendants')); | ||
| 54 | + $link->appendChild($feed->newAttr('href', CMIS_BASE_URI | ||
| 55 | + . strtolower($cmisEntry['properties']['ObjectTypeId']['value']) | ||
| 56 | + . '/' . $path . '/' . urlencode($cmisEntry['properties']['Name']['value']) | ||
| 57 | + . '/descendants')); | ||
| 58 | + $entry->appendChild($link); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + $link = $feed->newElement('link'); | ||
| 62 | + $link->appendChild($feed->newAttr('rel','cmis-type')); | ||
| 63 | + $link->appendChild($feed->newAttr('href', CMIS_BASE_URI . 'type/' . strtolower($cmisEntry['properties']['ObjectTypeId']['value']))); | ||
| 64 | + $entry->appendChild($link); | ||
| 65 | + $link = $feed->newElement('link'); | ||
| 66 | + $link->appendChild($feed->newAttr('rel','cmis-repository')); | ||
| 67 | + $link->appendChild($feed->newAttr('href', CMIS_BASE_URI . 'repository')); | ||
| 68 | + $entry->appendChild($link); | ||
| 69 | + // end links | ||
| 70 | + | ||
| 71 | + $entry->appendChild($feed->newElement('summary', $cmisEntry['properties']['Name']['value'])); | ||
| 72 | + $entry->appendChild($feed->newElement('title', $cmisEntry['properties']['Name']['value'])); | ||
| 73 | + | ||
| 74 | + // main CMIS entry | ||
| 75 | + $objectElement = $feed->newElement('cmis:object'); | ||
| 76 | + $propertiesElement = $feed->newElement('cmis:properties'); | ||
| 77 | + | ||
| 78 | + foreach($cmisEntry['properties'] as $propertyName => $property) | ||
| 79 | + { | ||
| 80 | + $propElement = $feed->newElement('cmis:' . $property['type']); | ||
| 81 | + $propElement->appendChild($feed->newAttr('cmis:name', $propertyName)); | ||
| 82 | + $feed->newField('value', CMISUtil::boolToString($property['value']), $propElement); | ||
| 83 | + $propertiesElement->appendChild($propElement); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + $objectElement->appendChild($propertiesElement); | ||
| 87 | + $entry->appendChild($objectElement); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +?> |
webservice/atompub/cmis/ObjectService.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +require_once KT_LIB_DIR . '/api/ktcmis/ktcmis.inc.php'; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * CMIS Service class which hooks into the KnowledgeTree interface | ||
| 7 | + * for processing of CMIS queries and responses via atompub/webservices | ||
| 8 | + */ | ||
| 9 | + | ||
| 10 | +class ObjectService extends KTObjectService { | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * Fetches the properties for the specified object | ||
| 14 | + * | ||
| 15 | + * @param string $repositoryId | ||
| 16 | + * @param string $objectId | ||
| 17 | + * @param boolean $includeAllowableActions | ||
| 18 | + * @param boolean $includeRelationships | ||
| 19 | + * @param boolean $returnVersion | ||
| 20 | + * @param string $filter | ||
| 21 | + * @return object CMIS object properties | ||
| 22 | + */ | ||
| 23 | + public function getProperties($repositoryId, $objectId, $includeAllowableActions, $includeRelationships, | ||
| 24 | + $returnVersion = false, $filter = '') | ||
| 25 | + { | ||
| 26 | + $result = parent::getProperties($repositoryId, $objectId, $includeAllowableActions, | ||
| 27 | + $returnVersion, $filter); | ||
| 28 | + | ||
| 29 | + if ($result['status_code'] == 0) | ||
| 30 | + { | ||
| 31 | + return $result['results']; | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +?> |
webservice/atompub/cmis/RepositoryService.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * CMIS Service class which hooks into the KnowledgeTree interface | ||
| 5 | + * for processing of CMIS queries and responses via atompub/webservices | ||
| 6 | + */ | ||
| 7 | + | ||
| 8 | +require_once KT_LIB_DIR . '/api/ktcmis/ktcmis.inc.php'; | ||
| 9 | + | ||
| 10 | +class RepositoryService extends KTRepositoryService { | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * Fetches a list of available repositories | ||
| 14 | + * | ||
| 15 | + * @return cmisRepositoryEntryType[] | ||
| 16 | + */ | ||
| 17 | + public function getRepositories() | ||
| 18 | + { | ||
| 19 | + $result = parent::getRepositories(); | ||
| 20 | + | ||
| 21 | + if ($result['status_code'] == 0) | ||
| 22 | + { | ||
| 23 | + return $result['results']; | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * Fetches information about the selected repository | ||
| 29 | + * | ||
| 30 | + * @param string $repositoryId | ||
| 31 | + * @return cmisRepositoryInfoType | ||
| 32 | + */ | ||
| 33 | + public function getRepositoryInfo($repositoryId) | ||
| 34 | + { | ||
| 35 | + $result = parent::getRepositoryInfo($repositoryId); | ||
| 36 | + | ||
| 37 | + if ($result['status_code'] == 0) | ||
| 38 | + { | ||
| 39 | + return $result['results']; | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * Fetch the list of supported object types for the selected repository | ||
| 45 | + * | ||
| 46 | + * @param string $repositoryId The ID of the repository for which object types must be returned | ||
| 47 | + * @param string $typeId The type to return, ALL if not set | ||
| 48 | + * @param boolean $returnPropertyDefinitions Return property definitions as well if TRUE | ||
| 49 | + * @param int $maxItems The maximum number of items to return | ||
| 50 | + * @param int $skipCount The number of items to skip before starting to return results | ||
| 51 | + * @param boolean $hasMoreItems TRUE if there are more items to return than were requested | ||
| 52 | + * @return cmisTypeDefinitionType[] | ||
| 53 | + */ | ||
| 54 | + public function getTypes($repositoryId, $typeId = '', $returnPropertyDefinitions = false, | ||
| 55 | + $maxItems = 0, $skipCount = 0, &$hasMoreItems = false) | ||
| 56 | + { | ||
| 57 | + $result = parent::getTypes($repositoryId, $typeId, $returnPropertyDefinitions, | ||
| 58 | + $maxItems, $skipCount, $hasMoreItems); | ||
| 59 | + | ||
| 60 | + if ($result['status_code'] == 0) | ||
| 61 | + { | ||
| 62 | + return $result['results']; | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + /** | ||
| 67 | + * Fetch the object type definition for the requested type | ||
| 68 | + * | ||
| 69 | + * @param string $repositoryId | ||
| 70 | + * @param string $typeId | ||
| 71 | + * @return cmisTypeDefinitionType | ||
| 72 | + */ | ||
| 73 | + public function getTypeDefinition($repositoryId, $typeId) | ||
| 74 | + { | ||
| 75 | + $result = parent::getTypeDefinition($repositoryId, $typeId); | ||
| 76 | + | ||
| 77 | + if ($result['status_code'] == 0) | ||
| 78 | + { | ||
| 79 | + return $result['results']; | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +?> |
webservice/atompub/cmis/index.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * Framework for an Atom Publication Protocol Service | ||
| 5 | + * | ||
| 6 | + * KnowledgeTree Community Edition | ||
| 7 | + * Document Management Made Simple | ||
| 8 | + * Copyright (C) 2008, 2009 KnowledgeTree Inc. | ||
| 9 | + * Portions copyright The Jam Warehouse Software (Pty) Limited | ||
| 10 | + * | ||
| 11 | + * This program is free software; you can redistribute it and/or modify it under | ||
| 12 | + * the terms of the GNU General Public License version 3 as published by the | ||
| 13 | + * Free Software Foundation. | ||
| 14 | + * | ||
| 15 | + * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 16 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 17 | + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| 18 | + * details. | ||
| 19 | + * | ||
| 20 | + * You should have received a copy of the GNU General Public License | ||
| 21 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 22 | + * | ||
| 23 | + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, | ||
| 24 | + * California 94120-7775, or email info@knowledgetree.com. | ||
| 25 | + * | ||
| 26 | + * The interactive user interfaces in modified source and object code versions | ||
| 27 | + * of this program must display Appropriate Legal Notices, as required under | ||
| 28 | + * Section 5 of the GNU General Public License version 3. | ||
| 29 | + * | ||
| 30 | + * In accordance with Section 7(b) of the GNU General Public License version 3, | ||
| 31 | + * these Appropriate Legal Notices must retain the display of the "Powered by | ||
| 32 | + * KnowledgeTree" logo and retain the original copyright notice. If the display of the | ||
| 33 | + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices | ||
| 34 | + * must display the words "Powered by KnowledgeTree" and retain the original | ||
| 35 | + * copyright notice. | ||
| 36 | + * Contributor( s): | ||
| 37 | + * Mark Holtzhausen <mark@knowledgetree.com> | ||
| 38 | + * Paul Barrett <paul@knowledgetree.com> | ||
| 39 | + * | ||
| 40 | + */ | ||
| 41 | + | ||
| 42 | +require_once('../../../config/dmsDefaults.php'); | ||
| 43 | +//include_once('lib/cmis/KTCMISAPPFeed.inc.php'); | ||
| 44 | + | ||
| 45 | +define('KT_APP_BASE_URI', "http://".$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/?/'); | ||
| 46 | +define('KT_APP_SYSTEM_URI', "http://".$_SERVER['HTTP_HOST']); | ||
| 47 | +define('KT_ATOM_LIB_FOLDER', '../../classes/atompub/'); | ||
| 48 | +//echo KT_ATOM_LIB_FOLDER.'KT_atom_server.inc.php';exit; | ||
| 49 | +define ('CMIS_APP_BASE_URI', trim(KT_APP_BASE_URI, '/')); | ||
| 50 | +define('CMIS_APP_SYSTEM_URI', KT_APP_SYSTEM_URI); | ||
| 51 | +define ('CMIS_ATOM_LIB_FOLDER', trim(KT_ATOM_LIB_FOLDER, '/') . '/cmis/'); | ||
| 52 | + | ||
| 53 | +// fetch username and password for auth; note that this apparently only works when PHP is run as an apache module | ||
| 54 | +// TODO method to fetch username and password when running PHP as CGI, if possible | ||
| 55 | +// HTTP Basic Auth: | ||
| 56 | +$username = $_SERVER['PHP_AUTH_USER']; | ||
| 57 | +$password = $_SERVER['PHP_AUTH_PW']; | ||
| 58 | + | ||
| 59 | +/** | ||
| 60 | + * Includes | ||
| 61 | + */ | ||
| 62 | +include_once(CMIS_ATOM_LIB_FOLDER.'KT_cmis_atom_server.inc.php'); | ||
| 63 | +include_once(CMIS_ATOM_LIB_FOLDER.'KT_cmis_atom_baseDoc.inc.php'); | ||
| 64 | +include_once(CMIS_ATOM_LIB_FOLDER.'KT_cmis_atom_responseFeed.inc.php'); //Containing the response feed class allowing easy atom feed generation | ||
| 65 | +include_once(CMIS_ATOM_LIB_FOLDER.'KT_cmis_atom_serviceDoc.inc.php'); //Containing the servicedoc class allowing easy ServiceDocument generation | ||
| 66 | +include_once(CMIS_ATOM_LIB_FOLDER.'KT_cmis_atom_service.inc.php'); //Containing the servicedoc class allowing easy ServiceDocument generation | ||
| 67 | + | ||
| 68 | +include_once('KT_cmis_atom_server.services.inc.php'); | ||
| 69 | + | ||
| 70 | + | ||
| 71 | +//Start the AtomPubProtocol Routing Engine | ||
| 72 | +$APP = new KT_cmis_atom_server(); | ||
| 73 | +$APP->initServiceDocument(); | ||
| 74 | +$APP->addWorkspaceTag('dms','atom:title','KnowledgeTree DMS'); | ||
| 75 | +/** | ||
| 76 | + * Register Services | ||
| 77 | + * | ||
| 78 | + * Registered services are classes extended from KT_atom_service | ||
| 79 | + * The registration process takes the following parameters | ||
| 80 | + * Workspace :The workspace within which the service collection will be grouped | ||
| 81 | + * ServiceName :This is the name by which the service/collection is exposed | ||
| 82 | + * ServiceClass :This is the class name of the class to be instantiated when this service is accessed | ||
| 83 | + * Title :This is the title given to the service/collection in the servicedocument | ||
| 84 | + * http://ktatompub/index.php?/service/param1/param2 | ||
| 85 | + * http://ktatompub/?/folder/children/whatfoldertolookat | ||
| 86 | + * http://ktatompub/{folder/folder2/folder3/}service/param1/param2 | ||
| 87 | +*/ | ||
| 88 | +$APP->registerService('dms', 'folder', 'KT_cmis_atom_service_folder', 'Root Folder Children Collection', array($APP->repositoryInfo['rootFolderId'], 'children'), 'root-children'); | ||
| 89 | +$APP->registerService('dms', 'folder', 'KT_cmis_atom_service_folder', 'Root Folder Children Collection', array($APP->repositoryInfo['rootFolderId'], 'descendants'), 'root-descendants'); | ||
| 90 | +$APP->registerService('dms', 'checkedout', 'KT_cmis_atom_service_checkedout', 'Checked Out Document Collection', null, 'checkedout'); | ||
| 91 | +$APP->registerService('dms', 'types', 'KT_cmis_atom_service_types', 'Object Type Collection', null, 'types-children'); | ||
| 92 | +$APP->registerService('dms', 'types', 'KT_cmis_atom_service_types', 'Object Type Collection', null, 'types-descendants'); | ||
| 93 | + | ||
| 94 | +//Execute the current url/header request | ||
| 95 | +$APP->execute(); | ||
| 96 | + | ||
| 97 | +//echo '<pre>'.print_r($APP,true).'</pre>'; | ||
| 98 | + | ||
| 99 | +//Render the resulting feed response | ||
| 100 | +$APP->render(); | ||
| 101 | + | ||
| 102 | +// TODO response if failed auth, need generic response which can be used by all code | ||
| 103 | + | ||
| 104 | +//$arg = (isset($query[1]) ? $query[1] : ''); | ||
| 105 | +// | ||
| 106 | +//switch($arg) | ||
| 107 | +//{ | ||
| 108 | +// case 'checkedout': | ||
| 109 | +// include('services/cmis/checkedout.inc.php'); | ||
| 110 | +// break; | ||
| 111 | +// case 'document': | ||
| 112 | +// include('services/cmis/document.inc.php'); | ||
| 113 | +// break; | ||
| 114 | +// case 'folder': | ||
| 115 | +// include('services/cmis/folder.inc.php'); | ||
| 116 | +// break; | ||
| 117 | +// case 'type': | ||
| 118 | +// case 'types': | ||
| 119 | +// include('services/cmis/types.inc.php'); | ||
| 120 | +// break; | ||
| 121 | +// case 'repository': | ||
| 122 | +// default: | ||
| 123 | +// include('services/cmis/servicedocument.inc.php'); | ||
| 124 | +// break; | ||
| 125 | +//} | ||
| 126 | + | ||
| 127 | +?> |
webservice/classes/atompub/cmis/KT_cmis_atom_baseDoc.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | +/** | ||
| 3 | + * Framework for an Atom Publication Protocol Service | ||
| 4 | + * | ||
| 5 | + * KnowledgeTree Community Edition | ||
| 6 | + * Document Management Made Simple | ||
| 7 | + * Copyright (C) 2008, 2009 KnowledgeTree Inc. | ||
| 8 | + * Portions copyright The Jam Warehouse Software (Pty) Limited | ||
| 9 | + * | ||
| 10 | + * This program is free software; you can redistribute it and/or modify it under | ||
| 11 | + * the terms of the GNU General Public License version 3 as published by the | ||
| 12 | + * Free Software Foundation. | ||
| 13 | + * | ||
| 14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 16 | + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| 17 | + * details. | ||
| 18 | + * | ||
| 19 | + * You should have received a copy of the GNU General Public License | ||
| 20 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | + * | ||
| 22 | + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, | ||
| 23 | + * California 94120-7775, or email info@knowledgetree.com. | ||
| 24 | + * | ||
| 25 | + * The interactive user interfaces in modified source and object code versions | ||
| 26 | + * of this program must display Appropriate Legal Notices, as required under | ||
| 27 | + * Section 5 of the GNU General Public License version 3. | ||
| 28 | + * | ||
| 29 | + * In accordance with Section 7(b) of the GNU General Public License version 3, | ||
| 30 | + * these Appropriate Legal Notices must retain the display of the "Powered by | ||
| 31 | + * KnowledgeTree" logo and retain the original copyright notice. If the display of the | ||
| 32 | + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices | ||
| 33 | + * must display the words "Powered by KnowledgeTree" and retain the original | ||
| 34 | + * copyright notice. | ||
| 35 | + * Contributor( s): | ||
| 36 | + * Mark Holtzhausen <mark@knowledgetree.com> | ||
| 37 | + * Paul Barrett <paul@knowledgetree.com> | ||
| 38 | + * | ||
| 39 | + */ | ||
| 40 | + | ||
| 41 | +/** | ||
| 42 | + * Includes | ||
| 43 | + */ | ||
| 44 | + | ||
| 45 | +include_once(KT_ATOM_LIB_FOLDER.'KT_atom_baseDoc.inc.php'); //Containing the parent class allowing easy XML manipulation | ||
| 46 | + | ||
| 47 | +class KT_cmis_atom_baseDoc extends KT_atom_baseDoc { | ||
| 48 | + | ||
| 49 | + // override and extend as needed | ||
| 50 | + | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +?> | ||
| 0 | \ No newline at end of file | 54 | \ No newline at end of file |
webservice/classes/atompub/cmis/KT_cmis_atom_responseFeed.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +include_once(KT_ATOM_LIB_FOLDER.'KT_atom_responseFeed.inc.php'); | ||
| 4 | + | ||
| 5 | +class KT_cmis_atom_responseFeed extends KT_atom_responseFeed { | ||
| 6 | + | ||
| 7 | + // override and extend as needed | ||
| 8 | + | ||
| 9 | +} | ||
| 10 | + | ||
| 11 | +class KT_cmis_atom_ResponseFeed_GET extends KT_cmis_atom_responseFeed{} | ||
| 12 | +class KT_cmis_atom_ResponseFeed_PUT extends KT_cmis_atom_responseFeed{} | ||
| 13 | +class KT_cmis_atom_ResponseFeed_POST extends KT_cmis_atom_responseFeed{} | ||
| 14 | +class KT_cmis_atom_ResponseFeed_DELETE extends KT_cmis_atom_responseFeed{} | ||
| 15 | + | ||
| 16 | +?> | ||
| 0 | \ No newline at end of file | 17 | \ No newline at end of file |
webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +include_once(KT_ATOM_LIB_FOLDER . 'KT_atom_server.inc.php'); | ||
| 4 | +include_once('RepositoryService.inc.php'); | ||
| 5 | + | ||
| 6 | +class KT_cmis_atom_server extends KT_atom_server { | ||
| 7 | + | ||
| 8 | + // override and extend as needed | ||
| 9 | + public $repositoryInfo; | ||
| 10 | + | ||
| 11 | + public function initServiceDocument() | ||
| 12 | + { | ||
| 13 | + $queryArray = split('/', trim($_SERVER['QUERY_STRING'], '/')); | ||
| 14 | + $workspace = strtolower(trim($queryArray[0])); | ||
| 15 | + if ($workspace == 'servicedocument') | ||
| 16 | + { | ||
| 17 | + include 'services/cmis/RepositoryService.inc.php'; | ||
| 18 | + $RepositoryService = new RepositoryService(); | ||
| 19 | + | ||
| 20 | + // fetch data for response | ||
| 21 | + $repositories = $RepositoryService->getRepositories(); | ||
| 22 | + // fetch for default first repo; NOTE that this will probably have to change at some point, quick and dirty for now | ||
| 23 | + $this->repositoryInfo = $RepositoryService->getRepositoryInfo($repositories[0]['repositoryId']); | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + public function serviceDocument() | ||
| 28 | + { | ||
| 29 | + $service = new KT_cmis_atom_serviceDoc(KT_APP_BASE_URI); | ||
| 30 | + | ||
| 31 | + foreach($this->services as $workspace => $collection) | ||
| 32 | + { | ||
| 33 | + //Creating the Default Workspace for use with standard atomPub Clients | ||
| 34 | + $ws = $service->newWorkspace(); | ||
| 35 | + | ||
| 36 | + $hadDetail=false; | ||
| 37 | + if(isset($this->workspaceDetail[$workspace]))if(is_array($this->workspaceDetail[$workspace])){ | ||
| 38 | + foreach ($this->workspaceDetail[$workspace] as $wsTag=>$wsValue){ | ||
| 39 | + $ws->appendChild($service->newElement($wsTag,$wsValue)); | ||
| 40 | + $hadDetail=true; | ||
| 41 | + } | ||
| 42 | + } | ||
| 43 | + if(!$hadDetail){ | ||
| 44 | + $ws->appendChild($service->newElement('atom:title',$workspace)); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + $ws->appendChild($service->newAttr('cmis:repositoryRelationship', $this->repositoryInfo['repositoryRelationship'])); | ||
| 48 | + | ||
| 49 | + // repository information | ||
| 50 | + $element = $service->newElement('cmis:repositoryInfo'); | ||
| 51 | + foreach($this->repositoryInfo as $key => $repoData) | ||
| 52 | + { | ||
| 53 | + if ($key == 'rootFolderId') | ||
| 54 | + { | ||
| 55 | + $repoData = CMIS_APP_BASE_URI . 'folder/' . $repoData; | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + if (!is_array($repoData)) | ||
| 59 | + { | ||
| 60 | + $element->appendChild($service->newElement('cmis:' . $key, $repoData)); | ||
| 61 | + } | ||
| 62 | + else | ||
| 63 | + { | ||
| 64 | + $elementSub = $service->newElement('cmis:' . $key); | ||
| 65 | + foreach($repoData as $key2 => $data) | ||
| 66 | + { | ||
| 67 | + $elementSub->appendChild($service->newElement('cmis:' . $key2, CMISUtil::boolToString($data))); | ||
| 68 | + } | ||
| 69 | + $element->appendChild($elementSub); | ||
| 70 | + } | ||
| 71 | + } | ||
| 72 | + $ws->appendChild($element); | ||
| 73 | + | ||
| 74 | + foreach($collection as $serviceName => $serviceInstance) | ||
| 75 | + { | ||
| 76 | + foreach($serviceInstance as $instance) | ||
| 77 | + { | ||
| 78 | + $collectionStr = CMIS_APP_BASE_URI . $workspace . '/' . $serviceName . '/' | ||
| 79 | + . (is_array($instance['parameters']) ? implode('/', $instance['parameters']).'/' : ''); | ||
| 80 | + $col = $service->newCollection($collectionStr, $instance['title'], $instance['collectionType'], $ws); | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + $this->output = $service->getAPPdoc(); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + public function registerService($workspace = NULL, $serviceName = NULL, $serviceClass = NULL, $title = NULL, | ||
| 89 | + $serviceParameters = NULL, $collectionType = NULL) | ||
| 90 | + { | ||
| 91 | + $workspace = strtolower(trim($workspace)); | ||
| 92 | + $serviceName = strtolower(trim($serviceName)); | ||
| 93 | + | ||
| 94 | + $serviceRecord = array( | ||
| 95 | + 'fileName' => $fileName, | ||
| 96 | + 'serviceClass' => $serviceClass, | ||
| 97 | + 'title' => $title, | ||
| 98 | + 'parameters' => $serviceParameters, | ||
| 99 | + 'collectionType' => $collectionType | ||
| 100 | + ); | ||
| 101 | + | ||
| 102 | + $this->services[$workspace][$serviceName][] = $serviceRecord; | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | +?> | ||
| 0 | \ No newline at end of file | 108 | \ No newline at end of file |
webservice/classes/atompub/cmis/KT_cmis_atom_service.inc.php
0 → 100644
webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | +/** | ||
| 3 | + * Framework for an Atom Publication Protocol Service | ||
| 4 | + * | ||
| 5 | + * KnowledgeTree Community Edition | ||
| 6 | + * Document Management Made Simple | ||
| 7 | + * Copyright (C) 2008, 2009 KnowledgeTree Inc. | ||
| 8 | + * Portions copyright The Jam Warehouse Software (Pty) Limited | ||
| 9 | + * | ||
| 10 | + * This program is free software; you can redistribute it and/or modify it under | ||
| 11 | + * the terms of the GNU General Public License version 3 as published by the | ||
| 12 | + * Free Software Foundation. | ||
| 13 | + * | ||
| 14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 16 | + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| 17 | + * details. | ||
| 18 | + * | ||
| 19 | + * You should have received a copy of the GNU General Public License | ||
| 20 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | + * | ||
| 22 | + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, | ||
| 23 | + * California 94120-7775, or email info@knowledgetree.com. | ||
| 24 | + * | ||
| 25 | + * The interactive user interfaces in modified source and object code versions | ||
| 26 | + * of this program must display Appropriate Legal Notices, as required under | ||
| 27 | + * Section 5 of the GNU General Public License version 3. | ||
| 28 | + * | ||
| 29 | + * In accordance with Section 7(b) of the GNU General Public License version 3, | ||
| 30 | + * these Appropriate Legal Notices must retain the display of the "Powered by | ||
| 31 | + * KnowledgeTree" logo and retain the original copyright notice. If the display of the | ||
| 32 | + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices | ||
| 33 | + * must display the words "Powered by KnowledgeTree" and retain the original | ||
| 34 | + * copyright notice. | ||
| 35 | + * Contributor( s): | ||
| 36 | + * Mark Holtzhausen <mark@knowledgetree.com> | ||
| 37 | + * Paul Barrett <paul@knowledgetree.com> | ||
| 38 | + * | ||
| 39 | + */ | ||
| 40 | + | ||
| 41 | + | ||
| 42 | +/** | ||
| 43 | + * Includes | ||
| 44 | + */ | ||
| 45 | +include_once(KT_ATOM_LIB_FOLDER.'KT_atom_serviceDoc.inc.php'); | ||
| 46 | +//include_once('KT_atom_baseDoc.inc.php'); | ||
| 47 | + | ||
| 48 | +class KT_cmis_atom_serviceDoc extends KT_atom_serviceDoc { | ||
| 49 | + | ||
| 50 | + // override and extend as needed | ||
| 51 | + | ||
| 52 | + public $repositoryInfo = array(); | ||
| 53 | + | ||
| 54 | + public function __construct($baseURI = NULL) | ||
| 55 | + { | ||
| 56 | + parent::__construct(); | ||
| 57 | + | ||
| 58 | + // get repositoryInfo | ||
| 59 | + // NOTE currently we only support one repository, which will be the first one found in the repositories.xml config | ||
| 60 | + // TODO multiple repositories as individual workspaces | ||
| 61 | + | ||
| 62 | + include 'services/cmis/RepositoryService.inc.php'; | ||
| 63 | + $RepositoryService = new RepositoryService(); | ||
| 64 | + | ||
| 65 | + // fetch data for response | ||
| 66 | + $repositories = $RepositoryService->getRepositories(); | ||
| 67 | + // fetch for default first repo; NOTE that this will probably have to change at some point, quick and dirty for now | ||
| 68 | + $this->repositoryInfo = $RepositoryService->getRepositoryInfo($repositories[0]['repositoryId']); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + protected function constructServiceDocumentHeaders() | ||
| 72 | + { | ||
| 73 | + $service = $this->newElement('service'); | ||
| 74 | + $service->appendChild($this->newAttr('xmlns', 'http://www.w3.org/2007/app')); | ||
| 75 | + $service->appendChild($this->newAttr('xmlns:atom', 'http://www.w3.org/2005/Atom')); | ||
| 76 | + $service->appendChild($this->newAttr('xmlns:cmis', 'http://www.cmis.org/2008/05')); | ||
| 77 | + $this->service =& $service; | ||
| 78 | + $this->DOM->appendChild($this->service); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + public function &newCollection($url = NULL, $title = NULL, $cmisCollectionType = NULL, &$ws = NULL) | ||
| 82 | + { | ||
| 83 | + $collection=$this->newElement('collection'); | ||
| 84 | + $collection->appendChild($this->newAttr('href', $url)); | ||
| 85 | + $collection->appendChild($this->newAttr('cmis:collectionType', $cmisCollectionType)); | ||
| 86 | + $collection->appendChild($this->newElement('atom:title', $title)); | ||
| 87 | + if(isset($ws))$ws->appendChild($collection); | ||
| 88 | + return $collection; | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +/** | ||
| 94 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 95 | +<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom"> | ||
| 96 | + <workspace> | ||
| 97 | + <atom:title>Main Site</atom:title> | ||
| 98 | + <collection href="http://example.org/blog/main" > | ||
| 99 | + <atom:title>My Blog Entries</atom:title> | ||
| 100 | + <categories href="http://example.com/cats/forMain.cats" /> | ||
| 101 | + </collection> | ||
| 102 | + <collection href="http://example.org/blog/pic" > | ||
| 103 | + <atom:title>Pictures</atom:title> | ||
| 104 | + <accept>image/png</accept> | ||
| 105 | + <accept>image/jpeg</accept> | ||
| 106 | + <accept>image/gif</accept> | ||
| 107 | + </collection> | ||
| 108 | + </workspace> | ||
| 109 | + <workspace> | ||
| 110 | + <atom:title>Sidebar Blog</atom:title> | ||
| 111 | + <collection href="http://example.org/sidebar/list" > | ||
| 112 | + <atom:title>Remaindered Links</atom:title> | ||
| 113 | + <accept>application/atom+xml;type=entry</accept> | ||
| 114 | + <categories fixed="yes"> | ||
| 115 | + <atom:category scheme="http://example.org/extra-cats/" term="joke" /> | ||
| 116 | + <atom:category scheme="http://example.org/extra-cats/" term="serious" /> | ||
| 117 | + </categories> | ||
| 118 | + </collection> | ||
| 119 | + </workspace> | ||
| 120 | +</service> | ||
| 121 | + | ||
| 122 | + */ | ||
| 123 | + | ||
| 124 | + | ||
| 125 | +?> | ||
| 0 | \ No newline at end of file | 126 | \ No newline at end of file |