Commit fb7f6cc4466d1a04251a3c56836eff2a85e887bd

Authored by Paul Barrett
2 parents 6d89e26e 93b8f07a

Merge branch 'cmisatom_delete' into edge

lib/api/ktcmis/ktcmis.inc.php
@@ -56,6 +56,7 @@ require_once(CMIS_DIR . '/exceptions/PermissionDeniedException.inc.php'); @@ -56,6 +56,7 @@ require_once(CMIS_DIR . '/exceptions/PermissionDeniedException.inc.php');
56 require_once(CMIS_DIR . '/services/CMISRepositoryService.inc.php'); 56 require_once(CMIS_DIR . '/services/CMISRepositoryService.inc.php');
57 require_once(CMIS_DIR . '/services/CMISNavigationService.inc.php'); 57 require_once(CMIS_DIR . '/services/CMISNavigationService.inc.php');
58 require_once(CMIS_DIR . '/services/CMISObjectService.inc.php'); 58 require_once(CMIS_DIR . '/services/CMISObjectService.inc.php');
  59 +require_once(CMIS_DIR . '/services/CMISVersioningService.inc.php');
59 require_once(CMIS_DIR . '/util/CMISUtil.inc.php'); 60 require_once(CMIS_DIR . '/util/CMISUtil.inc.php');
60 61
61 /** 62 /**
@@ -73,7 +74,6 @@ class KTCMISBase { @@ -73,7 +74,6 @@ class KTCMISBase {
73 { 74 {
74 // TODO confirm KTAPI instance active??? shouldn't really be responsibility of this code 75 // TODO confirm KTAPI instance active??? shouldn't really be responsibility of this code
75 if (is_null($ktapi) && (!is_null($username) && !is_null($password))) { 76 if (is_null($ktapi) && (!is_null($username) && !is_null($password))) {
76 -// echo ":WGHWTWGWGHW";  
77 $this->startSession($username, $password); 77 $this->startSession($username, $password);
78 } 78 }
79 else { 79 else {
@@ -87,18 +87,15 @@ class KTCMISBase { @@ -87,18 +87,15 @@ class KTCMISBase {
87 // NOTE left in to allow transport protocol to delegate auth to this level, but not actually used in any code at present 87 // NOTE left in to allow transport protocol to delegate auth to this level, but not actually used in any code at present
88 public function startSession($username, $password) 88 public function startSession($username, $password)
89 { 89 {
90 -// echo $username." :: ".$password."<BR>";  
91 // attempt to recover session if one exists 90 // attempt to recover session if one exists
92 if (!is_null(self::$session) && !PEAR::isError(self::$session)) 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 self::$session =& self::$ktapi->get_active_session(self::$session->get_sessionid()); 93 self::$session =& self::$ktapi->get_active_session(self::$session->get_sessionid());
96 } 94 }
97 95
98 // start new session if no existing session or problem getting existing session (expired, etc...) 96 // start new session if no existing session or problem getting existing session (expired, etc...)
99 if (is_null(self::$session) || PEAR::isError(self::$session)) 97 if (is_null(self::$session) || PEAR::isError(self::$session))
100 { 98 {
101 -// echo "ATTEMPT TO START NEW SESSION<BR>\n";  
102 self::$ktapi = new KTAPI(); 99 self::$ktapi = new KTAPI();
103 self::$session =& self::$ktapi->start_session($username, $password); 100 self::$session =& self::$ktapi->start_session($username, $password);
104 } 101 }
@@ -109,7 +106,6 @@ class KTCMISBase { @@ -109,7 +106,6 @@ class KTCMISBase {
109 throw new PermissionDeniedException('You must be authenticated to perform this action'); 106 throw new PermissionDeniedException('You must be authenticated to perform this action');
110 } 107 }
111 108
112 -// print_r(self::$ktapi);  
113 return self::$session; 109 return self::$session;
114 } 110 }
115 111
@@ -594,6 +590,54 @@ class KTObjectService extends KTCMISBase { @@ -594,6 +590,54 @@ class KTObjectService extends KTCMISBase {
594 'results' => $objectId 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 * Sets the content stream data for an existing document 643 * Sets the content stream data for an existing document
@@ -614,7 +658,7 @@ class KTObjectService extends KTCMISBase { @@ -614,7 +658,7 @@ class KTObjectService extends KTCMISBase {
614 function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null) 658 function setContentStream($repositoryId, $documentId, $overwriteFlag, $contentStream, $changeToken = null)
615 { 659 {
616 try { 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 catch (Exception $e) 663 catch (Exception $e)
620 { 664 {
@@ -632,4 +676,60 @@ class KTObjectService extends KTCMISBase { @@ -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,8 +144,9 @@ class CMISDocumentObject extends CMISBaseObject {
144 // NOTE see ktapi::is_latest_version 144 // NOTE see ktapi::is_latest_version
145 $this->_setPropertyInternal('IsLatestMajorVersion', true); 145 $this->_setPropertyInternal('IsLatestMajorVersion', true);
146 $this->_setPropertyInternal('VersionLabel', $objectProperties['version']); 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 if ($objectProperties['checked_out_by'] != 'n/a') 150 if ($objectProperties['checked_out_by'] != 'n/a')
150 { 151 {
151 $checkedOut = true; 152 $checkedOut = true;
lib/api/ktcmis/services/CMISNavigationService.inc.php
@@ -48,7 +48,7 @@ class CMISNavigationService { @@ -48,7 +48,7 @@ class CMISNavigationService {
48 protected $ktapi; 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 * @param object $ktapi The KnowledgeTree API interface 53 * @param object $ktapi The KnowledgeTree API interface
54 */ 54 */
lib/api/ktcmis/services/CMISObjectService.inc.php
@@ -21,11 +21,11 @@ class CMISObjectService { @@ -21,11 +21,11 @@ class CMISObjectService {
21 protected $ktapi; 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 * @param object $ktapi The KnowledgeTree API interface 26 * @param object $ktapi The KnowledgeTree API interface
27 */ 27 */
28 - function setInterface(&$ktapi) 28 + public function setInterface(&$ktapi)
29 { 29 {
30 $this->ktapi = $ktapi; 30 $this->ktapi = $ktapi;
31 } 31 }
@@ -43,8 +43,8 @@ class CMISObjectService { @@ -43,8 +43,8 @@ class CMISObjectService {
43 */ 43 */
44 // TODO optional parameter support 44 // TODO optional parameter support
45 // TODO FilterNotValidException: The Repository SHALL throw this exception if this property filter input parameter is not valid 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 $repository = new CMISRepository($repositoryId); 49 $repository = new CMISRepository($repositoryId);
50 50
@@ -87,8 +87,8 @@ class CMISObjectService { @@ -87,8 +87,8 @@ class CMISObjectService {
87 // TODO throw ConstraintViolationException if: 87 // TODO throw ConstraintViolationException if:
88 // value of any of the properties violates the min/max/required/length constraints 88 // value of any of the properties violates the min/max/required/length constraints
89 // specified in the property definition in the Object-Type. 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 $objectId = null; 93 $objectId = null;
94 94
@@ -344,7 +344,7 @@ class CMISObjectService { @@ -344,7 +344,7 @@ class CMISObjectService {
344 // TODO throw ConstraintViolationException if: 344 // TODO throw ConstraintViolationException if:
345 // value of any of the properties violates the min/max/required/length constraints 345 // value of any of the properties violates the min/max/required/length constraints
346 // specified in the property definition in the Object-Type. 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 $objectId = null; 349 $objectId = null;
350 350
@@ -399,6 +399,148 @@ class CMISObjectService { @@ -399,6 +399,148 @@ class CMISObjectService {
399 399
400 return $objectId; 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 // NOTE this function is presently incomplete and untested. Completion deferred to implementation of Checkout/Checkin 545 // NOTE this function is presently incomplete and untested. Completion deferred to implementation of Checkout/Checkin
404 // functionality 546 // functionality
@@ -428,7 +570,7 @@ class CMISObjectService { @@ -428,7 +570,7 @@ class CMISObjectService {
428 // updateConflictException: The operation is attempting to update an object that is no longer current 570 // updateConflictException: The operation is attempting to update an object that is no longer current
429 // (as determined by the repository). 571 // (as determined by the repository).
430 // versioningException: The repository MAY throw this exception if the object is a non-current Document Version. 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 // if no document id was supplied, we are going to create the underlying physical document 575 // if no document id was supplied, we are going to create the underlying physical document
434 // NOTE while it might have been nice to keep this out of here, KTAPI has no method for creating a document without 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,18 +33,20 @@ When POSTing an Atom Document, the atom fields take precedence over the CMIS pro
33 include_once CMIS_ATOM_LIB_FOLDER . 'RepositoryService.inc.php'; 33 include_once CMIS_ATOM_LIB_FOLDER . 'RepositoryService.inc.php';
34 include_once CMIS_ATOM_LIB_FOLDER . 'NavigationService.inc.php'; 34 include_once CMIS_ATOM_LIB_FOLDER . 'NavigationService.inc.php';
35 include_once CMIS_ATOM_LIB_FOLDER . 'ObjectService.inc.php'; 35 include_once CMIS_ATOM_LIB_FOLDER . 'ObjectService.inc.php';
  36 +include_once CMIS_ATOM_LIB_FOLDER . 'VersioningService.inc.php';
36 include_once 'KT_cmis_atom_service_helper.inc.php'; 37 include_once 'KT_cmis_atom_service_helper.inc.php';
37 38
38 // TODO auth failed response requires WWW-Authenticate: Basic realm="KnowledgeTree DMS" header 39 // TODO auth failed response requires WWW-Authenticate: Basic realm="KnowledgeTree DMS" header
39 40
40 /** 41 /**
41 * AtomPub Service: folder 42 * AtomPub Service: folder
42 - *  
43 - * Returns children, descendants (up to arbitrary depth) or detail for a particular folder  
44 - *  
45 */ 43 */
46 class KT_cmis_atom_service_folder extends KT_atom_service { 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 public function GET_action() 50 public function GET_action()
49 { 51 {
50 $RepositoryService = new RepositoryService(); 52 $RepositoryService = new RepositoryService();
@@ -88,6 +90,10 @@ class KT_cmis_atom_service_folder extends KT_atom_service { @@ -88,6 +90,10 @@ class KT_cmis_atom_service_folder extends KT_atom_service {
88 $this->responseFeed = $feed; 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 public function POST_action() 97 public function POST_action()
92 { 98 {
93 $RepositoryService = new RepositoryService(); 99 $RepositoryService = new RepositoryService();
@@ -131,23 +137,50 @@ class KT_cmis_atom_service_folder extends KT_atom_service { @@ -131,23 +137,50 @@ class KT_cmis_atom_service_folder extends KT_atom_service {
131 137
132 if ($typeId != 'Unknown') 138 if ($typeId != 'Unknown')
133 { 139 {
134 - /*$f = fopen('c:\kt-stuff\here.txt', 'w');  
135 - fwrite($f, 'fgfgfgfg');  
136 - fclose($f);*/  
137 $this->setStatus(self::STATUS_CREATED); 140 $this->setStatus(self::STATUS_CREATED);
138 $feed = KT_cmis_atom_service_helper::getObjectFeed($ObjectService, $repositoryId, $newObjectId, 'POST'); 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 $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $newObjectId['message']); 144 $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $newObjectId['message']);
146 } 145 }
147 146
148 //Expose the responseFeed 147 //Expose the responseFeed
149 $this->responseFeed = $feed; 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 * Retrieves children/descendants of the specified folder 186 * Retrieves children/descendants of the specified folder
@@ -217,9 +250,6 @@ class KT_cmis_atom_service_folder extends KT_atom_service { @@ -217,9 +250,6 @@ class KT_cmis_atom_service_folder extends KT_atom_service {
217 250
218 /** 251 /**
219 * AtomPub Service: types 252 * AtomPub Service: types
220 - *  
221 - * Returns a list of supported object types  
222 - *  
223 */ 253 */
224 class KT_cmis_atom_service_types extends KT_atom_service { 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,9 +271,6 @@ class KT_cmis_atom_service_types extends KT_atom_service {
241 271
242 /** 272 /**
243 * AtomPub Service: type 273 * AtomPub Service: type
244 - *  
245 - * Returns the type defintion for the selected type  
246 - *  
247 */ 274 */
248 class KT_cmis_atom_service_type extends KT_atom_service { 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,13 +345,13 @@ class KT_cmis_atom_service_type extends KT_atom_service {
318 345
319 /** 346 /**
320 * AtomPub Service: checkedout 347 * AtomPub Service: checkedout
321 - *  
322 - * Returns a list of checked out documents for the logged in user  
323 - *  
324 */ 348 */
325 // NOTE this is always an empty document, underlying API code still to be implemented 349 // NOTE this is always an empty document, underlying API code still to be implemented
326 class KT_cmis_atom_service_checkedout extends KT_atom_service { 350 class KT_cmis_atom_service_checkedout extends KT_atom_service {
327 - 351 +
  352 + /**
  353 + * Deals with GET actions for checkedout documents.
  354 + */
328 public function GET_action() 355 public function GET_action()
329 { 356 {
330 $RepositoryService = new RepositoryService(); 357 $RepositoryService = new RepositoryService();
@@ -350,7 +377,6 @@ class KT_cmis_atom_service_checkedout extends KT_atom_service { @@ -350,7 +377,6 @@ class KT_cmis_atom_service_checkedout extends KT_atom_service {
350 // TODO get actual most recent update time, only use current if no other available 377 // TODO get actual most recent update time, only use current if no other available
351 $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp())); 378 $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp()));
352 379
353 -//'urn:uuid:checkedout'  
354 foreach($checkedout as $document) 380 foreach($checkedout as $document)
355 { 381 {
356 $entry = $feed->newEntry(); 382 $entry = $feed->newEntry();
@@ -380,12 +406,13 @@ class KT_cmis_atom_service_checkedout extends KT_atom_service { @@ -380,12 +406,13 @@ class KT_cmis_atom_service_checkedout extends KT_atom_service {
380 406
381 /** 407 /**
382 * AtomPub Service: document 408 * AtomPub Service: document
383 - *  
384 - * Returns detail on a particular document  
385 - *  
386 */ 409 */
387 class KT_cmis_atom_service_document extends KT_atom_service { 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 public function GET_action() 416 public function GET_action()
390 { 417 {
391 $RepositoryService = new RepositoryService(); 418 $RepositoryService = new RepositoryService();
@@ -400,16 +427,48 @@ class KT_cmis_atom_service_document extends KT_atom_service { @@ -400,16 +427,48 @@ class KT_cmis_atom_service_document extends KT_atom_service {
400 $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); 427 $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI);
401 428
402 $feed->newField('title', $cmisEntry['properties']['ObjectTypeId']['value'], $feed); 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 KT_cmis_atom_service_helper::createObjectEntry($feed, $cmisEntry, $cmisEntry['properties']['ParentId']['value']); 432 KT_cmis_atom_service_helper::createObjectEntry($feed, $cmisEntry, $cmisEntry['properties']['ParentId']['value']);
406 433
407 - // <cmis:hasMoreItems>false</cmis:hasMoreItems>  
408 -  
409 //Expose the responseFeed 434 //Expose the responseFeed
410 $this->responseFeed=$feed; 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 \ No newline at end of file 475 \ No newline at end of file
webservice/classes/atompub/KT_atom_service.inc.php
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 class KT_atom_service{ 2 class KT_atom_service{
3 const STATUS_OK = '200 OK'; 3 const STATUS_OK = '200 OK';
4 const STATUS_NOT_FOUND = '204 No Content'; 4 const STATUS_NOT_FOUND = '204 No Content';
  5 + const STATUS_NO_CONTENT = '204 No Content';
5 const STATUS_NOT_ALLOWED = '204 Not Allowed'; 6 const STATUS_NOT_ALLOWED = '204 Not Allowed';
6 const STATUS_CREATED = '201 Created'; 7 const STATUS_CREATED = '201 Created';
7 const STATUS_UPDATED = '200 Updated'; 8 const STATUS_UPDATED = '200 Updated';
webservice/classes/atompub/cmis/ObjectService.inc.php
@@ -26,8 +26,7 @@ class ObjectService extends KTObjectService { @@ -26,8 +26,7 @@ class ObjectService extends KTObjectService {
26 $result = parent::getProperties($repositoryId, $objectId, $includeAllowableActions, 26 $result = parent::getProperties($repositoryId, $objectId, $includeAllowableActions,
27 $returnVersion, $filter); 27 $returnVersion, $filter);
28 28
29 - if ($result['status_code'] == 0)  
30 - { 29 + if ($result['status_code'] == 0) {
31 return $result['results']; 30 return $result['results'];
32 } 31 }
33 } 32 }
@@ -45,12 +44,10 @@ class ObjectService extends KTObjectService { @@ -45,12 +44,10 @@ class ObjectService extends KTObjectService {
45 { 44 {
46 $result = parent::createFolder($repositoryId, $typeId, $properties, $folderId); 45 $result = parent::createFolder($repositoryId, $typeId, $properties, $folderId);
47 46
48 - if ($result['status_code'] == 0)  
49 - { 47 + if ($result['status_code'] == 0) {
50 return $result['results']; 48 return $result['results'];
51 } 49 }
52 - else  
53 - { 50 + else {
54 return $result; 51 return $result;
55 } 52 }
56 } 53 }
@@ -75,15 +72,57 @@ class ObjectService extends KTObjectService { @@ -75,15 +72,57 @@ class ObjectService extends KTObjectService {
75 { 72 {
76 $result = parent::createDocument($repositoryId, $typeId, $properties, $folderId, $contentStream, $versioningState); 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 return $result['results']; 76 return $result['results'];
81 } 77 }
82 - else  
83 - { 78 + else {
84 return $result; 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 +?>