ACF for 4D: External functions

ACF Plugin for 4D adds a runtime for ACF packages inside a 4D database. ACF packages are plain source files that can be compiled into binary packages and loaded by the plugin. The same ACFcompiler is used for FileMaker and 4D, and the compiled package format is shared between the platforms. The host-specific part is the runtime: FileMaker and 4D each provide their own integration layer for variables, dialogs, files, SQL, blobs, and host expressions.

In 4D, ACF is normally used as a companion to 4D methods. You can keep business logic, report generation, document production, file handling, and integration code in external ACF source files, then call the compiled functions from 4D with typed runner functions.

Calling ACF functions from 4D

ACF function parameters are passed as a 4D object. Each object property should match the ACF parameter name.

C_OBJECT($params)
C_TEXT($textResult)

$params:=New object("name"; "Smith"; "age"; 40)
$textResult:=ACF_RunText("MyFunction"; $params)

Use the runner matching the ACF function return type:

ACF return type 4D Type 4D runner
string C_TEXT ACF_RunText
int / long C_LONGINT ACF_RunLong
float C_REAL ACF_RunReal
bool C_BOOLEAN ACF_RunBoolean
JSON C_OBJECT ACF_RunObject
container CBLOB / CPICTURE ACF_RunBlob

Blob and picture parameters can be passed by reference using a string marker in the parameter object:

$params:=New object("image"; "pict:[ACF_Packages]MediaPicture")
$params:=New object("data"; "blob:vxBlob")

Loading and compiling packages

An ACF source file starts with a package declaration:

package MyPackage "Short package description";

function hello(string name)
    return "Hello " + name;
end

The source can be compiled and installed from 4D:

C_TEXT($source; $compiled; $result)

$source:=ACFU_LoadFile("~/Documents/ACF/MyPackage.acf")
$compiled:=ACF_Compile($source; 0)
$result:=ACF_InstallBase64Text($compiled; 0)

For development, ACF_GetAllPrototypes4D is useful. It returns 4D-oriented call examples that can be pasted into a scratch method.

C_TEXT($prototypes)

$prototypes:=ACF_GetAllPrototypes4D

MainFunctions

MainFunctions are the core 4D entry points for registration, compilation, package installation, prototype export, runtime execution, dialogs, and progress windows.

Registration and version

Function Purpose
ACF_RegisterPlugin(licenseName; licenseText) Register the plugin with license name and license text. 4D licenses are stored separately from FileMaker licenses.
ACF_Version Return the numeric plugin version as text.
ACF_Version(format) Return formatted version information. Common value for format: "autoupdate".

Example:

C_TEXT($result)

$result:=ACF_RegisterPlugin("MyCompany"; [Settings]ACF_LicenseText)
ALERT($result)
C_TEXT($version)

$version:=ACF_Version("autoupdate")

Compile and install

Function Purpose
ACF_Compile(sourceText; debugLevel) Compile ACF source text. Returns a Base64 encoded binary package.
ACF_CompileFile(sourcePath; debugLevel) Compile an ACF source file from a path.
ACF_InstallBase64Text(base64Package; debugLevel) Install a compiled Base64 package into plugin memory.
ACF_InstallFile(packagePath; debugLevel) Install a compiled package from a file.
ACF_GetConsoleOutput Return console and compiler/runtime log output.

Example:

C_TEXT($source; $binary; $result; $console)

$source:=ACFU_LoadFile("~/Documents/ACF/MyPackage.acf")
$binary:=ACF_Compile($source; 0)
$result:=ACF_InstallBase64Text($binary; 0)
$console:=ACF_GetConsoleOutput

Prototypes

Function Purpose
ACF_GetAllPrototypes Return prototypes for ACF and 4D usage.
ACF_GetAllPrototypes4D Return 4D-only call examples suitable for a scratch method.

Example:

C_TEXT($prototypes)

$prototypes:=ACF_GetAllPrototypes4D

Runtime runners

Function Return type
ACF_RunText(functionName; parameters) Text
ACF_RunLong(functionName; parameters) Long integer
ACF_RunReal(functionName; parameters) Real
ACF_RunBoolean(functionName; parameters) Boolean as long integer (0 or 1)
ACF_RunObject(functionName; parameters) Object
ACF_RunBlob(functionName; parameters) Blob

