Email Send¶
Starting in FWD trunk revision 11187, FWD provides a handle-based resource that can be easily used for sending emails via SMTP. This revision was released as FWD v3.2 in August 2018. The original task for developing this feature was #3343. It has useful information about the implementation as well as details of mock/test environments.
Introduction¶
Included with FWD is a set of Java classes that make it easy to send emails via SMTP. The following features are available:
- Connection via 4 possible modes:
UNENCRYPTED
,STARTTLS_WHEN_AVAILABLE
,STARTTLS_REQUIRED
orSSL
. - Specify the SMTP host (IP address or hostname) and optionally the port.
- No authentication or authentication with userid/password.
- Main contents of the email can be plain text, HTML or both.
- Unlimited TO, CC and BCC recipients.
- Control over FROM and REPLY-TO addresses.
- Attach local files or files specified by URL.
- Embed images in HTML using a local file or an image via URL.
The backing support for this is implemented using the Apache Commons Email project. This in turn uses the standard JavaMail API that is part of the J2EE platform. No extra installation of software is needed. The supporting libraries are automatically pulled in to the installation by the FWD build process.
The current implementation is done as a 4GL handle-based resource. This is a short term approach. When the OO 4GL support is available, it will be possible to directly instantiate and access Java objects. Using that facility, the handle approach will be deprecated (and eventually removed) leaving behind a cleaner OO approach (and no need for extra 4GL language syntax).
4GL API Details¶
This is an implementation of the SMTP-EMAIL extension to the 4GL. The created object is a handle based resource with attributes and methods that can be used to define the email's state and to send the email. When the email is sent, the send will occur on the client.
This is a FWD-specific set of functionality. It does not exist and will not run in the Progress 4GL (e.g. OpenEdge).
To create an SMTP-EMAIL resource use the following syntax:
CREATE SMTP-EMAIL handle [ASSIGN { ATTR_OR_METHOD = value ...}].
The following list of attributes are available:
Attribute | Data Type | Required | Description |
---|---|---|---|
connection-type |
character |
Y | Must be "UNENCRYPTED", "STARTTLS_WHEN_AVAILABLE", "STARTTLS_REQUIRED" or "SSL". |
smtp-from |
character |
Y | This is the FROM address (the email address of the sender). |
smtp-host |
character |
Y | This is the SMTP server's IP address or hostname. |
smtp-html |
character |
N | If set, this is the content of the HTML body. |
smtp-port |
character |
N | Defaults to 25 for UNENCRYPTED, 587 for STARTTLS_* and 465 for SSL. |
smtp-reply-to |
character |
N | Optional REPLY-TO address. |
smtp-subject |
character |
Y | This is the subject line. |
smtp-text |
character |
N | If set, this is the content of the HTML body. |
smtp-user |
character |
N | If both the user and the password are NOT set, then no authentication will be attempted. If set, this is the userid for authentication with the SMTP server. |
smtp-password |
character |
N | If both the user and the password are NOT set, then no authentication will be attempted. If set, this is the password for authentication with the SMTP server. |
smtp-validate |
logical |
N | Defaults to TRUE. If FALSE, the SMTP server's SSL certificate will be treated as trusted. Ignored for UNENCRYPTED mode. |
The following list of methods are available:
Method | Return Type | Return Meaning | Required | Description |
---|---|---|---|---|
add-to-address(character addr) |
logical |
TRUE on success. |
N | This adds a TO address to the mail. At least one recipient is needed. |
add-cc-address(character addr) |
logical |
TRUE on success. |
N | This adds a CC address to the mail. At least one recipient is needed. |
add-bcc-address(character addr) |
logical |
TRUE on success. |
N | This adds a BCC address to the mail. At least one recipient is needed. |
attach-file(character filename) |
logical |
TRUE on success. |
N | This attaches a local file to the email. The file must exist and be accessible to the user. |
attach-url(character url) |
logical |
TRUE on success. |
N | This attaches a resource specified by URL to the email. The resource will be downloaded when the email is sent. The URL must exist and be accessible to the user. |
clear-attachments-list() |
logical |
TRUE on success. |
N | Deletes everything in the attachments list. |
clear-bcc-list() |
logical |
TRUE on success. |
N | Deletes everything in the BCC address list. |
clear-cc-list() |
logical |
TRUE on success. |
N | Deletes everything in the CC address list. |
clear-embedded-image-list() |
logical |
TRUE on success. |
N | Deletes everything in the embedded image list. |
clear-to-list() |
logical |
TRUE on success. |
N | Deletes everything in the TO address list. |
embed-file(character filename) |
character |
CONTENT-ID | N | Adds a local file to the embedded image list and returns the content ID to be substituted into the IMG tag of the HTML body. The file must exist and be accessible to the user. |
embed-url(character filename) |
character |
CONTENT-ID | N | Adds a URL resource to the embedded image list and returns the content ID to be substituted into the IMG tag of the HTML body. The resource will be downloaded when the email is sent. The URL must exist and be accessible to the user. |
get-attachments-list() |
character[] |
The list as an array. | N | Returns the attachments list as an array. |
get-bcc-list() |
character[] |
The list as an array. | N | Returns the BCC address list as an array. |
get-cc-list() |
character[] |
The list as an array. | N | Returns the CC address list as an array. |
get-embedded-image-list() |
character[] |
The list as an array. | N | Returns the embedded image list as an array. |
get-to-list() |
character[] |
The list as an array. | N | Returns the TO address list as an array. |
send() |
logical |
TRUE on success. |
Y | Sends the email as previously defined. You can call this more than once (possibly changing the state of the handle each time). Every time this is called it will attempt to send the defined email. This may raise an ERROR condition if there are any problems during send. |
At the end of lifetime of the object, it should be deleted, as usual with:
DELETE OBJECT handle.
Example 4GL Programs¶
All of these programs are designed for use in ChUI. They have not been implemented or testing in GUI.
Simple Text Email¶
This code allows sending a text-only email with no attachments.
def var c-type as character format "x(25)" label "Connection Type" view-as combo-box list-items "UNENCRYPTED", "STARTTLS_WHEN_AVAILABLE", "STARTTLS_REQUIRED", "SSL" size-chars 25 by 5. def var text-body as char view-as editor inner-chars 70 inner-lines 10 scrollbar-vertical. def var host as char format "x(65)" init "localhost" label "Host". def var port as int format "99999" init ? label "Port". def var s-user as char format "x(25)" init ? label "User". def var pw as char format "x(25)" init ? label "Password". def var from-addr as char format "x(65)" label "From". def var reply-to as char format "x(65)" label "Reply-To". def var to-addr as char format "x(65)" label "To". def var subject as char format "x(65)" label "Subject". def var c-valid as log init true label "Validate". def var mailer as handle. def var again as log init true. repeat while again: update c-type skip host help "Type hostname or IP address." skip port help "Type port number or leave blank for default port." skip s-user help "Userid for SMTP server authentication or leave blank for no auth." skip pw help "Password for SMTP server authentication or leave blank for no auth." skip c-valid help "Use 'no' to disable server certificate validation." skip from-addr skip reply-to skip to-addr skip subject skip text-body help "Type the body of the email, to be sent as plain text." no-label with frame compose side-labels title "Send a Text Email with No Attachments". create smtp-email mailer assign connection-type = c-type smtp-host = host smtp-port = string(port) smtp-user = s-user smtp-password = pw smtp-from = from-addr smtp-reply-to = reply-to smtp-subject = subject smtp-text = text-body smtp-validate = c-valid. mailer:add-to-address(to-addr). mailer:send(). again = false. message "Send another email?" update again format "Y/N". end.
This is an screen shot of a possible result:
HTML Email¶
This allows sending an HTML email with any number of attachments. Please note that the HTML itself cannot have embedded images (in this sample, see the sample below for the embedded images support).
def var c-type as character format "x(25)" label "Connection Type" view-as combo-box list-items "UNENCRYPTED", "STARTTLS_WHEN_AVAILABLE", "STARTTLS_REQUIRED", "SSL" size-chars 25 by 5. def var text-body as char view-as editor inner-chars 70 inner-lines 10 scrollbar-vertical. def var html-body as char view-as editor inner-chars 70 inner-lines 10 scrollbar-vertical. def var host as char format "x(65)" init "localhost" label "Host". def var port as int format "99999" init ? label "Port". def var s-user as char format "x(25)" init ? label "User". def var pw as char format "x(25)" init ? label "Password". def var from-addr as char format "x(65)" label "From". def var reply-to as char format "x(65)" label "Reply-To". def var to-addr as char format "x(65)" label "To". def var subject as char format "x(65)" label "Subject". def var c-valid as log init true label "Validate". def var cc-list as char extent 10 init ?. def var bcc-list as char extent 10 init ?. def var att-file-list as char extent 10 init ?. def var att-url-list as char extent 10 init ?. def button cc-button label "Add CC". def button bcc-button label "Add BCC". def button attach-file-button label "Attach File". def button attach-url-button label "Attach URL". def var mailer as handle. def var again as log init true. def frame compose c-type skip host help "Type hostname or IP address." skip port help "Type port number or leave blank for default port." skip s-user help "Userid for SMTP server authentication or leave blank for no auth." skip pw help "Password for SMTP server authentication or leave blank for no auth." skip c-valid help "Use 'no' to disable server certificate validation." skip from-addr skip reply-to skip to-addr skip subject skip text-body help "Type the plain text body of the email." no-label skip html-body help "Type the HTML body of the email." no-label skip cc-button bcc-button attach-file-button attach-url-button with side-labels title "Send an HTML Email and Attachments". function add-to-list returns integer (input txt as character, input-output list as character extent 10): return -1. end. on choose of cc-button do: message "Add CC Address" update addr as char format "x(60)". def var i as int. do i = 1 to extent(cc-list): if cc-list[i] eq ? then do: cc-list[i] = addr. return. end. end. message "Only 10 CC addresses are supported at this time.". end. on choose of bcc-button do: message "Add BCC Address" update addr as char format "x(60)". def var i as int. do i = 1 to extent(bcc-list): if bcc-list[i] eq ? then do: bcc-list[i] = addr. return. end. end. message "Only 10 BCC addresses are supported at this time.". end. on choose of attach-file-button do: message "File to Attach" update file as char format "x(65)". file = search(file). if file eq ? then message "File does not exist!" view-as alert-box error. else do: def var i as int. do i = 1 to extent(att-file-list): if att-file-list[i] eq ? then do: att-file-list[i] = file. return. end. end. message "Only 10 file attachments are supported at this time.". end. end. on choose of attach-url-button do: message "Resource URL to Attach" update url as char format "x(65)". def var i as int. do i = 1 to extent(att-url-list): if att-url-list[i] eq ? then do: att-url-list[i] = url. return. end. end. message "Only 10 URL attachments are supported at this time.". end. def var i as int. repeat while again: display c-type host port s-user pw c-valid from-addr reply-to to-addr subject text-body html-body with frame compose. enable all with frame compose. wait-for 'go' of frame compose. assign c-type host port s-user pw c-valid from-addr reply-to to-addr subject text-body html-body. create smtp-email mailer assign connection-type = c-type smtp-host = host smtp-port = string(port) smtp-user = s-user smtp-password = pw smtp-from = from-addr smtp-reply-to = reply-to smtp-subject = subject smtp-text = text-body smtp-html = html-body smtp-validate = c-valid. mailer:add-to-address(to-addr). do i = 1 to extent(cc-list): if cc-list[i] ne ? then mailer:add-cc-address(cc-list[i]). end. do i = 1 to extent(bcc-list): if bcc-list[i] ne ? then mailer:add-bcc-address(bcc-list[i]). end. do i = 1 to extent(att-file-list): if att-file-list[i] ne ? then mailer:attach-file(att-file-list[i]). end. do i = 1 to extent(att-url-list): if att-url-list[i] ne ? then mailer:attach-url(att-url-list[i]). end. mailer:send(). again = false. message "Send another email?" update again format "Y/N". end.
This is an screen shot of a possible result:
HTML Email with Embedded Images¶
This sends an HTML email with two embedded images (one via a local file and the other via URL).
def var c-type as character format "x(25)" label "Connection Type" view-as combo-box list-items "UNENCRYPTED", "STARTTLS_WHEN_AVAILABLE", "STARTTLS_REQUIRED", "SSL" size-chars 25 by 5. def var host as char format "x(65)" init "localhost" label "Host". def var port as int format "99999" init ? label "Port". def var s-user as char format "x(25)" init ? label "User". def var pw as char format "x(25)" init ? label "Password". def var from-addr as char format "x(65)" label "From". def var reply-to as char format "x(65)" label "Reply-To". def var to-addr as char format "x(65)" label "To". def var subject as char format "x(65)" label "Subject". def var c-valid as log init true label "Validate". def var filename as char format "x(65)" label "File to Embed". def var url as char format "x(65)" label "URL to Embed". def var html-body as char. def var cid1 as char. def var cid2 as char. def var mailer as handle. def var again as log init true. repeat while again: update c-type skip host help "Type hostname or IP address." skip port help "Type port number or leave blank for default port." skip s-user help "Userid for SMTP server authentication or leave blank for no auth." skip pw help "Password for SMTP server authentication or leave blank for no auth." skip c-valid help "Use 'no' to disable server certificate validation." skip from-addr skip reply-to skip to-addr skip subject skip filename validate(search(filename) ne ?, "Filename must exist on the local system!") skip url with frame compose side-labels title "HTML Email with Embedded Images". create smtp-email mailer assign connection-type = c-type smtp-host = host smtp-port = string(port) smtp-user = s-user smtp-password = pw smtp-from = from-addr smtp-reply-to = reply-to smtp-subject = subject smtp-validate = c-valid. mailer:add-to-address(to-addr). cid1 = mailer:embed-image-by-file(filename). cid2 = mailer:embed-image-by-url(url). html-body = "<html><body><img src=\"cid:" + cid1 + "\"><p>This is text in between two images.<p><img src=\"cid:" + cid2 +"\"></body></html>". mailer:smtp-html = html-body. mailer:send(). again = false. message "Send another email?" update again format "Y/N". end.
This is an screen shot of a possible result:
Java Implementation Details¶
The primary class that provides the handle based resource is com.goldencode.p2j.email.SmtpEmail
. This class has Java methods that match each 4GL attribute and method:
4GL Attribute | Java Method(s) |
---|---|
connection-type |
getConnectionType() , setConnectionType(character) , setConnectionType(String) |
smtp-from |
getSmtpFrom() , setSmtpFrom(character) , setSmtpFrom(String) |
smtp-host |
getSmtpHost() , setSmtpHost(character) , setSmtpHost(String) |
smtp-html |
getSmtpHtmlBody() , setSmtpHtmlBody(character) , setSmtpHtmlBody(String) |
smtp-port |
getSmtpPort() , setSmtpPort(character) , setSmtpPort(String) |
smtp-reply-to |
getSmtpReplyTo() , setSmtpReplyTo(character) , setSmtpReplyTo(String) |
smtp-subject |
getSmtpSubject() , setSmtpSubject(character) , setSmtpSubject(String) |
smtp-text |
getSmtpTextBody() , setSmtpTextBody(character) , setSmtpTextBody(String) |
smtp-user |
getSmtpUser() , setSmtpUser(character) , setSmtpUser(String) |
smtp-password |
getSmtpPassword() , setSmtpPassword(character) , setSmtpPassword(String) |
smtp-validate |
isSmtpValidate() , setSmtpValidate(character) , setSmtpValidate(String) |
4GL Method | Java Method(s) |
---|---|
add-to-address(character addr) |
addToAddress(character) , addToAddress(String) |
add-cc-address(character addr) |
addCcAddress(character) , addCcAddress(String) |
add-bcc-address(character addr) |
addBccAddress(character) , addBccAddress(String) |
attach-file(character filename) |
attachFile(character) , attachFile(String) |
attach-url(character url) |
attachURL(character) , attachURL(String) |
clear-attachments-list() |
clearAttachmentsList() |
clear-bcc-list() |
clearBccAddressList() |
clear-cc-list() |
clearCcAddressList() |
clear-embedded-image-list() |
clearEmdeddedImageList() |
clear-to-list() |
clearToAddressList() |
embed-file(character filename) |
embedFile(character) , embedFile(String) |
embed-url(character filename) |
embedURL(character) , embedURL(String) |
get-attachments-list() |
getAttachmentsList() |
get-bcc-list() |
getBccAddressList() |
get-cc-list() |
getCcAddressList() |
get-embedded-image-list() |
getEmbeddedImageList() |
get-to-list() |
getToAddressList() |
send() |
send() |
The Java method names were deliberately mapped directly to the keywords that were added to the language. When OO 4GL support is available, these keywords will be removed and the SmtpEmail
class will be used directly from 4GL code. When that happens, the method names will be simplified to take advantage of the natural namespace that occurs by using an OO approach.
Under the covers, the code in SmtpEmail
uses the com.goldencode.email.EmailDefinition
and com.goldencode.email.Emailer
classes, which in turn use the Apache Commons Email project.
Please see #3343 for more implementation details.
© 2004-2017 Golden Code Development Corporation. ALL RIGHTS RESERVED.