2008-09-29 05:18:23 +02:00
|
|
|
<?php
|
2010-07-07 11:17:37 +02:00
|
|
|
|
2017-10-09 21:48:16 +02:00
|
|
|
namespace SilverStripe\UserForms\Control;
|
2017-08-09 01:55:09 +02:00
|
|
|
|
2021-06-22 02:12:42 +02:00
|
|
|
use Exception;
|
2017-08-09 01:55:09 +02:00
|
|
|
use PageController;
|
2019-05-03 05:55:59 +02:00
|
|
|
use Psr\Log\LoggerInterface;
|
2020-05-27 02:26:14 +02:00
|
|
|
use SilverStripe\AssetAdmin\Controller\AssetAdmin;
|
2017-08-11 01:33:06 +02:00
|
|
|
use SilverStripe\Assets\File;
|
|
|
|
use SilverStripe\Assets\Upload;
|
|
|
|
use SilverStripe\Control\Controller;
|
2017-08-15 00:49:13 +02:00
|
|
|
use SilverStripe\Control\Email\Email;
|
2018-01-25 21:31:06 +01:00
|
|
|
use SilverStripe\Control\HTTPRequest;
|
2020-06-11 14:55:12 +02:00
|
|
|
use SilverStripe\Control\HTTPResponse;
|
2019-05-03 05:55:59 +02:00
|
|
|
use SilverStripe\Core\Injector\Injector;
|
2017-08-13 23:26:53 +02:00
|
|
|
use SilverStripe\Core\Manifest\ModuleLoader;
|
2017-08-11 01:33:06 +02:00
|
|
|
use SilverStripe\Forms\Form;
|
2017-08-09 01:55:09 +02:00
|
|
|
use SilverStripe\i18n\i18n;
|
2017-08-11 01:33:06 +02:00
|
|
|
use SilverStripe\ORM\ArrayList;
|
2017-08-09 01:55:09 +02:00
|
|
|
use SilverStripe\ORM\FieldType\DBField;
|
2017-08-11 01:33:06 +02:00
|
|
|
use SilverStripe\ORM\ValidationException;
|
2019-10-07 10:56:22 +02:00
|
|
|
use SilverStripe\ORM\ValidationResult;
|
2017-08-15 00:49:13 +02:00
|
|
|
use SilverStripe\Security\Security;
|
2020-05-28 03:14:34 +02:00
|
|
|
use SilverStripe\UserForms\Extension\UserFormFileExtension;
|
2017-08-11 01:33:06 +02:00
|
|
|
use SilverStripe\UserForms\Form\UserForm;
|
2019-03-25 03:09:38 +01:00
|
|
|
use SilverStripe\UserForms\Model\EditableFormField;
|
2017-08-09 01:55:09 +02:00
|
|
|
use SilverStripe\UserForms\Model\EditableFormField\EditableFileField;
|
2017-08-11 01:33:06 +02:00
|
|
|
use SilverStripe\UserForms\Model\Submission\SubmittedForm;
|
2020-06-30 23:00:31 +02:00
|
|
|
use SilverStripe\UserForms\Model\Submission\SubmittedFileField;
|
2020-06-10 06:59:01 +02:00
|
|
|
use SilverStripe\UserForms\Model\UserDefinedForm;
|
2022-05-13 06:25:51 +02:00
|
|
|
use SilverStripe\Versioned\Versioned;
|
2017-08-09 01:55:09 +02:00
|
|
|
use SilverStripe\View\ArrayData;
|
2017-08-11 01:33:06 +02:00
|
|
|
use SilverStripe\View\Requirements;
|
|
|
|
use SilverStripe\View\SSViewer;
|
2020-06-10 06:59:01 +02:00
|
|
|
use SilverStripe\View\ViewableData;
|
2019-05-03 05:55:59 +02:00
|
|
|
use Swift_RfcComplianceException;
|
2017-08-09 01:55:09 +02:00
|
|
|
|
2008-09-29 05:18:23 +02:00
|
|
|
/**
|
|
|
|
* Controller for the {@link UserDefinedForm} page type.
|
2009-04-14 04:06:18 +02:00
|
|
|
*
|
2012-10-18 09:01:56 +02:00
|
|
|
* @package userforms
|
2008-09-29 05:18:23 +02:00
|
|
|
*/
|
2017-08-09 01:47:47 +02:00
|
|
|
class UserDefinedFormController extends PageController
|
2016-07-21 07:53:59 +02:00
|
|
|
{
|
|
|
|
private static $finished_anchor = '#uff';
|
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
private static $allowed_actions = [
|
2016-07-21 07:53:59 +02:00
|
|
|
'index',
|
|
|
|
'ping',
|
|
|
|
'Form',
|
2020-06-10 06:59:01 +02:00
|
|
|
'finished',
|
2017-08-11 01:33:06 +02:00
|
|
|
];
|
2016-07-21 07:53:59 +02:00
|
|
|
|
2020-06-10 06:59:01 +02:00
|
|
|
/** @var string The name of the folder where form submissions will be placed by default */
|
|
|
|
private static $form_submissions_folder = 'Form-submissions';
|
|
|
|
|
2022-05-13 06:25:51 +02:00
|
|
|
private static string $file_upload_stage = Versioned::DRAFT;
|
|
|
|
|
2020-10-31 09:15:00 +01:00
|
|
|
/**
|
|
|
|
* Size that an uploaded file must not excede for it to be attached to an email
|
|
|
|
* Follows PHP "shorthand bytes" definition rules.
|
|
|
|
* @see self::parseByteSizeString()
|
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
* @config
|
|
|
|
*/
|
|
|
|
private static $maximum_email_attachment_size = '1M';
|
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
protected function init()
|
2016-07-21 07:53:59 +02:00
|
|
|
{
|
|
|
|
parent::init();
|
|
|
|
|
2017-04-01 12:02:18 +02:00
|
|
|
$page = $this->data();
|
|
|
|
|
|
|
|
// load the css
|
2017-08-11 01:33:06 +02:00
|
|
|
if (!$page->config()->get('block_default_userforms_css')) {
|
2017-10-18 11:02:49 +02:00
|
|
|
Requirements::css('silverstripe/userforms:client/dist/styles/userforms.css');
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
|
2017-04-01 12:02:18 +02:00
|
|
|
// load the jquery
|
2017-08-11 01:33:06 +02:00
|
|
|
if (!$page->config()->get('block_default_userforms_js')) {
|
2021-03-04 05:05:55 +01:00
|
|
|
Requirements::javascript('silverstripe/userforms:client/dist/js/jquery.min.js');
|
2017-08-11 01:33:06 +02:00
|
|
|
Requirements::javascript(
|
2021-03-04 05:05:55 +01:00
|
|
|
'silverstripe/userforms:client/dist/js/jquery-validation/jquery.validate.min.js'
|
2017-08-11 01:33:06 +02:00
|
|
|
);
|
2017-11-20 03:16:03 +01:00
|
|
|
Requirements::javascript('silverstripe/admin:client/dist/js/i18n.js');
|
2017-10-18 11:02:49 +02:00
|
|
|
Requirements::add_i18n_javascript('silverstripe/userforms:client/lang');
|
|
|
|
Requirements::javascript('silverstripe/userforms:client/dist/js/userforms.js');
|
2017-08-13 23:26:53 +02:00
|
|
|
|
2017-10-18 11:02:49 +02:00
|
|
|
$this->addUserFormsValidatei18n();
|
2017-08-11 01:33:06 +02:00
|
|
|
|
|
|
|
// Bind a confirmation message when navigating away from a partially completed form.
|
|
|
|
if ($page::config()->get('enable_are_you_sure')) {
|
2017-08-13 23:26:53 +02:00
|
|
|
Requirements::javascript(
|
2021-03-04 05:05:55 +01:00
|
|
|
'silverstripe/userforms:client/dist/js/jquery.are-you-sure/jquery.are-you-sure.js'
|
2017-08-13 23:26:53 +02:00
|
|
|
);
|
2017-08-11 01:33:06 +02:00
|
|
|
}
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-21 04:35:31 +02:00
|
|
|
/**
|
|
|
|
* Add the necessary jQuery validate i18n translation files, either by locale or by langauge,
|
|
|
|
* e.g. 'en_NZ' or 'en'. This adds "methods_abc.min.js" as well as "messages_abc.min.js" from the
|
2021-03-04 05:05:55 +01:00
|
|
|
* jQuery validate thirdparty library from dist/js.
|
2017-08-21 04:35:31 +02:00
|
|
|
*/
|
2017-10-18 11:02:49 +02:00
|
|
|
protected function addUserFormsValidatei18n()
|
2017-08-21 04:35:31 +02:00
|
|
|
{
|
2017-10-18 11:02:49 +02:00
|
|
|
$module = ModuleLoader::getModule('silverstripe/userforms');
|
|
|
|
|
2017-08-21 04:35:31 +02:00
|
|
|
$candidates = [
|
|
|
|
i18n::getData()->langFromLocale(i18n::config()->get('default_locale')),
|
|
|
|
i18n::config()->get('default_locale'),
|
|
|
|
i18n::getData()->langFromLocale(i18n::get_locale()),
|
|
|
|
i18n::get_locale(),
|
|
|
|
];
|
|
|
|
|
|
|
|
foreach ($candidates as $candidate) {
|
|
|
|
foreach (['messages', 'methods'] as $candidateType) {
|
2021-03-04 05:05:55 +01:00
|
|
|
$localisationCandidate = "client/dist/js/jquery-validation/localization/{$candidateType}_{$candidate}.min.js";
|
2017-10-18 11:02:49 +02:00
|
|
|
|
|
|
|
$resource = $module->getResource($localisationCandidate);
|
|
|
|
if ($resource->exists()) {
|
|
|
|
Requirements::javascript($resource->getRelativePath());
|
2017-08-21 04:35:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-21 07:53:59 +02:00
|
|
|
/**
|
|
|
|
* Using $UserDefinedForm in the Content area of the page shows
|
|
|
|
* where the form should be rendered into. If it does not exist
|
|
|
|
* then default back to $Form.
|
|
|
|
*
|
2018-07-09 04:32:34 +02:00
|
|
|
* @return array
|
2016-07-21 07:53:59 +02:00
|
|
|
*/
|
2018-01-25 21:31:06 +01:00
|
|
|
public function index(HTTPRequest $request = null)
|
2016-07-21 07:53:59 +02:00
|
|
|
{
|
2018-07-09 04:32:34 +02:00
|
|
|
$form = $this->Form();
|
|
|
|
if ($this->Content && $form && !$this->config()->disable_form_content_shortcode) {
|
2022-04-13 03:52:56 +02:00
|
|
|
$hasLocation = stristr($this->Content ?? '', '$UserDefinedForm');
|
2016-07-21 07:53:59 +02:00
|
|
|
if ($hasLocation) {
|
2017-09-26 00:30:56 +02:00
|
|
|
/** @see Requirements_Backend::escapeReplacement */
|
2022-04-13 03:52:56 +02:00
|
|
|
$formEscapedForRegex = addcslashes($form->forTemplate() ?? '', '\\$');
|
2017-08-13 23:26:53 +02:00
|
|
|
$content = preg_replace(
|
|
|
|
'/(<p[^>]*>)?\\$UserDefinedForm(<\\/p>)?/i',
|
2022-04-13 03:52:56 +02:00
|
|
|
$formEscapedForRegex ?? '',
|
|
|
|
$this->Content ?? ''
|
2017-08-13 23:26:53 +02:00
|
|
|
);
|
2017-08-11 01:33:06 +02:00
|
|
|
return [
|
2016-07-21 07:53:59 +02:00
|
|
|
'Content' => DBField::create_field('HTMLText', $content),
|
2017-08-11 01:33:06 +02:00
|
|
|
'Form' => ''
|
|
|
|
];
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
return [
|
2016-07-21 07:53:59 +02:00
|
|
|
'Content' => DBField::create_field('HTMLText', $this->Content),
|
|
|
|
'Form' => $this->Form()
|
2017-08-11 01:33:06 +02:00
|
|
|
];
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Keep the session alive for the user.
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function ping()
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the form for the page. Form can be modified by calling {@link updateForm()}
|
|
|
|
* on a UserDefinedForm extension.
|
|
|
|
*
|
2017-09-04 23:45:16 +02:00
|
|
|
* @return Form
|
2016-07-21 07:53:59 +02:00
|
|
|
*/
|
|
|
|
public function Form()
|
|
|
|
{
|
2017-04-18 03:11:32 +02:00
|
|
|
$form = UserForm::create($this, 'Form_' . $this->ID);
|
2017-08-14 00:08:22 +02:00
|
|
|
$form->setFormAction(Controller::join_links($this->Link(), 'Form'));
|
2016-07-21 07:53:59 +02:00
|
|
|
$this->generateConditionalJavascript();
|
|
|
|
return $form;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate the javascript for the conditional field show / hiding logic.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function generateConditionalJavascript()
|
|
|
|
{
|
2017-08-11 01:33:06 +02:00
|
|
|
$rules = '';
|
2018-02-12 04:56:36 +01:00
|
|
|
$form = $this->data();
|
2019-03-25 03:09:38 +01:00
|
|
|
if (!$form) {
|
|
|
|
return;
|
|
|
|
}
|
2018-02-12 04:56:36 +01:00
|
|
|
$formFields = $form->Fields();
|
2016-07-21 07:53:59 +02:00
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
$watch = [];
|
2016-07-21 07:53:59 +02:00
|
|
|
|
2018-02-12 04:56:36 +01:00
|
|
|
if ($formFields) {
|
2017-04-28 00:22:15 +02:00
|
|
|
/** @var EditableFormField $field */
|
2018-02-12 04:56:36 +01:00
|
|
|
foreach ($formFields as $field) {
|
2017-04-28 00:22:15 +02:00
|
|
|
if ($result = $field->formatDisplayRules()) {
|
|
|
|
$watch[] = $result;
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($watch) {
|
2017-04-28 00:22:15 +02:00
|
|
|
$rules .= $this->buildWatchJS($watch);
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
2013-02-27 04:05:30 +01:00
|
|
|
|
2016-07-21 07:53:59 +02:00
|
|
|
// Only add customScript if $default or $rules is defined
|
2017-04-28 00:22:15 +02:00
|
|
|
if ($rules) {
|
2016-07-21 07:53:59 +02:00
|
|
|
Requirements::customScript(<<<JS
|
2017-04-28 00:22:15 +02:00
|
|
|
(function($) {
|
|
|
|
$(document).ready(function() {
|
|
|
|
{$rules}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
2009-04-21 05:44:13 +02:00
|
|
|
JS
|
2020-05-27 02:26:14 +02:00
|
|
|
, 'UserFormsConditional-' . $form->ID);
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-31 09:15:00 +01:00
|
|
|
/**
|
|
|
|
* Returns the maximum size uploaded files can be before they're excluded from CMS configured recipient emails
|
|
|
|
*
|
|
|
|
* @return int size in megabytes
|
|
|
|
*/
|
|
|
|
public function getMaximumAllowedEmailAttachmentSize()
|
|
|
|
{
|
|
|
|
return $this->parseByteSizeString($this->config()->get('maximum_email_attachment_size'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert file sizes with a single character for unit size to true byte count.
|
|
|
|
* Just as with php.ini and e.g. 128M -> 1024 * 1024 * 128 bytes.
|
|
|
|
* @see https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
|
|
|
|
*
|
|
|
|
* @param string $byteSize
|
|
|
|
* @return int bytes
|
|
|
|
*/
|
|
|
|
protected function parseByteSizeString($byteSize)
|
|
|
|
{
|
|
|
|
// kilo, mega, giga
|
|
|
|
$validUnits = 'kmg';
|
|
|
|
$valid = preg_match("/^(?<number>\d+)((?<unit>[$validUnits])b?)?$/i", $byteSize, $matches);
|
|
|
|
if (!$valid) {
|
|
|
|
throw new \InvalidArgumentException(
|
|
|
|
"Expected a positive integer followed optionally by K, M, or G. Found '$byteSize' instead"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
$power = 0;
|
|
|
|
// prepend b for bytes to $validUnits to give correct mapping of ordinal position to exponent
|
|
|
|
if (isset($matches['unit'])) {
|
|
|
|
$power = stripos("b$validUnits", $matches['unit']);
|
|
|
|
}
|
|
|
|
return intval($matches['number']) * pow(1024, $power);
|
|
|
|
}
|
|
|
|
|
2016-07-21 07:53:59 +02:00
|
|
|
/**
|
|
|
|
* Process the form that is submitted through the site
|
|
|
|
*
|
|
|
|
* {@see UserForm::validate()} for validation step prior to processing
|
|
|
|
*
|
|
|
|
* @param array $data
|
|
|
|
* @param Form $form
|
|
|
|
*
|
2018-01-24 03:17:03 +01:00
|
|
|
* @return HTTPResponse
|
2016-07-21 07:53:59 +02:00
|
|
|
*/
|
|
|
|
public function process($data, $form)
|
|
|
|
{
|
2017-08-11 01:33:06 +02:00
|
|
|
$submittedForm = SubmittedForm::create();
|
2017-08-15 00:49:13 +02:00
|
|
|
$submittedForm->SubmittedByID = Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0;
|
2017-10-26 04:40:48 +02:00
|
|
|
$submittedForm->ParentClass = get_class($this->data());
|
2016-07-21 07:53:59 +02:00
|
|
|
$submittedForm->ParentID = $this->ID;
|
|
|
|
|
|
|
|
// if saving is not disabled save now to generate the ID
|
|
|
|
if (!$this->DisableSaveSubmissions) {
|
|
|
|
$submittedForm->write();
|
|
|
|
}
|
|
|
|
|
2018-01-24 03:17:03 +01:00
|
|
|
$attachments = [];
|
2017-08-11 01:33:06 +02:00
|
|
|
$submittedFields = ArrayList::create();
|
2016-07-21 07:53:59 +02:00
|
|
|
|
2017-10-31 23:43:53 +01:00
|
|
|
foreach ($this->data()->Fields() as $field) {
|
2016-07-21 07:53:59 +02:00
|
|
|
if (!$field->showInReports()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$submittedField = $field->getSubmittedFormField();
|
|
|
|
$submittedField->ParentID = $submittedForm->ID;
|
|
|
|
$submittedField->Name = $field->Name;
|
|
|
|
$submittedField->Title = $field->getField('Title');
|
|
|
|
|
|
|
|
// save the value from the data
|
|
|
|
if ($field->hasMethod('getValueFromData')) {
|
|
|
|
$submittedField->Value = $field->getValueFromData($data);
|
|
|
|
} else {
|
|
|
|
if (isset($data[$field->Name])) {
|
|
|
|
$submittedField->Value = $data[$field->Name];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-07 01:02:34 +01:00
|
|
|
// set visibility flag according to display rules
|
|
|
|
$submittedField->Displayed = $field->isDisplayed($data);
|
|
|
|
|
2016-07-21 07:53:59 +02:00
|
|
|
if (!empty($data[$field->Name])) {
|
2022-04-13 03:52:56 +02:00
|
|
|
if (in_array(EditableFileField::class, $field->getClassAncestry() ?? [])) {
|
2017-03-02 00:46:13 +01:00
|
|
|
if (!empty($_FILES[$field->Name]['name'])) {
|
2022-05-13 06:25:51 +02:00
|
|
|
if (!$field->getFolderExists()) {
|
|
|
|
$field->createProtectedFolder();
|
|
|
|
}
|
2020-10-31 09:15:00 +01:00
|
|
|
|
2022-05-13 06:25:51 +02:00
|
|
|
$file = Versioned::withVersionedMode(function () use ($field, $form) {
|
|
|
|
$stage = Injector::inst()->get(self::class)->config()->get('file_upload_stage');
|
|
|
|
Versioned::set_stage($stage);
|
|
|
|
|
|
|
|
$foldername = $field->getFormField()->getFolderName();
|
|
|
|
// create the file from post data
|
|
|
|
$upload = Upload::create();
|
|
|
|
try {
|
|
|
|
$upload->loadIntoFile($_FILES[$field->Name], null, $foldername);
|
|
|
|
} catch (ValidationException $e) {
|
|
|
|
$validationResult = $e->getResult();
|
|
|
|
foreach ($validationResult->getMessages() as $message) {
|
|
|
|
$form->sessionMessage($message['message'], ValidationResult::TYPE_ERROR);
|
|
|
|
}
|
|
|
|
Controller::curr()->redirectBack();
|
|
|
|
return null;
|
2019-03-26 02:28:36 +01:00
|
|
|
}
|
2022-05-13 06:25:51 +02:00
|
|
|
/** @var AssetContainer|File $file */
|
|
|
|
$file = $upload->getFile();
|
|
|
|
$file->ShowInSearch = 0;
|
|
|
|
$file->UserFormUpload = UserFormFileExtension::USER_FORM_UPLOAD_TRUE;
|
|
|
|
$file->write();
|
|
|
|
|
|
|
|
return $file;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (is_null($file)) {
|
2016-07-21 07:53:59 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-05-27 02:26:14 +02:00
|
|
|
|
|
|
|
// generate image thumbnail to show in asset-admin
|
|
|
|
// you can run userforms without asset-admin, so need to ensure asset-admin is installed
|
|
|
|
if (class_exists(AssetAdmin::class)) {
|
|
|
|
AssetAdmin::singleton()->generateThumbnails($file);
|
|
|
|
}
|
2016-07-21 07:53:59 +02:00
|
|
|
|
|
|
|
// write file to form field
|
|
|
|
$submittedField->UploadedFileID = $file->ID;
|
|
|
|
|
2020-10-31 09:15:00 +01:00
|
|
|
// attach a file to recipient email only if lower than configured size
|
|
|
|
if ($file->getAbsoluteSize() <= $this->getMaximumAllowedEmailAttachmentSize()) {
|
2016-07-21 07:53:59 +02:00
|
|
|
$attachments[] = $file;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$submittedField->extend('onPopulationFromField', $field);
|
|
|
|
|
|
|
|
if (!$this->DisableSaveSubmissions) {
|
|
|
|
$submittedField->write();
|
|
|
|
}
|
|
|
|
|
|
|
|
$submittedFields->push($submittedField);
|
|
|
|
}
|
|
|
|
|
2022-03-07 01:02:34 +01:00
|
|
|
$visibleSubmittedFields = $submittedFields->filter('Displayed', true);
|
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
$emailData = [
|
2017-08-15 00:49:13 +02:00
|
|
|
'Sender' => Security::getCurrentUser(),
|
2018-01-25 03:38:54 +01:00
|
|
|
'HideFormData' => false,
|
2021-10-19 01:58:23 +02:00
|
|
|
'SubmittedForm' => $submittedForm,
|
2018-01-25 03:38:54 +01:00
|
|
|
'Fields' => $submittedFields,
|
|
|
|
'Body' => '',
|
2017-08-11 01:33:06 +02:00
|
|
|
];
|
2016-07-21 07:53:59 +02:00
|
|
|
|
|
|
|
$this->extend('updateEmailData', $emailData, $attachments);
|
|
|
|
|
|
|
|
// email users on submit.
|
|
|
|
if ($recipients = $this->FilteredEmailRecipients($data, $form)) {
|
|
|
|
foreach ($recipients as $recipient) {
|
2017-08-15 00:49:13 +02:00
|
|
|
$email = Email::create()
|
2019-01-16 22:30:05 +01:00
|
|
|
->setHTMLTemplate('email/SubmittedFormEmail')
|
2019-01-15 23:02:04 +01:00
|
|
|
->setPlainTemplate('email/SubmittedFormEmailPlain');
|
2017-08-15 00:49:13 +02:00
|
|
|
|
2018-01-25 03:38:54 +01:00
|
|
|
// Merge fields are used for CMS authors to reference specific form fields in email content
|
2016-07-21 07:53:59 +02:00
|
|
|
$mergeFields = $this->getMergeFieldsMap($emailData['Fields']);
|
2017-04-01 12:02:18 +02:00
|
|
|
|
2021-03-12 09:30:11 +01:00
|
|
|
if ($attachments && (bool) $recipient->HideFormData === false) {
|
2016-07-21 07:53:59 +02:00
|
|
|
foreach ($attachments as $file) {
|
2018-01-24 23:24:36 +01:00
|
|
|
/** @var File $file */
|
|
|
|
if ((int) $file->ID === 0) {
|
2017-08-11 01:33:06 +02:00
|
|
|
continue;
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
2017-08-11 01:33:06 +02:00
|
|
|
|
2018-01-24 23:24:36 +01:00
|
|
|
$email->addAttachmentFromData(
|
|
|
|
$file->getString(),
|
|
|
|
$file->getFilename(),
|
|
|
|
$file->getMimeType()
|
2017-08-11 01:33:06 +02:00
|
|
|
);
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
}
|
2017-04-01 12:02:18 +02:00
|
|
|
|
2016-07-21 07:53:59 +02:00
|
|
|
if (!$recipient->SendPlain && $recipient->emailTemplateExists()) {
|
2017-08-15 00:49:13 +02:00
|
|
|
$email->setHTMLTemplate($recipient->EmailTemplate);
|
|
|
|
}
|
|
|
|
|
2018-01-25 03:38:54 +01:00
|
|
|
// Add specific template data for the current recipient
|
|
|
|
$emailData['HideFormData'] = (bool) $recipient->HideFormData;
|
|
|
|
// Include any parsed merge field references from the CMS editor - this is already escaped
|
2021-01-28 04:20:26 +01:00
|
|
|
// This string substitution works for both HTML and plain text emails.
|
|
|
|
// $recipient->getEmailBodyContent() will retrieve the relevant version of the email
|
2018-01-25 03:38:54 +01:00
|
|
|
$emailData['Body'] = SSViewer::execute_string($recipient->getEmailBodyContent(), $mergeFields);
|
2022-03-07 01:02:34 +01:00
|
|
|
// only include visible fields if recipient visibility flag is set
|
|
|
|
if ((bool) $recipient->HideInvisibleFields) {
|
|
|
|
$emailData['Fields'] = $visibleSubmittedFields;
|
|
|
|
}
|
2018-01-25 03:38:54 +01:00
|
|
|
|
|
|
|
// Push the template data to the Email's data
|
2017-08-15 00:49:13 +02:00
|
|
|
foreach ($emailData as $key => $value) {
|
|
|
|
$email->addData($key, $value);
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// check to see if they are a dynamic reply to. eg based on a email field a user selected
|
2019-01-17 00:47:09 +01:00
|
|
|
$emailFrom = $recipient->SendEmailFromField();
|
|
|
|
if ($emailFrom && $emailFrom->exists()) {
|
2016-07-21 07:53:59 +02:00
|
|
|
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailFromField()->Name);
|
|
|
|
|
2021-04-12 22:27:25 +02:00
|
|
|
if ($submittedFormField && $submittedFormField->Value && is_string($submittedFormField->Value)) {
|
2022-04-13 03:52:56 +02:00
|
|
|
$email->setReplyTo(explode(',', $submittedFormField->Value ?? ''));
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
2018-02-13 17:24:53 +01:00
|
|
|
} elseif ($recipient->EmailReplyTo) {
|
2022-04-13 03:52:56 +02:00
|
|
|
$email->setReplyTo(explode(',', $recipient->EmailReplyTo ?? ''));
|
2018-02-13 17:24:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// check for a specified from; otherwise fall back to server defaults
|
|
|
|
if ($recipient->EmailFrom) {
|
2022-04-13 03:52:56 +02:00
|
|
|
$email->setFrom(explode(',', $recipient->EmailFrom ?? ''));
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
2018-02-13 17:24:53 +01:00
|
|
|
|
2016-07-21 07:53:59 +02:00
|
|
|
// check to see if they are a dynamic reciever eg based on a dropdown field a user selected
|
2019-01-17 00:47:09 +01:00
|
|
|
$emailTo = $recipient->SendEmailToField();
|
2016-07-21 07:53:59 +02:00
|
|
|
|
2019-05-03 05:55:59 +02:00
|
|
|
try {
|
|
|
|
if ($emailTo && $emailTo->exists()) {
|
|
|
|
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailToField()->Name);
|
|
|
|
|
|
|
|
if ($submittedFormField && is_string($submittedFormField->Value)) {
|
2022-04-13 03:52:56 +02:00
|
|
|
$email->setTo(explode(',', $submittedFormField->Value ?? ''));
|
2019-05-03 05:55:59 +02:00
|
|
|
} else {
|
2022-04-13 03:52:56 +02:00
|
|
|
$email->setTo(explode(',', $recipient->EmailAddress ?? ''));
|
2019-05-03 05:55:59 +02:00
|
|
|
}
|
2018-02-13 18:21:03 +01:00
|
|
|
} else {
|
2022-04-13 03:52:56 +02:00
|
|
|
$email->setTo(explode(',', $recipient->EmailAddress ?? ''));
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
2019-05-03 05:55:59 +02:00
|
|
|
} catch (Swift_RfcComplianceException $e) {
|
|
|
|
// The sending address is empty and/or invalid. Log and skip sending.
|
|
|
|
$error = sprintf(
|
|
|
|
'Failed to set sender for userform submission %s: %s',
|
|
|
|
$submittedForm->ID,
|
|
|
|
$e->getMessage()
|
|
|
|
);
|
|
|
|
|
|
|
|
Injector::inst()->get(LoggerInterface::class)->notice($error);
|
|
|
|
|
|
|
|
continue;
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// check to see if there is a dynamic subject
|
2019-01-17 00:47:09 +01:00
|
|
|
$emailSubject = $recipient->SendEmailSubjectField();
|
|
|
|
if ($emailSubject && $emailSubject->exists()) {
|
2016-07-21 07:53:59 +02:00
|
|
|
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailSubjectField()->Name);
|
|
|
|
|
2022-04-13 03:52:56 +02:00
|
|
|
if ($submittedFormField && trim($submittedFormField->Value ?? '')) {
|
2016-07-21 07:53:59 +02:00
|
|
|
$email->setSubject($submittedFormField->Value);
|
2018-02-13 18:21:03 +01:00
|
|
|
} else {
|
2019-01-27 23:36:12 +01:00
|
|
|
$email->setSubject(SSViewer::execute_string($recipient->EmailSubject, $mergeFields));
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
2018-02-13 17:24:53 +01:00
|
|
|
} else {
|
2019-01-27 23:36:12 +01:00
|
|
|
$email->setSubject(SSViewer::execute_string($recipient->EmailSubject, $mergeFields));
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->extend('updateEmail', $email, $recipient, $emailData);
|
|
|
|
|
2018-01-25 03:38:54 +01:00
|
|
|
if ((bool)$recipient->SendPlain) {
|
2021-01-28 04:20:26 +01:00
|
|
|
// decode previously encoded html tags because the email is being sent as text/plain
|
2022-04-13 03:52:56 +02:00
|
|
|
$body = html_entity_decode($emailData['Body'] ?? '') . "\n";
|
2018-01-25 03:38:54 +01:00
|
|
|
if (isset($emailData['Fields']) && !$emailData['HideFormData']) {
|
|
|
|
foreach ($emailData['Fields'] as $field) {
|
2020-06-30 23:00:31 +02:00
|
|
|
if ($field instanceof SubmittedFileField) {
|
|
|
|
$body .= $field->Title . ': ' . $field->ExportValue ." \n";
|
|
|
|
} else {
|
|
|
|
$body .= $field->Title . ': ' . $field->Value . " \n";
|
|
|
|
}
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$email->setBody($body);
|
2021-06-22 02:12:42 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
$email->sendPlain();
|
|
|
|
} catch (Exception $e) {
|
|
|
|
Injector::inst()->get(LoggerInterface::class)->error($e);
|
|
|
|
}
|
2016-07-21 07:53:59 +02:00
|
|
|
} else {
|
2021-06-22 02:12:42 +02:00
|
|
|
try {
|
|
|
|
$email->send();
|
|
|
|
} catch (Exception $e) {
|
|
|
|
Injector::inst()->get(LoggerInterface::class)->error($e);
|
|
|
|
}
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-13 23:08:22 +01:00
|
|
|
$submittedForm->extend('updateAfterProcess', $emailData, $attachments);
|
2016-07-21 07:53:59 +02:00
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
$session = $this->getRequest()->getSession();
|
|
|
|
$session->clear("FormInfo.{$form->FormName()}.errors");
|
|
|
|
$session->clear("FormInfo.{$form->FormName()}.data");
|
2016-07-21 07:53:59 +02:00
|
|
|
|
|
|
|
$referrer = (isset($data['Referrer'])) ? '?referrer=' . urlencode($data['Referrer']) : "";
|
|
|
|
|
|
|
|
// set a session variable from the security ID to stop people accessing
|
|
|
|
// the finished method directly.
|
|
|
|
if (!$this->DisableAuthenicatedFinishAction) {
|
|
|
|
if (isset($data['SecurityID'])) {
|
2017-08-11 01:33:06 +02:00
|
|
|
$session->set('FormProcessed', $data['SecurityID']);
|
2016-07-21 07:53:59 +02:00
|
|
|
} else {
|
|
|
|
// if the form has had tokens disabled we still need to set FormProcessed
|
|
|
|
// to allow us to get through the finshed method
|
|
|
|
if (!$this->Form()->getSecurityToken()->isEnabled()) {
|
|
|
|
$randNum = rand(1, 1000);
|
2022-04-13 03:52:56 +02:00
|
|
|
$randHash = md5($randNum ?? '');
|
2017-08-11 01:33:06 +02:00
|
|
|
$session->set('FormProcessed', $randHash);
|
|
|
|
$session->set('FormProcessedNum', $randNum);
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$this->DisableSaveSubmissions) {
|
2017-08-11 01:33:06 +02:00
|
|
|
$session->set('userformssubmission'. $this->ID, $submittedForm->ID);
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
return $this->redirect($this->Link('finished') . $referrer . $this->config()->get('finished_anchor'));
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allows the use of field values in email body.
|
|
|
|
*
|
2017-09-04 23:45:16 +02:00
|
|
|
* @param ArrayList $fields
|
2016-07-21 07:53:59 +02:00
|
|
|
* @return ArrayData
|
|
|
|
*/
|
2017-08-15 00:49:13 +02:00
|
|
|
protected function getMergeFieldsMap($fields = [])
|
2016-07-21 07:53:59 +02:00
|
|
|
{
|
2017-08-11 01:33:06 +02:00
|
|
|
$data = ArrayData::create([]);
|
2016-07-21 07:53:59 +02:00
|
|
|
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* @return ViewableData
|
|
|
|
*/
|
|
|
|
public function finished()
|
|
|
|
{
|
2017-08-11 01:33:06 +02:00
|
|
|
$submission = $this->getRequest()->getSession()->get('userformssubmission'. $this->ID);
|
2016-07-21 07:53:59 +02:00
|
|
|
|
|
|
|
if ($submission) {
|
|
|
|
$submission = SubmittedForm::get()->byId($submission);
|
|
|
|
}
|
|
|
|
|
|
|
|
$referrer = isset($_GET['referrer']) ? urldecode($_GET['referrer']) : null;
|
|
|
|
|
|
|
|
if (!$this->DisableAuthenicatedFinishAction) {
|
2017-08-11 01:33:06 +02:00
|
|
|
$formProcessed = $this->getRequest()->getSession()->get('FormProcessed');
|
2016-07-21 07:53:59 +02:00
|
|
|
|
|
|
|
if (!isset($formProcessed)) {
|
|
|
|
return $this->redirect($this->Link() . $referrer);
|
|
|
|
} else {
|
2017-08-11 01:33:06 +02:00
|
|
|
$securityID = $this->getRequest()->getSession()->get('SecurityID');
|
2016-07-21 07:53:59 +02:00
|
|
|
// make sure the session matches the SecurityID and is not left over from another form
|
|
|
|
if ($formProcessed != $securityID) {
|
|
|
|
// they may have disabled tokens on the form
|
2022-04-13 03:52:56 +02:00
|
|
|
$securityID = md5($this->getRequest()->getSession()->get('FormProcessedNum') ?? '');
|
2016-07-21 07:53:59 +02:00
|
|
|
if ($formProcessed != $securityID) {
|
|
|
|
return $this->redirect($this->Link() . $referrer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
$this->getRequest()->getSession()->clear('FormProcessed');
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
$data = [
|
|
|
|
'Submission' => $submission,
|
|
|
|
'Link' => $referrer
|
|
|
|
];
|
2017-04-21 01:35:37 +02:00
|
|
|
|
|
|
|
$this->extend('updateReceivedFormSubmissionData', $data);
|
|
|
|
|
2017-08-11 01:33:06 +02:00
|
|
|
return $this->customise([
|
2017-08-14 02:29:57 +02:00
|
|
|
'Content' => $this->customise($data)->renderWith(__CLASS__ . '_ReceivedFormSubmission'),
|
2016-07-21 07:53:59 +02:00
|
|
|
'Form' => '',
|
2017-08-11 01:33:06 +02:00
|
|
|
]);
|
2016-07-21 07:53:59 +02:00
|
|
|
}
|
2017-04-28 00:22:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Outputs the required JS from the $watch input
|
|
|
|
*
|
|
|
|
* @param array $watch
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function buildWatchJS($watch)
|
|
|
|
{
|
|
|
|
$result = '';
|
|
|
|
foreach ($watch as $key => $rule) {
|
|
|
|
$events = implode(' ', $rule['events']);
|
|
|
|
$selectors = implode(', ', $rule['selectors']);
|
|
|
|
$conjunction = $rule['conjunction'];
|
|
|
|
$operations = implode(" {$conjunction} ", $rule['operations']);
|
|
|
|
$target = $rule['targetFieldID'];
|
2019-03-25 03:09:38 +01:00
|
|
|
$holder = $rule['holder'];
|
2022-04-13 03:52:56 +02:00
|
|
|
$isFormStep = strpos($target ?? '', 'EditableFormStep') !== false;
|
2017-04-28 00:22:15 +02:00
|
|
|
|
|
|
|
$result .= <<<EOS
|
|
|
|
\n
|
|
|
|
$('.userform').on('{$events}',
|
|
|
|
"{$selectors}",
|
|
|
|
function (){
|
2017-05-22 05:54:28 +02:00
|
|
|
if ({$operations}) {
|
|
|
|
$('{$target}').{$rule['view']};
|
2019-03-25 03:09:38 +01:00
|
|
|
{$holder}.{$rule['view']}.trigger('{$rule['holder_event']}');
|
2017-04-28 00:22:15 +02:00
|
|
|
} else {
|
2017-05-22 05:54:28 +02:00
|
|
|
$('{$target}').{$rule['opposite']};
|
2019-03-25 03:09:38 +01:00
|
|
|
{$holder}.{$rule['opposite']}.trigger('{$rule['holder_event_opposite']}');
|
2017-04-28 00:22:15 +02:00
|
|
|
}
|
2017-05-01 07:44:39 +02:00
|
|
|
});
|
2021-01-11 03:58:56 +01:00
|
|
|
EOS;
|
|
|
|
if ($isFormStep) {
|
|
|
|
// Hide the step jump button if the FormStep has is initially hidden.
|
|
|
|
// This is particularly important beacause the next/prev page buttons logic is controlled by
|
|
|
|
// the visibility of the FormStep buttons
|
|
|
|
// The HTML for the FormStep buttons is defined in UserFormProgress.ss
|
2022-04-13 03:52:56 +02:00
|
|
|
$id = str_replace('#', '', $target ?? '');
|
2021-01-11 03:58:56 +01:00
|
|
|
$result .= <<<EOS
|
|
|
|
$('.step-button-wrapper[data-for="{$id}"]').addClass('hide');
|
|
|
|
EOS;
|
|
|
|
} else {
|
|
|
|
// If a field's initial state is set to be hidden, a '.hide' class will be added to the field as well
|
|
|
|
// as the fieldholder. Afterwards, JS only removes it from the fieldholder, thus the field stays hidden.
|
|
|
|
// We'll update update the JS so that the '.hide' class is removed from the field from the beginning,
|
|
|
|
// though we need to ensure we don't do this on FormSteps (page breaks) otherwise we'll mistakenly
|
|
|
|
// target fields contained within the formstep
|
|
|
|
$result .= <<<EOS
|
2017-06-06 00:13:09 +02:00
|
|
|
$("{$target}").find('.hide').removeClass('hide');
|
2017-04-28 00:22:15 +02:00
|
|
|
EOS;
|
2021-01-11 03:58:56 +01:00
|
|
|
}
|
2017-04-28 00:22:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
2008-09-29 05:18:23 +02:00
|
|
|
}
|