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 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 +?>
... ...