Syncovery 8 adds the PascalScript language, allowing you to customize your profile’s behavior in many ways.
Please note that the syntax for many functions has changed since the Syncovery 8 release candidates. Any parameters named Profile have been removed.
To change a particular behavior, you need to write a hook function and write some code. In most cases, our tech support will write the code for you when we receive a feature request that is best implemented with a customized script. However, we will try to document the available hooks and functions fully so you can experiment on your own.
The script is specified in the profile via the “Pascal Script…” checkmark on the Job settings category tab sheet.
Useful ready-to-use scripts
The following scripts can be used as it is, just paste the script into the PascalScript dialog.
- Convert Disallowed Characters for Windows
This script allows you do have the disallowed characters /:\?|<>“* in file names on the left-hand side. The right-hand side can be Windows compliant storage such as a local NTFS drive. - Simple E-Mail Notifications
This script simplifies the e-mail notifications and can be edited easily to customize your emails. - Send E-Mail Notifications Depending on Profile Results
This script causes e-mail notifications to be sent only if less than 10 or more than 100 files were copied from left to right.
Available PascalScript Hooks
The following hooks are currently available. Additional hooks will be added as needed to fulfill customer requests.
- OnDownloadComplete
- OnGetCustomHeaders
- OnGetProfilePathBeforeListing
- OnGetProfilePathBeforeCopying
- OnHttpPost
- OnIncludeItem
- OnProfileResults
- OnMoveFileToDeletedFolder
- OnReplaceFilenameLeftToRight
- OnReplaceFilenameRightToLeft
- OnScanFolder
- OnSendEmail
- OnUploadComplete
- OnVolumeShadowComplete
- OnBeforeFileCopy
Available PascalScript Functions
The following functions are available for you to call.
- standard functions such as Pos, Copy, Length, Ord
- function ConcatPath(const a,b:UnicodeString; const t: Int64):UnicodeString;
- function ConcatPathWithDelim(const a,b,delim:UnicodeString):UnicodeString;
- function ExtractFileName(const s: UnicodeString):UnicodeString;
- function ExtractFilePath(const s: UnicodeString):UnicodeString;
- function ExtractURLPartAfterServer(const s: UnicodeString):UnicodeString;
- function ExtractFileExt(const s: UnicodeString):UnicodeString;
- function ChangeFileExt(const s,t: UnicodeString):UnicodeString;
- function FileExists(const FileName: UnicodeString):Boolean;
- procedure Log(const s:UnicodeString);
- function Execute(const s:UnicodeString; const TimeOutSeconds: Int64):Int64;
- function GetProfileName:UnicodeString;
- procedure SetProfileResult(const AResultText:UnicodeString);
- function ConnFileExists(const Connector:Opaque; const FileName: UnicodeString):Boolean;
- function ConnCustomFTPCommand(const Connector:Opaque; const ACommand: UnicodeString;const AOkResponse1,AOkResponse2,AOkResponse3:Integer;var ResponseText: UnicodeString):Integer;
- function ConnRenameFile(const Connector:Opaque; const AFromFileName,AToFileName: UnicodeString):Boolean;
- function StringReplace(const Source, OldPattern, NewPattern: UnicodeString;const CaseSensitive:Boolean): UnicodeString;
- function EncodeBase64(const s: UnicodeString):UnicodeString;
- function DecodeBase64(const s: UnicodeString):UnicodeString;
- function SimpleEncrypt(const s: UnicodeString):UnicodeString;
- function SimpleDecrypt(const s: UnicodeString):UnicodeString;
- function GetInput(const s: UnicodeString):UnicodeString;
- function GetPassword(const s: UnicodeString):UnicodeString;
- function ReadRegistryString(const Key,OptName:UnicodeString):UnicodeString;
- procedure WriteRegistryString(const Key,OptName,Value:UnicodeString);
- function GetProfileRunID:UnicodeString;
- procedure ClearBody;
- function GetBodyLine(const i:Integer):UnicodeString;
- procedure SetBodyLine(const i:Integer;const s:UnicodeString);
- procedure DeleteBodyLine(const i:Integer);
- procedure AddBodyText(const s:UnicodeString);
- function GetActionList:UnicodeString;
- function GetProfileSettings:UnicodeString;
- function Now:Double;
- function NowUTC:Double;
- function OffsetFromUTC:Double;
- function TimeToStr(const DateTime: Double):UnicodeString;
- function DateTimeToStr(const DateTime: Double):UnicodeString;
- function DateToStr(const DateTime: Double):UnicodeString’;
- function DateTimeToStrWithFormat(const DateTime: Double;const FormatString:UnicodeString):UnicodeString;
- function MakeSurePathExists(const s:UnicodeString;const isRightSide:Boolean):Boolean;
- function PathDelim:UnicodeString;
- function GetDelim(const Connector: Opaque):UnicodeString;
- function IncludeLeadingPathDelim(const s: UnicodeString):UnicodeString;
- function OpenTextFile(const s:UnicodeString):Opaque;
- function CreateTextFile(const s:UnicodeString):Opaque;
- procedure WriteLine(const F:Opaque;const ALine:UnicodeString);
- function ReadLine(const F:Opaque):UnicodeString;
- procedure CloseFile(const F:Opaque);
OnGetCustomHeaders Sample Script
This hook allows you to add custom HTTP headers to Amazon S3 requests. In future Syncovery versions, this feature can be added to other cloud storages and protocols (on customer request – just ask for it).
function OnGetCustomHeaders(const RelativePath: UnicodeString;const
URL:AnsiString;var MIMEType, Headers: AnsiString;const Connector:
Opaque):Boolean;
var strDate:string;
begin
Result:=true;
Headers:='Cache-Control: 10';
Log('');
Log('Headers Added to '+RelativePath);
Log(Headers);
Log('');
end;
OnIncludeItem Sample Scripts
The OnIncludeItem hook is called individually for each side of the synchronization. The isRightSide parameter indicates which side we are currently looking at.
The following script serves to exclude files without filename extension. Such an exclusion is not possible via the Exclusion Masks.
function OnIncludeItem(const FileName, RelativePath: UnicodeString;
const isRightSide, isFolder:Boolean;
const FileSize:Int64; const FileAttr:LongWord;
const Connector: Opaque):Boolean;
begin
Result:=isFolder or (Pos('.',FileName)>0);
end;
The second example for OnIncludeItem will only process subfolders that contain the file READY.toprocess, as well as any subfolders that exist on the right-hand side.
function OnIncludeItem(const FileName, RelativePath: UnicodeString;
const isRightSide, isFolder:Boolean;
const FileSize:Int64; const FileAttr:LongWord;
const Connector: Opaque):Boolean;
begin
Result:=isRightSide or not isFolder or
ConnFileExists(Connector,ConcatPath(ConcatPath(LeftBasePath,RelativePath,Connector),FileName,Connector)+
'\READY.toprocess');
end;
OnScanFolder Sample Script
This is probably a better way to lookfor ‘READY.toprocess’. The OnScanFolder hook is called just before the folder would be scanned, and knowledge of both sides can be used in the hook.
function OnScanFolder(const FolderLevel: Integer;
const RelativePath, LeftCompletePath, RightCompletePath: UnicodeString;
const LeftExists,RightExists:Boolean;
const LeftConnector, RightConnector: Opaque):Boolean;
begin
Result:=RightExists or ConnFileExists(LeftConnector,LeftCompletePath+'\READY.toprocess');
end;
OnProfileResults Sample Script
This script does nothing, but the function is called with some statistics when a profile completes. An example with some real use can be downloaded near the top of this page (“Send E-Mail Notifications Depending on Profile Results”). This functions return value (“Result”) is ignored.
function OnProfileResults(const FilesCopiedL2R,FilesCopiedR2L:Integer;
const FilesToCopyL2R,FilesToCopyR2L:Integer;
const BytesCopiedL2R,BytesCopiedR2L:Int64;
const ResultString:UnicodeString;
const Error:Boolean):Boolean;
begin
Result:=true;
end;
OnReplaceFilenameLeftToRight Sample Script
This script will rename files when the file is copied from left to right. In our example, ‘-draft’ is added before the filename extension.
function OnReplaceFilenameLeftToRight(const FileName: UnicodeString;
const isFolder: Boolean):UnicodeString;
begin
if isFolder then
Result:=FileName
else
Result:=ChangeFileExt(FileName,'')+'-draft'+ExtractFileExt(FileName);
end;
OnReplaceFilenameRightToLeft Sample Script
If you do a two-way sync, and new files might appear on the right-hand side, we also need a way to rename in the other direction. This sample script removes the ‘-draft’ insert from the names.
function OnReplaceFilenameRightToLeft(const FileName: UnicodeString;
const isFolder: Boolean):UnicodeString;
var ToFind:UnicodeString;
P:Integer;
begin
if isFolder then
Result:=FileName
else begin
ToFind:=''-draft''+ExtractFileExt(FileName);
p:=Pos(ToFind,FileName);
if p>0 then
Result:=Copy(FileName,1,p-1)+ExtractFileExt(FileName)
else
Result:=FileName
end;
end;
OnUploadComplete Sample Script
This script will set permissions for files uploaded via FTP to 777.
function OnUploadComplete(const FileName, LocalFilePath, CompleteURL: UnicodeString;
const isRightSide:Boolean;
const FileSize:Int64;
const Connector: Opaque):Boolean;
var CmdRes:Int64;
ResponseText:UnicodeString;
begin
CmdRes:=ConnCustomFTPCommand(Connector,'SITE CHMOD 777 '+FileName,200,200,200,ResponseText);
Log('Set Permissions for '+FileName+': '+ResponseText);
Result:=true;
end;
OnDownloadComplete Sample Script
This script will rename downloaded files by adding an additional “.downloaded” extension.
function OnDownloadComplete(const FileName, CompleteURL, LocalFilePath: UnicodeString;
const isRightSide:Boolean;
const FileSize:Int64;
const Connector: Opaque):Boolean;
begin
if ConnRenameFile(Connector,CompleteURL,FileName+'.downloaded') then
Log('Renamed '+FileName+' to '+FileName+'.downloaded')
else
Log('Rename failed: '+FileName+' to '+FileName+'.downloaded');
Result:=true;
end;
OnVolumeShadowComplete Sample Script
This script will execute an external command (batch or CMD file) just after a volume shadow has been created.
function OnVolumeShadowComplete(const Volume,ShadowPath:UnicodeString):Boolean;
begin
Result:=Execute('C:\Tests\test.bat',120)=0;
end;
OnBeforeFileCopySample Script
This script will manipulate the destination paths for copying (left to right), by adding an additional subfolder ‘archive’ which is not present on the source side.
function OnBeforeFileCopy(const DirectionIsL2R:Boolean;
var Source,Dest,DestPath,LeftSubPath,RightSubPath:UnicodeString):Boolean;
var AddFolderName,NewDestPath,NewDest,NewRightSubPath:UnicodeString;
begin
Result:=true;
if not DirectionIsL2R then
Exit;
AddFolderName:='archive'
NewDestPath:=ConcatPathWithDelim(DestPath,AddFolderName,PathDelim);
NewDest:=ConcatPathWithDelim(NewDestPath,ExtractFileName(Dest),PathDelim);
NewRightSubPath:=IncludeLeadingPathDelim(ConcatPathWithDelim(RightSubPath,AddFolderName,PathDelim));
if not MakeSurePathExists(NewDestPath,true) then begin
Log('Could not create '+NewDestPath);
Exit;
end;
Log('OnBeforeFileCopy');
Log('Source:'+Source);
Log('DestPath:'+DestPath+' changed to '+NewDestPath);
Log('Dest:'+Dest+' changed to '+NewDest);
Log('LeftSubPath:'+LeftSubPath);
Log('RightSubPath:'+RightSubPath+' changed to '+NewRightSubPath);
Log('');
Dest:=NewDest;
DestPath:=NewDestPath;
RightSubPath:=NewRightSubPath;
end;