Commit 0c5cbc2cd810cc43a9b478b2cd267fbb9f5f4814

Authored by Kevin G Fourie
2 parents 9e134cb5 422fc029

Merge branch 'master' of github.com:ktgit/knowledgetree

lib/api/ktcmis/exceptions/ContentAlreadyExistsException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class ContentAlreadyExistsException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
lib/api/ktcmis/exceptions/RuntimeException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class RuntimeException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
lib/api/ktcmis/exceptions/StreamNotSupportedException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class StreamNotSupportedException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
lib/api/ktcmis/exceptions/UpdateConflictException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class UpdateConflictException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
lib/api/ktcmis/exceptions/VersioningException.inc.php 0 → 100644
  1 +<?php
  2 +
  3 +class VersioningException extends Exception {
  4 +
  5 +}
  6 +
  7 +?>
lib/api/ktcmis/ktcmis.inc.php
@@ -468,7 +468,42 @@ class KTObjectService extends KTCMISBase { @@ -468,7 +468,42 @@ class KTObjectService extends KTCMISBase {
468 } 468 }
469 469
470 /** 470 /**
471 - * Function to create a folder 471 + * Creates a new document within the repository
  472 + *
  473 + * @param string $repositoryId The repository to which the document must be added
  474 + * @param string $typeId Object Type id for the document object being created
  475 + * @param array $properties Array of properties which must be applied to the created document object
  476 + * @param string $folderId The id of the folder which will be the parent of the created document object
  477 + * This parameter is optional IF unfilingCapability is supported
  478 + * @param contentStream $contentStream optional content stream data
  479 + * @param string $versioningState optional version state value: checkedout/major/minor
  480 + * @return string $objectId The id of the created folder object
  481 + */
  482 + function createDocument($repositoryId, $typeId, $properties, $folderId = null,
  483 + $contentStream = null, $versioningState = 'Major')
  484 + {
  485 + $objectId = null;
  486 +
  487 + try {
  488 + $objectId = $this->ObjectService->createDocument($repositoryId, $typeId, $properties, $folderId,
  489 + $contentStream, $versioningState);
  490 + }
  491 + catch (Exception $e)
  492 + {
  493 + return array(
  494 + "status_code" => 1,
  495 + "message" => $e->getMessage()
  496 + );
  497 + }
  498 +
  499 + return array(
  500 + 'status_code' => 0,
  501 + 'results' => $objectId
  502 + );
  503 + }
  504 +
  505 + /**
  506 + * Creates a new folder within the repository
472 * 507 *
473 * @param string $repositoryId The repository to which the folder must be added 508 * @param string $repositoryId The repository to which the folder must be added
474 * @param string $typeId Object Type id for the folder object being created 509 * @param string $typeId Object Type id for the folder object being created
@@ -497,6 +532,41 @@ class KTObjectService extends KTCMISBase { @@ -497,6 +532,41 @@ class KTObjectService extends KTCMISBase {
497 ); 532 );
498 } 533 }
499 534
  535 + /**
  536 + * Sets the content stream data for an existing document
  537 + *
  538 + * if $overwriteFlag = TRUE, the new content stream is applied whether or not the document has an existing content stream
  539 + * if $overwriteFlag = FALSE, the new content stream is applied only if the document does not have an existing content stream
  540 + *
  541 + * NOTE A Repository MAY automatically create new Document versions as part of this service method.
  542 + * Therefore, the documentId output NEED NOT be identical to the documentId input.
  543 + *
  544 + * @param string $repositoryId
  545 + * @param string $documentId
  546 + * @param boolean $overwriteFlag
  547 + * @param string $contentStream
  548 + * @param string $changeToken
  549 + * @return string $documentId
  550 + */
  551 + function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null)
  552 + {
  553 + try {
  554 + $objectId = $this->ObjectService->setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken);
  555 + }
  556 + catch (Exception $e)
  557 + {
  558 + return array(
  559 + "status_code" => 1,
  560 + "message" => $e->getMessage()
  561 + );
  562 + }
  563 +
  564 + return array(
  565 + 'status_code' => 0,
  566 + 'results' => $documentId
  567 + );
  568 + }
  569 +
