mirror of
https://github.com/silverstripe/silverstripe-userforms.git
synced 2024-10-22 17:05:42 +02:00
386eb363ad
Add support for minimum and maximum values
1259 lines
37 KiB
PHP
Executable File
1259 lines
37 KiB
PHP
Executable File
<?php
|
|
|
|
/**
|
|
* @package userforms
|
|
*/
|
|
|
|
class UserDefinedForm extends Page {
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private static $description = 'Adds a customizable form.';
|
|
|
|
/**
|
|
* @var string Required Identifier
|
|
*/
|
|
private static $required_identifier = null;
|
|
|
|
/**
|
|
* @var array Fields on the user defined form page.
|
|
*/
|
|
private static $db = array(
|
|
"SubmitButtonText" => "Varchar",
|
|
"ClearButtonText" => "Varchar",
|
|
"OnCompleteMessage" => "HTMLText",
|
|
"ShowClearButton" => "Boolean",
|
|
'DisableSaveSubmissions' => 'Boolean',
|
|
'EnableLiveValidation' => 'Boolean',
|
|
'HideFieldLabels' => 'Boolean'
|
|
);
|
|
|
|
/**
|
|
* @var array Default values of variables when this page is created
|
|
*/
|
|
private static $defaults = array(
|
|
'Content' => '$UserDefinedForm',
|
|
'DisableSaveSubmissions' => 0,
|
|
'OnCompleteMessage' => '<p>Thanks, we\'ve received your submission.</p>'
|
|
);
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
private static $has_many = array(
|
|
"Fields" => "EditableFormField",
|
|
"Submissions" => "SubmittedForm",
|
|
"EmailRecipients" => "UserDefinedForm_EmailRecipient"
|
|
);
|
|
|
|
/**
|
|
* Temporary storage of field ids when the form is duplicated.
|
|
* Example layout: array('EditableCheckbox3' => 'EditableCheckbox14')
|
|
* @var array
|
|
*/
|
|
protected $fieldsFromTo = array();
|
|
|
|
/**
|
|
* @return FieldList
|
|
*/
|
|
public function getCMSFields() {
|
|
// call updateCMSFields after userforms
|
|
SiteTree::disableCMSFieldsExtensions();
|
|
$fields = parent::getCMSFields();
|
|
SiteTree::enableCMSFieldsExtensions();
|
|
// define tabs
|
|
$fields->findOrMakeTab('Root.FormContent', _t('UserDefinedForm.FORM', 'Form'));
|
|
$fields->findOrMakeTab('Root.FormOptions', _t('UserDefinedForm.CONFIGURATION', 'Configuration'));
|
|
$fields->findOrMakeTab('Root.Submissions', _t('UserDefinedForm.SUBMISSIONS', 'Submissions'));
|
|
|
|
// field editor
|
|
$fields->addFieldToTab("Root.FormContent", new FieldEditor("Fields", 'Fields', "", $this ));
|
|
|
|
// text to show on complete
|
|
$onCompleteFieldSet = new CompositeField(
|
|
$label = new LabelField('OnCompleteMessageLabel',_t('UserDefinedForm.ONCOMPLETELABEL', 'Show on completion')),
|
|
$editor = new HtmlEditorField( "OnCompleteMessage", "", _t('UserDefinedForm.ONCOMPLETEMESSAGE', $this->OnCompleteMessage))
|
|
);
|
|
|
|
$onCompleteFieldSet->addExtraClass('field');
|
|
|
|
$editor->setRows(3);
|
|
$label->addExtraClass('left');
|
|
|
|
// Set the summary fields of UserDefinedForm_EmailRecipient dynamically via config system
|
|
Config::inst()->update(
|
|
'UserDefinedForm_EmailRecipient',
|
|
'summary_fields',
|
|
array(
|
|
'EmailAddress' => _t('UserDefinedForm.EMAILADDRESS', 'Email'),
|
|
'EmailSubject' => _t('UserDefinedForm.EMAILSUBJECT', 'Subject'),
|
|
'EmailFrom' => _t('UserDefinedForm.EMAILFROM', 'From'),
|
|
)
|
|
);
|
|
|
|
// who do we email on submission
|
|
$emailRecipients = new GridField("EmailRecipients", _t('UserDefinedForm.EMAILRECIPIENTS', 'Email Recipients'), $this->EmailRecipients(), GridFieldConfig_RecordEditor::create(10));
|
|
$emailRecipients->getConfig()->getComponentByType('GridFieldAddNewButton')->setButtonName(
|
|
_t('UserDefinedForm.ADDEMAILRECIPIENT', 'Add Email Recipient')
|
|
);
|
|
|
|
$fields->addFieldsToTab("Root.FormOptions", $onCompleteFieldSet);
|
|
$fields->addFieldToTab("Root.FormOptions", $emailRecipients);
|
|
$fields->addFieldsToTab("Root.FormOptions", $this->getFormOptions());
|
|
|
|
|
|
// view the submissions
|
|
$submissions = new GridField(
|
|
"Reports",
|
|
_t('UserDefinedForm.SUBMISSIONS', 'Submissions'),
|
|
$this->Submissions()->sort('Created', 'DESC')
|
|
);
|
|
|
|
// make sure a numeric not a empty string is checked against this int column for SQL server
|
|
$parentID = (!empty($this->ID)) ? $this->ID : 0;
|
|
|
|
// get a list of all field names and values used for print and export CSV views of the GridField below.
|
|
$columnSQL = <<<SQL
|
|
SELECT "Name", "Title"
|
|
FROM "SubmittedFormField"
|
|
LEFT JOIN "SubmittedForm" ON "SubmittedForm"."ID" = "SubmittedFormField"."ParentID"
|
|
WHERE "SubmittedForm"."ParentID" = '$parentID'
|
|
ORDER BY "Title" ASC
|
|
SQL;
|
|
$columns = DB::query($columnSQL)->map();
|
|
|
|
$config = new GridFieldConfig();
|
|
$config->addComponent(new GridFieldToolbarHeader());
|
|
$config->addComponent($sort = new GridFieldSortableHeader());
|
|
$config->addComponent($filter = new UserFormsGridFieldFilterHeader());
|
|
$config->addComponent(new GridFieldDataColumns());
|
|
$config->addComponent(new GridFieldEditButton());
|
|
$config->addComponent(new GridState_Component());
|
|
$config->addComponent(new GridFieldDeleteAction());
|
|
$config->addComponent(new GridFieldPageCount('toolbar-header-right'));
|
|
$config->addComponent($pagination = new GridFieldPaginator(25));
|
|
$config->addComponent(new GridFieldDetailForm());
|
|
$config->addComponent($export = new GridFieldExportButton());
|
|
$config->addComponent($print = new GridFieldPrintButton());
|
|
|
|
/**
|
|
* Support for {@link https://github.com/colymba/GridFieldBulkEditingTools}
|
|
*/
|
|
if(class_exists('GridFieldBulkManager')) {
|
|
$config->addComponent(new GridFieldBulkManager());
|
|
}
|
|
|
|
$sort->setThrowExceptionOnBadDataType(false);
|
|
$filter->setThrowExceptionOnBadDataType(false);
|
|
$pagination->setThrowExceptionOnBadDataType(false);
|
|
|
|
// attach every column to the print view form
|
|
$columns['Created'] = 'Created';
|
|
$filter->setColumns($columns);
|
|
|
|
// print configuration
|
|
$print->setPrintHasHeader(true);
|
|
$print->setPrintColumns($columns);
|
|
|
|
// export configuration
|
|
$export->setCsvHasHeader(true);
|
|
$export->setExportColumns($columns);
|
|
|
|
$submissions->setConfig($config);
|
|
$fields->addFieldToTab("Root.Submissions", $submissions);
|
|
$fields->addFieldToTab("Root.FormOptions", new CheckboxField('DisableSaveSubmissions',_t('UserDefinedForm.SAVESUBMISSIONS',"Disable Saving Submissions to Server")));
|
|
|
|
$this->extend('updateCMSFields', $fields);
|
|
|
|
return $fields;
|
|
}
|
|
|
|
|
|
/**
|
|
* When publishing copy the editable form fields to the live database
|
|
* Not going to version emails and submissions as they are likely to
|
|
* persist over multiple versions.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function doPublish() {
|
|
$parentID = (!empty($this->ID)) ? $this->ID : 0;
|
|
// remove fields on the live table which could have been orphaned.
|
|
$live = Versioned::get_by_stage("EditableFormField", "Live", "\"EditableFormField\".\"ParentID\" = $parentID");
|
|
|
|
if($live) {
|
|
foreach($live as $field) {
|
|
$field->doDeleteFromStage('Live');
|
|
}
|
|
}
|
|
|
|
// publish the draft pages
|
|
if($this->Fields()) {
|
|
foreach($this->Fields() as $field) {
|
|
$field->doPublish('Stage', 'Live');
|
|
}
|
|
}
|
|
|
|
parent::doPublish();
|
|
}
|
|
|
|
/**
|
|
* When un-publishing the page it has to remove all the fields from the
|
|
* live database table.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function doUnpublish() {
|
|
if($this->Fields()) {
|
|
foreach($this->Fields() as $field) {
|
|
$field->doDeleteFromStage('Live');
|
|
}
|
|
}
|
|
|
|
parent::doUnpublish();
|
|
}
|
|
|
|
/**
|
|
* Roll back a form to a previous version.
|
|
*
|
|
* @param string|int Version to roll back to
|
|
*/
|
|
public function doRollbackTo($version) {
|
|
parent::doRollbackTo($version);
|
|
|
|
/*
|
|
Not implemented yet
|
|
|
|
// get the older version
|
|
$reverted = Versioned::get_version($this->ClassName, $this->ID, $version);
|
|
|
|
if($reverted) {
|
|
|
|
// using the lastedited date of the reverted object we can work out which
|
|
// form fields to revert back to
|
|
if($this->Fields()) {
|
|
foreach($this->Fields() as $field) {
|
|
// query to see when the version of the page was pumped
|
|
$editedDate = DB::query("
|
|
SELECT LastEdited
|
|
FROM \"SiteTree_versions\"
|
|
WHERE \"RecordID\" = '$this->ID' AND \"Version\" = $version
|
|
")->value();
|
|
|
|
|
|
// find a the latest version which has been edited
|
|
$versionToGet = DB::query("
|
|
SELECT *
|
|
FROM \"EditableFormField_versions\"
|
|
WHERE \"RecordID\" = '$field->ID' AND \"LastEdited\" <= '$editedDate'
|
|
ORDER BY Version DESC
|
|
LIMIT 1
|
|
")->record();
|
|
|
|
if($versionToGet) {
|
|
Debug::show('publishing field'. $field->Name);
|
|
Debug::show($versionToGet);
|
|
$field->publish($versionToGet, "Stage", true);
|
|
$field->writeWithoutVersion();
|
|
}
|
|
else {
|
|
Debug::show('deleting field'. $field->Name);
|
|
$this->Fields()->remove($field);
|
|
|
|
$field->delete();
|
|
$field->destroy();
|
|
}
|
|
}
|
|
}
|
|
|
|
// @todo Emails
|
|
}
|
|
*/
|
|
}
|
|
|
|
/**
|
|
* Revert the draft site to the current live site
|
|
*
|
|
* @return void
|
|
*/
|
|
public function doRevertToLive() {
|
|
if($this->Fields()) {
|
|
foreach($this->Fields() as $field) {
|
|
$field->publish("Live", "Stage", false);
|
|
$field->writeWithoutVersion();
|
|
}
|
|
}
|
|
|
|
parent::doRevertToLive();
|
|
}
|
|
|
|
/**
|
|
* Allow overriding the EmailRecipients on a {@link DataExtension}
|
|
* so you can customise who receives an email.
|
|
* Converts the RelationList to an ArrayList so that manipulation
|
|
* of the original source data isn't possible.
|
|
*
|
|
* @return ArrayList
|
|
*/
|
|
public function FilteredEmailRecipients($data = null, $form = null) {
|
|
$recipients = new ArrayList($this->getComponents('EmailRecipients')->toArray());
|
|
|
|
$this->extend('updateFilteredEmailRecipients', $recipients, $data, $form);
|
|
|
|
return $recipients;
|
|
}
|
|
|
|
|
|
/**
|
|
* Store new and old ids of duplicated fields.
|
|
* This method also serves as a hook for descendant classes.
|
|
*/
|
|
protected function afterDuplicateField($page, $fromField, $toField) {
|
|
$this->fieldsFromTo[$fromField->ClassName . $fromField->ID] = $toField->ClassName . $toField->ID;
|
|
}
|
|
|
|
|
|
/**
|
|
* Duplicate this UserDefinedForm page, and its form fields.
|
|
* Submissions, on the other hand, won't be duplicated.
|
|
*
|
|
* @return Page
|
|
*/
|
|
public function duplicate($doWrite = true) {
|
|
$page = parent::duplicate($doWrite);
|
|
|
|
// the form fields
|
|
if($this->Fields()) {
|
|
foreach($this->Fields() as $field) {
|
|
$newField = $field->duplicate();
|
|
$newField->ParentID = $page->ID;
|
|
$newField->write();
|
|
$this->afterDuplicateField($page, $field, $newField);
|
|
}
|
|
}
|
|
|
|
// the emails
|
|
if($this->EmailRecipients()) {
|
|
foreach($this->EmailRecipients() as $email) {
|
|
$newEmail = $email->duplicate();
|
|
$newEmail->FormID = $page->ID;
|
|
$newEmail->write();
|
|
}
|
|
}
|
|
|
|
// Rewrite CustomRules
|
|
if($page->Fields()) {
|
|
foreach($page->Fields() as $field) {
|
|
// Rewrite name to make the CustomRules-rewrite below work.
|
|
$field->Name = $field->ClassName . $field->ID;
|
|
$rules = unserialize($field->CustomRules);
|
|
|
|
if (count($rules) && isset($rules[0]['ConditionField'])) {
|
|
$from = $rules[0]['ConditionField'];
|
|
|
|
if (array_key_exists($from, $this->fieldsFromTo)) {
|
|
$rules[0]['ConditionField'] = $this->fieldsFromTo[$from];
|
|
$field->CustomRules = serialize($rules);
|
|
}
|
|
}
|
|
|
|
$field->Write();
|
|
}
|
|
}
|
|
|
|
return $page;
|
|
}
|
|
|
|
/**
|
|
* Custom options for the form. You can extend the built in options by
|
|
* using {@link updateFormOptions()}
|
|
*
|
|
* @return FieldList
|
|
*/
|
|
public function getFormOptions() {
|
|
$submit = ($this->SubmitButtonText) ? $this->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
|
|
$clear = ($this->ClearButtonText) ? $this->ClearButtonText : _t('UserDefinedForm.CLEARBUTTON', 'Clear');
|
|
|
|
$options = new FieldList(
|
|
new TextField("SubmitButtonText", _t('UserDefinedForm.TEXTONSUBMIT', 'Text on submit button:'), $submit),
|
|
new TextField("ClearButtonText", _t('UserDefinedForm.TEXTONCLEAR', 'Text on clear button:'), $clear),
|
|
new CheckboxField("ShowClearButton", _t('UserDefinedForm.SHOWCLEARFORM', 'Show Clear Form Button'), $this->ShowClearButton),
|
|
new CheckboxField("EnableLiveValidation", _t('UserDefinedForm.ENABLELIVEVALIDATION', 'Enable live validation')),
|
|
new CheckboxField("HideFieldLabels", _t('UserDefinedForm.HIDEFIELDLABELS', 'Hide field labels'))
|
|
);
|
|
|
|
$this->extend('updateFormOptions', $options);
|
|
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* Return if this form has been modified on the stage site and not published.
|
|
* this is used on the workflow module and for a couple highlighting things
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function getIsModifiedOnStage() {
|
|
// new unsaved pages could be never be published
|
|
if($this->isNew()) {
|
|
return false;
|
|
}
|
|
|
|
$stageVersion = Versioned::get_versionnumber_by_stage('UserDefinedForm', 'Stage', $this->ID);
|
|
$liveVersion = Versioned::get_versionnumber_by_stage('UserDefinedForm', 'Live', $this->ID);
|
|
|
|
$isModified = ($stageVersion && $stageVersion != $liveVersion);
|
|
|
|
if(!$isModified) {
|
|
if($this->Fields()) {
|
|
foreach($this->Fields() as $field) {
|
|
if($field->getIsModifiedOnStage()) {
|
|
$isModified = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $isModified;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Controller for the {@link UserDefinedForm} page type.
|
|
*
|
|
* @package userforms
|
|
*/
|
|
|
|
class UserDefinedForm_Controller extends Page_Controller {
|
|
|
|
private static $allowed_actions = array(
|
|
'index',
|
|
'ping',
|
|
'Form',
|
|
'finished'
|
|
);
|
|
|
|
public function init() {
|
|
parent::init();
|
|
|
|
// load the jquery
|
|
$lang = i18n::get_lang_from_locale(i18n::get_locale());
|
|
Requirements::javascript(FRAMEWORK_DIR .'/thirdparty/jquery/jquery.js');
|
|
Requirements::javascript(USERFORMS_DIR . '/thirdparty/jquery-validate/jquery.validate.min.js');
|
|
Requirements::add_i18n_javascript(USERFORMS_DIR . '/javascript/lang');
|
|
Requirements::javascript(USERFORMS_DIR . '/javascript/UserForm_frontend.js');
|
|
Requirements::javascript(
|
|
USERFORMS_DIR . "/thirdparty/jquery-validate/localization/messages_{$lang}.min.js"
|
|
);
|
|
Requirements::javascript(
|
|
USERFORMS_DIR . "/thirdparty/jquery-validate/localization/methods_{$lang}.min.js"
|
|
);
|
|
if($this->HideFieldLabels) {
|
|
Requirements::javascript(USERFORMS_DIR . '/thirdparty/Placeholders.js/Placeholders.min.js');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function index() {
|
|
if($this->Content && $form = $this->Form()) {
|
|
$hasLocation = stristr($this->Content, '$UserDefinedForm');
|
|
if($hasLocation) {
|
|
$content = str_ireplace('$UserDefinedForm', $form->forTemplate(), $this->Content);
|
|
return array(
|
|
'Content' => DBField::create_field('HTMLText', $content),
|
|
'Form' => ""
|
|
);
|
|
}
|
|
}
|
|
|
|
return array(
|
|
'Content' => DBField::create_field('HTMLText', $this->Content),
|
|
'Form' => $this->Form()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @return Form|false
|
|
*/
|
|
public function Form() {
|
|
$fields = $this->getFormFields();
|
|
if(!$fields || !$fields->exists()) return false;
|
|
|
|
$actions = $this->getFormActions();
|
|
|
|
// get the required fields including the validation
|
|
$required = $this->getRequiredFields();
|
|
|
|
// generate the conditional logic
|
|
$this->generateConditionalJavascript();
|
|
|
|
$form = new Form($this, "Form", $fields, $actions, $required);
|
|
$form->setRedirectToFormOnValidationError(true);
|
|
|
|
$data = Session::get("FormInfo.{$form->FormName()}.data");
|
|
|
|
if(is_array($data)) $form->loadDataFrom($data);
|
|
|
|
$this->extend('updateForm', $form);
|
|
|
|
return $form;
|
|
}
|
|
|
|
/**
|
|
* Get the form fields for the form on this page. Can modify this FieldSet
|
|
* by using {@link updateFormFields()} on an {@link Extension} subclass which
|
|
* is applied to this controller.
|
|
*
|
|
* @return FieldList
|
|
*/
|
|
public function getFormFields() {
|
|
$fields = new FieldList();
|
|
|
|
if($this->Fields()) {
|
|
foreach($this->Fields() as $editableField) {
|
|
// get the raw form field from the editable version
|
|
$field = $editableField->getFormField();
|
|
if(!$field) break;
|
|
|
|
// set the error / formatting messages
|
|
$field->setCustomValidationMessage($editableField->getErrorMessage());
|
|
|
|
// set the right title on this field
|
|
if($right = $editableField->getSetting('RightTitle')) {
|
|
$field->setRightTitle($right);
|
|
}
|
|
|
|
// if this field is required add some
|
|
if($editableField->Required) {
|
|
$field->addExtraClass('requiredField');
|
|
|
|
if($identifier = UserDefinedForm::config()->required_identifier) {
|
|
|
|
$title = $field->Title() ." <span class='required-identifier'>". $identifier . "</span>";
|
|
$field->setTitle($title);
|
|
}
|
|
}
|
|
// if this field has an extra class
|
|
if($editableField->getSetting('ExtraClass')) {
|
|
$field->addExtraClass(Convert::raw2att(
|
|
$editableField->getSetting('ExtraClass')
|
|
));
|
|
}
|
|
|
|
// set the values passed by the url to the field
|
|
$request = $this->getRequest();
|
|
if($var = $request->getVar($field->name)) {
|
|
$field->value = Convert::raw2att($var);
|
|
}
|
|
|
|
$fields->push($field);
|
|
}
|
|
}
|
|
$this->extend('updateFormFields', $fields);
|
|
|
|
return $fields;
|
|
}
|
|
|
|
/**
|
|
* Generate the form actions for the UserDefinedForm. You
|
|
* can manipulate these by using {@link updateFormActions()} on
|
|
* a decorator.
|
|
*
|
|
* @todo Make form actions editable via their own field editor.
|
|
*
|
|
* @return FieldList
|
|
*/
|
|
public function getFormActions() {
|
|
$submitText = ($this->SubmitButtonText) ? $this->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
|
|
$clearText = ($this->ClearButtonText) ? $this->ClearButtonText : _t('UserDefinedForm.CLEARBUTTON', 'Clear');
|
|
|
|
$actions = new FieldList(
|
|
new FormAction("process", $submitText)
|
|
);
|
|
|
|
if($this->ShowClearButton) {
|
|
$actions->push(new ResetFormAction("clearForm", $clearText));
|
|
}
|
|
|
|
$this->extend('updateFormActions', $actions);
|
|
|
|
return $actions;
|
|
}
|
|
|
|
/**
|
|
* Get the required form fields for this form. Includes building the jQuery
|
|
* validate structure
|
|
*
|
|
* @return RequiredFields
|
|
*/
|
|
public function getRequiredFields() {
|
|
|
|
// set the custom script for this form
|
|
Requirements::customScript($this->renderWith('ValidationScript'), 'UserFormsValidation');
|
|
|
|
// Generate required field validator
|
|
$requiredNames = $this
|
|
->Fields()
|
|
->filter('Required', true)
|
|
->column('Name');
|
|
$required = new RequiredFields($requiredNames);
|
|
|
|
$this->extend('updateRequiredFields', $required);
|
|
|
|
return $required;
|
|
}
|
|
|
|
/**
|
|
* Generate the javascript for the conditional field show / hiding logic.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function generateConditionalJavascript() {
|
|
$default = "";
|
|
$rules = "";
|
|
|
|
$watch = array();
|
|
$watchLoad = array();
|
|
|
|
if($this->Fields()) {
|
|
foreach($this->Fields() as $field) {
|
|
$fieldId = $field->Name;
|
|
|
|
if($field->ClassName == 'EditableFormHeading') {
|
|
$fieldId = 'Form_Form_'.$field->Name;
|
|
}
|
|
|
|
// Is this Field Show by Default
|
|
if(!$field->getShowOnLoad()) {
|
|
$default .= "$(\"#" . $fieldId . "\").hide();\n";
|
|
}
|
|
|
|
// Check for field dependencies / default
|
|
if($field->Dependencies()) {
|
|
foreach($field->Dependencies() as $dependency) {
|
|
if(is_array($dependency) && isset($dependency['ConditionField']) && $dependency['ConditionField'] != "") {
|
|
// get the field which is effected
|
|
$formName = Convert::raw2sql($dependency['ConditionField']);
|
|
$formFieldWatch = DataObject::get_one("EditableFormField", "\"Name\" = '$formName'");
|
|
|
|
if(!$formFieldWatch) break;
|
|
|
|
// watch out for multiselect options - radios and check boxes
|
|
if(is_a($formFieldWatch, 'EditableDropdown')) {
|
|
$fieldToWatch = "$(\"select[name='".$dependency['ConditionField']."']\")";
|
|
$fieldToWatchOnLoad = $fieldToWatch;
|
|
}
|
|
// watch out for checkboxs as the inputs don't have values but are 'checked
|
|
else if(is_a($formFieldWatch, 'EditableCheckboxGroupField')) {
|
|
$fieldToWatch = "$(\"input[name='".$dependency['ConditionField']."[".$dependency['Value']."]']\")";
|
|
$fieldToWatchOnLoad = $fieldToWatch;
|
|
}
|
|
else if(is_a($formFieldWatch, 'EditableRadioField')) {
|
|
$fieldToWatch = "$(\"input[name='".$dependency['ConditionField']."']\")";
|
|
// We only want to trigger on load once for the radio group - hence we focus on the first option only.
|
|
$fieldToWatchOnLoad = "$(\"input[name='".$dependency['ConditionField']."']:first\")";
|
|
}
|
|
else {
|
|
$fieldToWatch = "$(\"input[name='".$dependency['ConditionField']."']\")";
|
|
$fieldToWatchOnLoad = $fieldToWatch;
|
|
}
|
|
|
|
// show or hide?
|
|
$view = (isset($dependency['Display']) && $dependency['Display'] == "Hide") ? "hide" : "show";
|
|
$opposite = ($view == "show") ? "hide" : "show";
|
|
|
|
// what action do we need to keep track of. Something nicer here maybe?
|
|
// @todo encapulsation
|
|
$action = "change";
|
|
|
|
if($formFieldWatch->ClassName == "EditableTextField") {
|
|
$action = "keyup";
|
|
}
|
|
|
|
// is this field a special option field
|
|
$checkboxField = false;
|
|
$radioField = false;
|
|
if(in_array($formFieldWatch->ClassName, array('EditableCheckboxGroupField', 'EditableCheckbox'))) {
|
|
$action = "click";
|
|
$checkboxField = true;
|
|
}
|
|
else if ($formFieldWatch->ClassName == "EditableRadioField") {
|
|
$radioField = true;
|
|
}
|
|
|
|
// Escape the values.
|
|
$dependency['Value'] = str_replace('"', '\"', $dependency['Value']);
|
|
|
|
// and what should we evaluate
|
|
switch($dependency['ConditionOption']) {
|
|
case 'IsNotBlank':
|
|
$expression = ($checkboxField || $radioField) ? '$(this).attr("checked")' :'$(this).val() != ""';
|
|
|
|
break;
|
|
case 'IsBlank':
|
|
$expression = ($checkboxField || $radioField) ? '!($(this).attr("checked"))' : '$(this).val() == ""';
|
|
|
|
break;
|
|
case 'HasValue':
|
|
if ($checkboxField) {
|
|
$expression = '$(this).attr("checked")';
|
|
} else if ($radioField) {
|
|
// We cannot simply get the value of the radio group, we need to find the checked option first.
|
|
$expression = '$(this).parents(".field, .control-group").find("input:checked").val()=="'. $dependency['Value'] .'"';
|
|
} else {
|
|
$expression = '$(this).val() == "'. $dependency['Value'] .'"';
|
|
}
|
|
|
|
break;
|
|
case 'ValueLessThan':
|
|
$expression = '$(this).val() < parseFloat("'. $dependency['Value'] .'")';
|
|
|
|
break;
|
|
case 'ValueLessThanEqual':
|
|
$expression = '$(this).val() <= parseFloat("'. $dependency['Value'] .'")';
|
|
|
|
break;
|
|
case 'ValueGreaterThan':
|
|
$expression = '$(this).val() > parseFloat("'. $dependency['Value'] .'")';
|
|
|
|
break;
|
|
case 'ValueGreaterThanEqual':
|
|
$expression = '$(this).val() >= parseFloat("'. $dependency['Value'] .'")';
|
|
|
|
break;
|
|
default: // ==HasNotValue
|
|
if ($checkboxField) {
|
|
$expression = '!$(this).attr("checked")';
|
|
} else if ($radioField) {
|
|
// We cannot simply get the value of the radio group, we need to find the checked option first.
|
|
$expression = '$(this).parents(".field, .control-group").find("input:checked").val()!="'. $dependency['Value'] .'"';
|
|
} else {
|
|
$expression = '$(this).val() != "'. $dependency['Value'] .'"';
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if(!isset($watch[$fieldToWatch])) {
|
|
$watch[$fieldToWatch] = array();
|
|
}
|
|
|
|
$watch[$fieldToWatch][] = array(
|
|
'expression' => $expression,
|
|
'field_id' => $fieldId,
|
|
'view' => $view,
|
|
'opposite' => $opposite
|
|
);
|
|
|
|
$watchLoad[$fieldToWatchOnLoad] = true;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if($watch) {
|
|
foreach($watch as $key => $values) {
|
|
$logic = array();
|
|
|
|
foreach($values as $rule) {
|
|
// Register conditional behaviour with an element, so it can be triggered from many places.
|
|
$logic[] = sprintf(
|
|
'if(%s) { $("#%s").%s(); } else { $("#%2$s").%s(); }',
|
|
$rule['expression'],
|
|
$rule['field_id'],
|
|
$rule['view'],
|
|
$rule['opposite']
|
|
);
|
|
}
|
|
|
|
$logic = implode("\n", $logic);
|
|
$rules .= $key.".each(function() {\n
|
|
$(this).data('userformConditions', function() {\n
|
|
$logic\n
|
|
}); \n
|
|
});\n";
|
|
|
|
$rules .= $key.".$action(function() {
|
|
$(this).data('userformConditions').call(this);\n
|
|
});\n";
|
|
}
|
|
}
|
|
|
|
if($watchLoad) {
|
|
foreach($watchLoad as $key => $value) {
|
|
$rules .= $key.".each(function() {
|
|
$(this).data('userformConditions').call(this);\n
|
|
});\n";
|
|
}
|
|
}
|
|
|
|
// Only add customScript if $default or $rules is defined
|
|
if($default || $rules) {
|
|
Requirements::customScript(<<<JS
|
|
(function($) {
|
|
$(document).ready(function() {
|
|
$default
|
|
|
|
$rules
|
|
})
|
|
})(jQuery);
|
|
JS
|
|
, 'UserFormsConditional');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert a PHP array to a JSON string. We cannot use {@link Convert::array2json}
|
|
* as it escapes our values with "" which appears to break the validate plugin
|
|
*
|
|
* @param Array array to convert
|
|
* @return JSON
|
|
*/
|
|
public function array2json($array) {
|
|
foreach($array as $key => $value) {
|
|
if(is_array( $value )) {
|
|
$result[] = "$key:" . $this->array2json($value);
|
|
} else {
|
|
$value = ( is_bool($value) || is_numeric($value) ) ? $value : "\"$value\"";
|
|
$result[] = "$key:$value";
|
|
}
|
|
}
|
|
|
|
return (isset($result)) ? "{\n".implode( ', ', $result ) ."\n}\n": '{}';
|
|
}
|
|
|
|
/**
|
|
* Process the form that is submitted through the site
|
|
*
|
|
* @param array $data
|
|
* @param Form $form
|
|
*
|
|
* @return Redirection
|
|
*/
|
|
public function process($data, $form) {
|
|
Session::set("FormInfo.{$form->FormName()}.data",$data);
|
|
Session::clear("FormInfo.{$form->FormName()}.errors");
|
|
|
|
foreach($this->Fields() as $field) {
|
|
$messages[$field->Name] = $field->getErrorMessage()->HTML();
|
|
$formField = $field->getFormField();
|
|
|
|
if($field->Required && $field->CustomRules()->Count() == 0) {
|
|
if(isset($data[$field->Name])) {
|
|
$formField->setValue($data[$field->Name]);
|
|
}
|
|
|
|
if(
|
|
!isset($data[$field->Name]) ||
|
|
!$data[$field->Name] ||
|
|
!$formField->validate($form->getValidator())
|
|
) {
|
|
$form->addErrorMessage($field->Name, $field->getErrorMessage(), 'bad');
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Session::get("FormInfo.{$form->FormName()}.errors")){
|
|
Controller::curr()->redirectBack();
|
|
|
|
return;
|
|
}
|
|
|
|
$submittedForm = Object::create('SubmittedForm');
|
|
$submittedForm->SubmittedByID = ($id = Member::currentUserID()) ? $id : 0;
|
|
$submittedForm->ParentID = $this->ID;
|
|
|
|
// if saving is not disabled save now to generate the ID
|
|
if(!$this->DisableSaveSubmissions) {
|
|
$submittedForm->write();
|
|
}
|
|
|
|
$values = array();
|
|
$attachments = array();
|
|
|
|
$submittedFields = new ArrayList();
|
|
|
|
foreach($this->Fields() as $field) {
|
|
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];
|
|
}
|
|
}
|
|
|
|
if(!empty($data[$field->Name])){
|
|
if(in_array("EditableFileField", $field->getClassAncestry())) {
|
|
if(isset($_FILES[$field->Name])) {
|
|
$foldername = $field->getFormField()->getFolderName();
|
|
|
|
// create the file from post data
|
|
$upload = new Upload();
|
|
$file = new File();
|
|
$file->ShowInSearch = 0;
|
|
try {
|
|
$upload->loadIntoFile($_FILES[$field->Name], $file, $foldername);
|
|
} catch( ValidationException $e ) {
|
|
$validationResult = $e->getResult();
|
|
$form->addErrorMessage($field->Name, $validationResult->message(), 'bad');
|
|
Controller::curr()->redirectBack();
|
|
return;
|
|
}
|
|
|
|
// write file to form field
|
|
$submittedField->UploadedFileID = $file->ID;
|
|
|
|
// attach a file only if lower than 1MB
|
|
if($file->getAbsoluteSize() < 1024*1024*1){
|
|
$attachments[] = $file;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$submittedField->extend('onPopulationFromField', $field);
|
|
|
|
if(!$this->DisableSaveSubmissions) {
|
|
$submittedField->write();
|
|
}
|
|
|
|
$submittedFields->push($submittedField);
|
|
}
|
|
|
|
$emailData = array(
|
|
"Sender" => Member::currentUser(),
|
|
"Fields" => $submittedFields
|
|
);
|
|
|
|
$this->extend('updateEmailData', $emailData, $attachments);
|
|
|
|
// email users on submit.
|
|
if($recipients = $this->FilteredEmailRecipients($data, $form)) {
|
|
$email = new UserDefinedForm_SubmittedFormEmail($submittedFields);
|
|
|
|
if($attachments) {
|
|
foreach($attachments as $file) {
|
|
if($file->ID != 0) {
|
|
$email->attachFile(
|
|
$file->Filename,
|
|
$file->Filename,
|
|
HTTP::get_mime_type($file->Filename)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach($recipients as $recipient) {
|
|
$email->populateTemplate($recipient);
|
|
$email->populateTemplate($emailData);
|
|
$email->setFrom($recipient->EmailFrom);
|
|
$email->setBody($recipient->EmailBody);
|
|
$email->setTo($recipient->EmailAddress);
|
|
$email->setSubject($recipient->EmailSubject);
|
|
|
|
if($recipient->EmailReplyTo) {
|
|
$email->setReplyTo($recipient->EmailReplyTo);
|
|
}
|
|
|
|
// check to see if they are a dynamic reply to. eg based on a email field a user selected
|
|
if($recipient->SendEmailFromField()) {
|
|
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailFromField()->Name);
|
|
|
|
if($submittedFormField && is_string($submittedFormField->Value)) {
|
|
$email->setReplyTo($submittedFormField->Value);
|
|
}
|
|
}
|
|
// check to see if they are a dynamic reciever eg based on a dropdown field a user selected
|
|
if($recipient->SendEmailToField()) {
|
|
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailToField()->Name);
|
|
|
|
if($submittedFormField && is_string($submittedFormField->Value)) {
|
|
$email->setTo($submittedFormField->Value);
|
|
}
|
|
}
|
|
|
|
// check to see if there is a dynamic subject
|
|
if($recipient->SendEmailSubjectField()) {
|
|
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailSubjectField()->Name);
|
|
|
|
if($submittedFormField && trim($submittedFormField->Value)) {
|
|
$email->setSubject($submittedFormField->Value);
|
|
}
|
|
}
|
|
|
|
$this->extend('updateEmail', $email, $recipient, $emailData);
|
|
|
|
if($recipient->SendPlain) {
|
|
$body = strip_tags($recipient->EmailBody) . "\n";
|
|
if(isset($emailData['Fields']) && !$recipient->HideFormData) {
|
|
foreach($emailData['Fields'] as $Field) {
|
|
$body .= $Field->Title .' - '. $Field->Value ." \n";
|
|
}
|
|
}
|
|
|
|
$email->setBody($body);
|
|
$email->sendPlain();
|
|
}
|
|
else {
|
|
$email->send();
|
|
}
|
|
}
|
|
}
|
|
|
|
$submittedForm->extend('updateAfterProcess');
|
|
|
|
Session::clear("FormInfo.{$form->FormName()}.errors");
|
|
Session::clear("FormInfo.{$form->FormName()}.data");
|
|
|
|
$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 (isset($data['SecurityID'])) {
|
|
Session::set('FormProcessed',$data['SecurityID']);
|
|
} 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);
|
|
$randHash = md5($randNum);
|
|
Session::set('FormProcessed',$randHash);
|
|
Session::set('FormProcessedNum',$randNum);
|
|
}
|
|
}
|
|
|
|
if(!$this->DisableSaveSubmissions) {
|
|
Session::set('userformssubmission'. $this->ID, $submittedForm->ID);
|
|
}
|
|
|
|
return $this->redirect($this->Link('finished') . $referrer);
|
|
}
|
|
|
|
/**
|
|
* This action handles rendering the "finished" message, which is
|
|
* customizable by editing the ReceivedFormSubmission template.
|
|
*
|
|
* @return ViewableData
|
|
*/
|
|
public function finished() {
|
|
$submission = Session::get('userformssubmission'. $this->ID);
|
|
|
|
if($submission) {
|
|
$submission = SubmittedForm::get()->byId($submission);
|
|
}
|
|
|
|
$referrer = isset($_GET['referrer']) ? urldecode($_GET['referrer']) : null;
|
|
|
|
$formProcessed = Session::get('FormProcessed');
|
|
if (!isset($formProcessed)) {
|
|
return $this->redirect($this->Link() . $referrer);
|
|
} else {
|
|
$securityID = Session::get('SecurityID');
|
|
// 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
|
|
$securityID = md5(Session::get('FormProcessedNum'));
|
|
if ($formProcessed != $securityID) {
|
|
return $this->redirect($this->Link() . $referrer);
|
|
}
|
|
}
|
|
}
|
|
// remove the session variable as we do not want it to be re-used
|
|
Session::clear('FormProcessed');
|
|
|
|
return $this->customise(array(
|
|
'Content' => $this->customise(array(
|
|
'Submission' => $submission,
|
|
'Link' => $referrer
|
|
))->renderWith('ReceivedFormSubmission'),
|
|
'Form' => '',
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A Form can have multiply members / emails to email the submission
|
|
* to and custom subjects
|
|
*
|
|
* @package userforms
|
|
*/
|
|
class UserDefinedForm_EmailRecipient extends DataObject {
|
|
|
|
private static $db = array(
|
|
'EmailAddress' => 'Varchar(200)',
|
|
'EmailSubject' => 'Varchar(200)',
|
|
'EmailFrom' => 'Varchar(200)',
|
|
'EmailReplyTo' => 'Varchar(200)',
|
|
'EmailBody' => 'Text',
|
|
'SendPlain' => 'Boolean',
|
|
'HideFormData' => 'Boolean'
|
|
);
|
|
|
|
private static $has_one = array(
|
|
'Form' => 'UserDefinedForm',
|
|
'SendEmailFromField' => 'EditableFormField',
|
|
'SendEmailToField' => 'EditableFormField',
|
|
'SendEmailSubjectField' => 'EditableFormField'
|
|
);
|
|
|
|
private static $summary_fields = array();
|
|
|
|
/**
|
|
* @return FieldList
|
|
*/
|
|
public function getCMSFields() {
|
|
|
|
$fields = new FieldList(
|
|
new TextField('EmailSubject', _t('UserDefinedForm.EMAILSUBJECT', 'Email subject')),
|
|
new LiteralField('EmailFromContent', '<p>'._t(
|
|
'UserDefinedForm.EmailFromContent',
|
|
"The from address allows you to set who the email comes from. On most servers this ".
|
|
"will need to be set to an email address on the same domain name as your site. ".
|
|
"For example on yoursite.com the from address may need to be something@yoursite.com. ".
|
|
"You can however, set any email address you wish as the reply to address."
|
|
) . "</p>"),
|
|
new TextField('EmailFrom', _t('UserDefinedForm.FROMADDRESS','Send email from')),
|
|
new TextField('EmailReplyTo', _t('UserDefinedForm.REPLYADDRESS', 'Email for reply to')),
|
|
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'))
|
|
);
|
|
|
|
if($this->Form()) {
|
|
$dropdowns = array();
|
|
// if they have email fields then we could send from it
|
|
$validEmailFields = EditableEmailField::get()->filter('ParentID', (int)$this->FormID);
|
|
// for the subject, only one-line entry boxes make sense
|
|
$validSubjectFields = EditableTextField::get()->filter('ParentID', (int)$this->FormID)->filterByCallback(function($item, $list) { return (int)$item->getSetting('Rows') === 1; });
|
|
// predefined choices are also candidates
|
|
$multiOptionFields = EditableMultipleOptionField::get()->filter('ParentID', (int)$this->FormID);
|
|
|
|
$fields->insertAfter($dropdowns[] = new DropdownField(
|
|
'SendEmailFromFieldID',
|
|
_t('UserDefinedForm.ORSELECTAFIELDTOUSEASFROM', '.. or select a field to use as reply to address'),
|
|
$validEmailFields->map('ID', 'Title')
|
|
), 'EmailReplyTo');
|
|
|
|
$validEmailFields = new ArrayList($validEmailFields->toArray());
|
|
$validEmailFields->merge($multiOptionFields);
|
|
$validSubjectFields->merge($multiOptionFields);
|
|
|
|
$fields->insertAfter($dropdowns[] = new DropdownField(
|
|
'SendEmailToFieldID',
|
|
_t('UserDefinedForm.ORSELECTAFIELDTOUSEASTO', '.. or select a field to use as the to address'),
|
|
$validEmailFields->map('ID', 'Title')
|
|
), 'EmailAddress');
|
|
$fields->insertAfter($dropdowns[] = new DropdownField(
|
|
'SendEmailSubjectFieldID',
|
|
_t('UserDefinedForm.SELECTAFIELDTOSETSUBJECT', '.. or select a field to use as the subject'),
|
|
$validSubjectFields->map('ID', 'Title')
|
|
), 'EmailSubject');
|
|
|
|
foreach($dropdowns as $dropdown) {
|
|
$dropdown->setHasEmptyDefault(true);
|
|
$dropdown->setEmptyString(" ");
|
|
}
|
|
}
|
|
|
|
$this->extend('updateCMSFields', $fields);
|
|
|
|
return $fields;
|
|
}
|
|
|
|
/**
|
|
* @param Member
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function canCreate($member = null) {
|
|
return $this->Form()->canCreate();
|
|
}
|
|
|
|
/**
|
|
* @param Member
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function canView($member = null) {
|
|
return $this->Form()->canView();
|
|
}
|
|
|
|
/**
|
|
* @param Member
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function canEdit($member = null) {
|
|
return $this->Form()->canEdit();
|
|
}
|
|
|
|
/**
|
|
* @param Member
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function canDelete($member = null) {
|
|
return $this->Form()->canDelete();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Email that gets sent to the people listed in the Email Recipients when a
|
|
* submission is made.
|
|
*
|
|
* @package userforms
|
|
*/
|
|
|
|
class UserDefinedForm_SubmittedFormEmail extends Email {
|
|
|
|
protected $ss_template = "SubmittedFormEmail";
|
|
|
|
protected $data;
|
|
|
|
public function __construct($submittedFields = null) {
|
|
parent::__construct($submittedFields = null);
|
|
}
|
|
|
|
/**
|
|
* Set the "Reply-To" header with an email address rather than append as
|
|
* {@link Email::replyTo} does.
|
|
*
|
|
* @param string $email The email address to set the "Reply-To" header to
|
|
*/
|
|
public function setReplyTo($email) {
|
|
$this->customHeaders['Reply-To'] = $email;
|
|
}
|
|
}
|