Add custom email templates

- Developers can add email templates via a configurable path.
  - Content editors can select the email templates via a dropdown.
  - Content editors can embed HTML content in emails.
  - Content editors can preview HTML emails.
  - Content editors can use field values (merge fields) in emails.
This commit is contained in:
David Craig 2015-07-16 15:06:53 +12:00
parent 494b9e5746
commit 350fdee850
9 changed files with 252 additions and 6 deletions

View File

@ -25,6 +25,11 @@ class UserDefinedForm extends Page {
private static $translate_excluded_fields = array( private static $translate_excluded_fields = array(
'Fields' 'Fields'
); );
/**
* @var string
*/
private static $email_template_directory = 'userforms/templates/email/';
/** /**
* @var array Fields on the user defined form page. * @var array Fields on the user defined form page.
@ -110,6 +115,10 @@ class UserDefinedForm extends Page {
$emailRecipients->getConfig()->getComponentByType('GridFieldAddNewButton')->setButtonName( $emailRecipients->getConfig()->getComponentByType('GridFieldAddNewButton')->setButtonName(
_t('UserDefinedForm.ADDEMAILRECIPIENT', 'Add Email Recipient') _t('UserDefinedForm.ADDEMAILRECIPIENT', 'Add Email Recipient')
); );
$emailRecipients
->getConfig()
->getComponentByType('GridFieldDetailForm')
->setItemRequestClass('UserDefinedForm_EmailRecipient_ItemRequest');
$fields->addFieldsToTab('Root.FormOptions', $onCompleteFieldSet); $fields->addFieldsToTab('Root.FormOptions', $onCompleteFieldSet);
$fields->addFieldToTab('Root.FormOptions', $emailRecipients); $fields->addFieldToTab('Root.FormOptions', $emailRecipients);
@ -989,7 +998,8 @@ JS
// email users on submit. // email users on submit.
if($recipients = $this->FilteredEmailRecipients($data, $form)) { if($recipients = $this->FilteredEmailRecipients($data, $form)) {
$email = new UserDefinedForm_SubmittedFormEmail($submittedFields); $email = new UserDefinedForm_SubmittedFormEmail($submittedFields);
$mergeFields = $this->getMergeFieldsMap($emailData['Fields']);
if($attachments) { if($attachments) {
foreach($attachments as $file) { foreach($attachments as $file) {
if($file->ID != 0) { if($file->ID != 0) {
@ -1003,10 +1013,16 @@ JS
} }
foreach($recipients as $recipient) { foreach($recipients as $recipient) {
$parsedBody = SSViewer::execute_string($recipient->getEmailBodyContent(), $mergeFields);
if (!$recipient->SendPlain && $recipient->emailTemplateExists()) {
$email->setTemplate($recipient->EmailTemplate);
}
$email->populateTemplate($recipient); $email->populateTemplate($recipient);
$email->populateTemplate($emailData); $email->populateTemplate($emailData);
$email->setFrom($recipient->EmailFrom); $email->setFrom($recipient->EmailFrom);
$email->setBody($recipient->EmailBody); $email->setBody($parsedBody);
$email->setTo($recipient->EmailAddress); $email->setTo($recipient->EmailAddress);
$email->setSubject($recipient->EmailSubject); $email->setSubject($recipient->EmailSubject);
@ -1043,7 +1059,7 @@ JS
$this->extend('updateEmail', $email, $recipient, $emailData); $this->extend('updateEmail', $email, $recipient, $emailData);
if($recipient->SendPlain) { if($recipient->SendPlain) {
$body = strip_tags($recipient->EmailBody) . "\n"; $body = strip_tags($recipient->getEmailBodyContent()) . "\n";
if(isset($emailData['Fields']) && !$recipient->HideFormData) { if(isset($emailData['Fields']) && !$recipient->HideFormData) {
foreach($emailData['Fields'] as $Field) { foreach($emailData['Fields'] as $Field) {
$body .= $Field->Title .': '. $Field->Value ." \n"; $body .= $Field->Title .': '. $Field->Value ." \n";
@ -1091,6 +1107,22 @@ JS
return $this->redirect($this->Link('finished') . $referrer . $this->config()->finished_anchor); return $this->redirect($this->Link('finished') . $referrer . $this->config()->finished_anchor);
} }
/**
* Allows the use of field values in email body.
*
* @param ArrayList fields
* @return ViewableData
*/
private function getMergeFieldsMap($fields = array()) {
$data = new ViewableData();
foreach ($fields as $field) {
$data->setField($field->Name, DBField::create_field('Text', $field->Value));
}
return $data;
}
/** /**
* This action handles rendering the "finished" message, which is * This action handles rendering the "finished" message, which is
* customizable by editing the ReceivedFormSubmission template. * customizable by editing the ReceivedFormSubmission template.
@ -1150,6 +1182,8 @@ class UserDefinedForm_EmailRecipient extends DataObject {
'EmailFrom' => 'Varchar(200)', 'EmailFrom' => 'Varchar(200)',
'EmailReplyTo' => 'Varchar(200)', 'EmailReplyTo' => 'Varchar(200)',
'EmailBody' => 'Text', 'EmailBody' => 'Text',
'EmailBodyHtml' => 'HTMLText',
'EmailTemplate' => 'Varchar',
'SendPlain' => 'Boolean', 'SendPlain' => 'Boolean',
'HideFormData' => 'Boolean' 'HideFormData' => 'Boolean'
); );
@ -1167,6 +1201,16 @@ class UserDefinedForm_EmailRecipient extends DataObject {
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields() {
// Only show the preview link if the recipient has been saved.
if (!empty($this->EmailTemplate)) {
$translatableKey = 'UserDefinedForm.EMAILPREVIEWAVAILABLE';
$previewHTML = '<p><a href="admin/pages/edit/EditForm/field/EmailRecipients/item/' . $this->ID . '/preview" target="_blank" class="ss-ui-button">Preview email</a></p>' .
'<em>Note: Unsaved changes will not appear in the preview.</em>';
} else {
$translatableKey = 'UserDefinedForm.EMAILPREVIEWUNAVAILABLE';
$previewHTML = '<em>You can preview this email once you have saved the Recipient.</em>';
}
$fields = new FieldList( $fields = new FieldList(
new TextField('EmailSubject', _t('UserDefinedForm.EMAILSUBJECT', 'Email subject')), new TextField('EmailSubject', _t('UserDefinedForm.EMAILSUBJECT', 'Email subject')),
@ -1182,7 +1226,10 @@ class UserDefinedForm_EmailRecipient extends DataObject {
new TextField('EmailAddress', _t('UserDefinedForm.SENDEMAILTO','Send email to')), new TextField('EmailAddress', _t('UserDefinedForm.SENDEMAILTO','Send email to')),
new CheckboxField('HideFormData', _t('UserDefinedForm.HIDEFORMDATA', 'Hide form data from email?')), new CheckboxField('HideFormData', _t('UserDefinedForm.HIDEFORMDATA', 'Hide form data from email?')),
new CheckboxField('SendPlain', _t('UserDefinedForm.SENDPLAIN', 'Send email as plain text? (HTML will be stripped)')), new CheckboxField('SendPlain', _t('UserDefinedForm.SENDPLAIN', 'Send email as plain text? (HTML will be stripped)')),
new TextareaField('EmailBody', _t('UserDefinedForm.EMAILBODY','Body')) new DropdownField('EmailTemplate', _t('UserDefinedForm.EMAILTEMPLATE', 'Email template'), $this->getEmailTemplateDropdownValues()),
new HTMLEditorField('EmailBodyHtml', _t('UserDefinedForm.EMAILBODYHTML','Body')),
new TextareaField('EmailBody', _t('UserDefinedForm.EMAILBODY','Body')),
new LiteralField('EmailPreview', '<div id="EmailPreview">' . _t($translatableKey, $previewHTML) . '</div>')
); );
$formID = ($this->FormID != 0) ? $this->FormID : Session::get('CMSMain.currentPage'); $formID = ($this->FormID != 0) ? $this->FormID : Session::get('CMSMain.currentPage');
@ -1260,6 +1307,79 @@ class UserDefinedForm_EmailRecipient extends DataObject {
public function canDelete($member = null) { public function canDelete($member = null) {
return $this->Form()->canDelete(); return $this->Form()->canDelete();
} }
/**
* Make sure the email template saved against the recipient exists on the file system.
*
* @param string
*
* @return boolean
*/
public function emailTemplateExists($template = '') {
$t = ($template ? $template : $this->EmailTemplate);
return in_array($t, $this->getEmailTemplateDropdownValues());
}
/**
* Get the email body for the current email format
*
* @return string
*/
public function getEmailBodyContent() {
return $this->SendPlain ? $this->EmailBody : $this->EmailBodyHtml;
}
/**
* Gets a list of email templates suitable for populating the email template dropdown.
*
* @return array
*/
public function getEmailTemplateDropdownValues() {
$templates = array();
$finder = new SS_FileFinder();
$finder->setOption('name_regex', '/^.*\.ss$/');
$found = $finder->find(BASE_PATH . '/' . UserDefinedForm::config()->email_template_directory);
foreach ($found as $key => $value) {
$template = pathinfo($value);
$templates[$template['filename']] = $template['filename'];
}
return $templates;
}
}
/**
* Controller that handles requests to EmailRecipient's
*
* @package userforms
*/
class UserDefinedForm_EmailRecipient_ItemRequest extends GridFieldDetailForm_ItemRequest {
private static $allowed_actions = array(
'edit',
'view',
'ItemEditForm',
'preview'
);
public function edit($request) {
Requirements::javascript(USERFORMS_DIR . '/javascript/Recipient.js');
return parent::edit($request);
}
/**
* Renders a preview of the recipient email.
*/
public function preview() {
return $this->customise(new ArrayData(array(
'Body' => $this->record->getEmailBodyContent()
)))->renderWith($this->record->EmailTemplate);
}
} }
/** /**

View File

@ -401,6 +401,16 @@ class EditableFormField extends DataObject {
public function getFieldConfiguration() { public function getFieldConfiguration() {
$extraClass = ($this->getSetting('ExtraClass')) ? $this->getSetting('ExtraClass') : ''; $extraClass = ($this->getSetting('ExtraClass')) ? $this->getSetting('ExtraClass') : '';
$mergeFieldName = new LiteralField('MergeFieldName', _t('EditableFormField.MERGEFIELDNAME',
'<div class="field">' .
'<label class="left" for="Fields-6-CustomSettings-RightTitle">Merge field</label>' .
'<div class="middleColumn">' .
'<p>$' . $this->Name . '</p>' .
'<em>Use this to display the field\'s value in email content.</em>' .
'</div>' .
'</div>'
));
if (is_array(self::$allowed_css) && !empty(self::$allowed_css)) { if (is_array(self::$allowed_css) && !empty(self::$allowed_css)) {
foreach(self::$allowed_css as $k => $v) { foreach(self::$allowed_css as $k => $v) {
if (!is_array($v)) $cssList[$k]=$v; if (!is_array($v)) $cssList[$k]=$v;
@ -429,6 +439,7 @@ class EditableFormField extends DataObject {
); );
$fields = FieldList::create( $fields = FieldList::create(
$mergeFieldName,
$ec, $ec,
$right $right
); );

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -16,6 +16,7 @@ and without getting involved in any PHP code.
* Construct a form using all major form fields (text, email, dropdown, radio, checkbox..) * Construct a form using all major form fields (text, email, dropdown, radio, checkbox..)
* Ability to extend userforms from other modules to provide extra fields. * Ability to extend userforms from other modules to provide extra fields.
* Ability to email multiple people the form submission * Ability to email multiple people the form submission
* Custom email templates
* View submitted submissions and export them to CSV * View submitted submissions and export them to CSV
* Define custom error messages and validation settings * Define custom error messages and validation settings
* Optionally display and hide fields using javascript based on users input * Optionally display and hide fields using javascript based on users input

View File

@ -18,3 +18,14 @@ Installation can be done either by composer or by manually downloading a release
After installation, make sure you rebuild your database through `dev/build`. After installation, make sure you rebuild your database through `dev/build`.
You should see a new PageType in the CMS 'User Defined Form'. This has a new 'Form' tab which has your form builder. You should see a new PageType in the CMS 'User Defined Form'. This has a new 'Form' tab which has your form builder.
### Custom email templates
If you want to use custom email templates set the following config option.
````
UserDefinedForm:
email_template_directory: your/template/path/
````
Any SilverStripe templates placed in your `email_template_directory` directory will be available for use with submission emails.

View File

@ -173,4 +173,18 @@ to determine the size and the number of rows in a text field.
**Or perhaps you'd like to add informational content to your form?** **Or perhaps you'd like to add informational content to your form?**
* Use [HTML Block](#html-block), with the appropriate level [Heading](#heading). * Use [HTML Block](#html-block), with the appropriate level [Heading](#heading).
## Sending emails
The UserForms module allows you to email form submissions to multiple people.
### Using form fields in submission emails
Each form field has a unique merge field located under the field's options.
![Merge field option](_images/mergefield.png)
Simply insert the merge field into the email content, and the field's value will be displayed, when the email is sent.
![Merge field in content](_images/mergefieldcontent.png)

44
javascript/Recipient.js Normal file
View File

@ -0,0 +1,44 @@
/**
* Email recipient behaviour.
*/
(function ($) {
$(document).ready(function () {
var recipient = {
// Some fields are only visible when HTML email are being sent.
updateFormatSpecificFields: function () {
var sendPlainChecked = $('#SendPlain').find('input[type="checkbox"]').is(':checked');
// Hide the preview link when 'SendPlain' is selected.
$('#EmailPreview')[sendPlainChecked ? 'hide' : 'show']();
// Hide the template selector when 'SendPlain' is selected.
$('#EmailTemplate')[sendPlainChecked ? 'hide' : 'show']();
// Hide the HTML editor when 'SendPlain' is selected.
$('#EmailBodyHtml')[sendPlainChecked ? 'hide' : 'show']();
// Show the body teaxtarea when 'SendPlain' is selected.
$('#EmailBody')[sendPlainChecked ? 'show' : 'hide']();
}
};
$.entwine('udf.recipient', function ($) {
$('#Form_ItemEditForm').entwine({
onmatch: function () {
recipient.updateFormatSpecificFields();
},
onunmatch: function () {
this._super();
}
});
$('#SendPlain').entwine({
onchange: function () {
recipient.updateFormatSpecificFields();
}
});
});
});
}(jQuery));

