{ Copyright (c) 2007, The Jam Warehouse Software (Pty) Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: i) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. ii) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. iii) Neither the name of the The Jam Warehouse Software (Pty) Ltd nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } {* This is a Delphi port of the php api for KnowledgeTree WebService. @Author Bjarte Kalstveit Vebjørnsen @Version 1.0 BKV 24.09.2007 Initial revision *} unit uktwsapi; interface uses Classes, SysUtils, SOAPHTTPClient, uwebservice; type /// Base exception class EKTWSAPI_Exception = class(Exception); TKTWSAPI_FolderItem = class; TKTWSAPI_Folder = class; TKTWSAPI_Document = class; TKTWSAPI = class; /// Base class for documents and folders TKTWSAPI_FolderItem = class(TObject) private FKTAPI: TKTWSAPI; /// Handle to KTAPI object FParentId: Integer; /// Id of parent folder function _GetFileSize(FileName: WideString): WideString; function _UploadFile(FileName, Action: WideString; DocumentId: Integer = 0): WideString; function _DownloadFile(Url, LocalPath, FileName: WideString): Boolean; function _SaveBase64StringAsFile(Base64String, LocalPath, FileName: WideString): Boolean; function _LoadFileAsBase64String(FileName: WideString): WideString; public function GetParentFolder:TKTWSAPI_Folder; end; /// Class representing a folder TKTWSAPI_Folder = class(TKTWSAPI_FolderItem) private FFolderName, /// Name of folder FFullPath: WideString; /// Full path to folder FFolderId: Integer; /// Id to folder public constructor Create(KTAPI:TKTWSAPI; FolderDetail: kt_folder_detail); overload; class function Get(KTAPI: TKTWSAPI; FolderId:Integer): TKTWSAPI_Folder; function GetParentFolderId: Integer; function GetFolderName: WideString; function GetFolderId: Integer; function GetFolderByName(FolderName: WideString): TKTWSAPI_Folder; function GetFullPath: WideString; function GetListing(Depth: Integer=1; What: WideString = 'DF'): kt_folder_contents; function GetDocumentByName(Title: WideString): TKTWSAPI_Document; function GetDocumentByFileName(FileName: WideString): TKTWSAPI_Document; function AddFolder(FolderName: WideString): TKTWSAPI_Folder; function Delete(Reason: WideString): Boolean; function Rename(NewName: WideString): Boolean; function Move(TargetFolder:TKTWSAPI_Folder; Reason: WideString): Boolean; function Copy(TargetFolder:TKTWSAPI_Folder; Reason: WideString): Boolean; function AddDocument(FileName: WideString; Title: WideString = ''; DocumentType: WideString = ''): TKTWSAPI_Document; function AddDocumentBase64(FileName: WideString; Title: WideString = ''; DocumentType: WideString = ''): TKTWSAPI_Document; published property FolderName: WideString read FFolderName write FFolderName; property FullPath: WideString read FFullPath write FFullPath; property FolderId: Integer read FFolderId write FFolderId; end; /// Class representing a document TKTWSAPI_Document = class(TKTWSAPI_FolderItem) private FDocumentId: Integer; /// Id of document FTitle, /// Title of document FDocumentType, /// Type of document FVersion, /// Document version FFileName, /// Original filename FCreatedDate, /// Date created FCreatedBy, /// Name of user who created FUpdatedDate, /// Date updated FUpdatedBy, /// Name of user who updated FWorkflow, /// Workflow FWorkflowState, /// Workflow state FCheckoutBy, /// Name of user who checked out FFullPath: WideString; /// Full path to document public constructor Create(KTAPI: TKTWSAPI; DocumentDetail: kt_document_detail); class function Get(KTAPI: TKTWSAPI; DocumentId: Integer; LoadInfo: Boolean = System.True): TKTWSAPI_Document; function Checkin(FileName, Reason: WideString; MajorUpdate: Boolean): Boolean; function Checkout(Reason: WideString; LocalPath: WideString = ''; DownloadFile: Boolean = True): Boolean; function UndoCheckout(Reason: WideString): Boolean; function Download(Version: WideString = ''; LocalPath: WideString = ''; FileName: WideString = ''): Boolean; function Delete(Reason: WideString): Boolean; function ChangeOwner(UserName, Reason: WideString): Boolean; function Copy(Folder: TKTWSAPI_Folder; Reason: WideString; NewTitle: WideString = ''; NewFileName: WideString = ''): Boolean; function Move(Folder: TKTWSAPI_Folder; Reason: WideString; NewTitle: WideString = ''; NewFileName: WideString = ''): Boolean; function ChangeDocumentType(DocumentType: WideString): Boolean; function RenameTitle(NewTitle: WideString): Boolean; function RenameFilename(NewFilename: WideString): Boolean; function StartWorkflow(WorkFlow: WideString): Boolean; function DeleteWorkflow: Boolean; function PeformWorkflowTransition(Transition, Reason: WideString): Boolean; function GetMetadata:kt_metadata_response; function UpdateMetadata(Metadata: kt_metadata_fieldsets): Boolean; function GetTransactionHistory: kt_document_transaction_history_response; function GetVersionHistory: kt_document_version_history_response; function GetLinks: kt_linked_document_response; function Link(DocumentId: Integer; const LinkType: WideString): Boolean; function Unlink(DocumentId: Integer): Boolean; function CheckinBase64(FileName, Reason: WideString; MajorUpdate: Boolean): Boolean; function CheckoutBase64(Reason: WideString; LocalPath: WideString = ''; DownloadFile: Boolean = True): Boolean; function DownloadBase64(Version: WideString = ''; LocalPath: WideString = ''): Boolean; function GetTypes: kt_document_types_response; function GetLinkTypes: kt_document_types_response; property DocumentId: Integer read FDocumentId write FDocumentId; property Title: WideString read FTitle write FTitle; property DocumentType: WideString read FDocumentType write FDocumentType; property Version: WideString read FVersion write FVersion; property FileName: WideString read FFileName write FFileName; property CreatedDate: WideString read FCreatedBy write FCreatedBy; property CreatedBy: WideString read FCreatedBy write FCreatedBy; property UpdatedDate: WideString read FUpdatedDate write FUpdatedDate; property UpdatedBy: WideString read FUpdatedBy write FUpdatedBy; property Workflow: WideString read FWorkflow write FWorkflow; property WorkflowState: WideString read FWorkflowState write FWorkflowState; property CheckoutBy: WideString read FCheckoutBy write FCheckoutBy; property FullPath: WideString read FFullPath write FFullPath; end; /// Api entry point TKTWSAPI = class private FSession, /// Current session id FDownloadPath: WideString; /// Current download path FSoapClient:KnowledgeTreePort; /// Object implementing the /// KnowledgeTreePort interface public constructor Create(); function GetDownloadPath: WideString; function SetDownloadPath(DownloadPath:WideString): Boolean; function StartAnonymousSession(Ip: WideString = ''): WideString; function StartSession(Username, Password: WideString; Ip: WideString = ''): WideString; function ActiveSession(Session: WideString; Ip: WideString = ''): WideString; function Logout: Boolean; function GetRootFolder: TKTWSAPI_Folder; function GetFolderById(FolderId: Integer): TKTWSAPI_Folder; function GetDocumentById(DocumentId: Integer): TKTWSAPI_Document; published property SoapClient: KnowledgeTreePort read FSoapClient write FSoapClient; property Session: WideString read FSession write FSession; end; var KTWebServerUrl: WideString; /// Your webserver url KTUploadUrl: WideString; /// URL to the web-service upload.php KTWebServiceUrl: WideString; /// URL to the web-service wsdl implementation uses IdComponent, IdURI, IdHttp, IdMultipartFormData, IdGlobalProtocols, uPHPSerialize, EncdDecd; const KTWSAPI_ERR_SESSION_IN_USE = 'There is a session already active.'; /// Exception message when session is in use KTWSAPI_ERR_SESSION_NOT_STARTED = 'An active session has not been started.'; /// Exception message when session is not started { TKTWSAPI_FolderItem } {* Finds the filesize of a file. @param FileName Path to the file @return The size of the file as a string *} function TKTWSAPI_FolderItem._GetFileSize(FileName: WideString): WideString; var SearchRec: TSearchRec; sgPath: string; inRetval, I1: Integer; begin sgPath := ExpandFileName(FileName); try inRetval := FindFirst(ExpandFileName(FileName), faAnyFile, SearchRec); if inRetval = 0 then I1 := SearchRec.Size else I1 := -1; finally SysUtils.FindClose(SearchRec); end; Result := IntToStr(I1); end; {* Reads a file into a string and base64 encodes it. @param Base64String Base64 encoded string @param LocalPath Path to load from @param FileName FileName to read @return base64 encoded string @throws EKTWSAPI_Exception 'Could not access file to read.' *} function TKTWSAPI_FolderItem._LoadFileAsBase64String(FileName: WideString): WideString; var Stream: TFileStream; InString: AnsiString; begin if not FileExists(FileName) then raise EKTWSAPI_Exception.Create('Could not access file to read.'); Stream := TFileStream.Create(FileName, fmOpenRead); try SetLength(InString, Stream.Size); Stream.ReadBuffer(InString[1], Length(InString)); Result := EncodeString(InString); finally Stream.Free; end; end; {* Save a Base64 encoded string as a file. @param Base64String Base64 encoded string @param LocalPath Path to save to @param FileName FileName to save as @return true if success *} function TKTWSAPI_FolderItem._SaveBase64StringAsFile(Base64String, LocalPath, FileName: WideString): Boolean; var OutString: AnsiString; Stream: TFileStream; LocalFileName: String; begin LocalFileName := LocalPath + '/' + FileName; OutString := DecodeString(Base64String); Stream := TFileStream.Create(LocalFileName, fmCreate); try // For some reason it fails if I use WideString instead of AnsiString Stream.WriteBuffer(Pointer(OutString)^, Length(OutString)); Result := true; finally Stream.Free; end; end; {* Upload a file to KT. @param FileName Path to upload file @param Action Which action to perform with the file (A = Add, C = Checkin) @param DocumentId Id of the document @return The temporary filename on the server @throws EKTWSAPI_Exception Could not access file to upload. @throws EKTWSAPI_Exception No response from server. @throws EKTWSAPI_Exception Could not upload file. *} function TKTWSAPI_FolderItem._UploadFile(FileName, Action: WideString; DocumentId: Integer): WideString; var UploadName, UploadStatus, SessionId, StatusCode: WideString; PostStream: TIdMultiPartFormDataStream; ResponseStream: TStringStream; Fields: TStringList; HTTP: TIdHTTP; UploadData: TPHPValue; FilesArr: TPHPArray; begin Result := ''; if not FileExists(FileName) then raise EKTWSAPI_Exception.Create('Could not access file to upload.'); // TODO: Check if file is readable if (DocumentId = 0) then UploadName := 'upload_document' else UploadName := 'upload_'+IntToStr(DocumentId); SessionId := FKTAPI.Session; HTTP := TIdHttp.Create(nil); try PostStream := TIdMultiPartFormDataStream.Create; ResponseStream := TStringStream.Create(''); Fields := TStringList.Create; try PostStream.AddFormField('session_id', SessionId); PostStream.AddFormField('action', Action); PostStream.AddFormField('document_id',IntToStr(DocumentId)); PostStream.AddFormField(UploadName,'@' + FileName); PostStream.AddFile('file',FileName,GetMIMETypeFromFile(FileName)); HTTP.Request.ContentType := PostStream.RequestContentType; HTTP.Post(KTUploadURL, PostStream, ResponseStream); if (ResponseStream.DataString = '') then raise EKTWSAPI_Exception.Create('No response from server.'); ExtractStrings(['&'], [' '], pAnsiChar(ResponseStream.DataString), Fields); StatusCode := Copy(Fields[0], Pos('=',Fields[0])+1, 1); if (StatusCode <> '0') then raise EKTWSAPI_Exception.Create('Could not upload file.'); UploadStatus := Copy(Fields[1], Pos('=',Fields[1])+1, Length(Fields[1])); UploadStatus := TIdURI.URLDecode(UploadStatus); UploadData := TPHPSerialize.Unserialize(TIdURI.URLDecode(UploadStatus)); Assert(Assigned(UploadData)); Assert(Assigned(UploadData.AsArray['file'])); try FilesArr := UploadData.AsArray['file'].AsArray; if (FilesArr['size'].AsString <> _GetFileSize(FileName)) then raise EKTWSAPI_Exception.Create('Could not upload file.'); Result := FilesArr['tmp_name'].AsString; finally UploadData.Free; end; finally PostStream.Free; ResponseStream.Free; Fields.Free; end; finally HTTP.Free; end; end; {* Downloads a file from KT. @param Url Http-url to download @param LocalPath Path to save to @param FileName FileName to save as @return true if success @throws EKTWSAPI_Exception Could not create local file *} function TKTWSAPI_FolderItem._DownloadFile(Url, LocalPath, FileName: WideString): Boolean; var Stream: TMemoryStream; LocalFileName: WideString; FP: File; HTTP: TIdHTTP; begin LocalFileName := LocalPath + '/' + FileName; AssignFile(FP, LocalFileName); {$I-} Rewrite(FP,1); {$I+} if (IOResult <> 0) then raise EKTWSAPI_Exception.Create('Could not create local file'); CloseFile(FP); HTTP := TIdHttp.Create(Nil); try Stream := TMemoryStream.Create; try HTTP.Get(Url, Stream); Stream.SaveToFile(LocalFileName); Result := true; finally Stream.Free; end; finally HTTP.Free; end; end; {* Returns a reference to the parent folder. @return Handle to parent folder *} function TKTWSAPI_FolderItem.GetParentFolder: TKTWSAPI_Folder; begin Result := FKTAPI.GetFolderById(FParentId); end; { TKTWSAPI_Folder } {* Constructor @param KTAPI Handle to KTAPI object @param FolderDetail Handle to kt_folder_detail *} constructor TKTWSAPI_Folder.Create(KTAPI: TKTWSAPI; FolderDetail: kt_folder_detail); begin FKTAPI := KTAPI; FFolderId := FolderDetail.id; FFolderName := FolderDetail.folder_name; FParentId := FolderDetail.parent_id; FFullPath := FolderDetail.full_path; end; {* Returns a reference to a TKTWSAPI_Folder @param KTAPI Handle to KTAPI object @param FolderId Id of folder to fetch @return folder handle @throws EKTWSAPI_Exception Response message *} class function TKTWSAPI_Folder.Get(KTAPI: TKTWSAPI; FolderId: Integer): TKTWSAPI_Folder; var FolderDetail: kt_folder_detail; begin Assert(Assigned(KTAPI)); Assert(KTAPI.ClassNameIs('TKTWSAPI')); Result := nil; FolderDetail := KTAPI.SoapClient.get_folder_detail(KTAPI.Session, FolderId); try if (FolderDetail.status_code <> 0) then raise EKTWSAPI_Exception.Create(FolderDetail.message_); Result := TKTWSAPI_Folder.Create(KTAPI, FolderDetail); finally FolderDetail.Free; end; end; {* Returns the parent folder id. @return parent folder id *} function TKTWSAPI_Folder.GetParentFolderId: Integer; begin Result := FParentId; end; {* Returns the folder name. @return folder name *} function TKTWSAPI_Folder.GetFolderName: WideString; begin Result := FFolderName; end; {* Returns the current folder id. @return current folder id *} function TKTWSAPI_Folder.GetFolderId: Integer; begin Result := FFolderId; end; {* Returns the folder based on foldername. @param FolderName Name of folder @return folder handle @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.GetFolderByName(FolderName: WideString): TKTWSAPI_Folder; var Path: WideString; FolderDetail: kt_folder_detail; begin Path := FFullPath + '/' + FolderName; if (System.Copy(Path, 0, 13) = '/Root Folder/') then Path := System.Copy(Path, 13, Length(Path)-1); if (System.Copy(Path, 0, 12) = 'Root Folder/') then Path := System.Copy(Path, 12, Length(Path)-1); FolderDetail := FKTAPI.SoapClient.get_folder_detail_by_name(FKTAPI.Session, Path); if (FolderDetail.status_code <> 0) then raise EKTWSAPI_Exception.Create(FolderDetail.message_); Result := TKTWSAPI_Folder.Create(FKTAPI, FolderDetail); end; {* Returns the full folder path. @return Full folder path *} function TKTWSAPI_Folder.GetFullPath: WideString; begin Result := FFullPath; end; {* Returns the contents of a folder. @param Depth How many sub-folders to fetch @param What to fetch (F=Folders, D=Documents, FD=Both) @return folder contents handle @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.GetListing(Depth: Integer; What: WideString): kt_folder_contents; begin Result := FKTAPI.SoapClient.get_folder_contents( FKTAPI.Session, FFolderId, Depth, What); if (Result.status_code <> 0) then raise EKTWSAPI_Exception.Create(Result.message_); end; {* Returns a document based on title. @param Title Title of document @return document handle @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.GetDocumentByName(Title: WideString): TKTWSAPI_Document; var Path: WideString; DocumentDetail: kt_document_detail; begin Path := FFullPath + '/' + Title; if (System.Copy(Path, 0, 13) = '/Root Folder/') then Path := System.Copy(Path, 13, Length(Path)-1); if (System.Copy(Path, 0, 12) = 'Root Folder/') then Path := System.Copy(Path, 12, Length(Path)-1); DocumentDetail := FKTAPI.SoapClient.get_document_detail_by_name(FKTAPI.Session, Path, 'T'); if (DocumentDetail.status_code <> 0) then raise EKTWSAPI_Exception.Create(DocumentDetail.message_); Result := TKTWSAPI_Document.Create(FKTAPI, DocumentDetail); end; {* Returns a document based on filename. @param FileName Name of file @return document handle @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.GetDocumentByFileName( FileName: WideString): TKTWSAPI_Document; var Path: WideString; DocumentDetail: kt_document_detail; begin Result := nil; Path := FFullPath + '/' + FileName; if (System.Copy(Path, 0, 13) = '/Root Folder/') then Path := System.Copy(Path, 13, Length(Path)-1); if (System.Copy(Path, 0, 12) = 'Root Folder/') then Path := System.Copy(Path, 12, Length(Path)-1); DocumentDetail := FKTAPI.SoapClient.get_document_detail_by_name(FKTAPI.Session, Path, 'F'); try if (DocumentDetail.status_code <> 0) then raise EKTWSAPI_Exception.Create(DocumentDetail.message_); Result := TKTWSAPI_Document.Create(FKTAPI, DocumentDetail); finally DocumentDetail.Free; end; end; {* Adds a sub folder. @param FolderName Name of folder @return new folder handle @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.AddFolder(FolderName: WideString): TKTWSAPI_Folder; var FolderDetail: kt_folder_detail; begin Result := nil; FolderDetail := FKTAPI.SoapClient.create_folder(FKTAPI.Session, FFolderId, FolderName); try if (FolderDetail.status_code <> 0) then raise EKTWSAPI_Exception.Create(FolderDetail.message_); Result := TKTWSAPI_Folder.Create(FKTAPI, FolderDetail); finally FolderDetail.Free; end; end; {* Deletes the current folder. @param Reason Reason for deletetion @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.Delete(Reason: WideString): Boolean; var response: kt_response; begin // TODO: check why no transaction in folder_transactions Result := System.False; response := FKTAPI.SoapClient.delete_folder(FKTAPI.Session, FFolderId, Reason); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Renames the current folder. @param NewName New folder name @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.Rename(NewName: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.rename_folder(FKTAPI.Session, FFolderId, NewName); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Moves a folder to another location. @param TargetFolder Handle to target folder @param Reason Reason for move @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.Move(TargetFolder: TKTWSAPI_Folder; Reason: WideString): Boolean; var response: kt_response; begin Result := System.False; Assert(Assigned(TargetFolder)); Assert(TargetFolder.ClassNameIs('TKTWSAPI_Folder')); response := FKTAPI.SoapClient.move_folder(FKTAPI.Session, FFolderId, TargetFolder.GetFolderId, Reason); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Copies a folder to another location @param TargetFolder Handle to target folder @param Reason Reason for copy @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.Copy(TargetFolder: TKTWSAPI_Folder; Reason: WideString): Boolean; var TargetId: Integer; response: kt_response; begin Result := System.False; Assert(Assigned(TargetFolder)); Assert(TargetFolder.ClassNameIs('TKTWSAPI_Folder')); TargetId := TargetFolder.GetFolderId; response := FKTAPI.SoapClient.copy_folder(FKTAPI.Session, FFolderId, TargetId, Reason); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Adds a document to the current folder. @param FileName FileName to upload @param Title Title to give document @param DocumentType Documenttype of document (Default is 'Default') @return handle to new document @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.AddDocument(FileName, Title, DocumentType: WideString): TKTWSAPI_Document; var BaseName, TempFileName: WideString; DocumentDetail: kt_document_detail; begin Result := nil; BaseName := ExtractFileName(FileName); if (Title = '') then Title := BaseName; if (DocumentType = '') then DocumentType := 'Default'; TempFileName := _UploadFile(FileName, 'A'); DocumentDetail := FKTAPI.FSoapClient.add_document(FKTAPI.Session, FFolderId, Title, BaseName, DocumentType, TempFileName); try if (DocumentDetail.status_code <> 0) then raise EKTWSAPI_Exception.Create(DocumentDetail.message_); Result := TKTWSAPI_Document.Create(FKTAPI, DocumentDetail); finally DocumentDetail.Free; end; end; {* Adds a document to the current folder through web service. @param FileName FileName to upload @param Title Title to give document @param DocumentType Documenttype of document (Default is 'Default') @return handle to new document @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Folder.AddDocumentBase64(FileName, Title, DocumentType: WideString): TKTWSAPI_Document; begin raise EKTWSAPI_Exception.Create('Not implemented yet!'); end; { TKTWSAPI_Document } {* Constructor @param KTAPI Handle to KTAPI object @param DocumentDetail handle to kt_document_detail *} constructor TKTWSAPI_Document.Create(KTAPI: TKTWSAPI; DocumentDetail: kt_document_detail); begin FKTAPI := KTAPI; FDocumentId := DocumentDetail.document_id; FTitle := DocumentDetail.title; FDocumentType := DocumentDetail.document_type; FVersion := DocumentDetail.version; FFilename := DocumentDetail.filename; FCreatedDate := DocumentDetail.created_date; FCreatedBy := DocumentDetail.created_by; FUpdatedDate := DocumentDetail.updated_date; FUpdatedBy := DocumentDetail.updated_by; FParentId := DocumentDetail.folder_id; FWorkflow := DocumentDetail.workflow; FWorkflowState := DocumentDetail.workflow_state; FCheckoutBy := DocumentDetail.checkout_by; FFullPath := DocumentDetail.full_path; end; {* Returns a reference to a document. @param KTAPI Handle to KTAPI object @param DocumentId Id of document @param LoadInfo Call web service to load document details @return handle to document @throws EKTWSAPI_Exception Response message *} class function TKTWSAPI_Document.Get(KTAPI: TKTWSAPI; DocumentId: Integer; LoadInfo: Boolean): TKTWSAPI_Document; var DocumentDetail:kt_document_detail; begin Assert(Assigned(KTAPI)); Assert(KTAPI.ClassNameIs('TKTWSAPI')); if LoadInfo then begin DocumentDetail := KTAPI.SoapClient.get_document_detail(KTAPI.Session, DocumentId); if (DocumentDetail.status_code <> 0) then raise EKTWSAPI_Exception.Create(DocumentDetail.message_); end else begin DocumentDetail := kt_document_detail.Create; DocumentDetail.document_id := DocumentId; end; try Result := TKTWSAPI_Document.Create(KTAPI, DocumentDetail); finally DocumentDetail.Free; end; end; {* Checks in a document. @param FileName Name of file to checkin @param Reason Reason for checkin @param MajorUpdate Checkin as a major update (bumps major version number) @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.Checkin(FileName, Reason: WideString; MajorUpdate: Boolean): Boolean; var BaseName, TempFileName: WideString; response: kt_response; begin Result := System.False; BaseName := ExtractFileName(FileName); TempFileName := _UploadFile(FileName, 'C', FDocumentId); response := FKTAPI.SoapClient.checkin_document(FKTAPI.Session, FDocumentId, BaseName, Reason, TempFileName, MajorUpdate); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Checks out a document. @param Reason Reason for checkout @param LocalPath to save downloaded file to @param DownloadFile if false then checkout will happen without download @return true @throws EKTWSAPI_Exception local path does not exist @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.Checkout(Reason, LocalPath: WideString; DownloadFile: Boolean): Boolean; var response: kt_response; Url: WideString; begin Result := System.False; if (LocalPath = '') then LocalPath := FKTAPI.GetDownloadPath; if not DirectoryExists(LocalPath) then raise EKTWSAPI_Exception.Create('local path does not exist'); // TODO check if Directory is writable response := FKTAPI.SoapClient.checkout_document(FKTAPI.Session, FDocumentId, Reason); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Url := response.message_; if DownloadFile then _DownloadFile(KTWebServerURL+Url, LocalPath, FFileName); Result := System.True; finally response.Free; end; end; {* Undo a document checkout @param Reason Reason for undoing the checkout @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.UndoCheckout(Reason: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.undo_document_checkout(FKTAPI.Session, FDocumentId, Reason); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Download a version of the document @param Version Which version of document to download @param LocalPath Optional path to save file to @param FileName Optional filename to save file as @return true @throws EKTWSAPI_Exception local path does not exist @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.Download(Version, LocalPath, FileName: WideString): Boolean; var response: kt_response; Url: WideString; begin Result := System.False; if (LocalPath = '') then LocalPath := FKTAPI.GetDownloadPath; if (FileName = '') then FileName := FFileName; if (not DirectoryExists(LocalPath)) then raise EKTWSAPI_Exception.Create('local path does not exist'); // TODO: Check if local path is writable response := FKTAPI.SoapClient.download_document(FKTAPI.Session, FDocumentId); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Url := response.message_; Result := _DownloadFile(KTWebServerURL+Url, LocalPath, FileName); finally response.Free; end; end; {* Download a version of the document through webservice @param Version Which version of document to download @param LocalPath Path to save file to @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.DownloadBase64(Version, LocalPath: WideString): Boolean; var response: kt_response; begin Result := System.False; if (LocalPath = '') then LocalPath := FKTAPI.GetDownloadPath; if (FileName = '') then FileName := FFileName; if (not DirectoryExists(LocalPath)) then raise EKTWSAPI_Exception.Create('local path does not exist'); // TODO: Check if local path is writable response := FKTAPI.SoapClient.download_base64_document(FKTAPI.Session, FDocumentId); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := _SaveBase64StringAsFile(response.message_, LocalPath, FileName); finally response.Free; end; end; {* Deletes the current document. @param Reason Reason for delete @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.Delete(Reason: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.delete_document(FKTAPI.Session, FDocumentId, Reason); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Changes the owner of the document. @param UserName Username of new owner @param Reason Reason for the owner change @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.ChangeOwner(UserName, Reason: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.change_document_owner(FKTAPI.Session, FDocumentId, UserName, Reason); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Copies the current document to the specified folder. @param Folder Handle to target folder @param Reason Reason for copy @param NewTitle New title of the file @param NewFileName New filename of the file @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.Copy(Folder: TKTWSAPI_Folder; Reason, NewTitle, NewFileName: WideString): Boolean; var response: kt_response; FolderId: Integer; begin Result := System.False; Assert(Assigned(Folder)); Assert(Folder.ClassNameIs('TKTWSAPI_Folder')); FolderId := Folder.GetFolderId; response := FKTAPI.SoapClient.copy_document(FKTAPI.Session, FDocumentId, FolderId, Reason, NewTitle, NewFileName); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Moves the current document to the specified folder. @param Folder Handle to target folder @param Reason Reason for move @param NewTitle New title of the file @param NewFileName New filename of the file @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.Move(Folder: TKTWSAPI_Folder; Reason, NewTitle, NewFileName: WideString): Boolean; var response: kt_response; FolderId: Integer; begin Result := System.False; Assert(Assigned(Folder)); Assert(Folder.ClassNameIs('TKTWSAPI_Folder')); FolderId := Folder.GetFolderId; response := FKTAPI.SoapClient.move_document(FKTAPI.Session, FDocumentId, FolderId, Reason, NewTitle, NewFileName); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Changes the document type. @param DocumentType DocumentType to change to @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.ChangeDocumentType(DocumentType: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.change_document_type(FKTAPI.Session, FDocumentId, DocumentType); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Renames the title of the current document. @param NewTitle New title of the document @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.RenameTitle(NewTitle: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.rename_document_title(FKTAPI.Session, FDocumentId, NewTitle); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Renames the filename of the current document. @param NewFilename New filename of the document @return true *} function TKTWSAPI_Document.RenameFilename(NewFilename: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.rename_document_filename(FKTAPI.Session, FDocumentId, NewFilename); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Starts a workflow on the current document. @param WorkFlow Workflow @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.StartWorkflow(WorkFlow: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.start_document_workflow(FKTAPI.Session, FDocumentId, WorkFlow); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Removes the workflow process from the current document. @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.DeleteWorkflow: Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.delete_document_workflow(FKTAPI.Session, FDocumentId); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Performs a transition on the current document. @param Transition Transition @param Reason Reason for transition @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.PeformWorkflowTransition(Transition, Reason: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.perform_document_workflow_transition(FKTAPI.Session, FDocumentId, Transition, Reason); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Returns metadata on the document. @return handle to metadata @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.GetMetadata: kt_metadata_response; begin Result := FKTAPI.SoapClient.get_document_metadata(FKTAPI.Session, FDocumentId); if (Result.status_code <> 0) then raise EKTWSAPI_Exception.Create(Result.message_); end; {* Updates the metadata on the current document. @param Metadata Handle to metadata @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.UpdateMetadata( Metadata: kt_metadata_fieldsets): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.update_document_metadata(FKTAPI.Session, FDocumentId, MetaData); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Returns the transaction history on the current document. @return handle to transaction history @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.GetTransactionHistory: kt_document_transaction_history_response; begin Result := FKTAPI.SoapClient.get_document_transaction_history(FKTAPI.Session, FDocumentId); if (Result.status_code <> 0) then raise EKTWSAPI_Exception.Create(Result.message_); end; {* Returns the version history on the current document. @return handle to document version history @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.GetVersionHistory: kt_document_version_history_response; begin Result := FKTAPI.SoapClient.get_document_version_history(FKTAPI.Session, FDocumentId); if (Result.status_code <> 0) then raise EKTWSAPI_Exception.Create(Result.message_); end; {* Returns the links of the current document @return handle to document version history @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.GetLinks: kt_linked_document_response; begin Result := FKTAPI.SoapClient.get_document_links(FKTAPI.Session, FDocumentId); if (Result.status_code <> 0) then raise EKTWSAPI_Exception.Create(Result.message_); end; {* Links the current document to a DocumentId @param DocumentId DocumentId to link to @param LinkType Type of link @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.Link(DocumentId: Integer; const LinkType: WideString): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.link_documents(FKTAPI.Session, FDocumentId, DocumentId, LinkType); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Unlinks the current document from a DocumentId @param DocumentId DocumentId to unlink to @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.Unlink(DocumentId: Integer): Boolean; var response: kt_response; begin Result := System.False; response := FKTAPI.SoapClient.unlink_documents(FKTAPI.Session, FDocumentId, DocumentId); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; {* Checks out a document and downloads document through webservice @param Reason Reason for checkout @param LocalPath to save downloaded file to @return true @throws EKTWSAPI_Exception local path does not exist @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.CheckoutBase64(Reason, LocalPath: WideString; DownloadFile: Boolean): Boolean; var response: kt_response; begin Result := System.False; if (LocalPath = '') then LocalPath := FKTAPI.GetDownloadPath; if not DirectoryExists(LocalPath) then raise EKTWSAPI_Exception.Create('local path does not exist'); // TODO check if Directory is writable response := FKTAPI.SoapClient.checkout_base64_document(FKTAPI.Session, FDocumentId, Reason, DownloadFile); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := True; if DownloadFile then Result := _SaveBase64StringAsFile(response.message_, LocalPath, FFileName); finally response.Free; end; end; {* Gets list of document types @return handle to document types response @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.GetTypes: kt_document_types_response; begin Result := FKTAPI.SoapClient.get_document_types(FKTAPI.Session); if (Result.status_code <> 0) then raise EKTWSAPI_Exception.Create(Result.message_); end; {* Get list of document link types @return handle to document types response @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.GetLinkTypes: kt_document_types_response; begin Result := FKTAPI.SoapClient.get_document_link_types(FKTAPI.Session); if (Result.status_code <> 0) then raise EKTWSAPI_Exception.Create(Result.message_); end; {* Checks in a document and uploads through webservice @param FileName Name of file to checkin @param Reason Reason for checkin @param MajorUpdate Checkin as a major update (bumps major version number) @return true @throws EKTWSAPI_Exception Response message *} function TKTWSAPI_Document.CheckinBase64(FileName, Reason: WideString; MajorUpdate: Boolean): Boolean; var Base64String, BaseName: WideString; response: kt_response; begin Result := System.False; Base64String := _LoadFileAsBase64String(FileName); BaseName := ExtractFileName(FileName); response := FKTAPI.SoapClient.checkin_base64_document(FKTAPI.Session, FDocumentId, BaseName, Reason, Base64String, MajorUpdate); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); Result := System.True; finally response.Free; end; end; { TKTWSAPI } {* Constructor *} constructor Tktwsapi.Create(); begin FSoapClient := GetKnowledgeTreePort(False, KTWebServiceUrl); FDownloadPath := ''; end; {* This returns the default location where documents are downloaded in download() and checkout(). @return string *} function TKTWSAPI.GetDownloadPath: WideString; begin Result := FDownloadPath; end; {* Allows the default location for downloaded documents to be changed. @param DownloadPath Path to writable folder @return true *} function TKTWSAPI.SetDownloadPath(DownloadPath: WideString): Boolean; begin if (not DirectoryExists(DownloadPath)) then raise EKTWSAPI_Exception.Create('local path is not writable'); // TODO : Check if folder is writable FDownloadPath := DownloadPath; Result := System.True; end; {* Starts an anonymous session. @param Ip Users Ip-adress @return Active session id *} function TKTWSAPI.StartAnonymousSession(Ip: WideString): WideString; begin Result := StartSession('anonymous', '', Ip); end; {* Starts a user session. @param Username Users username @param Password Users password @param Ip Users Ip-adress @return Active session id @throws EKTWSAPI_Exception KTWSAPI_ERR_SESSION_IN_USE @throws EKTWSAPI_Exception Response message *} function TKTWSAPI.StartSession(Username, Password, Ip: WideString): WideString; var response: kt_response; begin if (FSession <> '') then raise EKTWSAPI_Exception.Create(KTWSAPI_ERR_SESSION_IN_USE); response := FSoapClient.login(Username, Password, Ip); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); FSession := response.message_; Result := FSession; finally response.Free; end; end; {* Sets an active session. @param Session Session id to activate @param Ip Users Ip-adress @return Active session id @throws EKTWSAPI_Exception KTWSAPI_ERR_SESSION_IN_USE *} function TKTWSAPI.ActiveSession(Session, Ip: WideString): WideString; begin if (FSession <> '') then raise EKTWSAPI_Exception.Create(KTWSAPI_ERR_SESSION_IN_USE); FSession := Session; Result := FSession; end; {* Closes an active session. @return true *} function TKTWSAPI.Logout: Boolean; var response: kt_response; begin Result := System.False; if (FSession = '') then raise EKTWSAPI_Exception.Create(KTWSAPI_ERR_SESSION_NOT_STARTED); response := FSoapClient.logout(FSession); try if (response.status_code <> 0) then raise EKTWSAPI_Exception.Create(response.message_); FSession := ''; Result := System.True; finally response.Free; end; end; {* Returns a reference to the root folder. @return handle to folder *} function TKTWSAPI.GetRootFolder: TKTWSAPI_Folder; begin Result := GetFolderById(1); end; {* Returns a reference to a folder based on id. @param FolderId Id of folder @return handle to folder *} function TKTWSAPI.GetFolderById(FolderId: Integer): TKTWSAPI_Folder; begin if FSession = '' then raise EKTWSAPI_Exception.Create('A session is not active'); Result := TKTWSAPI_Folder.Get(Self, FolderId); end; {* Returns a reference to a document based on id. @param DocumentId Id of document @return handle to document *} function TKTWSAPI.GetDocumentById(DocumentId: Integer): TKTWSAPI_Document; begin if FSession = '' then raise EKTWSAPI_Exception.Create('A session is not active'); Result := TKTWSAPI_Document.Get(Self, DocumentId) end; end.