Examples:

C_OBJECT($params; $objectResult)
C_TEXT($textResult)
C_LONGINT($longResult; $boolResult)
C_REAL($realResult)
BLOB($blobResult)

$params:=New object("number"; "123401")
$textResult:=ACF_RunText("AddMod10"; $params)

$params:=New object("degrees"; 180)
$realResult:=ACF_RunReal("degreesToRadians"; $params)

$params:=New object("path"; "~/Desktop/test.txt")
$boolResult:=ACF_RunBoolean("FileExists"; $params)

$params:=New object()
$objectResult:=ACF_RunObject("SQLTest4D"; $params)

$params:=New object("test"; "Dette er en blob")
$blobResult:=ACF_RunBlob("returnAsBlob"; $params)

Dialogs

Function Purpose
ACF_OpenDialog(dialogDefinition) Open an ACF JSON-defined dialog. Returns a 4D object with button and values.
ACF_DialogApplyValues(dialogDefinition; values) Apply values to a dialog definition object.

Example:

C_OBJECT($definition; $objectResult)

$definition:=New object(\
"title"; "Dialog title"; \
"subtitle"; "Configure the values below."; \
"size"; New collection(620; 430); \
"labelPosition"; "left"; \
"rows"; New collection(\
New object(\
"fields"; New collection(\
New object("name"; "Fornavn"; "label"; "Fornavn"; "type"; "text"); \
New object("name"; "Etternavn"; "label"; "Etternavn"; "type"; "text"); \
New object("name"; "Comments"; "label"; "Kommentarer"; "type"; "textarea"; "height"; 80); \
New object("name"; "Field4"; "label"; "Field4"; "type"; "date")\
); \
"values"; New collection(""; ""; ""; "")\
)\
); \
"buttons"; New collection(\
New object("id"; "ok"; "label"; "OK"; "default"; True); \
New object("id"; "cancel"; "label"; "Cancel"; "cancel"; True)\
)\
)

$objectResult:=ACF_OpenDialog($definition)

Progress windows

Function Purpose
ACF_ProgressOpen(title; message; progressPercent) Open a progress window. Returns a progress id.
ACF_ProgressUpdate(progressID; progressPercent; message) Update an existing progress window.
ACF_ProgressClose(progressID) Close a progress window.

Example:

C_LONGINT($progress)

$progress:=ACF_ProgressOpen("Export"; "Preparing"; 0)
$ok:=ACF_ProgressUpdate($progress; 50; "Half way")
$ok:=ACF_ProgressClose($progress)

ACF Built in functions

The ACFU_ functions are public wrappers around selected functions from the built-in bootstrap package. They are useful directly from 4D methods, without writing a separate ACF wrapper function.

File loading and saving

Function Purpose
ACFU_LoadFile(path) Read a text file.
ACFU_SaveFile(path; content) Save text to a file.
ACFU_SelectAndGetFile(startFolder; prompt) Select a file and return its text content.
ACFU_SelectFileOnly(startFolder; prompt) Select a file and return the path.
ACFU_SelectFolder(prompt) Select a folder and return the path.
ACFU_SaveFileDialogue(prompt; proposedFolder; proposedName) Show a save-file dialog and return the selected path.

Example:

C_TEXT($path; $content)

