1. Send an e-mail from The ACF plugin

The ACF plugin has evolved, and now we have the first function in the email functionality in testing. This is the send_email.

  1. Send an e-mail from The ACF plugin
    1. The demo mail application
  2. A walkthrough of the ACF functions supporting the demo code
    1. Building JSON with the account parameters.
    2. The AddAttachment function
    3. The Preview Mail function
    4. The Editor Button
    5. The SendMail function
    6. Screenshots
    7. Deliverability
    8. Conclusion
  3. Where can I download the demo?

OK, you can send emails from FileMaker. But this function in the plugin adds new features to send emails, like styled HTML emails made from plain text.

1.1. The demo mail application

We have made a demo mail application to showcase and test the e-mail functions. It contains three tables for this to work. We have one table for the e-mail account setup, one for the messages, and one for the attachments to the e-mails. They are all related.

mail-composer-window2

The window has some buttons:

Over 90% of the programming done in this application is done with ACF functions. This means that the button scripts are short 2-3 liners mostly. This makes the functions very easy to implement into any other application where this feature is needed. Maybe your target application already has the needed tables, so it is only possible to change their name in the ACF code to fit your target application.

2. A walkthrough of the ACF functions supporting the demo code

In the following boxes, I will show the source code in the ACF language that is loaded into the plugin to make it work.

2.1. Building JSON with the account parameters.

function emailServerPar (string ShortName)

    ARRAY string SMTP_Host, SMTP_User, SMTP_Password, SMTP_Port, FromEmail,  FromName, Signature, PlainSignature;

    string sql = "SELECT
        SMTP_Host,
        SMTP_User,
        SMTP_Password,
        SMTP_Port,
        FromEmail,
        FromName,
        Signature,
        PlainSignature
    FROM
        Email_Accounts
    WHERE
        ShortName = :ShortName

    INTO  :SMTP_Host, :SMTP_User, :SMTP_Password, :SMTP_Port, :FromEmail,  :FromName, :Signature, :PlainSignature
    ";

    string res = ExecuteSQL (sql); 
    if ( sizeof (SMTP_User) == 0) then
        return json("ERROR", "No Data retrieved in SQL query", "shortname", ShortName); 
    end if

    JSON server;
    server["server"] = JSON ("host", SMTP_Host[1], "port", SMTP_Port[1], "user", SMTP_User[1], 
                        "password",SMTP_Password[1], "Signature", Signature[1],"PlainSignature", PlainSignature[1] ); 
    return server; 
end

ShortName is the key in the account table. We use an SQL query to get the account parameters. Then we create a JSON variable called server, with key/value pairs for all the parameters. This JSON is returned from the function. Calling this function with a valid account name from the account table returns a JSON object with all the necessary mail config data.

2.2. The AddAttachment function

This function is called when you press the "+" button right to the attachment portal. It selects a file, then finds out if it's added already, and if not, inserts a record in the attachment table.

function AddAttachment ()
    string path = select_file ("Select file to add to the e-mail", desktop_directory());
    string primkey, sql, res; 
    if ( path != "" ) then
        primkey = Email_Messages::PrimaryKey; 
        sql = "SELECT FilePath FROM Email_Attachments WHERE fk_EmailMessage = :primkey AND FilePath = :path"; 
        res = ExecuteSQL ( sql); 
        if ( res != "") then
            alert ( "File already added: " + res); 
            return ""; 
        end if
        sql = "INSERT INTO Email_Attachments (fk_EmailMessage, FilePath) VALUES (:primkey, :path)"; 
        res = ExecuteSQL ( sql);
        return res; 
    end if
    return ""; 
end

The button script just calls this function and does a "refresh portal" script step to have it update the rows in it.

The Minus button just does a single "delete portal record" script step.

2.3. The Preview Mail function

This function is called when you hit the "Preview" button. The function converts the MarkDown formatted to HTML, saves it to disk, and returns the path to FileMaker. Then the script uses the "Open URL" script step to launch Safari or any other web browser.

function PreviewMail ()

    string acc = Email_Messages::Account;
    string htmlfolder = Email_Accounts::HTML_Folder; 

    set_markdown_html_root ( htmlfolder );
    string fileBase = htmlfolder+"/Temp_"+string(now(), "%Y%m%d_%h%i%s");
    string mdFile = fileBase+".md"; 
    string htmlFile = fileBase + ".html"; 
    string mdContent = Email_Messages::MarkdownText; 

    JSON params = emailServerPar(acc); // the other function returns the server config...
    // print string(params); 
    mdContent += params["server.Signature"]; 
    int x = open(mdFile, "w");
    write(x, mdContent); 
    close (x); 

    string res = markdown2html (mdFile, "solarized-dark,monokai", htmlFile);
    
    return htmlFile; 

end

