/** * Deals with GET actions for folders. * This includes children and tree/descendant listings as well as individual folder retrieval */ public function GET_action() { $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); // TODO implement full path/node separation as with Alfresco - i.e. path requests come in on path/ and node requests come in on node/ // path request e.g.: path/Root Folder/DroppedDocuments // node request e.g.: node/F1/children // node request e.g.: node/F2/parent // node request e.g.: node/F2 if (urldecode($this->params[0]) == 'Root Folder') { $folderId = CMISUtil::encodeObjectId(FOLDER, 1); $folderName = urldecode($this->params[0]); } else { if ($this->params[0] == 'path') { $ktapi =& KT_cmis_atom_service_helper::getKt(); $folderId = KT_cmis_atom_service_helper::getFolderId($this->params, $ktapi); } else { if ($this->params[1] == 'children' || $this->params[1] == 'descendants') { $folderId = $this->params[0]; $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); $response = $ObjectService->getProperties($repositoryId, $folderId, false, false); if (PEAR::isError($response)) { $feed = KT_cmis_atom_service_helper::getErrorFeed($this, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response->getMessage()); $this->responseFeed = $feed; return null; } $folderName = $response['properties']['Name']['value']; } else { if ($this->params[1] == 'parent') { // abstract this to be used also by the document service (and the PWC service?) ??? // alternatively use getFolderParent here makes sense and use getObjectParents when document service? $folderId = $this->params[0]; $NavigationService = new NavigationService(KT_cmis_atom_service_helper::getKt()); $response = $NavigationService->getFolderParent($repositoryId, $folderId, false, false, false); if (PEAR::isError($response)) { $feed = KT_cmis_atom_service_helper::getErrorFeed($this, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response->getMessage()); $this->responseFeed = $feed; return null; } // we know that a folder will only have one parent, so we can assume element 0 $folderId = $response[0]['properties']['ObjectId']['value']; $folderName = $response[0]['properties']['Name']['value']; } else { $folderId = $this->params[0]; } } } } if (!empty($this->params[1]) && ($this->params[1] == 'children' || $this->params[1] == 'descendants')) { $NavigationService = new NavigationService(KT_cmis_atom_service_helper::getKt()); $feed = $this->getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $folderName, $this->params[1]); } else { $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $folderId); } // Expose the responseFeed $this->responseFeed = $feed; }
/** * Deals with GET actions for folders. * This includes children and tree/descendant listings as well as individual folder retrieval */ public function GET_action() { $RepositoryService = new RepositoryService(); $repositories = $RepositoryService->getRepositories(); $repositoryId = $repositories[0]['repositoryId']; // TODO implement full path/node separation as with Alfresco - i.e. path requests come in on path/ and node requests come in on node/ // path request e.g.: Root Folder/DroppedDocuments // node request e.g.: F1/children // node request e.g.: F2 if (urldecode($this->params[0]) == 'Root Folder') { $folderId = CMISUtil::encodeObjectId('Folder', 1); $folderName = urldecode($this->params[0]); } else { if ($this->params[0] == 'path') { $ktapi =& KT_cmis_atom_service_helper::getKt(); $folderId = KT_cmis_atom_service_helper::getFolderId($this->params, $ktapi); } else { if ($this->params[1] == 'children' || $this->params[1] == 'descendants') { $folderId = $this->params[0]; $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); $response = $ObjectService->getProperties($repositoryId, $folderId, false, false); if (PEAR::isError($response)) { $feed = KT_cmis_atom_service_helper::getErrorFeed($this, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response->getMessage()); $this->responseFeed = $feed; return null; } $folderName = $response['properties']['Name']['value']; } else { $folderId = $this->params[0]; } } } if (!empty($this->params[1]) && ($this->params[1] == 'children' || $this->params[1] == 'descendants')) { $NavigationService = new NavigationService(KT_cmis_atom_service_helper::getKt()); $feed = $this->getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $folderName, $this->params[1]); } else { $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $folderId); } //Expose the responseFeed $this->responseFeed = $feed; }
public function deleteTree($repositoryId, $objectId, $changeToken = null, $unfileNonfolderObject = 'delete', $continueOnFailure = false) { // NOTE since we do not currently allow partial deletes this will always be empty // (unless there is a failure at the requested folder level - what do we do then? exception or array of all objects?) $failedToDelete = array(); // determine object type and internal id $objectId = CMISUtil::decodeObjectId($objectId, $typeId); // throw updateConflictException if the operation is attempting to update an object that is no longer current (as determined by the repository). $exists = true; if ($typeId == 'Folder') { $object = $this->ktapi->get_folder_by_id($objectId); if (PEAR::isError($object)) { $exists = false; } } else { throw new RuntimeException('Cannot call deleteTree on a non-folder object.'); } if (!$exists) { throw new updateConflictException('Unable to delete the object as it cannot be found.'); } // attempt to delete tree, throw RuntimeException if failed // TODO add a default reason // TODO add the electronic signature capability $result = $this->ktapi->delete_folder($objectId, $reason, $sig_username, $sig_password); // if there was an error performing the delete, throw exception // TODO list of objects which failed in $failedToDelete array; // since we do not delete the folder or any contents if anything cannot be deleted, this will contain the entire tree listing // NOTE once we do this we will need to deal with it externally as well, since we can no longer just catch an exception. if ($result['status_code'] == 1) { // TODO consider sending back full properties on each object? // Not sure yet what this output may be used for by a client, and the current specification (0.61c) says: // "A list of identifiers of objects in the folder tree that were not deleted", so let's leave it returning just ids for now. $failedToDelete[] = CMISUtil::encodeObjectId('Folder', $objectId); $folderContents = $object->get_full_listing(); foreach ($folderContents as $folderObject) { if ($folderObject['item_type'] == 'F') { $type = 'Folder'; } else { if ($folderObject['item_type'] == 'D') { $type = 'Document'; } else { continue; } } // TODO find out whether this is meant to be a hierarchical list or simply a list. // for now we are just returning the list in non-hierarchical form // (seeing as we don't really know how CMIS AtomPub is planning to deal with hierarchies at this time.) $failedToDelete[] = CMISUtil::encodeObjectId($type, $folderObject['id']); } } return $failedToDelete; }
private function get($folderId) { $object = $this->ktapi->get_folder_by_id((int) $folderId); // error? if (PEAR::isError($object)) { // throw an exception? return $object; } // static $allowedChildObjectTypeIds; $objectProperties = $object->get_detail(); $this->_setPropertyInternal('ObjectId', CMISUtil::encodeObjectId($this->typeId, $objectProperties['id'])); // prevent doubled '/' chars $uri = preg_replace_callback('/([^:]\\/)\\//', create_function('$matches', 'return $matches[1];'), $this->uri . '/browse.php?fFolderId=' . $objectProperties['id']); // TODO this url is probably incorrect...needs to be checked // $this->_setPropertyInternal('Uri', $uri); $this->_setPropertyInternal('Uri', ''); // TODO what is this? Assuming it is the object type id, and not OUR document type? $this->_setPropertyInternal('ObjectTypeId', $this->getAttribute('typeId')); // Needed to distinguish type $this->_setPropertyInternal('BaseType', strtolower($this->getAttribute('typeId'))); $this->_setPropertyInternal('CreatedBy', $objectProperties['created_by']); // TODO cannot currently retrieve via ktapi or regular folder code - add as with created by $this->_setPropertyInternal('CreationDate', $objectProperties['created_date']); // TODO cannot currently retrieve via ktapi or regular folder code - add as with created by $this->_setPropertyInternal('LastModifiedBy', $objectProperties['modified_by']); // TODO cannot currently retrieve via ktapi or regular folder code - add as with created by $this->_setPropertyInternal('LastModificationDate', $objectProperties['modified_date']); $this->_setPropertyInternal('ChangeToken', null); $this->_setPropertyInternal('Name', $objectProperties['folder_name']); $this->_setPropertyInternal('ParentId', $objectProperties['parent_id']); $this->_setPropertyInternal('AllowedChildObjectTypeIds', array('Document', 'Folder')); $this->_setPropertyInternal('Author', $objectProperties['created_by']); }
function testVersioningService() { $VersioningService = new KTVersioningService($this->ktapi); $NavigationService = new KTNavigationService($this->ktapi); // set up the folder/doc tree structure with which we will be testing $this->createFolderDocStructure(); $RepositoryService = new KTRepositoryService(); $response = $RepositoryService->getRepositories(); $this->assertEqual($response['status_code'], 0); $this->assertNotNull($response['results'][0]); // // we only expect one repository $repository = $response['results'][0]; $repositoryId = $repository['repositoryId']; // test deletion of document via deleteAllVersions $versionSeriesId = 'D' . $this->docs[0]->get_documentid(); $response = $VersioningService->deleteAllVersions($repositoryId, $versionSeriesId); $this->assertEqual($response['status_code'], 0); $this->assertNotNull($response['results']); // TODO test checkout of document $documentId = CMISUtil::encodeObjectId('Document', $this->docs[1]->get_documentid()); $response = $VersioningService->checkOut($repositoryId, $documentId); $this->assertEqual($response['status_code'], 0); $this->assertNotNull($response['results']); //// // use this id for cancel checkout and checkin, not the original document id //// $pwcId = $response['results']; $pwcId = CMISUtil::encodeObjectId(DOCUMENT, $this->docs[1]->get_documentid()); // try again, this time it should fail - not working at the moment as ktapi registers the same user for download // even if already checked out, so no error is generated unless a different user attempts to do a checkout /* $response = $VersioningService->checkOut($repositoryId, $documentId); $this->assertEqual($response['status_code'], 1); $this->assertNotNull($response['message']); */ // test cancel checkout // echo "WITH: $pwcId<BR>"; $response = $VersioningService->cancelCheckOut($repositoryId, $pwcId); $this->assertEqual($response['status_code'], 0); $this->assertNotNull($response['results']); // test cancel checkout of document no longer checked out $response = $VersioningService->cancelCheckOut($repositoryId, $pwcId); $this->assertEqual($response['status_code'], 1); $this->assertNotNull($response['message']); // test listing of checked out documents // first check out the document again :) $response = $VersioningService->checkOut($repositoryId, $documentId); // now check that it appears in the listing $response = $NavigationService->getCheckedOutDocs($repositoryId, false, false); $this->assertEqual($response['status_code'], 0); $this->assertNotNull($response['results']); $this->assertTrue($this->findInPropertiesArray('ObjectId', $documentId, $response['results'])); // now let's cancel the checkout so that we can delete later during cleanup :) $response = $VersioningService->cancelCheckOut($repositoryId, $pwcId); // TODO test checkin // TODO add testing of failure conditions - e.g. checked out/immutable document (for all appropriate functions) // tear down the folder/doc tree structure with which we were testing $this->cleanupFolderDocStructure(); }
private function get($documentId) { $object = $this->ktapi->get_document_by_id((int) $documentId); // document does not exist? if (PEAR::isError($object)) { throw new ObjectNotFoundException('The document you are trying to access does not exist or is inaccessible'); } $objectProperties = $object->get_detail(); $this->_setPropertyInternal('ObjectId', CMISUtil::encodeObjectId($this->typeId, $objectProperties['document_id'])); // prevent doubled '/' chars $uri = preg_replace_callback('/([^:]\\/)\\//', create_function('$matches', 'return $matches[1];'), $this->uri . 'action.php?kt_path_info=ktnetwork.inlineview.actions.view&fDocumentId=' . $objectProperties['document_id']); // NOTE what about instead creating a downloadable version with appropriate link? see ktapi::download_document // also ktapidocument::get_download_url // $this->_setPropertyInternal('Uri', $uri); $this->_setPropertyInternal('Uri', ''); // TODO what is this? Assuming it is the object type id, and not OUR document type? $this->_setPropertyInternal('ObjectTypeId', $this->getAttribute('typeId')); // Needed to distinguish type $this->_setPropertyInternal('BaseType', strtolower($this->getAttribute('typeId'))); $this->_setPropertyInternal('CreatedBy', $objectProperties['created_by']); $this->_setPropertyInternal('CreationDate', $objectProperties['created_date']); $this->_setPropertyInternal('LastModifiedBy', $objectProperties['modified_by']); $this->_setPropertyInternal('LastModificationDate', $objectProperties['modified_date']); $this->_setPropertyInternal('ChangeToken', null); $this->_setPropertyInternal('Name', $objectProperties['title']); $this->_setPropertyInternal('ParentId', $objectProperties['folder_id']); $this->_setPropertyInternal('IsImmutable', $objectProperties['is_immutable']); // NOTE if access to older versions is allowed, this will need to be checked, else just set to yes // see ktapi::get_document_version_history // NOTE see ktapi::is_latest_version $this->_setPropertyInternal('IsLatestVersion', true); $this->_setPropertyInternal('IsMajorVersion', strstr($objectProperties['version'], '.') ? false : true); // NOTE if access to older versions is allowed, this will need to be checked, else just set to yes // see ktapi::get_document_version_history // NOTE see ktapi::is_latest_version $this->_setPropertyInternal('IsLatestMajorVersion', true); $this->_setPropertyInternal('VersionLabel', $objectProperties['version']); // VersionSeriesId should be the id of the latest version // NOTE this may change in the future but is easiest for the current implementation $this->_setPropertyInternal('VersionSeriesId', $objectProperties['version']); if ($objectProperties['checked_out_by'] != 'n/a') { $checkedOut = true; $checkedOutBy = $objectProperties['checked_out_by']; // TODO this is not what it will actually be, just a convenient placeholder $checkedOutId = $objectProperties['version']; } else { $checkedOut = false; $checkedOutBy = null; $checkedOutId = null; } $this->_setPropertyInternal('IsVersionSeriesCheckedOut', $checkedOut); $this->_setPropertyInternal('VersionSeriesCheckedOutBy', $checkedOutBy); // TODO presumably this is the ID of the Private Working Copy created on checkout? // will find out more when we do checkout/checkin $this->_setPropertyInternal('VersionSeriesCheckedOutId', $checkedOutId); // TODO currently not returned by KnowledgeTree? $this->_setPropertyInternal('CheckinComment', null); $this->_setPropertyInternal('ContentStreamLength', $objectProperties['filesize']); $this->_setPropertyInternal('ContentStreamMimeType', $objectProperties['mime_type']); $this->_setPropertyInternal('ContentStreamFilename', $objectProperties['filename']); $this->_setPropertyInternal('ContentStreamUri', $this->getProperty('ObjectId') . '/' . $objectProperties['filename']); $this->_setPropertyInternal('Author', $objectProperties['created_by']); }
public function checkIn($repositoryId, $documentId, $major, $contentStream = null, $changeToken = '', $properties = array(), $checkinComment = '') { $documentId = CMISUtil::decodeObjectId($documentId, $typeId); // throw updateConflictException if the operation is attempting to update an object that is no longer current (as determined by the repository). try { $pwc = new CMISDocumentObject($documentId, $this->ktapi); } catch (exception $e) { throw new UpdateConflictException($e->getMessage()); } // throw exception if the object is not versionable if (!$pwc->getAttribute('versionable')) { throw new ConstraintViolationException('This document is not versionable and may not be checked in'); } $RepositoryService = new CMISRepositoryService(); try { $typeDefinition = $RepositoryService->getTypeDefinition($repositoryId, $typeId); } catch (exception $e) { // if we can't get the type definition, then we can't store the content throw new StorageException($e->getMessage()); } if ($typeDefinition['attributes']['contentStreamAllowed'] == 'notAllowed' && !empty($contentStream)) { throw new StreamNotSupportedException('Content Streams are not supported'); } // check that this is the latest version if ($pwc->getProperty('IsLatestVersion') != true) { throw new VersioningException('The document is not the latest version and cannot be checked in'); } // now do the checkin $tempfilename = CMISUtil::createTemporaryFile($contentStream); $response = $this->ktapi->checkin_document($documentId, $pwc->getProperty('ContentStreamFilename'), $reason, $tempfilename, $major, $sig_username, $sig_password); // if there was any error in cancelling the checkout if ($response['status_code'] == 1) { throw new RuntimeException('There was an error checking in the document: ' . $response['message']); } return CMISUtil::encodeObjectId(DOCUMENT, $documentId); }
public static function getFolderId($path, &$ktapi) { // lose first item array_shift($path); $numQ = count($path); $numFolders = $numQ; $folderId = 1; $start = 0; while ($start < $numFolders) { $name = $path[$numQ - $numFolders + $start]; // fix for possible url encoding issue $name = str_replace('%2520', '%20', $name); $folderName = urldecode($name); $folder = $ktapi->get_folder_by_name($folderName, $folderId); $folderId = $folder->get_folderid(); ++$start; } return CMISUtil::encodeObjectId('Folder', $folderId); }
public function checkOut($repositoryId, &$documentId, $changeToken = '') { $contentCopied = false; $documentId = CMISUtil::decodeObjectId($documentId, $typeId); // NOTE We are not planning on persisting the PWC beyond the current session, it will be re-created on access of the checked out document // TODO consider persisting in the database? How will this relate to JSR if we are switching to that? // NOTE within the current system it is assumed if a new document metadata version is created that this is the latest version of the document // TODO see if there is an easy way to modify this, else we may not have an easy way to persist PWC objects // throw updateConflictException if the operation is attempting to update an object that is no longer current (as determined by the repository). try { $pwc = new CMISDocumentObject($documentId, $this->ktapi); } catch (exception $e) { throw new UpdateConflictException($e->getMessage()); } // throw exception if the object is not versionable if (!$pwc->getAttribute('versionable')) { throw new ConstraintViolationException('This document is not versionable and may not be checked out'); } // NOTE KTAPI as currently implemented does not give a direct response which indicates if the document is already checked out, // as long as the same use is calling the checkout again, so should we add a check here specifically? // run checkout process - set $download = false (third function argument) as we want to return the document content via the contentStream $response = $this->ktapi->checkout_document($documentId, 'CMIS Checkout Action', false, $sig_username, $sig_password); // if there was an error, throw an exception if ($response['status_code'] == 1) { throw new StorageException($response['message']); } // if successful, set $contentCopied = true; unless contentStream is not set if ($pwc->getProperty('ContentStreamFilename') != '') { $contentCopied = true; } $documentId = CMISUtil::encodeObjectId('Document', $documentId); // mark document object as checked out $pwc->setProperty('IsVersionSeriesCheckedOut', true); $userName = ''; $user = $this->ktapi->get_user(); if (!PEAR::isError($user)) { $userName = $user->getName(); } $pwc->setProperty('VersionSeriesCheckedOutBy', $userName); $pwc->setProperty('VersionSeriesCheckedOutId', $documentId); return $contentCopied; }