View File

@ -84,7 +84,52 @@ class UserDefinedFormTest extends FunctionalTest {
$popup->delete(); $popup->delete();
} }
function testGetEmailBodyContent() {
$recipient = new UserDefinedForm_EmailRecipient();
$emailBody = 'not html';
$emailBodyHtml = '<p>html</p>';
$recipient->EmailBody = $emailBody;
$recipient->EmailBodyHtml = $emailBodyHtml;
$recipient->write();
$this->assertEquals($recipient->SendPlain, 0);
$this->assertEquals($recipient->getEmailBodyContent(), $emailBodyHtml);
$recipient->SendPlain = 1;
$recipient->write();
$this->assertEquals($recipient->getEmailBodyContent(), $emailBody);
$recipient->delete();
}
function testGetEmailTemplateDropdownValues() {
$recipient = new UserDefinedForm_EmailRecipient();
$defaultValues = array('SubmittedFormEmail' => 'SubmittedFormEmail');
$this->assertEquals($recipient->getEmailTemplateDropdownValues(), $defaultValues);
}
function testEmailTemplateExists() {
$recipient = new UserDefinedForm_EmailRecipient();
// Set the default template
$recipient->EmailTemplate = current(array_keys($recipient->getEmailTemplateDropdownValues()));
$recipient->write();
// The default template exists
$this->assertTrue($recipient->emailTemplateExists());
// A made up template doesn't exists
$this->assertFalse($recipient->emailTemplateExists('MyTemplateThatsNotThere'));
$recipient->delete();
}
function testCanEditAndDeleteRecipient() { function testCanEditAndDeleteRecipient() {
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');