PascalScript in Syncovery


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.

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
  • OnBeforeFolderCreate
  • 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;

OnBeforeFolderCreate Sample Script

This script will prevent any folder creation except where necessary to copy files to the destination. You can also use this hook to manipulate folder paths on the destination.

function OnBeforeFolderCreate(const DirectionIsL2R:Boolean;
        var Source,Dest,Reason:UnicodeString):Boolean;
begin
  Result:=false;
  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;

OnGetProfilePathBeforeListing Sample Script

This script uses this hook to specify an individual TEMP folder for the profile. The original purpose of the function is to modify the left or right path and/or the credentials.

function OnGetProfilePathBeforeListing(const isRightSide:Boolean;
        var Path,UserName,Password:UnicodeString;
        var AuthKey,AuthKeyPassword:AnsiString;
        var Port:Integer):Boolean;
begin
  ProfileTempDir:='H:\TEMP';
  Result:=true;
  end;