
1. The Bootstrap Package
The Bootstrap package is a collection of utility ACF functions used by the development environment to simplify the selection of files, directories, and more. It should be installed as one of the core packages in your application.
Most functions within this package have a FunctionID
assigned, allowing you to use them directly in FileMaker calculations without the 'ACF_run' notation. Simply prefix them with 'ACFU_.' Here's a function reference, along with a listing of the package's source.
These functions are primarily intended for the development environment and are designed to serve as a standard ACF package in any application using the ACF-Plugin. They offer versatility and can be employed for various tasks related to operating system files.
- The Bootstrap Package
- bootstrap_AreWeLoaded ()
- LoadFile (filename)
- SaveFile (filename, content)
- SelectAndGetFile (startPath, Prompt)
- SelectFileOnly (startPath, Prompt)
- SelectFolder (Prompt)
- DirectoryExists (DirectoryPath)
- FileExists (FilePath)
- GetFilenameFromPath (path)
- GetDirectoriesFromPath (path)
- GetExtensionFromPath (path)
- SaveFileDialog (Prompt, proposed_folder, proposed_name)
- OpenOutputFile (path)
- WriteOutputMacFileUTF8 (FileNo, data)
- WriteOutputWinFileIso8859_1 (FileNo, data)
- WriteOutputWinFileUTF8 (FileNo, data)
- CloseFile (FileNo)
- NumFormat ( number, comma )
- BSBO_SaveDocumentDesktop (DocStore, SubPath, Title)
- GetPlatformString ( MacString, WinString )
- Save_Logg ( LoggText, LoggName )
- Append_Logg ( loggText , FilePath)
- AddMod10 ( Number )
- AddMod11 ( Number )
- Source code listing for the Bootstrap package
1.1. bootstrap_AreWeLoaded () ↑
This is a simple function to tell if we have loaded the bootstrap package. Also, see the Article about the startup script to see a full example of its use.
Example from startup script after loading the ACF compiled packages:
# The bootstrap package should be in Preferences::ACF_Pack1, but if it's not...
If [ ACF_run ("bootstrap_AreWeLoaded") ≠ "Yes" ]
# We lack the bootstrap package, compile it from Source....
...
End If
1.2. LoadFile (filename) ↑
The LoadFile
function opens a file and returns its content.
Example from a FileMaker script step:
Set Field [Preferences::SourceCode4; ACFU_LoadFile (Preferences::SourceFile4)]
1.3. SaveFile (filename, content) ↑
The SaveFile
function saves text to a disk file specified by the filename and its content.
Example:
If the source file does not exist, but you have its name and content, create the file and save the content to it.
Set Variable [$res; ACFU_SaveFile (Preferences::SourceFile4; Preferences::SourceCode4)]
1.4. SelectAndGetFile (startPath, Prompt) ↑
The SelectAndGetFile
function opens a file selection dialog, retrieves its content, and returns it. You can specify the starting path for the dialog and provide a prompt as the dialog heading. If the user selects a file, the global variable $$FileName
is set to the selected file's path.
Here's an example of selecting the source file using a button and then setting the content and the path:
# Select a file and get its content.
Set Field [Preferences::SourceCode4; ACFU_SelectAndGetFile (Preferences::SourcecodeFolder; "Select the source file")]
If [Preferences::SourceCode4 ≠ ""]
# The user did not hit Cancel.
Set Field [Preferences::SourceFile4; $$FileName]
End If
1.5. SelectFileOnly (startPath, Prompt) ↑
The SelectFileOnly
function works similarly to SelectAndGetFile
but returns only the selected file's path without opening it.
Example:
Set Field [Preferences::SourceFile4; ACFU_SelectFileOnly (Preferences::SourcecodeFolder; "Select the sourceFile")]
# If the SourceFile4 field is blank, the user did cancel the dialog.
1.6. SelectFolder (Prompt) ↑
This function opens a directory selection dialog and returns the path to the selected directory.
Example:
Set Field [Preferences::SourcecodeFolder; ACFU_SelectFolder ("Where do you keep your ACF source files?")]
1.7. DirectoryExists (DirectoryPath) ↑
The DirectoryExists
function checks for the existence of a directory path and returns a boolean value (1 for true, 0 for false in FileMaker scripts).
Example:
If [Not (ACFU_DirectoryExists (Preferences::SourcecodeFolder))]
# Take appropriate actions for a missing folder configuration.
# (Select a new folder or present an error...)
End If
1.8. FileExists (FilePath) ↑
This function checks for the existence of a disk file and returns true or false (1 or 0 in FileMaker scripts).
Example:
If [Not (ACFU_FileExists (Preferences::SourceFile4))]
# Take appropriate actions for a non-existent source file.
# Select the file or present an error, for example.
End If
1.9. GetFilenameFromPath (path) ↑
The GetFilenameFromPath
function returns only the last part of the filename from a full path.
Example:
# Suppose our SourceFile4 contains: /Users/ole/MyACFFiles/bootstrap.acf
Set Variable [$SourceFileName; ACFU_GetFilenameFromPath(Preferences::SourceFile4)]
# The $SourceFileName now contains bootstrap.acf
1.10. GetDirectoriesFromPath (path) ↑
The GetDirectoriesFromPath
function returns the directory path leading up to the filename of a full path, including the filename at the end.
Example:
# Suppose our SourceFile4 contains: /Users/ole/MyACFFiles/bootstrap.acf
Set Variable [$SourceDirectory; ACFU_GetDirectoriesFromPath(Preferences::SourceFile4)]
# The $SourceDirectory now contains /Users/ole/MyACFFiles/
1.11. GetExtensionFromPath (path) ↑
The GetExtensionFromPath
function returns the extension or file type (what appears after the final dot) of a filename or a full path.
Example:
# Suppose our SourceFile4 contains: /Users/ole/MyACFFiles/bootstrap.acf
Set Variable [$FileExtension; ACFU_GetExtensionFromPath(Preferences::SourceFile4)]
# The $FileExtension now contains: acf
1.12. SaveFileDialog (Prompt, proposed_folder, proposed_name) ↑
The SaveFileDialog
function presents a dialog to the user for saving a new file. You can specify a proposed folder and a proposed name for the file. If the file already exists, the user is alerted and can choose to overwrite it.
Example:
Set Field [Preferences::SourceFile4; ACFU_SaveFileDialog ("Save the file"; Preferences::SourcecodeFolder; "NewName.acf")]
If [Preferences::SourceFile4 ≠ ""]
# A valid file has been selected; start writing to it...
End If
1.13. OpenOutputFile (path) ↑
The OpenOutputFile
function opens a file, keeping it open for subsequent write calls. It returns an open file number, which can be used for later writing operations.
For additional write functions and the close function, refer to the combined example below.
1.14. WriteOutputMacFileUTF8 (FileNo, data) ↑
This function writes text to an open output file. For example, if you are exporting data, you can open the file with the OpenOutputFile
function, write export content to it in a loop, and then close the file.
The file content will be in Mac UTF8 format.
1.15. WriteOutputWinFileIso8859_1 (FileNo, data) ↑
This function is similar to the previous one, except it converts the data to ISO-8859-1 format for Windows systems and replaces carriage return codes with Windows Carriage-Return-Line-Feed codes. This is useful for exporting data from a Mac to a Windows system.
1.16. WriteOutputWinFileUTF8 (FileNo, data) ↑
This function is also for writing data but produces it in UTF8 format for Windows.
1.17. CloseFile (FileNo) ↑
The CloseFile
function closes the open file after you have written the content to it.
Here's a combined example:
# Present a File-Save dialog
Set Variable [$FileName; ACFU_SaveFileDialog ("Save the file"; ""; "NewName.txt")]
If [$FileName ≠ ""]
# Open the selected file
Set Variable [$FileNo; ACFU_OpenOutputFile($FileName)]
# Loop through the records
Go to record/request/page [First]
loop
# Generate and write content to the file
Set Variable [$LineContent; ... some content for the file]
Set Variable [$xx; ACFU_WriteOutputWinFileIso8859_1($FileNo; $LineContent & Char(13))]
Go to record/Request/Page [Next; Exit after last: On]
end loop
# Close the file
Set Variable [$xx; ACFU_CloseFile($FileNo)]
End If
1.18. NumFormat ( number, comma ) ↑
This function formats a number value to a text string with 1000 separators and two decimals. You specify the type of comma used in your application being a period or a regular comma. The function is used to merge fields in your layout to increase the readability of the numbers.
Example:
Set variable [$$SumInvoice; "$ " & ACFU_NumFOrmat(Invoice::InviceTotal; ".")]
# Ausme Invoice::InviceTotal contains 10055.35, The $$SSumInvoice now contains "$ 10 055.35"
# <<$$SSumInvoice>> can now be placed on your layout as a merge-field.
1.19. BSBO_SaveDocumentDesktop (DocStore, SubPath, Title) ↑
This function is suited for a document portal in your application. The document record points to some documents in the operating system, a copy "document to desktop" button on the portal row is sometimes desirable. The user is on the way to a customer and needs some documents copied to his desktop or a folder on his customer visit.
If the user holds the "Alt-key" then a Save-File dialog appears where he can select the destination for the document.
The Parameters:
- DocStore - Usually a configuration parameter in your system, where the document store starts. It can be different paths for Mac and Windows users in a mixed environment.
- SubPath - The relative path from the start of the DocStore, including the filename. Sometimes filenames are prefixed and suffixed by date numbers and customer numbers as such.
- The title is the name of the destination file. The title does not need to have a file-type ending, as this is taken from the source file.
Example:
# Example (Win):
Set variable [$xx;
ACFU_BSBO_SaveDocumentDesktop ( "\\192.168.1.20\vArchive\myDocArchive";
"Customers/11213/Contract.docx"; "Customer 11213 Contract" ) ]
# or (mac)
Set variable [$xx;
ACFU_BSBO_SaveDocumentDesktop ( "vArchive:myDocArchive";
"Customers/11213/Contract.docx"; "Customer 11213 Contract" ) ]
1.20. GetPlatformString ( MacString, WinString ) ↑
The GetPlatformString is used in a mixed environment where you need separate text strings for Mac versus Windows users.
Example:
Set variable [$DocStore; ACFU_GetPlatformString ( Preferences::MacArchive; Preferences::WinArchive ) ]
# Then the $DocStore is from the field MacArchive for the Mac Users, and WinArchive for the Windows users.
1.21. Save_Logg ( LoggText, LoggName ) ↑
The Save_Logg function is useful for providing logg files from functions in your application. It creates a folder in your Documents directory called ACFLoggFiles
. It adds a timestamp to the name given and writes the LoggText to the file.
Example:
Set Variable [$LoggPath; ACFU_Save_Logg($$ProcessLogg; "ProcessLogg")]
1.22. Append_Logg ( loggText , FilePath) ↑
The Append_Logg function is useful for appending some logg text that has already been created. The Save_Logg
function above returns a path to the logg-file, and this can be applied to the FilePath parameter in this function.
Example:
Set Variable [$xx; ACFU_Append_Logg($$ProcessLogg2; $LoggPath)]
1.23. AddMod10 ( Number ) ↑
The AddMod10 generates a Modulo-10 Control-digit to a string of numbers supplied, using the Luhn algorithm.
1.24. AddMod11 ( Number ) ↑
The AddMod11 generates a Modulo-11 Control-digit to a string of numbers supplied, using the Luhn algorithm.
2. Source code listing for the Bootstrap package ↑
package bootstrap "Functions to facilitate load and save source and binary files
- Require ACF_Plugin ver 1.6.0.3 as minimum.
";
/*
Common use ACF functions.
If you duplicate some of the functions to change, Remember to change or remove the FunctionID that need to be a unique number, between 100 an 32767.
We have used 200-214 and 229-236 in this package.
The FunctionID statement makes the functions available as plugin-function directly in the FileMaker calculations using ACFU_FunctionName...
You can use ACF_GetAllPrototypes to retrieve the prototypes for all the functions, for cut & paste into your calculations.
*/
function bootstrap_AreWeLoaded ()
return "Yes";
end
function LoadFile (string filename)
FunctionID 200;
string content;
int x;
x = open (filename, "r");
content = read (x);
close ( x ) ;
return content;
end
function SaveFile (string filename, string content )
FunctionID 201;
int x;
x = open (filename, "w");
write (x, content);
close ( x );
return 1;
end
function SelectAndGetFile (string startPath, string Prompt)
FunctionID 202;
string cc = "";
string filename = select_file (Prompt, startPath);
if (filename != "") then
cc = LoadFile(filename);
end if
$$FileName = filename;
return cc;
end
function SelectFolder ( string prompt )
FunctionID 203;
string folder = select_directory ( prompt ) ;
return folder;
end
function DirectoryExists ( string DirectoryPath )
FunctionID 204;
if ( isMac ) then
DirectoryPath = substitute ( DirectoryPath, ":", "/");
if ( ! directory_exists ( DirectoryPath ) ) then
if ( left ( DirectoryPath, 1 ) != "/") then
DirectoryPath = "/Volumes/" + DirectoryPath;
else
return false;
end if
else
return true;
end if
else
DirectoryPath = substitute ( DirectoryPath, "/", "\\");
end if
return directory_exists ( DirectoryPath ) ;
end
function FileExists ( string FilePath )
FunctionID 229;
if ( isMac ) then
FilePath = substitute ( FilePath, ":", "/");
if ( ! file_exists ( FilePath ) ) then
if ( left ( FilePath, 1 ) != "/") then
FilePath = "/Volumes/" + FilePath;
else
return false;
end if
else
return true;
end if
else
FilePath = substitute ( FilePath, "/", "\\");
end if
return file_exists ( FilePath ) ;
end
function GetFilenameFromPath ( string path )
FunctionID 205;
path = substitute ( path, ":", "/");
path = substitute ( path, "\\", "/");
return regex_replace("^(.+)/(.+\..+)$", path, "\2");
end
function GetDirectoriesFromPath ( string path )
FunctionID 206;
path = substitute ( path, ":", "/");
path = substitute ( path, "\\", "/");
return regex_replace("^(.+)/(.+\..+)$", path, "\1");
end
function GetExtentionFromPath ( string path )
FunctionID 207;
path = substitute ( path, ":", "/");
path = substitute ( path, "\\", "/");
return regex_replace("^(.+/)(.+\.)(.+)$", path, "\3");
end
function SelectFileOnly (string startPath, string Prompt)
FunctionID 208;
string cc = "";
return select_file (Prompt, startPath);
end
function SaveFileDialogue ( string prompt, string proposed_folder, string proposed_name )
functionID 209;
string newfn = save_file_dialogue (prompt, proposed_folder, proposed_name) ;
return newfn;
end
function OpenOutputFile ( string path )
FunctionID 210;
int x = open ( path, "w" );
return x;
end
function WriteOutputMacFileUTF8 ( int FileNo, string data )
FunctionID 211;
write ( FileNo , substitute ( data, "\r", "\n" )) ;
return "OK";
end
function WriteOutputWinFileIso8859_1 ( int FileNo, string data )
FunctionID 212;
write ( FileNo , from_utf ( substitute ( data, "\r", "\r\n" ), "ISO-8859-1" )) ;
return "OK";
end
function CloseFile ( int FileNo )
FunctionID 213;
close ( FileNo ) ;
return "OK";
end
function WriteOutputWinFileUTF8 ( int FileNo, string data )
FunctionID 214;
write ( FileNo , substitute ( data, "\r", "\r\n" )) ;
return "OK";
end
/*
Formattering av nummer - med komma og 1000 gruppe-skille.
Her benyttes fast " " som 1000 gruppe skille
Eksempel fra FileMaker kalkulasjon: ACFU_NumFormat ( beløp, ",")
Der beløp er 12345,2 => 12 345,20
*/
function NumFormat ( float num, string comma )
functionID 230;
string sNum;
if ( comma != ".") then
sNum = substitute ( format ( "%.2f", num ), ".", comma ) ;
else
sNum = format ( "%.2f", num );
end if
return regex_replace ( "\d{1,3}(?=(\d{3})+(?!\d))", sNum, "$& ") ;
end
/*
Copy a document from a document store to user's desktop.
DocStore: the start path for the archive
ArchiveSubPath: The relative path starting from DocStore (including the filename). Designed to be a
common subpath to be used from both Mac and Windows, using :, / or \ as directory seps.
Title: An optional title of the document to be used as the target filename (+ extension of the original file )
Example (Win):
ACFU_BSBO_SaveDocumentDesktop ( "\\192.168.1.20\vArchive\myDocArchive"; "Customers/11213/Contract.docx"; "Customer 11213 Contract" )
or (mac)
ACFU_BSBO_SaveDocumentDesktop ( "vArchive:myDocArchive"; "Customers/11213/Contract.docx"; "Customer 11213 Contract" )
If the user holds the Alt key down while doing this, a File Save dialogue appears for the user to select an alternate name or location.
Else, it will be copied to the user's desktop.
*/
function BSBO_SaveDocumentDesktop (string DocStore, string ArchiveSubPath, string Title)
functionID 231;
if ( DocStore == "" ) then
throw "DocStore for document not set";
end if
if ( ArchiveSubPath == "" ) then
throw "ArchiveSubPath for document not set";
end if
if ( isMac ) then
DocStore = "/Volumes/" + substitute ( DocStore, ":", "/");
else
DocStore = substitute ( DocStore, "\\", "/");
end if
if ( ! directory_exists ( DocStore ) ) then
throw "Doc Store volume not available: " + DocStore;
end if
if ( right(DocStore, 1) != "/") then
DocStore += "/";
end if
ArchiveSubPath = substitute ( ArchiveSubPath, ":", "/");
ArchiveSubPath = substitute ( ArchiveSubPath, "\\", "/");
string SourceFile = DocStore + ArchiveSubPath;
string defname;
if ( Title == "" ) then
defname = regex_replace("^(.+)/(.+\..+)$", SourceFile, "\2");
else
Title = substitute ( Title, ":", "" ) ;
Title = substitute ( Title, "/", "" ) ;
Title = substitute ( Title, "\\", "" ) ;
string ext = regex_replace("^(.+/)(.+\.)(.+)$", SourceFile, "\3");
defname = Title + "." + ext;
end if
string newfn;
bool abort = false;
if ( int ( @get(ActiveModifierKeys)@ ) == 8 ) then
newfn = save_file_dialogue ("Where do you want to save the file?", desktop_directory(), defname) ;
if ( newfn == "" ) then
abort = true;
end if
else
newfn = desktop_directory() + defname;
end if
print format ( "Source:%s\nDestination:%s\n", SourceFile,newfn ) ;
if ( ! abort ) then
string res = copy_file ( SourceFile, newfn ) ;
return res;
else
return "OK";
end if
end
/* To Simplify the use of alternate strings on Mac and Windows
Example using the above function from a FM Calculation to work on both platforms
(Shown using hard coded strings, but normally one would use fields from preference table):
ACFU_BSBO_SaveDocumentDesktop ( ACFU_GetPlatformString ( "vArchive:myDocArchive"; "\\192.168.1.20\vArchive\myDocArchive"); "Customers/11213/Contract.docx"; "Customer 11213 Contract" )
*/
function GetPlatformString ( string MacString, string WinString )
FunctionID 232;
if ( isWindows ) then
return WinString;
else
return MacString;
end if
end
/*
To save content of log to a file in users document directory.
Folder name: ~/Documents/ACFLoggFiles/
creates directory if it does not exists.
Returns Path to logg file, to be used in Append_Logg.
Parameters:
logg : The content to be written to the logg file.
name : The name part of the full fulename as: YYYYmmdd_hhmmss_<name>_logg.txt
*/
function Save_Logg ( string logg , string name)
FunctionID 233;
string logdir = documents_directory()+"ACFLoggFiles";
string res;
if ( ! directory_exists ( logdir )) then
res = create_directory (logdir );
end if
logg = substitute ( logg, "\r", "\n");
string path = logdir+format("/%s_%s_logg.txt", string ( now()+3600, "%Y%m%d_%H%M%S"), name);
res = delete_file ( path );
int x = open ( path, "w");
if ( isWindows ) then
write ( x, substitute ( logg, "\n", "\r\n"));
else
write ( x, logg);
end if
close ( x );
return path;
end
/*
Append logg entries to previously created logg with Save_Logg.
*/
function Append_Logg ( string logg , string FilePath)
FunctionID 234;
logg = substitute ( logg, "\r", "\n");
int x = open ( FilePath, "wa");
if ( isWindows ) then
write ( x, substitute ( logg, "\n", "\r\n"));
else
write ( x, logg);
end if
close ( x );
return "OK";
end
/*
Add Modulo 10 (Luhn Algorithm) control digit to a string of numbers
*/
function AddMod10 ( string number )
FunctionID 235;
array int digits;
int l = length ( number ) ;
int i;
// Check only digits.
if ( regex_match ( "\d+", number ) ) then
// Build an array of integers.
for (i=1, l )
digits[] = ascii ( substring ( number, i-1, 1 ) ) - 48 ;
end for
// Back traverse every second and double it. If > 9, subtract 9.
for ( i = l, 1, -2 )
digits[i]=digits[i]*2;
if (digits[i]>9) then
digits[i] = digits[i]-9;
end if
end for
// Sum of the digits
int sum;
for (i=1, l )
sum += digits[i];
end for
// sum multiplied by 9, modulo 10 gives the digit.
int digx = mod ( sum*9 , 10);
return number + digx;
else
return "ERROR: Expects only digits as parameter: " + number;
end if
end
/*
Add Modulo 11 (Luhn Algorithm) control digit to a string of numbers
This one might also add "-" at the end, according to the spesifications.
*/
function AddMod11 ( string number )
FunctionID 236;
array int digits;
int l = length ( number ) ;
int i, mult;
// Check only digits.
if ( regex_match ( "\d+", number ) ) then
// Build an array of integers.
for (i=1, l )
digits[] = ascii ( substring ( number, i-1, 1 ) ) - 48 ;
end for
// Back traverse every one and multiply
mult = 2;
int sum;
for ( i = l, 1, -1 )
sum += digits[i]*mult;
mult ++;
if ( mult > 7) then
mult = 2;
end if
end for
print sum;
// difference 11 and mod 11 gives the digit.
int digx = 11 - mod ( sum , 11);
string ctrl;
if ( digx > 9) then
ctrl = "-";
else
ctrl = string ( digx );
end if
return number + ctrl;
else
return "ERROR: Expects only digits as parameter: " + number;
end if
end