diff --git a/code/model/UserDefinedForm.php b/code/model/UserDefinedForm.php index 212c688..ec6a6c7 100755 --- a/code/model/UserDefinedForm.php +++ b/code/model/UserDefinedForm.php @@ -25,6 +25,11 @@ class UserDefinedForm extends Page { private static $translate_excluded_fields = array( 'Fields' ); + + /** + * @var string + */ + private static $email_template_directory = 'userforms/templates/email/'; /** * @var array Fields on the user defined form page. @@ -110,6 +115,10 @@ class UserDefinedForm extends Page { $emailRecipients->getConfig()->getComponentByType('GridFieldAddNewButton')->setButtonName( _t('UserDefinedForm.ADDEMAILRECIPIENT', 'Add Email Recipient') ); + $emailRecipients + ->getConfig() + ->getComponentByType('GridFieldDetailForm') + ->setItemRequestClass('UserDefinedForm_EmailRecipient_ItemRequest'); $fields->addFieldsToTab('Root.FormOptions', $onCompleteFieldSet); $fields->addFieldToTab('Root.FormOptions', $emailRecipients); @@ -989,7 +998,8 @@ JS // email users on submit. if($recipients = $this->FilteredEmailRecipients($data, $form)) { $email = new UserDefinedForm_SubmittedFormEmail($submittedFields); - + $mergeFields = $this->getMergeFieldsMap($emailData['Fields']); + if($attachments) { foreach($attachments as $file) { if($file->ID != 0) { @@ -1003,10 +1013,16 @@ JS } 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($emailData); $email->setFrom($recipient->EmailFrom); - $email->setBody($recipient->EmailBody); + $email->setBody($parsedBody); $email->setTo($recipient->EmailAddress); $email->setSubject($recipient->EmailSubject); @@ -1043,7 +1059,7 @@ JS $this->extend('updateEmail', $email, $recipient, $emailData); if($recipient->SendPlain) { - $body = strip_tags($recipient->EmailBody) . "\n"; + $body = strip_tags($recipient->getEmailBodyContent()) . "\n"; if(isset($emailData['Fields']) && !$recipient->HideFormData) { foreach($emailData['Fields'] as $Field) { $body .= $Field->Title .': '. $Field->Value ." \n"; @@ -1091,6 +1107,22 @@ JS 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 * customizable by editing the ReceivedFormSubmission template. @@ -1150,6 +1182,8 @@ class UserDefinedForm_EmailRecipient extends DataObject { 'EmailFrom' => 'Varchar(200)', 'EmailReplyTo' => 'Varchar(200)', 'EmailBody' => 'Text', + 'EmailBodyHtml' => 'HTMLText', + 'EmailTemplate' => 'Varchar', 'SendPlain' => 'Boolean', 'HideFormData' => 'Boolean' ); @@ -1167,6 +1201,16 @@ class UserDefinedForm_EmailRecipient extends DataObject { * @return FieldList */ public function getCMSFields() { + + // Only show the preview link if the recipient has been saved. + if (!empty($this->EmailTemplate)) { + $translatableKey = 'UserDefinedForm.EMAILPREVIEWAVAILABLE'; + $previewHTML = '

Preview email

' . + 'Note: Unsaved changes will not appear in the preview.'; + } else { + $translatableKey = 'UserDefinedForm.EMAILPREVIEWUNAVAILABLE'; + $previewHTML = 'You can preview this email once you have saved the Recipient.'; + } $fields = new FieldList( 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 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 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', '
' . _t($translatableKey, $previewHTML) . '
') ); $formID = ($this->FormID != 0) ? $this->FormID : Session::get('CMSMain.currentPage'); @@ -1260,6 +1307,79 @@ class UserDefinedForm_EmailRecipient extends DataObject { public function canDelete($member = null) { 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); + } } /** diff --git a/code/model/formfields/EditableFormField.php b/code/model/formfields/EditableFormField.php index 79f3ea3..7e9f559 100755 --- a/code/model/formfields/EditableFormField.php +++ b/code/model/formfields/EditableFormField.php @@ -401,6 +401,16 @@ class EditableFormField extends DataObject { public function getFieldConfiguration() { $extraClass = ($this->getSetting('ExtraClass')) ? $this->getSetting('ExtraClass') : ''; + $mergeFieldName = new LiteralField('MergeFieldName', _t('EditableFormField.MERGEFIELDNAME', + '
' . + '' . + '
' . + '

$' . $this->Name . '

' . + 'Use this to display the field\'s value in email content.' . + '
' . + '
' + )); + if (is_array(self::$allowed_css) && !empty(self::$allowed_css)) { foreach(self::$allowed_css as $k => $v) { if (!is_array($v)) $cssList[$k]=$v; @@ -429,6 +439,7 @@ class EditableFormField extends DataObject { ); $fields = FieldList::create( + $mergeFieldName, $ec, $right ); diff --git a/docs/en/_images/mergefield.png b/docs/en/_images/mergefield.png new file mode 100644 index 0000000..60a7aae Binary files /dev/null and b/docs/en/_images/mergefield.png differ diff --git a/docs/en/_images/mergefieldcontent.png b/docs/en/_images/mergefieldcontent.png new file mode 100644 index 0000000..ea6b256 Binary files /dev/null and b/docs/en/_images/mergefieldcontent.png differ diff --git a/docs/en/index.md b/docs/en/index.md index 49ccb27..0a7aab6 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -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..) * Ability to extend userforms from other modules to provide extra fields. * Ability to email multiple people the form submission +* Custom email templates * View submitted submissions and export them to CSV * Define custom error messages and validation settings * Optionally display and hide fields using javascript based on users input diff --git a/docs/en/installation.md b/docs/en/installation.md index 7c93fc5..d810c40 100644 --- a/docs/en/installation.md +++ b/docs/en/installation.md @@ -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`. 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. diff --git a/docs/en/user-documentation.md b/docs/en/user-documentation.md index 4344bd2..fb0e0e0 100644 --- a/docs/en/user-documentation.md +++ b/docs/en/user-documentation.md @@ -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?** -* Use [HTML Block](#html-block), with the appropriate level [Heading](#heading). \ No newline at end of file +* 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) diff --git a/javascript/Recipient.js b/javascript/Recipient.js new file mode 100644 index 0000000..1cc88d0 --- /dev/null +++ b/javascript/Recipient.js @@ -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)); diff --git a/tests/UserDefinedFormTest.php b/tests/UserDefinedFormTest.php index 3287a3d..1856cd9 100644 --- a/tests/UserDefinedFormTest.php +++ b/tests/UserDefinedFormTest.php @@ -84,7 +84,52 @@ class UserDefinedFormTest extends FunctionalTest { $popup->delete(); } - + + function testGetEmailBodyContent() { + $recipient = new UserDefinedForm_EmailRecipient(); + + $emailBody = 'not html'; + $emailBodyHtml = '

html

'; + + $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() { $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');