Commit 0c5cbc2cd810cc43a9b478b2cd267fbb9f5f4814
Merge branch 'master' of github.com:ktgit/knowledgetree
Showing
12 changed files
with
633 additions
and
344 deletions
lib/api/ktcmis/exceptions/ContentAlreadyExistsException.inc.php
0 → 100644
lib/api/ktcmis/exceptions/RuntimeException.inc.php
0 → 100644
lib/api/ktcmis/exceptions/StreamNotSupportedException.inc.php
0 → 100644
lib/api/ktcmis/exceptions/UpdateConflictException.inc.php
0 → 100644
lib/api/ktcmis/exceptions/VersioningException.inc.php
0 → 100644
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 |