$path:=ACFU_SelectFileOnly(""; "Select an ACF source file")
If ($path#"")
    $content:=ACFU_LoadFile($path)
End if

Path utilities

Function Purpose
ACFU_DirectoryExists(path) Return 1 if the directory exists.
ACFU_FileExists(path) Return 1 if the file exists.
ACFU_GetFilenameFromPath(path) Return filename from a path.
ACFU_GetDirectoriesFromPath(path) Return directory part from a path.
ACFU_GetExtentionFromPath(path) Return file extension from a path.

Example:

C_TEXT($fileName)
C_LONGINT($exists)

$exists:=ACFU_FileExists("~/Desktop/test.txt")
$fileName:=ACFU_GetFilenameFromPath("~/Desktop/test.txt")

Output files and logging

Function Purpose
ACFU_OpenOutputFile(path) Open a file for streamed output.
ACFU_WriteOutputMacFileUTF8(fileNo; data) Write UTF-8 text to an output file.
ACFU_WriteOutputWinFileUTF8(fileNo; data) Write UTF-8 text with Windows line handling.
ACFU_WriteOutputWinFileIso88591(fileNo; data) Write ISO-8859-1 text with Windows line handling.
ACFU_CloseFile(fileNo) Close an output file.
ACFU_Save_Logg(path; data) Save a log file.
ACFU_Append_Logg(path; data) Append to a log file.

Example:

C_LONGINT($fileNo)
C_TEXT($result)

$fileNo:=ACFU_OpenOutputFile("~/Desktop/output.txt")
$result:=ACFU_WriteOutputMacFileUTF8($fileNo; "Hello from 4D"+Char(13))
$result:=ACFU_CloseFile($fileNo)

Formatting, platform strings, and checksums

Function Purpose
ACFU_NumFormat(value; format) Format a number.
ACFU_NumFormatDec(value; format; decimals) Format a number with decimal settings.
ACFU_GetPlatformString(macText; windowsText) Select a platform-specific string.
ACFU_GetPlatformStringFull(macText; windowsText; linuxText; defaultText) Select a platform-specific string with more variants.
ACFU_AddMod10(number) Append a Modulo 10 check digit.
ACFU_AddMod11(number) Append a Modulo 11 check digit.
ACFU_BSBO_SaveDocumentDesktop(data; fileName; folderName) Save a document to desktop using the built-in bootstrap helper.

Example:

C_TEXT($kid)

$kid:=ACFU_AddMod10("123401")
// Returns "1234012"

DocuService

The DocuService functions are used for encrypted document storage and retrieval, and for related helper functions used by systems that exchange encrypted document payloads.

Function Purpose
dsPD_Version(format) Return DocuService version/status information.
dsPD_Auth(configObject; userName; password; otpCode) Authenticate with DocuService.
dsPD_AuthBase64Encrypted(encryptedConfig; otpCode) Authenticate from an encrypted configuration block.
dsPD_GetDocument(remotePath; archiveFolder; targetFolder; targetFileName) Download and decrypt a document.
dsPD_SaveDocument(remotePath; localPath; overwrite; fileName) Save/encrypt/upload a document.
dsPD_SetDocumentEncryptionKey(configObject; encryptionKey) Set or change document encryption key data.
dsPD_EncryptParBlock2Base64(parameterBlock; userName; password) Encrypt a parameter block to Base64.
dsPD_DecryptBase64_2ParBlock(base64Text; userName; password) Decrypt a Base64 parameter block.
dsPD_GetPHPSampleEncryptDecryptCode(userName; password) Generate sample PHP encryption/decryption code.
dsPD_SHA512_digestHex(sourceText) Return SHA-512 digest as hexadecimal text.
dsPD_Text2Hex(sourceText) Generate a Hex representation for a string. Can be used for standard sorting.
dsPD_Text2HexInv(hexText) Generate a Inverse Hex representation of a generated string. Each byte is 255-character code. Can be used in sorting oposite direction-

Example using an encrypted configuration block:

C_TEXT($authResult)

$authResult:=dsPD_AuthBase64Encrypted([Configuration]DocconfigObject; "")

Example document retrieval:

C_TEXT($result; $remotePath; $fileName)

$remotePath:="../../../DocStore/Documents/DocArchive/2024/07/27/Test.doc" // its name is suffixed .encrypted
$fileName:=ACFU_GetFilenameFromPath($remotePath)
$result:=dsPD_GetDocument($remotePath; [Configuration]ArchiveFolder; "[TEMP]"; $fileName)

[TEMP] is expanded by the 4D plugin to the process temporary folder.

AMQP

The AMQP functions expose message-queue operations directly as 4D external functions. Use them when a 4D method needs to initialize a broker connection, publish messages, listen for messages, or close the AMQP connection without wrapping the call in an ACF package first.

Function Purpose
ACF_AMQP_Initialize(host; userName; password; port) Initialize an AMQP connection.
ACF_AMQP_Send(exchange; routingKey; message; contentType; deliveryMode; priority) Send a message.
ACF_AMQP_Listen(queueName; timeoutMs; consumerTag; acknowledge) Listen for messages.
ACF_AMQP_Close Close the AMQP connection.

Example:

C_TEXT($result; $message)

$result:=ACF_AMQP_Initialize("localhost"; "guest"; "guest"; 5672)
$result:=ACF_AMQP_Send("exchange"; "routing.key"; "Hello from 4D"; "text/plain"; 0; 0)
$message:=ACF_AMQP_Listen("queue_name"; 5000; ""; 1)
$result:=ACF_AMQP_Close

Translation

The translation functions are license-free entry points. They can be used for UI translation without depending on the main ACF runtime license state. This is useful because a loaded translation catalog should continue to serve application UI strings even if the license grace period has expired.

Function Purpose
_(textKey) Translate a text key.
_(textKey; value1) Translate with one substitution argument.
_(textKey; value1; value2) Translate with two substitution arguments.
_n(singularText; pluralText; count) Translate singular/plural text by count.
_n(singularText; pluralText; count; value1) Plural translation with one substitution argument.
_n(singularText; pluralText; count; value1; value2) Plural translation with two substitution arguments.
ACF_Load_TranslationPO(pathOrPOText) Load PO text or a PO file path.
ACF_Load_TranslationPO(poBlob) Load PO data from a blob.
ACF_Clear_Translation Clear loaded translations.
ACF_UpdatePOT(potPath) Update a POT file with missing translation keys.

Example:

C_TEXT($result; $hello)

$result:=ACF_Load_TranslationPO("~/Documents/ACF/i18n/no.po")
$hello:=_("Hello")

Plural example:

C_LONGINT($count)
C_TEXT($message)

$count:=2
$message:=_n("%1 file"; "%1 files"; $count; String($count))

Host expressions and 4D variables from ACF

ACF functions can call back into 4D for host expressions using @...@ and @@...@@. This makes it possible to use selected 4D methods, variables, and table/field references from ACF code.

Example ACF function:

function test4Dcalc()
    string test = @@Method5(16;2)
    + 4@@;
    return test;
end

If Method5 returns Square root($1)*$2, the result is 12.

Process and local variables can also be accessed. For portable source, the diamond variable form can be used in ACF source and mapped to the host runtime.

function test4Dvar()
    ◊my4Dvar = "This works!";
    return "Value: " + ◊my4Dvar;
end

Procedural 4D statements can be executed by placing host statements on their own lines:

function customerNames()
    @ALL RECORDS([Customers])@;
    @FIRST RECORD([Customers])@;

    string names;
    while (@Not(End selection([Customers]))@ == "1")
        names += @[Customers]Name@ + ",";
        @NEXT RECORD([Customers])@;
    end while

    return names;
end

SQL from ACF

ACF can use 4D SQL through ExecuteSQL. The SQL statement can include an ACF INTO clause. The INTO part is handled by ACF and is not sent as part of the SQL statement to 4D.

INTO should be placed on its own line.

function SQLTest4D()
    JSON result;
    string sql = "SELECT ID, PackageName, SourceText FROM ACF_Packages
    INTO :result";

    string res = ExecuteSQL(sql);
    return result;
end

The function can then be called from 4D:

C_OBJECT($params; $objectResult)

$params:=New object()
$objectResult:=ACF_RunObject("SQLTest4D"; $params)

SQL can also bind results into typed ACF arrays:

array int ids;
array string names;

string sql = "SELECT ID, Name FROM Customers
INTO :ids, :names";

string res = ExecuteSQL(sql);

For compiled 4D applications, include the ACF compiler declarations method supplied with the 4D sample so the SQL bridge variables are typed.

Excel, PDF, images, and other ACF runtime functions

The external functions listed above are only the 4D entry points. Most day-to-day work happens inside ACF packages, where the runtime provides a large function library for Excel, PDF, images, HTTP, cryptography, Markdown, SQL, file handling, dialogs, and more.

For example, a 4D method can call one ACF function that produces an Excel workbook:

C_OBJECT($params)
C_TEXT($textResult)

$params:=New object("path"; "~/Desktop/SQL4D_AliasInto_test.xlsx")
$textResult:=ACF_RunText("SQL4D_AliasIntoExcel"; $params)
ALERT($textResult)

The ACF package can contain all the details: SQL query, formatting, workbook creation, chart generation, image placement, and file output.

This is the main idea of ACF for 4D: keep 4D as the application host, and use ACF packages for portable, source-controlled automation logic that can be edited, compiled, tested, and reused across solutions.