
markdown
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.
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.
In most recent versions of the ACF plugin, the Bootstrap package is automatically loaded to simplify developer tools that depend on this package to be loaded. Therefore, this function is seldom needed.
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)]
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)]
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
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 source file")]
# If the SourceFile4 field is blank, the user did cancel the dialog.
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?")]
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
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
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
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/
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
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
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.
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.
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.
WriteOutputWinFileUTF8 (FileNo, data)
This function is also for writing data but produces it in UTF8 format for Windows.
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
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::InvoiceTotal; ".")]
# Assume Invoice::InvoiceTotal contains 10055.35, The $$SumInvoice now contains "$ 10,055.35"
# <<$$SumInvoice>> can now be placed on your layout as a merge-field.
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" ) ]
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.
Save_Log ( LogText, LogName )
The Save_Log function is useful for providing log files from functions in your application. It creates a folder in your Documents directory called ACFLogFiles. It adds a timestamp to the name given and writes the LogText to the file.
Example:
Set Variable [$LogPath; ACFU_Save_Log($$ProcessLog; "ProcessLog")]
Append_Log ( logText , FilePath)
The Append_Log function is useful for appending some log text that has already been created. The Save_Log function above returns a path to the log file, and this can be applied to the FilePath parameter in this function.
Example:
Set Variable [$xx; ACFU_Append_Log($$ProcessLog2; $LogPath)]
AddMod10 ( Number )
The AddMod10 generates a Modulo-10 Control digit to a string of numbers supplied, using the Luhn algorithm.
AddMod11 ( Number )
The AddMod11 generates a Modulo-11 Control digit to a string of numbers supplied, using the Luhn algorithm.
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 and 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 GetExtensionFromPath ( 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 SaveFileDialog ( string prompt, string proposed_folder, string proposed_name )
functionID 209;
string newfn = save_file_dialog (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
/*
Formatting of numbers - with comma and 1000 group separator.
Here a fixed " " is used as 1000 group separator
Example from FileMaker calculation: ACFU_NumFormat ( amount, ",")
Where amount is 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 separators.
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 dialog 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_dialog ("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 user's document directory.
Folder name: ~/Documents/ACFLogFiles/
creates directory if it does not exist.
Returns Path to log file, to be used in Append_Log.
Parameters:
logg : The content to be written to the log file.
name : The name part of the full filename as: YYYYmmdd_hhmmss_<name>_log.txt
*/
function Save_Log ( string logg , string name)
FunctionID 233;
string logdir = documents_directory()+"ACFLogFiles";
string res;
if ( ! directory_exists ( logdir )) then
res = create_directory (logdir );
end if
logg = substitute ( logg, "\r", "\n");
string path = logdir+format("/%s_%s_log.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 log entries to previously created log with Save_Log.
*/
function Append_Log ( 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 specifications.
*/
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
