Commit fb7f6cc4466d1a04251a3c56836eff2a85e887bd
Merge branch 'cmisatom_delete' into edge
Showing
9 changed files
with
504 additions
and
55 deletions
lib/api/ktcmis/ktcmis.inc.php
| ... | ... | @@ -56,6 +56,7 @@ require_once(CMIS_DIR . '/exceptions/PermissionDeniedException.inc.php'); |
| 56 | 56 | require_once(CMIS_DIR . '/services/CMISRepositoryService.inc.php'); |
| 57 | 57 | require_once(CMIS_DIR . '/services/CMISNavigationService.inc.php'); |
| 58 | 58 | require_once(CMIS_DIR . '/services/CMISObjectService.inc.php'); |
| 59 | +require_once(CMIS_DIR . '/services/CMISVersioningService.inc.php'); | |
| 59 | 60 | require_once(CMIS_DIR . '/util/CMISUtil.inc.php'); |
| 60 | 61 | |
| 61 | 62 | /** |
| ... | ... | @@ -73,7 +74,6 @@ class KTCMISBase { |
| 73 | 74 | { |
| 74 | 75 | // TODO confirm KTAPI instance active??? shouldn't really be responsibility of this code |
| 75 | 76 | if (is_null($ktapi) && (!is_null($username) && !is_null($password))) { |
| 76 | -// echo ":WGHWTWGWGHW"; | |
| 77 | 77 | $this->startSession($username, $password); |
| 78 | 78 | } |
| 79 | 79 | else { |
| ... | ... | @@ -87,18 +87,15 @@ class KTCMISBase { |
| 87 | 87 | // NOTE left in to allow transport protocol to delegate auth to this level, but not actually used in any code at present |
| 88 | 88 | public function startSession($username, $password) |
| 89 | 89 | { |
| 90 | -// echo $username." :: ".$password."<BR>"; | |
| 91 | 90 | // attempt to recover session if one exists |
| 92 | 91 | if (!is_null(self::$session) && !PEAR::isError(self::$session)) |
| 93 | 92 | { |
| 94 | -// echo "ATTEMPT TO RECOVER SESSION: ".print_r(self::$session, true)."<BR>\n"; | |
| 95 | 93 | self::$session =& self::$ktapi->get_active_session(self::$session->get_sessionid()); |
| 96 | 94 | } |
| 97 | 95 | |
| 98 | 96 | // start new session if no existing session or problem getting existing session (expired, etc...) |
| 99 | 97 | if (is_null(self::$session) || PEAR::isError(self::$session)) |
| 100 | 98 | { |
| 101 | -// echo "ATTEMPT TO START NEW SESSION<BR>\n"; | |
| 102 | 99 | self::$ktapi = new KTAPI(); |
| 103 | 100 | self::$session =& self::$ktapi->start_session($username, $password); |
| 104 | 101 | } |
| ... | ... | @@ -109,7 +106,6 @@ class KTCMISBase { |
| 109 | 106 | throw new PermissionDeniedException('You must be authenticated to perform this action'); |
| 110 | 107 | } |
| 111 | 108 | |
| 112 | -// print_r(self::$ktapi); | |
| 113 | 109 | return self::$session; |
| 114 | 110 | } |
| 115 | 111 | |
| ... | ... | @@ -594,6 +590,54 @@ class KTObjectService extends KTCMISBase { |
| 594 | 590 | 'results' => $objectId |
| 595 | 591 | ); |
| 596 | 592 | } |
| 593 | + | |
| 594 | + /** | |
| 595 | + * Deletes an object from the repository | |
| 596 | + * | |
| 597 | + * @param string $repositoryId | |
| 598 | + * @param string $objectId | |
| 599 | + * @param string $changeToken [optional] | |
| 600 | + * @return array | |
| 601 | + */ | |
| 602 | + // NOTE Invoking this service method on an object SHALL not delete the entire version series for a Document Object. | |
| 603 | + // To delete an entire version series, use the deleteAllVersions() service | |
| 604 | + function deleteObject($repositoryId, $objectId, $changeToken = null) | |
| 605 | + { | |
| 606 | + try { | |
| 607 | + $result = $this->ObjectService->deleteObject($repositoryId, $objectId, $changeToken); | |
| 608 | + } | |
| 609 | + catch (Exception $e) | |
| 610 | + { | |
| 611 | + return array( | |
| 612 | + "status_code" => 1, | |
| 613 | + "message" => $e->getMessage() | |
| 614 | + ); | |
| 615 | + } | |
| 616 | + | |
| 617 | + return array( | |
| 618 | + 'status_code' => 0, | |
| 619 | + 'results' => $objectId | |
| 620 | + ); | |
| 621 | + } | |
| 622 | + | |
| 623 | + public function deleteTree($repositoryId, $objectId, $changeToken = null, $unfileNonfolderObject = 'delete', $continueOnFailure = false) | |
| 624 | + { | |
| 625 | + try { | |
| 626 | + $result = $this->ObjectService->deleteTree($repositoryId, $objectId, $changeToken, $unfileNonfolderObject, $continueOnFailure); | |
| 627 | + } | |
| 628 | + catch (Exception $e) | |
| 629 | + { | |
| 630 | + return array( | |
| 631 | + "status_code" => 1, | |
| 632 | + "message" => $e->getMessage() | |
| 633 | + ); | |
| 634 | + } | |
| 635 | + | |
| 636 | + return array( | |
| 637 | + 'status_code' => 0, | |
| 638 | + 'results' => $objectId | |
| 639 | + ); | |
| 640 | + } | |
| 597 | 641 | |
| 598 | 642 | /** |
| 599 | 643 | * Sets the content stream data for an existing document |
| ... | ... | @@ -614,7 +658,7 @@ class KTObjectService extends KTCMISBase { |
| 614 | 658 | function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null) |
| 615 | 659 | { |
| 616 | 660 | try { |
| 617 | - $objectId = $this->ObjectService->setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken); | |
| 661 | + $documentId = $this->ObjectService->setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken); | |
| 618 | 662 | } |
| 619 | 663 | catch (Exception $e) |
| 620 | 664 | { |
| ... | ... | @@ -632,4 +676,60 @@ class KTObjectService extends KTCMISBase { |
| 632 | 676 | |
| 633 | 677 | } |
| 634 | 678 | |
| 679 | +/** | |
| 680 | + * Handles requests for and actions on versionable objects | |
| 681 | + */ | |
| 682 | +class KTVersioningService extends KTCMISBase { | |
| 683 | + | |
| 684 | + protected $VersioningService; | |
| 685 | + | |
| 686 | + public function __construct(&$ktapi = null, $username = null, $password = null) | |
| 687 | + { | |
| 688 | + parent::__construct($ktapi, $username, $password); | |
| 689 | + // instantiate underlying CMIS service | |
| 690 | + $this->VersioningService = new CMISVersioningService(); | |
| 691 | + $this->setInterface(); | |
| 692 | + } | |
| 693 | + | |
| 694 | + public function startSession($username, $password) | |
| 695 | + { | |
| 696 | + parent::startSession($username, $password); | |
| 697 | + $this->setInterface(); | |
| 698 | + return self::$session; | |
| 699 | + } | |
| 700 | + | |
| 701 | + public function setInterface(&$ktapi = null) | |
| 702 | + { | |
| 703 | + parent::setInterface($ktapi); | |
| 704 | + $this->VersioningService->setInterface(self::$ktapi); | |
| 705 | + } | |
| 706 | + | |
| 707 | + /** | |
| 708 | + * Deletes all Document Objects in the specified Version Series, including the Private Working Copy | |
| 709 | + * | |
| 710 | + * @param string $repositoryId | |
| 711 | + * @param string $versionSeriesId | |
| 712 | + * @return boolean true if successful | |
| 713 | + */ | |
| 714 | + public function deleteAllVersions($repositoryId, $versionSeriesId) | |
| 715 | + { | |
| 716 | + try { | |
| 717 | + $result = $this->VersioningService->deleteAllVersions($repositoryId, $versionSeriesId); | |
| 718 | + } | |
| 719 | + catch (Exception $e) | |
| 720 | + { | |
| 721 | + return array( | |
| 722 | + "status_code" => 1, | |
| 723 | + "message" => $e->getMessage() | |
| 724 | + ); | |
| 725 | + } | |
| 726 | + | |
| 727 | + return array( | |
| 728 | + 'status_code' => 0, | |
| 729 | + 'results' => $result | |
| 730 | + ); | |
| 731 | + } | |
| 732 | + | |
| 733 | +} | |
| 734 | + | |
| 635 | 735 | ?> | ... | ... |
lib/api/ktcmis/objecttypes/CMISDocumentObject.inc.php
| ... | ... | @@ -144,8 +144,9 @@ class CMISDocumentObject extends CMISBaseObject { |
| 144 | 144 | // NOTE see ktapi::is_latest_version |
| 145 | 145 | $this->_setPropertyInternal('IsLatestMajorVersion', true); |
| 146 | 146 | $this->_setPropertyInternal('VersionLabel', $objectProperties['version']); |
| 147 | - // TODO what determines this, do we have anything? | |
| 148 | - $this->_setPropertyInternal('VersionSeriesId', null); | |
| 147 | + // VersionSeriesId should be the id of the latest version | |
| 148 | + // NOTE this may change in the future but is easiest for the current implementation | |
| 149 | + $this->_setPropertyInternal('VersionSeriesId', $objectProperties['version']); | |
| 149 | 150 | if ($objectProperties['checked_out_by'] != 'n/a') |
| 150 | 151 | { |
| 151 | 152 | $checkedOut = true; | ... | ... |
lib/api/ktcmis/services/CMISNavigationService.inc.php
| ... | ... | @@ -48,7 +48,7 @@ class CMISNavigationService { |
| 48 | 48 | protected $ktapi; |
| 49 | 49 | |
| 50 | 50 | /** |
| 51 | - * Sets the interface to be used to query the repository | |
| 51 | + * Sets the interface to be used to interact with the repository | |
| 52 | 52 | * |
| 53 | 53 | * @param object $ktapi The KnowledgeTree API interface |
| 54 | 54 | */ | ... | ... |
lib/api/ktcmis/services/CMISObjectService.inc.php
| ... | ... | @@ -21,11 +21,11 @@ class CMISObjectService { |
| 21 | 21 | protected $ktapi; |
| 22 | 22 | |
| 23 | 23 | /** |
| 24 | - * Sets the interface to be used to query the repository | |
| 24 | + * Sets the interface to be used to interact with the repository | |
| 25 | 25 | * |
| 26 | 26 | * @param object $ktapi The KnowledgeTree API interface |
| 27 | 27 | */ |
| 28 | - function setInterface(&$ktapi) | |
| 28 | + public function setInterface(&$ktapi) | |
| 29 | 29 | { |
| 30 | 30 | $this->ktapi = $ktapi; |
| 31 | 31 | } |
| ... | ... | @@ -43,8 +43,8 @@ class CMISObjectService { |
| 43 | 43 | */ |
| 44 | 44 | // TODO optional parameter support |
| 45 | 45 | // TODO FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid |
| 46 | - function getProperties($repositoryId, $objectId, $includeAllowableActions, $includeRelationships, | |
| 47 | - $returnVersion = false, $filter = '') | |
| 46 | + public function getProperties($repositoryId, $objectId, $includeAllowableActions, $includeRelationships, | |
| 47 | + $returnVersion = false, $filter = '') | |
| 48 | 48 | { |
| 49 | 49 | $repository = new CMISRepository($repositoryId); |
| 50 | 50 | |
| ... | ... | @@ -87,8 +87,8 @@ class CMISObjectService { |
| 87 | 87 | // TODO throw ConstraintViolationException if: |
| 88 | 88 | // value of any of the properties violates the min/max/required/length constraints |
| 89 | 89 | // specified in the property definition in the Object-Type. |
| 90 | - function createDocument($repositoryId, $typeId, $properties, $folderId = null, | |
| 91 | - $contentStream = null, $versioningState = null) | |
| 90 | + public function createDocument($repositoryId, $typeId, $properties, $folderId = null, | |
| 91 | + $contentStream = null, $versioningState = null) | |
| 92 | 92 | { |
| 93 | 93 | $objectId = null; |
| 94 | 94 | |
| ... | ... | @@ -344,7 +344,7 @@ class CMISObjectService { |
| 344 | 344 | // TODO throw ConstraintViolationException if: |
| 345 | 345 | // value of any of the properties violates the min/max/required/length constraints |
| 346 | 346 | // specified in the property definition in the Object-Type. |
| 347 | - function createFolder($repositoryId, $typeId, $properties, $folderId) | |
| 347 | + public function createFolder($repositoryId, $typeId, $properties, $folderId) | |
| 348 | 348 | { |
| 349 | 349 | $objectId = null; |
| 350 | 350 | |
| ... | ... | @@ -399,6 +399,148 @@ class CMISObjectService { |
| 399 | 399 | |
| 400 | 400 | return $objectId; |
| 401 | 401 | } |
| 402 | + | |
| 403 | + /** | |
| 404 | + * Deletes an object from the repository | |
| 405 | + * | |
| 406 | + * @param string $repositoryId | |
| 407 | + * @param string $objectId | |
| 408 | + * @param string $changeToken [optional] | |
| 409 | + * @return boolean true on success (exception should be thrown otherwise) | |
| 410 | + */ | |
| 411 | + // NOTE Invoking this service method on an object SHALL not delete the entire version series for a Document Object. | |
| 412 | + // To delete an entire version series, use the deleteAllVersions() service | |
| 413 | + public function deleteObject($repositoryId, $objectId, $changeToken = null) | |
| 414 | + { | |
| 415 | + // determine object type and internal id | |
| 416 | + $objectId = CMISUtil::decodeObjectId($objectId, $typeId); | |
| 417 | + | |
| 418 | + // throw updateConflictException if the operation is attempting to update an object that is no longer current (as determined by the repository). | |
| 419 | + $exists = true; | |
| 420 | + if ($typeId == 'Folder') { | |
| 421 | + $object = $this->ktapi->get_folder_by_id($objectId); | |
| 422 | + if (PEAR::isError($object)) { | |
| 423 | + $exists = false; | |
| 424 | + } | |
| 425 | + } | |
| 426 | + else if ($typeId == 'Document') { | |
| 427 | + $object = $this->ktapi->get_document_by_id($objectId); | |
| 428 | + if (PEAR::isError($object)) { | |
| 429 | + $exists = false; | |
| 430 | + } | |
| 431 | + } | |
| 432 | + else { | |
| 433 | + $exists = false; | |
| 434 | + } | |
| 435 | + | |
| 436 | + if (!$exists) { | |
| 437 | + throw new updateConflictException('Unable to delete the object as it cannot be found.'); | |
| 438 | + } | |
| 439 | + | |
| 440 | + // throw ConstraintViolationException if method is invoked on a Folder object that contains one or more objects | |
| 441 | + if ($typeId == 'Folder') | |
| 442 | + { | |
| 443 | + $folderContent = $object->get_listing(); | |
| 444 | + if (!PEAR::isError($folderContent)) | |
| 445 | + { | |
| 446 | + if (count($folderContent) > 0) { | |
| 447 | + throw new ConstraintViolationException('Unable to delete a folder with content. Use deleteTree instead.'); | |
| 448 | + } | |
| 449 | + } | |
| 450 | + | |
| 451 | + // now try the delete and throw an exception if there is an error | |
| 452 | + // TODO add a default reason | |
| 453 | + // TODO add the electronic signature capability | |
| 454 | + $result = $this->ktapi->delete_folder($objectId, $reason, $sig_username, $sig_password); | |
| 455 | + } | |
| 456 | + else if ($typeId == 'Document') | |
| 457 | + { | |
| 458 | + // since we do not allow deleting of only the latest version we must throw an exception when this function is called on any document | |
| 459 | + // which has more than one version. Okay to delete if there is only the one version. | |
| 460 | + $versions = $object->get_version_history(); | |
| 461 | + if (count($versions) > 1) | |
| 462 | + { | |
| 463 | + // NOTE possibly may want to just throw a RuntimeException rather than this CMIS specific exception. | |
| 464 | + throw new ConstraintViolationException('This function may not be used to delete an object which has multiple versions. ' | |
| 465 | + . 'Since the repository does not allow deleting of only the latest version, nothing can be deleted.'); | |
| 466 | + } | |
| 467 | + | |
| 468 | + // do not allow deletion of a checked out document - this is actually handled by the ktapi code, | |
| 469 | + // but is possibly slightly more efficient to check before trying to delete | |
| 470 | + if ($object->is_checked_out()) { | |
| 471 | + throw new RuntimeException('The object cannot be deleted as it is currently checked out'); | |
| 472 | + } | |
| 473 | + | |
| 474 | + // now try the delete and throw an exception if there is an error | |
| 475 | + // TODO add a default reason | |
| 476 | + // TODO add the electronic signature capability | |
| 477 | + $auth_sig = true; | |
| 478 | + $result = $this->ktapi->delete_document($objectId, $reason, $auth_sig, $sig_username, $sig_password); | |
| 479 | + } | |
| 480 | + | |
| 481 | + // if there was an error performing the delete, throw exception | |
| 482 | + if ($result['status_code'] == 1) { | |
| 483 | + throw new RuntimeException('There was an error deleting the object: ' . $result['message']); | |
| 484 | + } | |
| 485 | + | |
| 486 | + return true; | |
| 487 | + } | |
| 488 | + | |
| 489 | + /** | |
| 490 | + * Deletes an entire tree including all subfolders and other filed objects | |
| 491 | + * | |
| 492 | + * @param string $repositoryId | |
| 493 | + * @param string $objectId | |
| 494 | + * @param string $changeToken [optional] | |
| 495 | + * @param boolean $unfileNonfolderObject [optional] - note that since KnowledgeTree does not allow unfiling this will be ignored | |
| 496 | + * @param boolean $continueOnFailure [optional] - note that since KnowledgeTree does not allow continue on failure this will be ignored | |
| 497 | + * @return array $failedToDelete A list of identifiers of objects in the folder tree that were not deleted. | |
| 498 | + */ | |
| 499 | + // NOTE • A Repository MAY attempt to delete child- and descendant-objects of the specified folder in any order. | |
| 500 | + // • Any child- or descendant-object that the Repository cannot delete SHALL persist in a valid state in the CMIS domain model. | |
| 501 | + // • This is not transactional. | |
| 502 | + // • However, if DeleteSingleFiled is chosen and some objects fail to delete, then single-filed objects are either deleted or kept, | |
| 503 | + // never just unfiled. This is so that a user can call this command again to recover from the error by using the same tree. | |
| 504 | + public function deleteTree($repositoryId, $objectId, $changeToken = null, $unfileNonfolderObject = 'delete', $continueOnFailure = false) | |
| 505 | + { | |
| 506 | + // NOTE since we do not currently allow partial deletes this will always be empty | |
| 507 | + // (unless there is a failure at the requested folder level - what do we do then? exception or array of all objects?) | |
| 508 | + $failedToDelete = array(); | |
| 509 | + | |
| 510 | + // determine object type and internal id | |
| 511 | + $objectId = CMISUtil::decodeObjectId($objectId, $typeId); | |
| 512 | + | |
| 513 | + // throw updateConflictException if the operation is attempting to update an object that is no longer current (as determined by the repository). | |
| 514 | + $exists = true; | |
| 515 | + if ($typeId == 'Folder') { | |
| 516 | + $object = $this->ktapi->get_folder_by_id($objectId); | |
| 517 | + if (PEAR::isError($object)) { | |
| 518 | + $exists = false; | |
| 519 | + } | |
| 520 | + } | |
| 521 | + // if not of type folder then we have a general problem, throw exception | |
| 522 | + else { | |
| 523 | + throw new RuntimeException('Cannot call deleteTree on a non-folder object.'); | |
| 524 | + } | |
| 525 | + | |
| 526 | + if (!$exists) { | |
| 527 | + throw new updateConflictException('Unable to delete the object as it cannot be found.'); | |
| 528 | + } | |
| 529 | + | |
| 530 | + // attempt to delete tree, throw RuntimeException if failed | |
| 531 | + // TODO add a default reason | |
| 532 | + // TODO add the electronic signature capability | |
| 533 | + $result = $this->ktapi->delete_folder($objectId, $reason, $sig_username, $sig_password); | |
| 534 | + // if there was an error performing the delete, throw exception | |
| 535 | + // TODO list of objects which failed in $failedToDelete array; | |
| 536 | + // since we do not delete the folder or any contents if anything cannot be deleted, this will contain the entire tree listing | |
| 537 | + // NOTE once we do this we will need to deal with it externally as well, since we can no longer just catch an exception. | |
| 538 | + if ($result['status_code'] == 1) { | |
| 539 | + throw new RuntimeException('There was an error deleting the object: ' . $result['message']); | |
| 540 | + } | |
| 541 | + | |
| 542 | + return $failedToDelete; | |
| 543 | + } | |
| 402 | 544 | |
| 403 | 545 | // NOTE this function is presently incomplete and untested. Completion deferred to implementation of Checkout/Checkin |
| 404 | 546 | // functionality |
| ... | ... | @@ -428,7 +570,7 @@ class CMISObjectService { |
| 428 | 570 | // updateConflictException: The operation is attempting to update an object that is no longer current |
| 429 | 571 | // (as determined by the repository). |
| 430 | 572 | // versioningException: The repository MAY throw this exception if the object is a non-current Document Version. |
| 431 | - function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null) | |
| 573 | + public function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null) | |
| 432 | 574 | { |
| 433 | 575 | // if no document id was supplied, we are going to create the underlying physical document |
| 434 | 576 | // NOTE while it might have been nice to keep this out of here, KTAPI has no method for creating a document without | ... | ... |
lib/api/ktcmis/services/CMISVersioningService.inc.php
0 → 100644
| 1 | +<?php | |
| 2 | + | |
| 3 | +require_once(KT_DIR . '/ktapi/ktapi.inc.php'); | |
| 4 | +//require_once(CMIS_DIR . '/exceptions/ConstraintViolationException.inc.php'); | |
| 5 | +//require_once(CMIS_DIR . '/exceptions/ContentAlreadyExistsException.inc.php'); | |
| 6 | +//require_once(CMIS_DIR . '/exceptions/ObjectNotFoundException.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'); | |
| 11 | +//require_once(CMIS_DIR . '/services/CMISRepositoryService.inc.php'); | |
| 12 | +//require_once(CMIS_DIR . '/objecttypes/CMISDocumentObject.inc.php'); | |
| 13 | +//require_once(CMIS_DIR . '/objecttypes/CMISFolderObject.inc.php'); | |
| 14 | +//require_once(CMIS_DIR . '/classes/CMISRepository.inc.php'); | |
| 15 | +//require_once(CMIS_DIR . '/util/CMISUtil.inc.php'); | |
| 16 | + | |
| 17 | +class CMISVersioningService { | |
| 18 | + | |
| 19 | + protected $ktapi; | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * Sets the interface to be used to interact with the repository | |
| 23 | + * | |
| 24 | + * @param object $ktapi The KnowledgeTree API interface | |
| 25 | + */ | |
| 26 | + public function setInterface(&$ktapi) | |
| 27 | + { | |
| 28 | + $this->ktapi = $ktapi; | |
| 29 | + } | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * Deletes all Document Objects in the specified Version Series, including the Private Working Copy | |
| 33 | + * | |
| 34 | + * @param string $repositoryId | |
| 35 | + * @param string $versionSeriesId | |
| 36 | + * @return boolean true if successful | |
| 37 | + */ | |
| 38 | + // NOTE For KnowledgeTree the $versionSeriesId should be the latest version, if not it will be taken as implied. | |
| 39 | + // Should we decide to implement the ability to delete individual versions, | |
| 40 | + // then an exception may be thrown under certain circumstances (to be determined) | |
| 41 | + // NOTE I am not really sure how this is going to be handled by CMIS clients. | |
| 42 | + // Testing with CMISSpaces we have it sending the actual document id, not a version series id. | |
| 43 | + // This may be due to the data sent back from our code, or it may just be how CMISSpaces does it. | |
| 44 | + // There is a note in their source code about this. | |
| 45 | + // Meantime we will try based on document id and adjust as needed later | |
| 46 | + public function deleteAllVersions($repositoryId, $versionSeriesId) | |
| 47 | + { | |
| 48 | + // attempt to delete based on versionSeriesId as document/object id | |
| 49 | + // determine object type and internal id | |
| 50 | + $objectId = CMISUtil::decodeObjectId($versionSeriesId, $typeId); | |
| 51 | + | |
| 52 | + // if not a versionable object, throw exception | |
| 53 | + // NOTE that we are assuming only documents are versionable at the moment | |
| 54 | + if ($typeId != 'Document') { | |
| 55 | + throw new RuntimeException('The object type is not versionable and cannot be deleted using deleteAllVersions.'); | |
| 56 | + } | |
| 57 | + | |
| 58 | + // try to delete | |
| 59 | + // TODO add a default reason | |
| 60 | + // TODO add the electronic signature capability | |
| 61 | + $auth_sig = true; | |
| 62 | + $result = $this->ktapi->delete_document($objectId, $reason, $auth_sig, $sig_username, $sig_password); | |
| 63 | + | |
| 64 | + // if there was an error performing the delete, throw exception | |
| 65 | + if ($result['status_code'] == 1) { | |
| 66 | + throw new RuntimeException('There was an error deleting the object: ' . $result['message']); | |
| 67 | + } | |
| 68 | + | |
| 69 | + return true; | |
| 70 | + } | |
| 71 | + | |
| 72 | +} | |
| 73 | + | |
| 74 | +?> | ... | ... |
webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php
| ... | ... | @@ -33,18 +33,20 @@ When POSTing an Atom Document, the atom fields take precedence over the CMIS pro |
| 33 | 33 | include_once CMIS_ATOM_LIB_FOLDER . 'RepositoryService.inc.php'; |
| 34 | 34 | include_once CMIS_ATOM_LIB_FOLDER . 'NavigationService.inc.php'; |
| 35 | 35 | include_once CMIS_ATOM_LIB_FOLDER . 'ObjectService.inc.php'; |
| 36 | +include_once CMIS_ATOM_LIB_FOLDER . 'VersioningService.inc.php'; | |
| 36 | 37 | include_once 'KT_cmis_atom_service_helper.inc.php'; |
| 37 | 38 | |
| 38 | 39 | // TODO auth failed response requires WWW-Authenticate: Basic realm="KnowledgeTree DMS" header |
| 39 | 40 | |
| 40 | 41 | /** |
| 41 | 42 | * AtomPub Service: folder |
| 42 | - * | |
| 43 | - * Returns children, descendants (up to arbitrary depth) or detail for a particular folder | |
| 44 | - * | |
| 45 | 43 | */ |
| 46 | 44 | class KT_cmis_atom_service_folder extends KT_atom_service { |
| 47 | 45 | |
| 46 | + /** | |
| 47 | + * Deals with GET actions for folders. | |
| 48 | + * This includes children and tree/descendant listings as well as individual folder retrieval | |
| 49 | + */ | |
| 48 | 50 | public function GET_action() |
| 49 | 51 | { |
| 50 | 52 | $RepositoryService = new RepositoryService(); |
| ... | ... | @@ -88,6 +90,10 @@ class KT_cmis_atom_service_folder extends KT_atom_service { |
| 88 | 90 | $this->responseFeed = $feed; |
| 89 | 91 | } |
| 90 | 92 | |
| 93 | + /** | |
| 94 | + * Deals with folder service POST actions. | |
| 95 | + * This includes creation of both folders and documents. | |
| 96 | + */ | |
| 91 | 97 | public function POST_action() |
| 92 | 98 | { |
| 93 | 99 | $RepositoryService = new RepositoryService(); |
| ... | ... | @@ -131,23 +137,50 @@ class KT_cmis_atom_service_folder extends KT_atom_service { |
| 131 | 137 | |
| 132 | 138 | if ($typeId != 'Unknown') |
| 133 | 139 | { |
| 134 | - /*$f = fopen('c:\kt-stuff\here.txt', 'w'); | |
| 135 | - fwrite($f, 'fgfgfgfg'); | |
| 136 | - fclose($f);*/ | |
| 137 | 140 | $this->setStatus(self::STATUS_CREATED); |
| 138 | 141 | $feed = KT_cmis_atom_service_helper::getObjectFeed($ObjectService, $repositoryId, $newObjectId, 'POST'); |
| 139 | 142 | } |
| 140 | - else | |
| 141 | - { | |
| 142 | - /*$f = fopen('c:\kt-stuff\failed.txt', 'w'); | |
| 143 | - fwrite($f, 'fgfgfgfg'); | |
| 144 | - fclose($f);*/ | |
| 143 | + else { | |
| 145 | 144 | $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $newObjectId['message']); |
| 146 | 145 | } |
| 147 | 146 | |
| 148 | 147 | //Expose the responseFeed |
| 149 | 148 | $this->responseFeed = $feed; |
| 150 | 149 | } |
| 150 | + | |
| 151 | + /** | |
| 152 | + * Deals with DELETE actions for folders. | |
| 153 | + * This includes deleting a single folder (with no content) and deleting an entire folder tree | |
| 154 | + * | |
| 155 | + * @return 204 on success, 500 on error | |
| 156 | + */ | |
| 157 | + public function DELETE_action() | |
| 158 | + { | |
| 159 | + // NOTE due to the way KnowledgeTree works with folders this is always going to call deleteTree. | |
| 160 | + // we COULD call deleteObject but when we delete a folder we expect to be trying to delete | |
| 161 | + // the folder and all content. | |
| 162 | + | |
| 163 | + $RepositoryService = new RepositoryService(); | |
| 164 | + $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); | |
| 165 | + | |
| 166 | + $repositories = $RepositoryService->getRepositories(); | |
| 167 | + $repositoryId = $repositories[0]['repositoryId']; | |
| 168 | + | |
| 169 | + // attempt delete | |
| 170 | + $result = $ObjectService->deleteTree($repositoryId, $this->params[0]); | |
| 171 | + | |
| 172 | + // error? | |
| 173 | + if (PEAR::isError($result)) | |
| 174 | + { | |
| 175 | + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $result->getMessage()); | |
| 176 | + //Expose the responseFeed | |
| 177 | + $this->responseFeed = $feed; | |
| 178 | + return null; | |
| 179 | + } | |
| 180 | + | |
| 181 | + // success | |
| 182 | + $this->setStatus(self::STATUS_NO_CONTENT); | |
| 183 | + } | |
| 151 | 184 | |
| 152 | 185 | /** |
| 153 | 186 | * Retrieves children/descendants of the specified folder |
| ... | ... | @@ -217,9 +250,6 @@ class KT_cmis_atom_service_folder extends KT_atom_service { |
| 217 | 250 | |
| 218 | 251 | /** |
| 219 | 252 | * AtomPub Service: types |
| 220 | - * | |
| 221 | - * Returns a list of supported object types | |
| 222 | - * | |
| 223 | 253 | */ |
| 224 | 254 | class KT_cmis_atom_service_types extends KT_atom_service { |
| 225 | 255 | |
| ... | ... | @@ -241,9 +271,6 @@ class KT_cmis_atom_service_types extends KT_atom_service { |
| 241 | 271 | |
| 242 | 272 | /** |
| 243 | 273 | * AtomPub Service: type |
| 244 | - * | |
| 245 | - * Returns the type defintion for the selected type | |
| 246 | - * | |
| 247 | 274 | */ |
| 248 | 275 | class KT_cmis_atom_service_type extends KT_atom_service { |
| 249 | 276 | |
| ... | ... | @@ -318,13 +345,13 @@ class KT_cmis_atom_service_type extends KT_atom_service { |
| 318 | 345 | |
| 319 | 346 | /** |
| 320 | 347 | * AtomPub Service: checkedout |
| 321 | - * | |
| 322 | - * Returns a list of checked out documents for the logged in user | |
| 323 | - * | |
| 324 | 348 | */ |
| 325 | 349 | // NOTE this is always an empty document, underlying API code still to be implemented |
| 326 | 350 | class KT_cmis_atom_service_checkedout extends KT_atom_service { |
| 327 | - | |
| 351 | + | |
| 352 | + /** | |
| 353 | + * Deals with GET actions for checkedout documents. | |
| 354 | + */ | |
| 328 | 355 | public function GET_action() |
| 329 | 356 | { |
| 330 | 357 | $RepositoryService = new RepositoryService(); |
| ... | ... | @@ -350,7 +377,6 @@ class KT_cmis_atom_service_checkedout extends KT_atom_service { |
| 350 | 377 | // TODO get actual most recent update time, only use current if no other available |
| 351 | 378 | $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp())); |
| 352 | 379 | |
| 353 | -//'urn:uuid:checkedout' | |
| 354 | 380 | foreach($checkedout as $document) |
| 355 | 381 | { |
| 356 | 382 | $entry = $feed->newEntry(); |
| ... | ... | @@ -380,12 +406,13 @@ class KT_cmis_atom_service_checkedout extends KT_atom_service { |
| 380 | 406 | |
| 381 | 407 | /** |
| 382 | 408 | * AtomPub Service: document |
| 383 | - * | |
| 384 | - * Returns detail on a particular document | |
| 385 | - * | |
| 386 | 409 | */ |
| 387 | 410 | class KT_cmis_atom_service_document extends KT_atom_service { |
| 388 | 411 | |
| 412 | + /** | |
| 413 | + * Deals with GET actions for documents. | |
| 414 | + * This includes individual document retrieval | |
| 415 | + */ | |
| 389 | 416 | public function GET_action() |
| 390 | 417 | { |
| 391 | 418 | $RepositoryService = new RepositoryService(); |
| ... | ... | @@ -400,16 +427,48 @@ class KT_cmis_atom_service_document extends KT_atom_service { |
| 400 | 427 | $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); |
| 401 | 428 | |
| 402 | 429 | $feed->newField('title', $cmisEntry['properties']['ObjectTypeId']['value'], $feed); |
| 403 | - $feed->newField('id', 'urn:uuid:' . $cmisEntry['properties']['ObjectId']['value'], $feed); ; | |
| 430 | + $feed->newField('id', 'urn:uuid:' . $cmisEntry['properties']['ObjectId']['value'], $feed); | |
| 404 | 431 | |
| 405 | 432 | KT_cmis_atom_service_helper::createObjectEntry($feed, $cmisEntry, $cmisEntry['properties']['ParentId']['value']); |
| 406 | 433 | |
| 407 | - // <cmis:hasMoreItems>false</cmis:hasMoreItems> | |
| 408 | - | |
| 409 | 434 | //Expose the responseFeed |
| 410 | 435 | $this->responseFeed=$feed; |
| 411 | 436 | } |
| 412 | 437 | |
| 438 | + /** | |
| 439 | + * Deals with DELETE actions for documents. | |
| 440 | + * This includes deletion of a specific version of a document (latest version) via deleteObject | |
| 441 | + * as well as deleteAllVersions | |
| 442 | + * | |
| 443 | + * @return 204 on success, 500 on error | |
| 444 | + */ | |
| 445 | + public function DELETE_action() | |
| 446 | + { | |
| 447 | + // NOTE due to the way KnowledgeTree works with documents this is always going to call deleteAllVersions. | |
| 448 | + // we do not have support for deleting only specific versions (this may be added in the future.) | |
| 449 | + | |
| 450 | + $RepositoryService = new RepositoryService(); | |
| 451 | + $VersioningService = new VersioningService(KT_cmis_atom_service_helper::getKt()); | |
| 452 | + | |
| 453 | + $repositories = $RepositoryService->getRepositories(); | |
| 454 | + $repositoryId = $repositories[0]['repositoryId']; | |
| 455 | + | |
| 456 | + // attempt delete | |
| 457 | + $result = $VersioningService->deleteAllVersions($repositoryId, $this->params[0]); | |
| 458 | + | |
| 459 | + // error? | |
| 460 | + if (PEAR::isError($result)) | |
| 461 | + { | |
| 462 | + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $result->getMessage()); | |
| 463 | + //Expose the responseFeed | |
| 464 | + $this->responseFeed = $feed; | |
| 465 | + return null; | |
| 466 | + } | |
| 467 | + | |
| 468 | + // success | |
| 469 | + $this->setStatus(self::STATUS_NO_CONTENT); | |
| 470 | + } | |
| 471 | + | |
| 413 | 472 | } |
| 414 | 473 | |
| 415 | 474 | ?> |
| 416 | 475 | \ No newline at end of file | ... | ... |
webservice/classes/atompub/KT_atom_service.inc.php
| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | class KT_atom_service{ |
| 3 | 3 | const STATUS_OK = '200 OK'; |
| 4 | 4 | const STATUS_NOT_FOUND = '204 No Content'; |
| 5 | + const STATUS_NO_CONTENT = '204 No Content'; | |
| 5 | 6 | const STATUS_NOT_ALLOWED = '204 Not Allowed'; |
| 6 | 7 | const STATUS_CREATED = '201 Created'; |
| 7 | 8 | const STATUS_UPDATED = '200 Updated'; | ... | ... |
webservice/classes/atompub/cmis/ObjectService.inc.php
| ... | ... | @@ -26,8 +26,7 @@ class ObjectService extends KTObjectService { |
| 26 | 26 | $result = parent::getProperties($repositoryId, $objectId, $includeAllowableActions, |
| 27 | 27 | $returnVersion, $filter); |
| 28 | 28 | |
| 29 | - if ($result['status_code'] == 0) | |
| 30 | - { | |
| 29 | + if ($result['status_code'] == 0) { | |
| 31 | 30 | return $result['results']; |
| 32 | 31 | } |
| 33 | 32 | } |
| ... | ... | @@ -45,12 +44,10 @@ class ObjectService extends KTObjectService { |
| 45 | 44 | { |
| 46 | 45 | $result = parent::createFolder($repositoryId, $typeId, $properties, $folderId); |
| 47 | 46 | |
| 48 | - if ($result['status_code'] == 0) | |
| 49 | - { | |
| 47 | + if ($result['status_code'] == 0) { | |
| 50 | 48 | return $result['results']; |
| 51 | 49 | } |
| 52 | - else | |
| 53 | - { | |
| 50 | + else { | |
| 54 | 51 | return $result; |
| 55 | 52 | } |
| 56 | 53 | } |
| ... | ... | @@ -75,15 +72,57 @@ class ObjectService extends KTObjectService { |
| 75 | 72 | { |
| 76 | 73 | $result = parent::createDocument($repositoryId, $typeId, $properties, $folderId, $contentStream, $versioningState); |
| 77 | 74 | |
| 78 | - if ($result['status_code'] == 0) | |
| 79 | - { | |
| 75 | + if ($result['status_code'] == 0) { | |
| 80 | 76 | return $result['results']; |
| 81 | 77 | } |
| 82 | - else | |
| 83 | - { | |
| 78 | + else { | |
| 84 | 79 | return $result; |
| 85 | 80 | } |
| 86 | 81 | } |
| 82 | + | |
| 83 | + /** | |
| 84 | + * Deletes an object from the repository | |
| 85 | + * | |
| 86 | + * @param string $repositoryId | |
| 87 | + * @param string $objectId | |
| 88 | + * @param string $changeToken [optional] | |
| 89 | + * @return boolean true on success, false on failure | |
| 90 | + */ | |
| 91 | + // NOTE Invoking this service method on an object SHALL not delete the entire version series for a Document Object. | |
| 92 | + // To delete an entire version series, use the deleteAllVersions() service | |
| 93 | + function deleteObject($repositoryId, $objectId, $changeToken = null) | |
| 94 | + { | |
| 95 | + $result = parent::deleteObject($repositoryId, $objectId, $changeToken); | |
| 96 | + | |
| 97 | + if ($result['status_code'] == 0) { | |
| 98 | + return $result['results']; | |
| 99 | + } | |
| 100 | + else { | |
| 101 | + return new PEAR_Error($result['message']); | |
| 102 | + } | |
| 103 | + } | |
| 104 | + | |
| 105 | + /** | |
| 106 | + * Deletes an entire tree including all subfolders and other filed objects | |
| 107 | + * | |
| 108 | + * @param string $repositoryId | |
| 109 | + * @param string $objectId | |
| 110 | + * @param string $changeToken [optional] | |
| 111 | + * @param boolean $unfileNonfolderObject [optional] - note that since KnowledgeTree does not allow unfiling this will be ignored | |
| 112 | + * @param boolean $continueOnFailure [optional] - note that since KnowledgeTree does not allow continue on failure this will be ignored | |
| 113 | + * @return array $failedToDelete A list of identifiers of objects in the folder tree that were not deleted. | |
| 114 | + */ | |
| 115 | + public function deleteTree($repositoryId, $objectId, $changeToken = null, $unfileNonfolderObject = 'delete', $continueOnFailure = false) | |
| 116 | + { | |
| 117 | + $result = parent::deleteTree($repositoryId, $objectId, $changeToken, $unfileNonfolderObject, $continueOnFailure); | |
| 118 | + | |
| 119 | + if ($result['status_code'] == 0) { | |
| 120 | + return $result['results']; | |
| 121 | + } | |
| 122 | + else { | |
| 123 | + return new PEAR_Error($result['message']); | |
| 124 | + } | |
| 125 | + } | |
| 87 | 126 | |
| 88 | 127 | } |
| 89 | 128 | ... | ... |
webservice/classes/atompub/cmis/VersioningService.inc.php
0 → 100644
| 1 | +<?php | |
| 2 | + | |
| 3 | +require_once KT_LIB_DIR . '/api/ktcmis/ktcmis.inc.php'; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * CMIS Service class which hooks into the KnowledgeTree interface | |
| 7 | + * for processing of CMIS queries and responses via atompub/webservices | |
| 8 | + */ | |
| 9 | + | |
| 10 | +class VersioningService extends KTVersioningService { | |
| 11 | + | |
| 12 | + /** | |
| 13 | + * Deletes all Document Objects in the specified Version Series, including the Private Working Copy | |
| 14 | + * | |
| 15 | + * @param string $repositoryId | |
| 16 | + * @param string $versionSeriesId | |
| 17 | + * @return boolean true if successful | |
| 18 | + */ | |
| 19 | + public function deleteAllVersions($repositoryId, $versionSeriesId) | |
| 20 | + { | |
| 21 | + $result = parent::deleteAllVersions($repositoryId, $versionSeriesId); | |
| 22 | + | |
| 23 | + if ($result['status_code'] == 0) { | |
| 24 | + return $result['results']; | |
| 25 | + } | |
| 26 | + else { | |
| 27 | + return new PEAR_Error($result['message']); | |
| 28 | + } | |
| 29 | + } | |
| 30 | + | |
| 31 | +} | |
| 32 | + | |
| 33 | +?> | ... | ... |