We call the emailServerPar to get the config object since it contains the mail signature. The account has configured a folder for temporary storage, and styling in a Themes folder. We use this to create a temporary filename both for the MarkDown file and the HTML file, add the signature to the MarkDown text, and write it to disk. Then it uses set_markdown_html_root command for defining the work dir for the conversion. Finally, we use the ACF function markdown2html to complete the conversion. We return the path of the HTML file to FileMaker.

2.4. The Editor Button

This function is called when we hit the Editor button in the layout. We have a field in the messages containing the path for the temporary MarkDown document edited in an external editor. If this field is empty, we create a temporary file for the text, and set a FileMaker variable $$mdFile for the button script to set in the field. Then we simply return the original text. The function is called in a Set Field Script step, so the markdown text is updated in the record. If the field has a path, it means that we load it back, so we read the file and return the content. The $$mdFile variable is cleared, so the second set field in the button script sets the filename.

function Editor_button()
    string MDText = Email_Messages::MarkdownText; 
    string tmpMDfile = Email_Messages::TempMDFile;
    string htmlfolder = Email_Accounts::HTML_Folder; 
    string mdContent; 
    int x; 
    if ( tmpMDfile == "") then
        tmpMDfile = htmlfolder+"/Temp_"+string(now(), "%Y%m%d_%h%i%s")+".md"; 
        x = open (tmpMDfile, "w" ); 
        write (x, Email_Messages::MarkdownText); 
        close (x); 
        $$mdFile = tmpMDfile; 
        return Email_Messages::MarkdownText; 
    
    else
        x = open ( tmpMDfile, "r"); 
        mdContent = read ( x); 
        close x; 
        $$mdFile = "";
        return mdContent; 
    end if
    return "OK"; 
end 

2.5. The SendMail function

This function does the important work of assembling the JSON parameter object. We chose to use this method, as we otherwise would need to have a long list of parameters for the function. With JSON, all the parameters have key names that make it both easier to read and remember when using the function. Also easy to cut and paste it from somewhere else and just change the parameters to what fits for a particular use.

function SendMail ()

    string htmlFile = PreviewMail(); 
    string sql, primkey, res, acc = Email_Messages::Account;
    JSON params = emailServerPar(acc);
    int x; 
    x = open ( htmlFile, "r"); 
    string htmlContent = read (x); 
    close (x); 
    
    string plainContent = regex_replace ("<[^>]*>", Email_Messages::MarkdownText, "") +"\r\n";
    plainContent += params["server.PlainSignature"]; 
    params["from"] = Email_Messages::From; 
    params["to"] = Email_Messages::to;
    if (Email_Messages::cc != "" ) then
        params["cc"] = Email_Messages::cc;
    end if
    if ( Email_Messages::bcc != "") then    
        params["bcc"] = Email_Messages::bcc;
    end if
    params["subject"] = Email_Messages::Subject;
    params["body.plain"] = plainContent; 
    params["body.html"] = htmlContent; 
    
    // Add attachments
    primkey = Email_Messages::PrimaryKey; 
    ARRAY STRING atts; 
    sql = "SELECT FilePath FROM Email_Attachments WHERE fk_EmailMessage = :primkey
    INTO :atts"; 
    res = ExecuteSQL ( sql); 
    if (sizeof (atts) > 0) then
        params["attachments"] = atts; 
    end if
    
    return   send_email(params); 
end

The function uses the preview function to generate the HTML file and then puts it into the body.html tag in the JSON. The plain content is washed for potential HTML codes that might appear in the MarkDown text. This is done using a regex_replace function.

Finally, we send the mail and return its result, which should normally be "OK".

If any error should occur, like malformatted email addresses, mail-server config issues, or anything like that, there will be raised an exception. If an Exception occurs that will terminate the ACF function, returning the error message instead.

2.6. Screenshots

Typora - External editor composing.

Typora-screenshot2

The received e-mail using the chosen theme. It lacks config for the table, but it's OK.

gmail-screenshot2

It is of course easy to design the template for anything you like.

You see the logo at the bottom of the mail. It comes from the signature in the account setup, just having an <img src="/path/to/logo/logo.png"/> along with the other texts in the signature. The logo is read by the send_email function and added as a related attachment to the mail and the image tag is changed to reference a content ID that the attachment is labeled with.

2.7. Deliverability

Of course, one important thing about mail sending is that the mail gets delivered. We have tested against the "MailTrap" email tool suite, to get a spam score report. Here is the result:

Deliverability

2.8. Conclusion

This package can easily be applied in a target application. It can be used for sending offers from a CRM system, invoices, and order confirmations that are looking good. It is easy to have logos for your company. It is also very easy for a FileMaker script to create the MarkDown source for an automated e-mail function since MarkDown is just plain text with some codes in it to define the styling.

You can download the demo from the download section and it will also require Mac until the Windows version is released. The send_email function was introduced in the plugin version 1.7.0.10 which is also available for download.

3. Where can I download the demo?

The demo application is in the ACF-Plugin download package together with the plugin, all the other examples, and documentation.

Download the demo mail app and the ACF Plugin