500 } 570 }
501 571
502 ?> 572 ?>
lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php
@@ -81,10 +81,9 @@ class CMISDocumentObject extends CMISBaseObject { @@ -81,10 +81,9 @@ class CMISDocumentObject extends CMISBaseObject {
81 $this->includedInSupertypeQuery = true; // 81 $this->includedInSupertypeQuery = true; //
82 // TODO determine what these next 3 should be 82 // TODO determine what these next 3 should be
83 $this->controllable = false; // <repository-specific> 83 $this->controllable = false; // <repository-specific>
84 - $this->versionable = false; // <repository-specific>  
85 - $this->contentStreamAllowed = false; // <repository-specific>  
86 - $this->contentStreamLength = 0;  
87 - $this->contentStreamMimeType = ''; 84 + $this->versionable = true; // <repository-specific>
  85 + $this->contentStreamAllowed = 'required'; // <repository-specific> notAllowed/allowed/required
  86 +
88 // properties 87 // properties
89 $this->properties = new CMISDocumentPropertyCollection(); 88 $this->properties = new CMISDocumentPropertyCollection();
90 89
lib/api/ktcmis/services/CMISObjectService.inc.php
@@ -2,8 +2,12 @@ @@ -2,8 +2,12 @@
2 2
3 require_once(KT_DIR . '/ktapi/ktapi.inc.php'); 3 require_once(KT_DIR . '/ktapi/ktapi.inc.php');
4 require_once(CMIS_DIR . '/exceptions/ConstraintViolationException.inc.php'); 4 require_once(CMIS_DIR . '/exceptions/ConstraintViolationException.inc.php');
  5 +require_once(CMIS_DIR . '/exceptions/ContentAlreadyExistsException.inc.php');
5 require_once(CMIS_DIR . '/exceptions/ObjectNotFoundException.inc.php'); 6 require_once(CMIS_DIR . '/exceptions/ObjectNotFoundException.inc.php');
6 require_once(CMIS_DIR . '/exceptions/StorageException.inc.php'); 7 require_once(CMIS_DIR . '/exceptions/StorageException.inc.php');
  8 +require_once(CMIS_DIR . '/exceptions/StreamNotSupportedException.inc.php');
  9 +require_once(CMIS_DIR . '/exceptions/UpdateConflictException.inc.php');
  10 +require_once(CMIS_DIR . '/exceptions/VersioningException.inc.php');
7 require_once(CMIS_DIR . '/services/CMISRepositoryService.inc.php'); 11 require_once(CMIS_DIR . '/services/CMISRepositoryService.inc.php');
8 require_once(CMIS_DIR . '/objecttypes/CMISDocumentObject.inc.php'); 12 require_once(CMIS_DIR . '/objecttypes/CMISDocumentObject.inc.php');
9 require_once(CMIS_DIR . '/objecttypes/CMISFolderObject.inc.php'); 13 require_once(CMIS_DIR . '/objecttypes/CMISFolderObject.inc.php');
@@ -68,7 +72,128 @@ class CMISObjectService { @@ -68,7 +72,128 @@ class CMISObjectService {
68 } 72 }
69 73
70 /** 74 /**
71 - * Function to create a folder 75 + * Creates a new document within the repository
  76 + *
  77 + * @param string $repositoryId The repository to which the document must be added
  78 + * @param string $typeId Object Type id for the document object being created
  79 + * @param array $properties Array of properties which must be applied to the created document object
  80 + * @param string $folderId The id of the folder which will be the parent of the created document object
  81 + * This parameter is optional IF unfilingCapability is supported
  82 + * @param contentStream $contentStream optional content stream data
  83 + * @param string $versioningState optional version state value: checkedout/major/minor
  84 + * @return string $objectId The id of the created folder object
  85 + */
  86 + // TODO throw ConstraintViolationException if:
  87 + // value of any of the properties violates the min/max/required/length constraints
  88 + // specified in the property definition in the Object-Type.
  89 + // TODO throw ConstraintViolationException if:
  90 + // The Object-Type definition specified by the typeId parameter's "contentStreamAllowed" attribute
  91 + // is set to "required" and no contentStream input parameter is provided
  92 + // TODO throw ConstraintViolationException if:
  93 + // The Object-Type definition specified by the typeId parameter's "versionable" attribute
  94 + // is set to "false" and a value for the versioningState input parameter is provided
  95 + function createDocument($repositoryId, $typeId, $properties, $folderId = null,
  96 + $contentStream = null, $versioningState = 'Major')
  97 + {
  98 + $objectId = null;
  99 +
  100 + // fetch type definition of supplied type and check for base type "document", if not true throw exception
  101 + $RepositoryService = new CMISRepositoryService();
  102 + try {
  103 + $typeDefinition = $RepositoryService->getTypeDefinition($repositoryId, $typeId);
  104 + }
  105 + // NOTE Not sure that we should throw this specific exception, maybe just let the underlying
  106 + // exception propogate upward...
  107 + // Alternatively: throw new exception with original exception message appended
  108 + // NOTE The latter method has been adopted for the moment
  109 + catch (Exception $e)
  110 + {
  111 + throw new ConstraintViolationException('Object is not of base type document. ' . $e->getMessage());
  112 + }
  113 +
  114 + if ($typeDefinition['attributes']['baseType'] != 'document')
  115 + {
  116 + throw new ConstraintViolationException('Object is not of base type document');
  117 + }
  118 +
  119 + // if no $folderId submitted and repository does not support "unfiling" throw exception
  120 + if (empty($folderId))
  121 + {
  122 + $repositoryInfo = $RepositoryService->getRepositoryInfo($repositoryId);
  123 + $capabilities = $repositoryInfo->getCapabilities();
  124 + if (!$capabilities->hasCapabilityUnfiling())
  125 + {
  126 + throw new ConstraintViolationException('Repository does not support the Unfiling capability and no folder id was supplied');
  127 + }
  128 + }
  129 +
  130 + // Attempt to decode $folderId, use as is if not detected as encoded
  131 + $tmpObjectId = $folderId;
  132 + $tmpTypeId = CMISUtil::decodeObjectId($tmpObjectId);
  133 + if ($tmpTypeId != 'Unknown')
  134 + $folderId = $tmpObjectId;
  135 +
  136 + // if parent folder is not allowed to hold this type, throw exception
  137 + $CMISFolder = new CMISFolderObject($folderId, $this->ktapi);
  138 + $folderProperties = $CMISFolder->getProperties();
  139 + $allowed = $folderProperties->getValue('AllowedChildObjectTypeIds');
  140 + if (!is_array($allowed) || !in_array($typeId, $allowed))
  141 + {
  142 + throw new ConstraintViolationException('Parent folder may not hold objects of this type (' . $typeId . ')');
  143 + }
  144 +
  145 + // set title and name identical if only one submitted
  146 + if ($properties['title'] == '')
  147 + {
  148 + $properties['title'] = $properties['name'];
  149 + }
  150 + else if ($properties['name'] == '')
  151 + {
  152 + $properties['name'] = $properties['title'];
  153 + }
  154 +
  155 + if ($properties['type'] == '')
  156 + {
  157 + $properties['type'] = $properties['Default'];
  158 + }
  159 +
  160 + // if content stream is required and no content stream is supplied, throw a ConstraintViolationException
  161 + if (($typeDefinition['attributes']['contentStreamAllowed'] == 'required') && empty($contentStream))
  162 + {
  163 + throw new ConstraintViolationException('The Knowledgetree Repository requires a content stream for document creation. '
  164 + . 'Refusing to create an empty document');
  165 + }
  166 + else if (($typeDefinition['attributes']['contentStreamAllowed'] == 'notAllowed') && !empty($contentStream))
  167 + {
  168 + throw new StreamNotSupportedException('Content Streams are not supported');
  169 + }
  170 +
  171 + // TODO deal with $versioningState when supplied
  172 +
  173 + // TODO Use add_document_with_metadata instead if metadata content submitted
  174 + $response = $this->ktapi->add_document($folderId, $properties['title'], $properties['name'], $properties['type'], $uploadedFile);
  175 + if ($response['status_code'] != 0)
  176 + {
  177 + throw new StorageException('The repository was unable to create the document - ' . $response['message']);
  178 + }
  179 + else
  180 + {
  181 + $objectId = CMISUtil::encodeObjectId('Document', $response['results']['id']);
  182 + }
  183 +
  184 + // now that the document object exists, create the content stream from the supplied data
  185 + if (!empty($contentStream))
  186 + {
  187 + // TODO changeToken support
  188 + $changeToken = null;
  189 + $this->setContentStream($repositoryId, $objectId, false, $contentStream, $changeToken);
  190 + }
  191 +
  192 + return $objectId;
  193 + }
  194 +
  195 + /**
  196 + * Creates a new folder within the repository
72 * 197 *
73 * @param string $repositoryId The repository to which the folder must be added 198 * @param string $repositoryId The repository to which the folder must be added
74 * @param string $typeId Object Type id for the folder object being created 199 * @param string $typeId Object Type id for the folder object being created
@@ -81,14 +206,20 @@ class CMISObjectService { @@ -81,14 +206,20 @@ class CMISObjectService {
81 // specified in the property definition in the Object-Type. 206 // specified in the property definition in the Object-Type.
82 function createFolder($repositoryId, $typeId, $properties, $folderId) 207 function createFolder($repositoryId, $typeId, $properties, $folderId)
83 { 208 {
  209 + $objectId = null;
  210 +
84 // fetch type definition of supplied type and check for base type "folder", if not true throw exception 211 // fetch type definition of supplied type and check for base type "folder", if not true throw exception
85 $RepositoryService = new CMISRepositoryService(); 212 $RepositoryService = new CMISRepositoryService();
86 try { 213 try {
87 $typeDefinition = $RepositoryService->getTypeDefinition($repositoryId, $typeId); 214 $typeDefinition = $RepositoryService->getTypeDefinition($repositoryId, $typeId);
88 } 215 }
  216 + // NOTE Not sure that we should throw this specific exception, maybe just let the underlying
  217 + // exception propogate upward...
  218 + // Alternatively: throw new exception with original exception message appended
  219 + // NOTE The latter method has been adopted for the moment
89 catch (Exception $e) 220 catch (Exception $e)
90 { 221 {
91 - throw new ConstraintViolationException('Object is not of base type folder.'); 222 + throw new ConstraintViolationException('Object is not of base type folder. ' . $e->getMessage());
92 } 223 }
93 224
94 if ($typeDefinition['attributes']['baseType'] != 'folder') 225 if ($typeDefinition['attributes']['baseType'] != 'folder')
@@ -96,12 +227,11 @@ class CMISObjectService { @@ -96,12 +227,11 @@ class CMISObjectService {
96 throw new ConstraintViolationException('Object is not of base type folder'); 227 throw new ConstraintViolationException('Object is not of base type folder');
97 } 228 }
98 229
99 - // TODO determine whether this is in fact necessary or if we should require decoding in the calling code  
100 // Attempt to decode $folderId, use as is if not detected as encoded 230 // Attempt to decode $folderId, use as is if not detected as encoded
101 - $objectId = $folderId;  
102 - $tmpTypeId = CMISUtil::decodeObjectId($objectId); 231 + $tmpObjectId = $folderId;
  232 + $tmpTypeId = CMISUtil::decodeObjectId($tmpObjectId);
103 if ($tmpTypeId != 'Unknown') 233 if ($tmpTypeId != 'Unknown')
104 - $folderId = $objectId; 234 + $folderId = $tmpObjectId;
105 235
106 // if parent folder is not allowed to hold this type, throw exception 236 // if parent folder is not allowed to hold this type, throw exception
107 $CMISFolder = new CMISFolderObject($folderId, $this->ktapi); 237 $CMISFolder = new CMISFolderObject($folderId, $this->ktapi);
@@ -115,7 +245,6 @@ class CMISObjectService { @@ -115,7 +245,6 @@ class CMISObjectService {
115 $response = $this->ktapi->create_folder($folderId, $properties['name'], $sig_username = '', $sig_password = '', $reason = ''); 245 $response = $this->ktapi->create_folder($folderId, $properties['name'], $sig_username = '', $sig_password = '', $reason = '');
116 if ($response['status_code'] != 0) 246 if ($response['status_code'] != 0)
117 { 247 {
118 - // throw storageException  
119 throw new StorageException('The repository was unable to create the folder - ' . $response['message']); 248 throw new StorageException('The repository was unable to create the folder - ' . $response['message']);
120 } 249 }
121 else 250 else
@@ -126,6 +255,62 @@ class CMISObjectService { @@ -126,6 +255,62 @@ class CMISObjectService {
126 return $objectId; 255 return $objectId;
127 } 256 }
128 257
  258 + /**
  259 + * Sets the content stream data for an existing document
  260 + *
  261 + * if $overwriteFlag = TRUE, the new content stream is applied whether or not the document has an existing content stream
  262 + * if $overwriteFlag = FALSE, the new content stream is applied only if the document does not have an existing content stream
  263 + *
  264 + * NOTE A Repository MAY automatically create new Document versions as part of this service method.
  265 + * Therefore, the documentId output NEED NOT be identical to the documentId input.
  266 + *
  267 + * @param string $repositoryId
  268 + * @param string $documentId
  269 + * @param boolean $overwriteFlag
  270 + * @param string $contentStream
  271 + * @param string $changeToken
  272 + * @return string $documentId
  273 + */
  274 + // TODO exceptions:
  275 + // updateConflictException: The operation is attempting to update an object that is no longer current
  276 + // (as determined by the repository).
  277 + // versioningException: The repository MAY throw this exception if the object is a non-current Document Version.
  278 + function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null)
  279 + {
  280 + // fetch type definition of supplied document
  281 + $CMISDocument = new CMISDocumentObject($documentId, $this->ktapi);
  282 +
  283 + // if content stream is not allowed for this object type definition, throw a ConstraintViolationException
  284 + if (($CMISDocument->getAttribute('contentStreamAllowed') == 'notAllowed'))
  285 + {
  286 + // NOTE spec version 0.61c specifies both a ConstraintViolationException and a StreamNotSupportedException
  287 + // for this case. Choosing to throw StreamNotSupportedException until the specification is clarified
  288 + // as it is a more specific exception
  289 + throw new StreamNotSupportedException('Content Streams are not allowed for this object type');
  290 + }
  291 +
  292 + $properties = $CMISDocument->getProperties();
  293 + if (!empty($properties->getValue('ContentStreamFilename')) && (!$overwriteFlag))
  294 + {
  295 + throw new ContentAlreadyExistsException('Unable to overwrite existing content stream');
  296 + }
  297 +
  298 + // in order to set the stream we need to do the following:
  299 + // 1. decode the stream from the supplied base64 encoding
  300 + // 2. create a temporary file as if it were uploaded via a file upload dialog
  301 + // 3. create the document content as per usual
  302 + // 4. link to the created document object? this perhaps only happens on read anyway
  303 +
  304 + // if there is any problem updating the content stream, throw StorageException
  305 + // TODO real test parameter instead of hard-coded FALSE
  306 + if (false)
  307 + {
  308 + throw new StorageException('Unable to update the content stream');
  309 + }
  310 +
  311 + return $documentId;
  312 + }
  313 +
129 } 314 }
130 315
131 ?> 316 ?>
tests/ktcmis/testCmisApi.php
@@ -592,14 +592,14 @@ class CMISTestCase extends KTUnitTestCase { @@ -592,14 +592,14 @@ class CMISTestCase extends KTUnitTestCase {
592 foreach($results as $key => $value) 592 foreach($results as $key => $value)
593 { 593 {
594 ?><tr><td colspan="<?php echo 1 + $depth; ?>"><div style="padding: 8px; background-color: lightblue; color: black;"><?php 594 ?><tr><td colspan="<?php echo 1 + $depth; ?>"><div style="padding: 8px; background-color: lightblue; color: black;"><?php
595 - echo $value['properties']['name']; 595 + echo $value['properties']['name']['value'];
596 ?></div></td></tr><?php 596 ?></div></td></tr><?php
597 // properties first 597 // properties first
598 foreach ($value['properties'] as $propkey => $propval) 598 foreach ($value['properties'] as $propkey => $propval)
599 { 599 {
600 - if ($propval == '') continue; 600 + if ($propval['value'] == '') continue;
601 601
602 - $this->printRow($propkey, $propval); 602 + $this->printRow($propkey, $propval['value']);
603 } 603 }
604 604
605 // now any children 605 // now any children
webservice/classes/soap/WSHelper.class.php
1 -<?php  
2 -/**  
3 - * Class that generates a WSDL file and creates documentation  
4 - * for the webservices.  
5 - *  
6 - *@author KnowledgeTree Team  
7 - *@package Webservice  
8 - *@version Version 0.9  
9 - */  
10 -class WSHelper {  
11 - private $uri;  
12 - private $class = null; //IPReflectionClass object  
13 - private $name; //class name  
14 - private $persistence = SOAP_PERSISTENCE_SESSION;  
15 - private $wsdlfile; //wsdl file name  
16 - private $server; //soap server object  
17 -  
18 - public $actor;  
19 - public $structureMap = array();  
20 - public $classNameArr = array();  
21 - public $wsdlFolder; //WSDL cache folder  
22 - public $useWSDLCache = true;  
23 -  
24 - public $type = SOAP_RPC;  
25 - public $use = SOAP_LITERAL;  
26 -  
27 - /**  
28 - * Constructor  
29 - * @param string The Uri name  
30 - * @return void  
31 - */  
32 - public function __construct($uri, $class=null){  
33 - $this->uri = $uri;  
34 - $this->setWSDLCacheFolder($_SERVER['DOCUMENT_ROOT'].dirname($_SERVER['PHP_SELF'])."/wsdl/");  
35 - if($class) $this->setClass($class);  
36 - }  
37 -  
38 - /**  
39 - * Adds the given class name to the list of classes  
40 - * to be included in the documentation/WSDL/Request handlers  
41 - * @param string  
42 - * @return void  
43 - */  
44 - public function setClass($name){  
45 - $this->name = $name;  
46 - $this->wsdlfile = $this->wsdlFolder.$this->name.".wsdl";  
47 - }  
48 -  
49 - public function setWSDLCacheFolder($folder) {  
50 - $this->wsdlFolder = $folder;  
51 - //reset wsdlfile  
52 - $this->wsdlfile = $this->wsdlFolder.$this->name.".wsdl";  
53 - }  
54 - /**  
55 - * Sets the persistence level for the soap class  
56 - */  
57 - public function setPersistence($persistence) {  
58 - $this->persistence = $persistence;  
59 - }  
60 -  
61 - /**  
62 - * Handles everything. Makes sure the webservice is handled,  
63 - * documentations is generated, or the wsdl is generated,  
64 - * according to the page request  
65 - * @return void  
66 - */  
67 - public function handle(){  
68 - if(substr($_SERVER['QUERY_STRING'], -4) == 'wsdl'){  
69 - $this->showWSDL();  
70 - }elseif(isset($GLOBALS['HTTP_RAW_POST_DATA']) && strlen($GLOBALS['HTTP_RAW_POST_DATA'])>0){  
71 - $this->handleRequest();  
72 - }else{  
73 - $this->createDocumentation();  
74 - }  
75 - }  
76 - /**  
77 - * Checks if the current WSDL is up-to-date, regenerates if necessary and outputs the WSDL  
78 - * @return void  
79 - */  
80 - public function showWSDL(){  
81 - //check if it's a legal webservice class  
82 - if(!in_array($this->name, $this->classNameArr))  
83 - throw new Exception("No valid webservice class.");  
84 -  
85 - //@TODO: nog een mooie oplossing voor het cachen zoeken  
86 - header("Content-type: text/xml");  
87 - if($this->useWSDLCache && file_exists($this->wsdlfile)){  
88 - readfile($this->wsdlfile);  
89 - }else{  
90 - //make sure to refresh PHP WSDL cache system  
91 - ini_set("soap.wsdl_cache_enabled",0);  
92 - echo $this->createWSDL();  
93 - }  
94 - }  
95 -  
96 - private function createWSDL(){  
97 - $this->class = new IPReflectionClass($this->name);  
98 - $wsdl = new WSDLStruct($this->uri, "http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."?class=".$this->name, $this->type, $this->use);  
99 - $wsdl->setService($this->class);  
100 -  
101 - try {  
102 - $gendoc = $wsdl->generateDocument();  
103 - } catch (WSDLException $exception) {  
104 - $exception->Display();  
105 - exit();  
106 - }  
107 -  
108 - $fh = fopen($this->wsdlfile, "w+");  
109 - fwrite($fh, $gendoc);  
110 - fclose($fh);  
111 -  
112 - return $gendoc;  
113 - }  
114 -  
115 - /**  
116 - * Lets the native PHP5 soap implementation handle the request  
117 - * after registrating the class  
118 - * @return void  
119 - */  
120 - private function handleRequest(){  
121 - //check if it's a legal webservice class  
122 - if(!in_array($this->name, $this->classNameArr))  
123 - throw new Exception("No valid webservice class.");  
124 -  
125 - //check cache  
126 - //if(!file_exists($this->wsdlfile))  
127 - $this->createWSDL();  
128 -  
129 - $options = Array('actor' => $this->actor, 'classmap' => $this->structureMap);  
130 -  
131 - header("Content-type: text/xml");  
132 - $this->server = new SoapServer($this->wsdlfile, $options);  
133 - $this->server->setClass($this->name);  
134 - $this->server->setPersistence($this->persistence);  
135 -  
136 - use_soap_error_handler(true);  
137 - $this->server->handle();  
138 - }  
139 -  
140 - /**  
141 - * @param string code  
142 - * @param string string  
143 - * @param string actor  
144 - * @param mixed details  
145 - * @param string name  
146 - * @return void  
147 - */  
148 - public function fault($code, $string, $actor, $details, $name='') {  
149 - return $this->server->fault($code, $string, $actor, $details, $name);  
150 - }  
151 -  
152 - /**  
153 - * Generates the documentations for the webservice usage.  
154 - * @TODO: "int", "boolean", "double", "float", "string", "void"  
155 - * @param string Template filename  
156 - * @return void  
157 - */  
158 - public function createDocumentation($template="classes/soap/templates/docclass.xsl") {  
159 - if(!is_file($template))  
160 - throw new WSException("Could not find the template file: '$template'");  
161 - $this->class = new IPReflectionClass($this->name);  
162 - $xtpl = new IPXSLTemplate($template);  
163 - $documentation = Array();  
164 - $documentation['menu'] = Array();  
165 - //loop menu items  
166 - sort($this->classNameArr);  
167 - foreach($this->classNameArr as $className) {  
168 - $documentation['menu'][] = new IPReflectionClass($className);  
169 - }  
170 -  
171 - if($this->class){  
172 - $this->class->properties = $this->class->getProperties(false, false);  
173 - $this->class->methods = $this->class->getMethods(false, false);  
174 - foreach((array)$this->class->methods as $method) {  
175 - $method->params = $method->getParameters();  
176 - }  
177 -  
178 - $documentation['class'] = $this->class;  
179 - }  
180 - echo $xtpl->execute($documentation);  
181 - }  
182 -} 1 +<?php
  2 +/**
  3 + * Class that generates a WSDL file and creates documentation
  4 + * for the webservices.
  5 + *
  6 + *@author KnowledgeTree Team
  7 + *@package Webservice
  8 + *@version Version 0.9
  9 + */
  10 +class WSHelper {
  11 + private $uri;
  12 + private $class = null; //IPReflectionClass object
  13 + private $name; //class name
  14 + private $persistence = SOAP_PERSISTENCE_SESSION;
  15 + private $wsdlfile; //wsdl file name
  16 + private $server; //soap server object
  17 +
  18 + public $actor;
  19 + public $structureMap = array();
  20 + public $classNameArr = array();
  21 + public $wsdlFolder; //WSDL cache folder
  22 + public $useWSDLCache = true;
  23 +
  24 + public $type = SOAP_RPC;
  25 + public $use = SOAP_LITERAL;
  26 +
  27 + /**
  28 + * Constructor
  29 + * @param string The Uri name
  30 + * @return void
  31 + */
  32 + public function __construct($uri, $class=null){
  33 + $this->uri = $uri;
  34 + $this->setWSDLCacheFolder($_SERVER['DOCUMENT_ROOT'].dirname($_SERVER['PHP_SELF'])."/wsdl/");
  35 + if($class) $this->setClass($class);
  36 + }
  37 +
  38 + /**
  39 + * Adds the given class name to the list of classes
  40 + * to be included in the documentation/WSDL/Request handlers
  41 + * @param string
  42 + * @return void
  43 + */
  44 + public function setClass($name){
  45 + $this->name = $name;
  46 + $this->wsdlfile = $this->wsdlFolder.$this->name.".wsdl";
  47 + }
  48 +
  49 + public function setWSDLCacheFolder($folder) {
  50 + $this->wsdlFolder = $folder;
  51 + //reset wsdlfile
  52 + $this->wsdlfile = $this->wsdlFolder.$this->name.".wsdl";
  53 + }
  54 + /**
  55 + * Sets the persistence level for the soap class
  56 + */
  57 + public function setPersistence($persistence) {
  58 + $this->persistence = $persistence;
  59 + }
  60 +
  61 + /**
  62 + * Handles everything. Makes sure the webservice is handled,
  63 + * documentations is generated, or the wsdl is generated,
  64 + * according to the page request
  65 + * @return void
  66 + */
  67 + public function handle(){
  68 + if(substr($_SERVER['QUERY_STRING'], -4) == 'wsdl'){
  69 + $this->showWSDL();
  70 + }elseif(isset($GLOBALS['HTTP_RAW_POST_DATA']) && strlen($GLOBALS['HTTP_RAW_POST_DATA'])>0){
  71 + $this->handleRequest();
  72 + }else{
  73 + $this->createDocumentation();
  74 + }
  75 + }
  76 + /**
  77 + * Checks if the current WSDL is up-to-date, regenerates if necessary and outputs the WSDL
  78 + * @return void
  79 + */
  80 + public function showWSDL(){
  81 + //check if it's a legal webservice class
  82 + if(!in_array($this->name, $this->classNameArr))
  83 + throw new Exception("No valid webservice class.");
  84 +
  85 + //@TODO: nog een mooie oplossing voor het cachen zoeken
  86 + header("Content-type: text/xml");
  87 + if($this->useWSDLCache && file_exists($this->wsdlfile)){
  88 + readfile($this->wsdlfile);
  89 + }else{
  90 + //make sure to refresh PHP WSDL cache system
  91 + ini_set("soap.wsdl_cache_enabled",0);
  92 + echo $this->createWSDL();
  93 + }
  94 + }
  95 +
  96 + private function createWSDL(){
  97 + $this->class = new IPReflectionClass($this->name);
  98 + $wsdl = new WSDLStruct($this->uri, "http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."?class=".$this->name, $this->type, $this->use);
  99 + $wsdl->setService($this->class);
  100 +
  101 + try {
  102 + $gendoc = $wsdl->generateDocument();
  103 + } catch (WSDLException $exception) {
  104 + $exception->Display();
  105 + exit();
  106 + }
  107 +
  108 + $fh = fopen($this->wsdlfile, "w+");
  109 + fwrite($fh, $gendoc);
  110 + fclose($fh);
  111 +
  112 + return $gendoc;
  113 + }
  114 +
  115 + /**
  116 + * Lets the native PHP5 soap implementation handle the request
  117 + * after registrating the class
  118 + * @return void
  119 + */
  120 + private function handleRequest(){
  121 + //check if it's a legal webservice class
  122 + if(!in_array($this->name, $this->classNameArr))
  123 + throw new Exception("No valid webservice class.");
  124 +
  125 + //check cache
  126 + //if(!file_exists($this->wsdlfile))
  127 + $this->createWSDL();
  128 +
  129 + $options = Array('actor' => $this->actor, 'classmap' => $this->structureMap);
  130 +
  131 + header("Content-type: text/xml");
  132 + $this->server = new SoapServer($this->wsdlfile, $options);
  133 + $this->server->setClass($this->name);
  134 + $this->server->setPersistence($this->persistence);
  135 +
  136 + use_soap_error_handler(true);
  137 + $this->server->handle();
  138 + }
  139 +
  140 + /**
  141 + * @param string code
  142 + * @param string string
  143 + * @param string actor
  144 + * @param mixed details
  145 + * @param string name
  146 + * @return void
  147 + */
  148 + public function fault($code, $string, $actor, $details, $name='') {
  149 + return $this->server->fault($code, $string, $actor, $details, $name);
  150 + }
  151 +
  152 + /**
  153 + * Generates the documentations for the webservice usage.
  154 + * @TODO: "int", "boolean", "double", "float", "string", "void"
  155 + * @param string Template filename
  156 + * @return void
  157 + */
  158 + public function createDocumentation($template="classes/soap/templates/docclass.xsl") {
  159 + if(!is_file($template))
  160 + throw new WSException("Could not find the template file: '$template'");
  161 + $this->class = new IPReflectionClass($this->name);
  162 + $xtpl = new IPXSLTemplate($template);
  163 + $documentation = Array();
  164 + $documentation['menu'] = Array();
  165 + //loop menu items
  166 + sort($this->classNameArr);
  167 + foreach($this->classNameArr as $className) {
  168 + $documentation['menu'][] = new IPReflectionClass($className);
  169 + }
  170 +
  171 + if($this->class){
  172 + $this->class->properties = $this->class->getProperties(false, false);
  173 + $this->class->methods = $this->class->getMethods(false, false);
  174 + foreach((array)$this->class->methods as $method) {
  175 + $method->params = $method->getParameters();
  176 + }
  177 +
  178 + $documentation['class'] = $this->class;
  179 + }
  180 + echo $xtpl->execute($documentation);
  181 + }
  182 +}
183 ?> 183 ?>
184 \ No newline at end of file 184 \ No newline at end of file
webservice/classes/soap/templates/str.replace.function.xsl
1 -<?xml version="1.0"?>  
2 -<xsl:stylesheet version="1.0"  
3 - xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
4 - xmlns:str="http://exslt.org/strings"  
5 - xmlns:func="http://exslt.org/functions"  
6 - xmlns:exsl="http://exslt.org/common"  
7 - extension-element-prefixes="str exsl func">  
8 -  
9 -<func:function name="str:replace">  
10 - <xsl:param name="string" select="''" />  
11 - <xsl:param name="search" select="/.." />  
12 - <xsl:param name="replace" select="/.." />  
13 - <xsl:choose>  
14 - <xsl:when test="not($string)">  
15 - <func:result select="/.." />  
16 - </xsl:when>  
17 - <xsl:when test="function-available('exsl:node-set')">  
18 - <!-- this converts the search and replace arguments to node sets  
19 - if they are one of the other XPath types -->  
20 - <xsl:variable name="search-nodes-rtf">  
21 - <xsl:copy-of select="$search" />  
22 - </xsl:variable>  
23 - <xsl:variable name="replace-nodes-rtf">  
24 - <xsl:copy-of select="$replace" />  
25 - </xsl:variable>  
26 - <xsl:variable name="replacements-rtf">  
27 - <xsl:for-each select="exsl:node-set($search-nodes-rtf)/node()">  
28 - <xsl:variable name="pos" select="position()" />  
29 - <replace search="{.}">  
30 - <xsl:copy-of select="exsl:node-set($replace-nodes-rtf)/node()[$pos]" />  
31 - </replace>  
32 - </xsl:for-each>  
33 - </xsl:variable>  
34 - <xsl:variable name="sorted-replacements-rtf">  
35 - <xsl:for-each select="exsl:node-set($replacements-rtf)/replace">  
36 - <xsl:sort select="string-length(@search)" data-type="number" order="descending" />  
37 - <xsl:copy-of select="." />  
38 - </xsl:for-each>  
39 - </xsl:variable>  
40 - <xsl:variable name="result">  
41 - <xsl:choose>  
42 - <xsl:when test="not($search)">  
43 - <xsl:value-of select="$string" />  
44 - </xsl:when>  
45 - <xsl:otherwise>  
46 - <xsl:call-template name="str:_replace">  
47 - <xsl:with-param name="string" select="$string" />  
48 - <xsl:with-param name="replacements" select="exsl:node-set($sorted-replacements-rtf)/replace" />  
49 - </xsl:call-template>  
50 - </xsl:otherwise>  
51 - </xsl:choose>  
52 - </xsl:variable>  
53 - <func:result select="exsl:node-set($result)/node()" />  
54 - </xsl:when>  
55 - <xsl:otherwise>  
56 - <xsl:message terminate="yes">  
57 - ERROR: function implementation of str:replace() relies on exsl:node-set().  
58 - </xsl:message>  
59 - </xsl:otherwise>  
60 - </xsl:choose>  
61 -</func:function>  
62 -  
63 -<xsl:template name="str:_replace">  
64 - <xsl:param name="string" select="''" />  
65 - <xsl:param name="replacements" select="/.." />  
66 - <xsl:choose>  
67 - <xsl:when test="not($string)" />  
68 - <xsl:when test="not($replacements)">  
69 - <xsl:value-of select="$string" />  
70 - </xsl:when>  
71 - <xsl:otherwise>  
72 - <xsl:variable name="replacement" select="$replacements[1]" />  
73 - <xsl:variable name="search" select="$replacement/@search" />  
74 - <xsl:choose>  
75 - <xsl:when test="not(string($search))">  
76 - <xsl:value-of select="substring($string, 1, 1)" />  
77 - <xsl:copy-of select="$replacement/node()" />  
78 - <xsl:call-template name="str:_replace">  
79 - <xsl:with-param name="string" select="substring($string, 2)" />  
80 - <xsl:with-param name="replacements" select="$replacements" />  
81 - </xsl:call-template>  
82 - </xsl:when>  
83 - <xsl:when test="contains($string, $search)">  
84 - <xsl:call-template name="str:_replace">  
85 - <xsl:with-param name="string" select="substring-before($string, $search)" />  
86 - <xsl:with-param name="replacements" select="$replacements[position() > 1]" />  
87 - </xsl:call-template>  
88 - <xsl:copy-of select="$replacement/node()" />  
89 - <xsl:call-template name="str:_replace">  
90 - <xsl:with-param name="string" select="substring-after($string, $search)" />  
91 - <xsl:with-param name="replacements" select="$replacements" />  
92 - </xsl:call-template>  
93 - </xsl:when>  
94 - <xsl:otherwise>  
95 - <xsl:call-template name="str:_replace">  
96 - <xsl:with-param name="string" select="$string" />  
97 - <xsl:with-param name="replacements" select="$replacements[position() > 1]" />  
98 - </xsl:call-template>  
99 - </xsl:otherwise>  
100 - </xsl:choose>  
101 - </xsl:otherwise>  
102 - </xsl:choose>  
103 -</xsl:template>  
104 - 1 +<?xml version="1.0"?>
  2 +<xsl:stylesheet version="1.0"
  3 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4 + xmlns:str="http://exslt.org/strings"
  5 + xmlns:func="http://exslt.org/functions"
  6 + xmlns:exsl="http://exslt.org/common"
  7 + extension-element-prefixes="str exsl func">
  8 +
  9 +<func:function name="str:replace">
  10 + <xsl:param name="string" select="''" />
  11 + <xsl:param name="search" select="/.." />
  12 + <xsl:param name="replace" select="/.." />
  13 + <xsl:choose>
  14 + <xsl:when test="not($string)">
  15 + <func:result select="/.." />
  16 + </xsl:when>
  17 + <xsl:when test="function-available('exsl:node-set')">
  18 + <!-- this converts the search and replace arguments to node sets
  19 + if they are one of the other XPath types -->
  20 + <xsl:variable name="search-nodes-rtf">
  21 + <xsl:copy-of select="$search" />
  22 + </xsl:variable>
  23 + <xsl:variable name="replace-nodes-rtf">
  24 + <xsl:copy-of select="$replace" />
  25 + </xsl:variable>
  26 + <xsl:variable name="replacements-rtf">
  27 + <xsl:for-each select="exsl:node-set($search-nodes-rtf)/node()">
  28 + <xsl:variable name="pos" select="position()" />
  29 + <replace search="{.}">
  30 + <xsl:copy-of select="exsl:node-set($replace-nodes-rtf)/node()[$pos]" />
  31 + </replace>
  32 + </xsl:for-each>
  33 + </xsl:variable>
  34 + <xsl:variable name="sorted-replacements-rtf">
  35 + <xsl:for-each select="exsl:node-set($replacements-rtf)/replace">
  36 + <xsl:sort select="string-length(@search)" data-type="number" order="descending" />
  37 + <xsl:copy-of select="." />
  38 + </xsl:for-each>
  39 + </xsl:variable>
  40 + <xsl:variable name="result">
  41 + <xsl:choose>
  42 + <xsl:when test="not($search)">
  43 + <xsl:value-of select="$string" />
  44 + </xsl:when>
  45 + <xsl:otherwise>
  46 + <xsl:call-template name="str:_replace">
  47 + <xsl:with-param name="string" select="$string" />
  48 + <xsl:with-param name="replacements" select="exsl:node-set($sorted-replacements-rtf)/replace" />
  49 + </xsl:call-template>
  50 + </xsl:otherwise>
  51 + </xsl:choose>
  52 + </xsl:variable>
  53 + <func:result select="exsl:node-set($result)/node()" />
  54 + </xsl:when>
  55 + <xsl:otherwise>
  56 + <xsl:message terminate="yes">
  57 + ERROR: function implementation of str:replace() relies on exsl:node-set().
  58 + </xsl:message>
  59 + </xsl:otherwise>
  60 + </xsl:choose>
  61 +</func:function>
  62 +
  63 +<xsl:template name="str:_replace">
  64 + <xsl:param name="string" select="''" />
  65 + <xsl:param name="replacements" select="/.." />
  66 + <xsl:choose>
  67 + <xsl:when test="not($string)" />
  68 + <xsl:when test="not($replacements)">
  69 + <xsl:value-of select="$string" />
  70 + </xsl:when>
  71 + <xsl:otherwise>
  72 + <xsl:variable name="replacement" select="$replacements[1]" />
  73 + <xsl:variable name="search" select="$replacement/@search" />
  74 + <xsl:choose>
  75 + <xsl:when test="not(string($search))">
  76 + <xsl:value-of select="substring($string, 1, 1)" />
  77 + <xsl:copy-of select="$replacement/node()" />
  78 + <xsl:call-template name="str:_replace">
  79 + <xsl:with-param name="string" select="substring($string, 2)" />
  80 + <xsl:with-param name="replacements" select="$replacements" />
  81 + </xsl:call-template>
  82 + </xsl:when>
  83 + <xsl:when test="contains($string, $search)">
  84 + <xsl:call-template name="str:_replace">
  85 + <xsl:with-param name="string" select="substring-before($string, $search)" />
  86 + <xsl:with-param name="replacements" select="$replacements[position() > 1]" />
  87 + </xsl:call-template>
  88 + <xsl:copy-of select="$replacement/node()" />
  89 + <xsl:call-template name="str:_replace">
  90 + <xsl:with-param name="string" select="substring-after($string, $search)" />
  91 + <xsl:with-param name="replacements" select="$replacements" />
  92 + </xsl:call-template>
  93 + </xsl:when>
  94 + <xsl:otherwise>
  95 + <xsl:call-template name="str:_replace">
  96 + <xsl:with-param name="string" select="$string" />
  97 + <xsl:with-param name="replacements" select="$replacements[position() > 1]" />
  98 + </xsl:call-template>
  99 + </xsl:otherwise>
  100 + </xsl:choose>
  101 + </xsl:otherwise>
  102 + </xsl:choose>
  103 +</xsl:template>
  104 +
105 </xsl:stylesheet> 105 </xsl:stylesheet>
106 \ No newline at end of file 106 \ No newline at end of file
webservice/tests/annotations.php
1 -<?  
2 -chdir("..");  
3 -include "common.php";  
4 -  
5 -class DefaultController {  
6 - const TYPE_PLAIN = 1;  
7 - const TYPE_HTML = 2;  
8 - public $type;  
9 - public $length;  
10 -}  
11 -/**  
12 - * @ann1('me'=>'you');  
13 - */  
14 -class something{  
15 - /**  
16 - * @var string  
17 - * @Controller(type => DefaultController::TYPE_PLAIN, length => 100)  
18 - */  
19 - public $propertyA;  
20 -  
21 - /**  
22 - * @var string  
23 - * @Controller(type => DefaultController::TYPE_HTML, length => 100)  
24 - */  
25 - public function methodB () {  
26 - return "aap";  
27 - }  
28 -}  
29 -  
30 -/* Annotation example */  
31 -$rel = new IPReflectionClass("something");  
32 -$properties = $rel->getProperties();  
33 -$methods = $rel->getMethods();  
34 -  
35 -var_dump($rel->getAnnotation("ann1", "stdClass"));  
36 -  
37 -$property = $properties["propertyA"];  
38 -$ann = $property->getAnnotation("Controller", "DefaultController");  
39 -var_dump($ann);  
40 -  
41 -$method = $methods["methodB"];  
42 -$ann = $method->getAnnotation("Controller", "DefaultController");  
43 -var_dump($ann); 1 +<?
  2 +chdir("..");
  3 +include "common.php";
  4 +
  5 +class DefaultController {
  6 + const TYPE_PLAIN = 1;
  7 + const TYPE_HTML = 2;
  8 + public $type;
  9 + public $length;
  10 +}
  11 +/**
  12 + * @ann1('me'=>'you');
  13 + */
  14 +class something{
  15 + /**
  16 + * @var string
  17 + * @Controller(type => DefaultController::TYPE_PLAIN, length => 100)
  18 + */
  19 + public $propertyA;
  20 +
  21 + /**
  22 + * @var string
  23 + * @Controller(type => DefaultController::TYPE_HTML, length => 100)
  24 + */
  25 + public function methodB () {
  26 + return "aap";
  27 + }
  28 +}
  29 +
  30 +/* Annotation example */
  31 +$rel = new IPReflectionClass("something");
  32 +$properties = $rel->getProperties();
  33 +$methods = $rel->getMethods();
  34 +
  35 +var_dump($rel->getAnnotation("ann1", "stdClass"));
  36 +
  37 +$property = $properties["propertyA"];
  38 +$ann = $property->getAnnotation("Controller", "DefaultController");
  39 +var_dump($ann);
  40 +
  41 +$method = $methods["methodB"];
  42 +$ann = $method->getAnnotation("Controller", "DefaultController");
  43 +var_dump($ann);
44 ?> 44 ?>
45 \ No newline at end of file 45 \ No newline at end of file