Replace the form editor with GridField

This commit is contained in:
David Craig 2015-07-24 14:37:48 +12:00
parent 8f92d75975
commit ae7dd98be3
33 changed files with 1270 additions and 1569 deletions

View File

@ -4,6 +4,5 @@ name: userforms
LeftAndMain: LeftAndMain:
extra_requirements_javascript: extra_requirements_javascript:
- userforms/javascript/UserForm.js - userforms/javascript/UserForm.js
extra_requirements_css: extra_requirements_css:
- userforms/css/FieldEditor.css - userforms/css/FieldEditor.css

View File

@ -0,0 +1,180 @@
<?php
/**
* @package userforms
*/
class UserFormFieldEditorExtension extends DataExtension {
/**
* @var array
*/
private static $has_many = array(
'Fields' => 'EditableFormField'
);
/**
* Adds the field editor to the page.
*
* @return FieldList
*/
public function updateCMSFields(FieldList $fields) {
$fieldEditor = $this->getFieldEditorGrid();
$fields->findOrMakeTab('Root.Form', _t('UserDefinedForm.FORM', 'Form'));
$fields->addFieldToTab('Root.Form', $fieldEditor);
return $fields;
}
/**
* Gets the field editor, for adding and removing EditableFormFields.
*
* @return GridField
*/
public function getFieldEditorGrid() {
$fieldEditor = GridField::create('Fields', _t('UserDefinedForm.FIELDS', 'Fields'), $this->owner->Fields());
$config = GridFieldConfig::create()
->addComponents(
(new GridFieldEditableColumns())
->setDisplayFields(array(
'ClassName' => function($record, $column, $grid) {
return DropdownField::create($column, '', $this->getEditableFieldClasses());
},
'Title' => function($record, $column, $grid) {
return TextField::create($column, ' ')
->setAttribute('placeholder', _t('UserDefinedForm.TITLE', 'Title'));
}
)),
new GridFieldButtonRow(),
new GridFieldAddNewInlineButton(),
new GridFieldEditButton(),
new GridFieldDeleteAction(),
new GridFieldToolbarHeader(),
new GridFieldOrderableRows('Sort'),
new GridState_Component(),
new GridFieldDetailForm()
);
$fieldEditor->setConfig($config);
return $fieldEditor;
}
/**
* @return array
*/
public function getEditableFieldClasses() {
$classes = ClassInfo::getValidSubClasses('EditableFormField');
// Remove classes we don't want to display in the dropdown.
$classes = array_diff($classes, array(
'EditableFormField',
'EditableMultipleOptionField'
));
$editableFieldClasses = array();
foreach ($classes as $key => $className) {
$singleton = singleton($className);
if(!$singleton->canCreate()) {
continue;
}
$editableFieldClasses[$className] = $singleton->i18n_singular_name();
}
return $editableFieldClasses;
}
/**
* @see SiteTree::doPublish
* @param Page $original
*
* @return void
*/
public function onAfterPublish($original) {
// Remove fields on the live table which could have been orphaned.
$live = Versioned::get_by_stage("EditableFormField", "Live", array(
'ParentID' => $original->ID
));
if($live) {
foreach($live as $field) {
$field->doDeleteFromStage('Live');
}
}
foreach($this->owner->Fields() as $field) {
$field->doPublish('Stage', 'Live');
}
}
/**
* @see SiteTree::doUnpublish
* @param Page $page
*
* @return void
*/
public function onAfterUnpublish($page) {
foreach($page->Fields() as $field) {
$field->doDeleteFromStage('Live');
}
}
/**
* @see SiteTree::duplicate
* @param DataObject $newPage
*
* @return DataObject
*/
public function onAfterDuplicate($newPage) {
foreach($this->owner->Fields() as $field) {
$newField = $field->duplicate();
$newField->ParentID = $newPage->ID;
$newField->ParentClass = $newPage->ClassName;
$newField->write();
foreach ($field->CustomRules() as $customRule) {
$newRule = $customRule->duplicate();
$newRule->ParentID = $newField->ID;
$newRule->write();
}
}
return $newPage;
}
/**
* @see SiteTree::getIsModifiedOnStage
* @param boolean $isModified
*
* @return boolean
*/
public function getIsModifiedOnStage($isModified) {
if(!$isModified) {
foreach($this->owner->Fields() as $field) {
if($field->getIsModifiedOnStage()) {
$isModified = true;
break;
}
}
}
return $isModified;
}
/**
* @see SiteTree::doRevertToLive
* @param Page $page
*
* @return void
*/
public function onAfterRevertToLive($page) {
foreach($page->Fields() as $field) {
$field->publish('Live', 'Stage', false);
$field->writeWithoutVersion();
}
}
}

View File

@ -1,338 +0,0 @@
<?php
/**
* A form field allowing a user to customize and create form fields.
* for saving into a {@link UserDefinedForm}
*
* @package userforms
*/
class FieldEditor extends FormField {
private static $url_handlers = array(
'$Action!/$ID' => '$Action'
);
private static $allowed_actions = array(
'addfield',
'addoptionfield',
'handleField'
);
/**
* @param array $properties
*
* @return HTML
*/
public function FieldHolder($properties = array()) {
$add = Controller::join_links($this->Link('addfield'));
$this->setAttribute('data-add-url', '\''. $add.'\'');
return $this->renderWith("FieldEditor");
}
/**
* Returns whether a user can edit the form.
*
* @param Member $member
*
* @return boolean
*/
public function canEdit($member = null) {
if($this->readonly) {
return false;
}
return $this->form->getRecord()->canEdit();
}
/**
* Returns whether a user delete a field in the form. The
* {@link EditableFormField} instances check if they can delete themselves
* but this counts as an {@link self::canEdit()} function rather than a
* delete.
*
* @param Member $member
* @return boolean
*/
public function canDelete($member = null) {
if($this->readonly) return false;
return $this->form->getRecord()->canEdit();
}
/**
* Transform this form field to a readonly version.
*
* @return ViewableData_Customised
*/
public function performReadonlyTransformation() {
$clone = clone $this;
$clone->readonly = true;
$fields = $clone->Fields();
if($fields) {
foreach($fields as $field) {
$field->setReadonly();
}
}
return $clone->customise(array(
'Fields' => $fields
));
}
/**
* Return the fields.
*
* @return RelationList
*/
public function Fields() {
if($this->form && $this->form->getRecord() && $this->name) {
$relationName = $this->name;
$fields = $this->form->getRecord()->getComponents($relationName);
if($fields) {
foreach($fields as $field) {
if(!$this->canEdit() && is_a($field, 'FormField')) {
$fields->remove($field);
$fields->push($field->performReadonlyTransformation());
}
}
}
return $fields;
}
}
/**
* Return a {@link ArrayList} of all the addable fields to populate the add
* field menu.
*
* @return ArrayList
*/
public function CreatableFields() {
$fields = ClassInfo::subclassesFor('EditableFormField');
if($fields) {
array_shift($fields); // get rid of subclass 0
asort($fields); // get in order
$output = new ArrayList();
foreach($fields as $field => $title) {
// get the nice title and strip out field
$niceTitle = _t(
$field.'.SINGULARNAME',
$title
);
if($niceTitle && $field != "EditableMultipleOptionField") {
$output->push(new ArrayData(array(
'ClassName' => $field,
'Title' => "$niceTitle"
)));
}
}
return $output;
}
return false;
}
/**
* Handles saving the page. Needs to keep an eye on fields and options which
* have been removed / added
*
* @param DataObject $record
*/
public function saveInto(DataObjectInterface $record) {
$name = $this->name;
$fieldSet = $record->$name();
// store the field IDs and delete the missing fields
// alternatively, we could delete all the fields and re add them
$missingFields = array();
foreach($fieldSet as $existingField) {
$missingFields[$existingField->ID] = $existingField;
}
if(isset($_REQUEST[$name]) && is_array($_REQUEST[$name])) {
foreach($_REQUEST[$name] as $newEditableID => $newEditableData) {
if(!is_numeric($newEditableID)) continue;
// get it from the db
$editable = DataObject::get_by_id('EditableFormField', $newEditableID);
// if it exists in the db update it
if($editable) {
// remove it from the removed fields list
if(isset($missingFields[$editable->ID]) && isset($newEditableData) && is_array($newEditableData)) {
unset($missingFields[$editable->ID]);
}
// set form id
if($editable->ParentID == 0) {
$editable->ParentID = $record->ID;
}
// save data
$editable->populateFromPostData($newEditableData);
}
}
}
// remove the fields not saved
if($this->canEdit()) {
foreach($missingFields as $removedField) {
if(is_numeric($removedField->ID)) {
// check we can edit this
$removedField->delete();
}
}
}
}
/**
* Add a field to the field editor. Called via a ajax get.
*
* @return bool|html
*/
public function addfield() {
if(!SecurityToken::inst()->checkRequest($this->request)) {
return $this->httpError(400);
}
// get the last field in this form editor
$parentID = $this->form->getRecord()->ID;
if($parentID) {
$parentID = (int)$parentID;
$sqlQuery = new SQLQuery();
$sqlQuery = $sqlQuery
->setSelect("MAX(\"Sort\")")
->setFrom("\"EditableFormField\"")
->setWhere("\"ParentID\" = $parentID");
$sort = $sqlQuery->execute()->value() + 1;
$className = (isset($_REQUEST['Type'])) ? $_REQUEST['Type'] : '';
if(!$className) {
// A possible reason for the classname being blank is because we have execeded
// the number of input requests
// http://www.php.net/manual/en/info.configuration.php#ini.max-input-vars
$maxRequests = ini_get('max_input_vars');
$numRequests = count($_REQUEST, COUNT_RECURSIVE);
if ($numRequests > $maxRequests) {
$error = sprintf('You have exceded the maximum number %s of input requests',
"[$maxRequests]");
user_error($error, E_USER_WARNING);
}
user_error('Please select a field type to created', E_USER_WARNING);
}
if(is_subclass_of($className, "EditableFormField")) {
$field = new $className();
$field->ParentID = $this->form->getRecord()->ID;
$field->Name = $field->class . $field->ID;
$field->Sort = $sort;
$field->write();
return $field->EditSegment();
}
}
return false;
}
/**
* Return the html for a field option such as a
* dropdown field or a radio check box field
*
* @return bool|html
*/
public function addoptionfield() {
if(!SecurityToken::inst()->checkRequest($this->request)) {
return $this->httpError(400);
}
// passed via the ajax
$parent = (isset($_REQUEST['Parent'])) ? $_REQUEST['Parent'] : false;
// work out the sort by getting the sort of the last field in the form +1
if($parent) {
$sql_parent = (int)$parent;
$parentObj = EditableFormField::get()->byID($parent);
$optionClass = ($parentObj && $parentObj->exists()) ? $parentObj->getRelationClass('Options') : 'EditableOption';
$sqlQuery = new SQLQuery();
$sqlQuery = $sqlQuery
->setSelect("MAX(\"Sort\")")
->setFrom("\"EditableOption\"")
->setWhere("\"ParentID\" = $sql_parent");
$sort = $sqlQuery->execute()->value() + 1;
if($parent) {
$object = Injector::inst()->create($optionClass);
$object->write();
$object->ParentID = $parent;
$object->Sort = $sort;
$object->Name = 'option' . $object->ID;
$object->write();
return $object->EditSegment();
}
}
return false;
}
/**
* Pass sub {@link FormField} requests through the editor. For example,
* option fields need to be able to call themselves.
*
* @param SS_HTTPRequest
*/
public function handleField(SS_HTTPRequest $request) {
if(!SecurityToken::inst()->checkRequest($this->request)) {
return $this->httpError(400);
}
$fields = $this->Fields();
// extract the ID and option field name
preg_match(
'/Fields\[(?P<ID>\d+)\]\[CustomSettings\]\[(?P<Option>\w+)\]/',
$request->param('ID'), $matches
);
if(isset($matches['ID']) && isset($matches['Option'])) {
foreach($fields as $field) {
$formField = $field->getFormField();
if($matches['ID'] == $field->ID) {
if($field->canEdit()) {
// find the option to handle
$options = $field->getFieldConfiguration();
$optionField = $options->fieldByName($request->param('ID'));
if($optionField) {
return $optionField->handleRequest($request, $optionField);
} else {
return $this->httpError(404);
}
}
}
}
}
return $this->httpError(403);
}
}

158
code/forms/UserForm.php Normal file
View File

@ -0,0 +1,158 @@
<?php
/**
* @package userforms
*/
class UserForm extends Form {
/**
* @param Controller $controller
* @param string $name
*/
public function __construct(Controller $controller, $name = 'Form') {
$this->controller = $controller;
$this->setRedirectToFormOnValidationError(true);
parent::__construct(
$controller,
$name,
$this->getFormFields(),
$this->getFormActions(),
$this->getRequiredFields()
);
if($controller->DisableCsrfSecurityToken) {
$this->disableSecurityToken();
}
$data = Session::get("FormInfo.{$this->FormName()}.data");
if(is_array($data)) {
$this->loadDataFrom($data);
}
$this->extend('updateForm');
}
/**
* 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();
foreach($this->controller->Fields() as $editableField) {
// get the raw form field from the editable version
$field = $editableField->getFormField();
if(!$field) continue;
// set the error / formatting messages
$field->setCustomValidationMessage($editableField->getErrorMessage());
// set the right title on this field
if($right = $editableField->getSetting('RightTitle')) {
// Since this field expects raw html, safely escape the user data prior
$field->setRightTitle(Convert::raw2xml($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($extraClass = $editableField->getSetting('ExtraClass')) {
$field->addExtraClass(Convert::raw2att($extraClass));
}
// set the values passed by the url to the field
$request = $this->controller->getRequest();
if($value = $request->getVar($field->getName())) {
$field->setValue($value);
}
$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->controller->SubmitButtonText) ? $this->controller->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
$clearText = ($this->controller->ClearButtonText) ? $this->controller->ClearButtonText : _t('UserDefinedForm.CLEARBUTTON', 'Clear');
$actions = new FieldList(
new FormAction("process", $submitText)
);
if($this->controller->ShowClearButton) {
$actions->push(new ResetFormAction("clearForm", $clearText));
}
$this->extend('updateFormActions', $actions);
return $actions;
}
/**
* Get the required form fields for this form.
*
* @return RequiredFields
*/
public function getRequiredFields() {
// Generate required field validator
$requiredNames = $this
->controller
->Fields()
->filter('Required', true)
->column('Name');
$required = new RequiredFields($requiredNames);
$this->extend('updateRequiredFields', $required);
return $required;
}
/**
* Override validation so conditional fields can be validated correctly.
*
* @return boolean
*/
public function validate() {
$data = $this->getData();
Session::set("FormInfo.{$this->FormName()}.data", $data);
Session::clear("FormInfo.{$this->FormName()}.errors");
foreach ($this->controller->Fields() as $key => $field) {
$field->validateField($data, $this);
}
if(Session::get("FormInfo.{$this->FormName()}.errors")) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,60 @@
<?php
/**
* A custom rule for showing / hiding an EditableFormField
* based the value of another EditableFormField.
*
* @package userforms
*/
class EditableCustomRule extends DataObject {
private static $condition_options = array(
"IsBlank" => "Is blank",
"IsNotBlank" => "Is not blank",
"HasValue" => "Equals",
"ValueNot" => "Doesn't equal",
"ValueLessThan" => "Less than",
"ValueLessThanEqual" => "Less than or equal",
"ValueGreaterThan" => "Greater than",
"ValueGreaterThanEqual" => "Greater than or equal"
);
private static $db = array(
'Display' => 'Enum("Show,Hide")',
'ConditionOption' => 'Enum("IsBlank,IsNotBlank,HasValue,ValueNot,ValueLessThan,ValueLessThanEqual,ValueGreaterThan,ValueGreaterThanEqual")',
'FieldValue' => 'Varchar'
);
private static $has_one = array(
'Parent' => 'EditableFormField',
'ConditionField' => 'EditableFormField'
);
/**
* Built in extensions required
*
* @config
* @var array
*/
private static $extensions = array(
"Versioned('Stage', 'Live')"
);
/**
* Publish this custom rule to the live site
*
* Wrapper for the {@link Versioned} publish function
*/
public function doPublish($fromStage, $toStage, $createNewVersion = false) {
$this->publish($fromStage, $toStage, $createNewVersion);
}
/**
* Delete this custom rule from a given stage
*
* Wrapper for the {@link Versioned} deleteFromStage function
*/
public function doDeleteFromStage($stage) {
$this->deleteFromStage($stage);
}
}

View File

@ -12,14 +12,24 @@ class EditableCheckbox extends EditableFormField {
private static $singular_name = 'Checkbox Field'; private static $singular_name = 'Checkbox Field';
private static $plural_name = 'Checkboxes'; private static $plural_name = 'Checkboxes';
public function getFieldConfiguration() { /**
$options = parent::getFieldConfiguration(); * @return FieldList
$options->push(new CheckboxField("Fields[$this->ID][CustomSettings][Default]", _t('EditableFormField.CHECKEDBYDEFAULT', 'Checked by Default?'), $this->getSetting('Default'))); */
public function getCMSFields() {
return $options; $fields = parent::getCMSFields();
$fields->removeByName('Default');
$fields->addFieldToTab('Root.Main', CheckboxField::create(
"Fields[$this->ID][CustomSettings][Default]",
_t('EditableFormField.CHECKEDBYDEFAULT', 'Checked by Default?'),
$this->getSetting('Default')
));
return $fields;
} }
public function getFormField() { public function getFormField() {
$field = CheckboxField::create( $this->Name, $this->Title, $this->getSetting('Default')); $field = CheckboxField::create( $this->Name, $this->Title, $this->getSetting('Default'));
@ -31,6 +41,8 @@ class EditableCheckbox extends EditableFormField {
$field->setAttribute('data-rule-required', 'true'); $field->setAttribute('data-rule-required', 'true');
$field->setAttribute('data-msg-required', $errorMessage); $field->setAttribute('data-msg-required', $errorMessage);
} }
$field->setValue($this->getSetting('Default'));
return $field; return $field;
} }

View File

@ -19,7 +19,16 @@ class EditableCheckboxGroupField extends EditableMultipleOptionField {
$optionMap = ($optionSet) ? $optionSet->map('EscapedTitle', 'Title') : array(); $optionMap = ($optionSet) ? $optionSet->map('EscapedTitle', 'Title') : array();
return new UserFormsCheckboxSetField($this->Name, $this->Title, $optionMap); $field = new UserFormsCheckboxSetField($this->Name, $this->Title, $optionMap);
// Set the default checked items
$defaultCheckedItems = $optionSet->filter('Default', 1);
if ($defaultCheckedItems->count()) {
$field->setDefaultItems($optionSet->filter('Default', 1)->map('EscapedTitle')->keys());
}
return $field;
} }
public function getValueFromData($data) { public function getValueFromData($data) {

View File

@ -10,9 +10,20 @@ class EditableCountryDropdownField extends EditableFormField {
private static $singular_name = 'Country Dropdown'; private static $singular_name = 'Country Dropdown';
private static $plural_name = 'Country Dropdowns'; private static $plural_name = 'Country Dropdowns';
/**
* @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('Default');
return $fields;
}
public function getFormField() { public function getFormField() {
return new CountryDropdownField($this->Name, $this->Title); return CountryDropdownField::create($this->Name, $this->Title);
} }
public function getValueFromData($data) { public function getValueFromData($data) {

View File

@ -12,24 +12,25 @@ class EditableDateField extends EditableFormField {
private static $singular_name = 'Date Field'; private static $singular_name = 'Date Field';
private static $plural_name = 'Date Fields'; private static $plural_name = 'Date Fields';
public function getFieldConfiguration() { /**
* @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('Default');
$default = ($this->getSetting('DefaultToToday')) ? $this->getSetting('DefaultToToday') : false; $default = ($this->getSetting('DefaultToToday')) ? $this->getSetting('DefaultToToday') : false;
$label = _t('EditableFormField.DEFAULTTOTODAY', 'Default to Today?'); $label = _t('EditableFormField.DEFAULTTOTODAY', 'Default to Today?');
return new FieldList( $fields->addFieldToTab('Root.Main', CheckboxField::create(
new CheckboxField($this->getSettingName("DefaultToToday"), $label, $default) $this->getSettingName('DefaultToToday'),
); $label,
} $default
));
public function populateFromPostData($data) {
$fieldPrefix = 'Default-'; return $fields;
if(empty($data['Default']) && !empty($data[$fieldPrefix.'Year']) && !empty($data[$fieldPrefix.'Month']) && !empty($data[$fieldPrefix.'Day'])) {
$data['Default'] = $data['Year'] . '-' . $data['Month'] . '-' . $data['Day'];
}
parent::populateFromPostData($data);
} }
/** /**

View File

@ -12,12 +12,24 @@ class EditableDropdown extends EditableMultipleOptionField {
private static $singular_name = 'Dropdown Field'; private static $singular_name = 'Dropdown Field';
private static $plural_name = 'Dropdowns'; private static $plural_name = 'Dropdowns';
/**
* @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('Default');
return $fields;
}
/** /**
* @return DropdownField * @return DropdownField
*/ */
public function getFormField() { public function getFormField() {
$optionSet = $this->Options(); $optionSet = $this->Options();
$defaultOptions = $optionSet->filter('Default', 1);
$options = array(); $options = array();
if($optionSet) { if($optionSet) {
@ -35,6 +47,10 @@ class EditableDropdown extends EditableMultipleOptionField {
$field->setAttribute('data-rule-required', 'true'); $field->setAttribute('data-rule-required', 'true');
$field->setAttribute('data-msg-required', $errorMessage); $field->setAttribute('data-msg-required', $errorMessage);
} }
if($defaultOptions->count()) {
$field->setValue($defaultOptions->First()->EscapedTitle);
}
return $field; return $field;
} }

View File

@ -28,6 +28,8 @@ class EditableEmailField extends EditableFormField {
$field->setAttribute('data-rule-required', 'true'); $field->setAttribute('data-rule-required', 'true');
$field->setAttribute('data-msg-required', $errorMessage); $field->setAttribute('data-msg-required', $errorMessage);
} }
$field->setValue($this->Default);
return $field; return $field;
} }

View File

@ -12,21 +12,21 @@ class EditableFileField extends EditableFormField {
private static $plural_names = 'File Fields'; private static $plural_names = 'File Fields';
public function getFieldConfiguration() { /**
$field = parent::getFieldConfiguration(); * @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$folder = ($this->getSetting('Folder')) ? $this->getSetting('Folder') : null; $folder = ($this->getSetting('Folder')) ? $this->getSetting('Folder') : null;
$tree = UserformsTreeDropdownField::create( $fields->addFieldToTab('Root.Main', UserformsTreeDropdownField::create(
$this->getSettingName("Folder"), $this->getSettingName('Folder'),
_t('EditableUploadField.SELECTUPLOADFOLDER', 'Select upload folder'), _t('EditableUploadField.SELECTUPLOADFOLDER', 'Select upload folder'),
"Folder" 'Folder'
); )->setValue($folder));
$tree->setValue($folder); return $fields;
$field->push($tree);
return $field;
} }
public function getFormField() { public function getFormField() {

View File

@ -7,8 +7,14 @@
*/ */
class EditableFormField extends DataObject { class EditableFormField extends DataObject {
private static $default_sort = "Sort"; /**
* Default sort order
*
* @config
* @var string
*/
private static $default_sort = '"Sort"';
/** /**
* A list of CSS classes that can be added * A list of CSS classes that can be added
@ -16,7 +22,19 @@ class EditableFormField extends DataObject {
* @var array * @var array
*/ */
public static $allowed_css = array(); public static $allowed_css = array();
/**
* @config
* @var array
*/
private static $summary_fields = array(
'Title'
);
/**
* @config
* @var array
*/
private static $db = array( private static $db = array(
"Name" => "Varchar", "Name" => "Varchar",
"Title" => "Varchar(255)", "Title" => "Varchar(255)",
@ -24,24 +42,40 @@ class EditableFormField extends DataObject {
"Sort" => "Int", "Sort" => "Int",
"Required" => "Boolean", "Required" => "Boolean",
"CustomErrorMessage" => "Varchar(255)", "CustomErrorMessage" => "Varchar(255)",
"CustomRules" => "Text", "CustomSettings" => "Text"
"CustomSettings" => "Text",
"CustomParameter" => "Varchar(200)"
); );
/**
* @config
* @var array
*/
private static $has_one = array( private static $has_one = array(
"Parent" => "UserDefinedForm", "Parent" => "UserDefinedForm",
); );
/**
* Built in extensions required
*
* @config
* @var array
*/
private static $extensions = array( private static $extensions = array(
"Versioned('Stage', 'Live')" "Versioned('Stage', 'Live')"
); );
/**
* @config
* @var array
*/
private static $has_many = array(
"CustomRules" => "EditableCustomRule.Parent"
);
/** /**
* @var bool * @var bool
*/ */
protected $readonly; protected $readonly;
/** /**
* Set the visibility of an individual form field * Set the visibility of an individual form field
* *
@ -59,14 +93,181 @@ class EditableFormField extends DataObject {
private function isReadonly() { private function isReadonly() {
return $this->readonly; return $this->readonly;
} }
/** /**
* Template to render the form field into * @return FieldList
*
* @return String
*/ */
public function EditSegment() { public function getCMSFields() {
return $this->renderWith('EditableFormField'); $fields = $this->scaffoldFormFields(array(
'tabbed' => true
));
$fields->removeByName('Sort');
$fields->removeByName('Version');
$fields->removeByName('ParentID');
$fields->removeByName('CustomSettings');
$fields->removeByName('Name');
$fields->insertBefore(ReadonlyField::create(
'Type',
_t('EditableFormField.TYPE', 'Type'),
$this->config()->get('singular_name')),
'Title'
);
$fields->insertBefore(LiteralField::create(
'MergeField',
_t('EditableFormField.MERGEFIELDNAME',
'<div class="field readonly">' .
'<label class="left">Merge field</label>' .
'<div class="middleColumn">' .
'<span class="readonly">$' . $this->Name . '</span>' .
'</div>' .
'</div>')),
'Title'
);
/*
* Validation
*/
$requiredCheckbox = $fields->fieldByName('Root')->fieldByName('Main')->fieldByName('Required');
$customErrorMessage = $fields->fieldByName('Root')->fieldByName('Main')->fieldByName('CustomErrorMessage');
$fields->removeFieldFromTab('Root.Main', 'Required');
$fields->removeFieldFromTab('Root.Main', 'CustomErrorMessage');
$fields->addFieldToTab('Root.Validation', $requiredCheckbox);
$fields->addFieldToTab('Root.Validation', $customErrorMessage);
/*
* Custom rules
*/
$customRulesConfig = GridFieldConfig::create()
->addComponents(
(new GridFieldEditableColumns())
->setDisplayFields(array(
'Display' => '',
'ConditionFieldID' => function($record, $column, $grid) {
return DropdownField::create(
$column,
'',
EditableFormField::get()
->filter(array(
'ParentID' => $this->ParentID
))
->exclude(array(
'ID' => $this->ID
))
->map('ID', 'Title')
);
},
'ConditionOption' => function($record, $column, $grid) {
$options = Config::inst()->get('EditableCustomRule', 'condition_options');
return DropdownField::create($column, '', $options);
},
'FieldValue' => function($record, $column, $grid) {
return TextField::create($column);
},
'ParentID' => function($record, $column, $grid) {
return HiddenField::create($column, '', $this->ID);
}
)),
new GridFieldButtonRow(),
new GridFieldToolbarHeader(),
new GridFieldAddNewInlineButton(),
new GridFieldDeleteAction(),
new GridState_Component()
);
$customRulesGrid = GridField::create(
'CustomRules',
_t('EditableFormField.CUSTOMRULES', 'Custom Rules'),
$this->CustomRules(),
$customRulesConfig
);
$fields->addFieldToTab('Root.CustomRules', $customRulesGrid);
/*
* Custom settings
*/
$extraClass = ($this->getSetting('ExtraClass')) ? $this->getSetting('ExtraClass') : '';
if (is_array(self::$allowed_css) && !empty(self::$allowed_css)) {
foreach(self::$allowed_css as $k => $v) {
if (!is_array($v)) $cssList[$k]=$v;
elseif ($k == $this->ClassName()) $cssList = array_merge($cssList, $v);
}
$fields->addFieldToTab('Root.Main',
new DropdownField(
$this->getSettingName('ExtraClass'),
_t('EditableFormField.EXTRACLASSA', 'Extra Styling/Layout'),
$cssList,
$extraClass
)
);
} else {
$fields->addFieldToTab('Root.Main',
new TextField(
$this->getSettingName('ExtraClass'),
_t('EditableFormField.EXTRACLASSB', 'Extra CSS class - separate multiples with a space'),
$extraClass
)
);
}
$fields->addFieldToTab('Root.Main',
new TextField(
$this->getSettingName('RightTitle'),
_t('EditableFormField.RIGHTTITLE', 'Right Title'),
$this->getSetting('RightTitle')
)
);
$this->extend('updateCMSFields', $fields);
return $fields;
}
/**
* @return void
*/
public function onBeforeWrite() {
parent::onBeforeWrite();
// Save custom settings.
$fields = $this->toMap();
$settings = $this->getSettings();
foreach($fields as $field => $value) {
if(preg_match("/\[CustomSettings\]\[((\w)+)\]$/", $field, $matches)) {
$settings[$matches[1]] = $value;
}
}
$this->setSettings($settings);
if(!isset($this->Sort)) {
$parentID = ($this->ParentID) ? $this->ParentID : 0;
$this->Sort = EditableFormField::get()->filter('ParentID', $parentID)->max('Sort') + 1;
}
}
/**
* @return void
*/
public function onAfterWrite() {
parent::onAfterWrite();
// Set a field name.
if(!$this->Name) {
$this->Name = $this->RecordClassName . $this->ID;
$this->write();
}
} }
/** /**
@ -85,7 +286,11 @@ class EditableFormField extends DataObject {
* @return bool * @return bool
*/ */
public function canDelete($member = null) { public function canDelete($member = null) {
return ($this->Parent()->canEdit($member = null) && !$this->isReadonly()); if($this->Parent()) {
return $this->Parent()->canEdit($member) && !$this->isReadonly();
}
return true;
} }
/** /**
@ -95,7 +300,11 @@ class EditableFormField extends DataObject {
* @return bool * @return bool
*/ */
public function canEdit($member = null) { public function canEdit($member = null) {
return ($this->Parent()->canEdit($member = null) && !$this->isReadonly()); if($this->Parent()) {
return $this->Parent()->canEdit($member) && !$this->isReadonly();
}
return true;
} }
/** /**
@ -105,6 +314,11 @@ class EditableFormField extends DataObject {
*/ */
public function doPublish($fromStage, $toStage, $createNewVersion = false) { public function doPublish($fromStage, $toStage, $createNewVersion = false) {
$this->publish($fromStage, $toStage, $createNewVersion); $this->publish($fromStage, $toStage, $createNewVersion);
// Don't forget to publish the related custom rules...
foreach ($this->CustomRules() as $rule) {
$rule->doPublish($fromStage, $toStage, $createNewVersion);
}
} }
/** /**
@ -114,6 +328,11 @@ class EditableFormField extends DataObject {
*/ */
public function doDeleteFromStage($stage) { public function doDeleteFromStage($stage) {
$this->deleteFromStage($stage); $this->deleteFromStage($stage);
// Don't forget to delete the related custom rules...
foreach ($this->CustomRules() as $rule) {
$rule->deleteFromStage($stage);
}
} }
/** /**
@ -136,7 +355,7 @@ class EditableFormField extends DataObject {
if($this->isNew()) return false; if($this->isNew()) return false;
$stageVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Stage', $this->ID); $stageVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Stage', $this->ID);
$liveVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Live', $this->ID); $liveVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Live', $this->ID);
return ($stageVersion && $stageVersion != $liveVersion); return ($stageVersion && $stageVersion != $liveVersion);
} }
@ -243,51 +462,6 @@ class EditableFormField extends DataObject {
public function showExtraOptions() { public function showExtraOptions() {
return true; return true;
} }
/**
* Return the custom validation fields for this field for the CMS
*
* @return array
*/
public function Dependencies() {
return ($this->CustomRules) ? unserialize($this->CustomRules) : array();
}
/**
* Return the custom validation fields for the field
*
* @return DataObjectSet
*/
public function CustomRules() {
$output = new ArrayList();
$fields = $this->Parent()->Fields();
// check for existing ones
if($rules = $this->Dependencies()) {
foreach($rules as $rule => $data) {
// recreate all the field object to prevent caching
$outputFields = new ArrayList();
foreach($fields as $field) {
$new = clone $field;
$new->isSelected = ($new->Name == $data['ConditionField']) ? true : false;
$outputFields->push($new);
}
$output->push(new ArrayData(array(
'FieldName' => $this->getFieldName(),
'Display' => $data['Display'],
'Fields' => $outputFields,
'ConditionField' => $data['ConditionField'],
'ConditionOption' => $data['ConditionOption'],
'Value' => $data['Value']
)));
}
}
return $output;
}
/** /**
* Title field of the field in the backend of the page * Title field of the field in the backend of the page
@ -336,118 +510,6 @@ class EditableFormField extends DataObject {
return $name . '[' . $field .']'; return $name . '[' . $field .']';
} }
/**
* How to save the data submitted in this field into the database object
* which this field represents.
*
* Any class's which call this should also call
* {@link parent::populateFromPostData()} to ensure that this method is
* called
*
* @access public
*
* @param array $data
*/
public function populateFromPostData($data) {
$this->Title = (isset($data['Title'])) ? $data['Title']: "";
$this->Default = (isset($data['Default'])) ? $data['Default'] : "";
$this->Sort = (isset($data['Sort'])) ? $data['Sort'] : null;
$this->Required = !empty($data['Required']) ? 1 : 0;
$this->Name = $this->class.$this->ID;
$this->CustomRules = "";
$this->CustomErrorMessage = (isset($data['CustomErrorMessage'])) ? $data['CustomErrorMessage'] : "";
$this->CustomSettings = "";
// custom settings
if(isset($data['CustomSettings'])) {
$this->setSettings($data['CustomSettings']);
}
// custom validation
if(isset($data['CustomRules'])) {
$rules = array();
foreach($data['CustomRules'] as $key => $value) {
if(is_array($value)) {
$fieldValue = (isset($value['Value'])) ? $value['Value'] : '';
if(isset($value['ConditionOption']) && $value['ConditionOption'] == "Blank" || $value['ConditionOption'] == "NotBlank") {
$fieldValue = "";
}
$rules[] = array(
'Display' => (isset($value['Display'])) ? $value['Display'] : "",
'ConditionField' => (isset($value['ConditionField'])) ? $value['ConditionField'] : "",
'ConditionOption' => (isset($value['ConditionOption'])) ? $value['ConditionOption'] : "",
'Value' => $fieldValue
);
}
}
$this->CustomRules = serialize($rules);
}
$this->extend('onPopulateFromPostData', $data);
$this->write();
}
/**
* Implement custom field Configuration on this field. Includes such things as
* settings and options of a given editable form field
*
* @return FieldSet
*/
public function getFieldConfiguration() {
$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)) {
foreach(self::$allowed_css as $k => $v) {
if (!is_array($v)) $cssList[$k]=$v;
elseif ($k == $this->ClassName()) $cssList = array_merge($cssList, $v);
}
$ec = new DropdownField(
$this->getSettingName('ExtraClass'),
_t('EditableFormField.EXTRACLASSA', 'Extra Styling/Layout'),
$cssList, $extraClass
);
}
else {
$ec = new TextField(
$this->getSettingName('ExtraClass'),
_t('EditableFormField.EXTRACLASSB', 'Extra css Class - separate multiples with a space'),
$extraClass
);
}
$right = new TextField(
$this->getSettingName('RightTitle'),
_t('EditableFormField.RIGHTTITLE', 'Right Title'),
$this->getSetting('RightTitle')
);
$fields = FieldList::create(
$mergeFieldName,
$ec,
$right
);
$this->extend('updateFieldConfiguration', $fields);
return $fields;
}
/** /**
* Append custom validation fields to the default 'Validation' * Append custom validation fields to the default 'Validation'
* section in the editable options view * section in the editable options view
@ -533,4 +595,32 @@ class EditableFormField extends DataObject {
return DBField::create_field('Varchar', $errorMessage); return DBField::create_field('Varchar', $errorMessage);
} }
/**
* Validate the field taking into account its custom rules.
*
* @param Array $data
* @param UserForm $form
*
* @return boolean
*/
public function validateField($data, $form) {
if($this->Required && $this->CustomRules()->Count() == 0) {
$formField = $this->getFormField();
if(isset($data[$this->Name])) {
$formField->setValue($data[$this->Name]);
}
if(
!isset($data[$this->Name]) ||
!$data[$this->Name] ||
!$formField->validate($form->getValidator())
) {
$form->addErrorMessage($this->Name, $this->getErrorMessage(), 'bad');
}
}
return true;
}
} }

View File

@ -10,8 +10,16 @@ class EditableFormHeading extends EditableFormField {
private static $singular_name = 'Heading'; private static $singular_name = 'Heading';
private static $plural_name = 'Headings'; private static $plural_name = 'Headings';
public function getFieldConfiguration() { /**
* @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('Default');
$fields->removeByName('Validation');
$levels = array( $levels = array(
'1' => '1', '1' => '1',
'2' => '2', '2' => '2',
@ -23,26 +31,22 @@ class EditableFormHeading extends EditableFormField {
$level = ($this->getSetting('Level')) ? $this->getSetting('Level') : 3; $level = ($this->getSetting('Level')) ? $this->getSetting('Level') : 3;
$label = _t('EditableFormHeading.LEVEL', 'Select Heading Level'); $label = _t('EditableFormHeading.LEVEL', 'Select Heading Level');
$options = parent::getFieldConfiguration();
$options->push( $fields->addFieldsToTab('Root.Main', array(
new DropdownField($this->getSettingName("Level"), $label, $levels, $level) DropdownField::create(
); $this->getSettingName('Level'),
$label,
if($this->readonly) { $levels,
$extraFields = $options->makeReadonly(); $level
} ),
CheckboxField::create(
$options->push(
new CheckboxField(
$this->getSettingName('HideFromReports'), $this->getSettingName('HideFromReports'),
_t('EditableLiteralField.HIDEFROMREPORT', 'Hide from reports?'), _t('EditableLiteralField.HIDEFROMREPORT', 'Hide from reports?'),
$this->getSetting('HideFromReports') $this->getSetting('HideFromReports')
) )
); ));
return $options; return $fields;
} }
public function getFormField() { public function getFormField() {

View File

@ -70,24 +70,31 @@ class EditableLiteralField extends EditableFormField {
$content = $this->sanitiseContent($content); $content = $this->sanitiseContent($content);
$this->setSetting('Content', $content); $this->setSetting('Content', $content);
} }
public function getFieldConfiguration() { /**
$textAreaField = new HTMLEditorField( * @return FieldList
$this->getSettingName('Content'), */
"HTML", public function getCMSFields() {
$this->getContent() $fields = parent::getCMSFields();
);
$textAreaField->setRows(4); $fields->removeByName('Default');
$textAreaField->setColumns(20); $fields->removeByName('Validation');
return new FieldList( $fields->addFieldsToTab('Root.Main', array(
$textAreaField, HTMLEditorField::create(
new CheckboxField( $this->getSettingName('Content'),
'HTML',
$this->getContent())
->setRows(4)
->setColumns(20),
CheckboxField::create(
$this->getSettingName('HideFromReports'), $this->getSettingName('HideFromReports'),
_t('EditableLiteralField.HIDEFROMREPORT', 'Hide from reports?'), _t('EditableLiteralField.HIDEFROMREPORT', 'Hide from reports?'),
$this->getSetting('HideFromReports') $this->getSetting('HideFromReports')
) )
); ));
return $fields;
} }
public function getFormField() { public function getFormField() {

View File

@ -10,17 +10,30 @@ class EditableMemberListField extends EditableFormField {
private static $singular_name = 'Member List Field'; private static $singular_name = 'Member List Field';
private static $plural_name = 'Member List Fields'; private static $plural_name = 'Member List Fields';
public function getFieldConfiguration() { /**
* @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('Default');
$fields->removeByName('Validation');
$groupID = ($this->getSetting('GroupID')) ? $this->getSetting('GroupID') : 0; $groupID = ($this->getSetting('GroupID')) ? $this->getSetting('GroupID') : 0;
$groups = DataObject::get("Group"); $groups = DataObject::get('Group');
if($groups) $groups = $groups->map('ID', 'Title'); if($groups) {
$groups = $groups->map('ID', 'Title');
$fields = new FieldList( }
new DropdownField("Fields[$this->ID][CustomSettings][GroupID]", _t('EditableFormField.GROUP', 'Group'), $groups, $groupID)
);
$fields->addFieldToTab('Root.Main', DropdownField::create(
"Fields[$this->ID][CustomSettings][GroupID]",
_t('EditableFormField.GROUP', 'Group'),
$groups,
$groupID
));
return $fields; return $fields;
} }

View File

@ -18,6 +18,46 @@ class EditableMultipleOptionField extends EditableFormField {
private static $has_many = array( private static $has_many = array(
"Options" => "EditableOption" "Options" => "EditableOption"
); );
/**
* @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$optionsGrid = GridField::create(
'Options',
_t('EditableFormField.CUSTOMOPTIONS', 'Options'),
$this->Options()
);
$optionsConfig = GridFieldConfig::create()
->addComponents(
(new GridFieldEditableColumns())
->setDisplayFields(array(
'Title' => function($record, $column, $grid) {
return TextField::create($column);
},
'Default' => function($record, $column, $grid) {
return CheckboxField::create($column);
},
'ParentID' => function($record, $column, $grid) {
return HiddenField::create($column, '', $this->ID);
}
)),
new GridFieldButtonRow(),
new GridFieldToolbarHeader(),
new GridFieldAddNewInlineButton(),
new GridFieldDeleteAction(),
new GridState_Component()
);
$optionsGrid->setConfig($optionsConfig);
$fields->addFieldToTab('Root.Options', $optionsGrid);
return $fields;
}
/** /**
* Publishing Versioning support. * Publishing Versioning support.
@ -100,29 +140,6 @@ class EditableMultipleOptionField extends EditableFormField {
return $clonedNode; return $clonedNode;
} }
/**
* On before saving this object we need to go through and keep an eye on
* all our option fields that are related to this field in the form
*
* @param ArrayData
*/
public function populateFromPostData($data) {
parent::populateFromPostData($data);
// get the current options
$fieldSet = $this->Options();
// go over all the current options and check if ID and Title still exists
foreach($fieldSet as $option) {
if(isset($data[$option->ID]) && isset($data[$option->ID]['Title']) && $data[$option->ID]['Title'] != "field-node-deleted") {
$option->populateFromPostData($data[$option->ID]);
}
else {
$option->delete();
}
}
}
/** /**
* Return whether or not this field has addable options such as a * Return whether or not this field has addable options such as a
* {@link EditableDropdownField} or {@link EditableRadioField} * {@link EditableDropdownField} or {@link EditableRadioField}

View File

@ -23,6 +23,7 @@ class EditableNumericField extends EditableFormField {
public function getFormField() { public function getFormField() {
$field = new NumericField($this->Name, $this->Title); $field = new NumericField($this->Name, $this->Title);
$field->addExtraClass('number'); $field->addExtraClass('number');
$field->setValue($this->Default);
if ($this->Required) { if ($this->Required) {
// Required and numeric validation can conflict so add the // Required and numeric validation can conflict so add the

View File

@ -26,23 +26,23 @@ class EditableOption extends DataObject {
"Versioned('Stage', 'Live')" "Versioned('Stage', 'Live')"
); );
/** /**
* @param Member $member * @param Member $member
* *
* @return boolean * @return boolean
*/ */
public function canEdit($member = null) { public function canEdit($member = null) {
return ($this->Parent()->canEdit($member)); return ($this->Parent()->canEdit($member));
} }
/** /**
* @param Member $member * @param Member $member
* *
* @return boolean * @return boolean
*/ */
public function canDelete($member = null) { public function canDelete($member = null) {
return ($this->Parent()->canDelete($member)); return ($this->Parent()->canDelete($member));
} }
/** /**
* Template for the editing view of this option field * Template for the editing view of this option field
@ -68,20 +68,6 @@ class EditableOption extends DataObject {
public function FieldName() { public function FieldName() {
return "Fields[{$this->ParentID}][{$this->ID}]"; return "Fields[{$this->ParentID}][{$this->ID}]";
} }
/**
* Populate this option from the form field
*
* @param Array Data
*/
public function populateFromPostData($data) {
$this->Title = (isset($data['Title'])) ? $data['Title'] : "";
$this->Default = (isset($data['Default'])) ? $data['Default'] : "";
$this->Sort = (isset($data['Sort'])) ? $data['Sort'] : 0;
$this->extend('onPopulateFromPostData', $data);
$this->write();
}
/** /**
* Make this option readonly * Make this option readonly
@ -91,7 +77,7 @@ class EditableOption extends DataObject {
return $this->EditSegment(); return $this->EditSegment();
} }
public function getEscapedTitle() { public function getEscapedTitle() {
return Convert::raw2att($this->Title); return Convert::raw2att($this->Title);
} }
} }

View File

@ -12,17 +12,35 @@ class EditableRadioField extends EditableMultipleOptionField {
private static $singular_name = 'Radio field'; private static $singular_name = 'Radio field';
private static $plural_name = 'Radio fields'; private static $plural_name = 'Radio fields';
/**
* @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('Default');
return $fields;
}
public function getFormField() { public function getFormField() {
$optionSet = $this->Options(); $optionSet = $this->Options();
$defaultOptions = $optionSet->filter('Default', 1);
$options = array(); $options = array();
if($optionSet) { if($optionSet) {
foreach( $optionSet as $option ) { foreach($optionSet as $option) {
$options[$option->EscapedTitle] = $option->Title; $options[$option->EscapedTitle] = $option->Title;
} }
} }
return new OptionsetField($this->Name, $this->Title, $options); $field = OptionsetField::create($this->Name, $this->Title, $options);
if($defaultOptions->count()) {
$field->setValue($defaultOptions->First()->EscapedTitle);
}
return $field;
} }
} }

View File

@ -12,27 +12,32 @@ class EditableTextField extends EditableFormField {
private static $singular_name = 'Text Field'; private static $singular_name = 'Text Field';
private static $plural_name = 'Text Fields'; private static $plural_name = 'Text Fields';
public function getFieldConfiguration() { /**
$fields = parent::getFieldConfiguration(); * @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$min = ($this->getSetting('MinLength')) ? $this->getSetting('MinLength') : ''; $min = ($this->getSetting('MinLength')) ? $this->getSetting('MinLength') : '';
$max = ($this->getSetting('MaxLength')) ? $this->getSetting('MaxLength') : ''; $max = ($this->getSetting('MaxLength')) ? $this->getSetting('MaxLength') : '';
$rows = ($this->getSetting('Rows')) ? $this->getSetting('Rows') : '1'; $rows = ($this->getSetting('Rows')) ? $this->getSetting('Rows') : '1';
$extraFields = new FieldList( $fields->addFieldsToTab('Root.Main', array(
new FieldGroup(_t('EditableTextField.TEXTLENGTH', 'Text length'), FieldGroup::create(
new NumericField($this->getSettingName('MinLength'), "", $min), _t('EditableTextField.TEXTLENGTH', 'Text length'),
new NumericField($this->getSettingName('MaxLength'), " - ", $max) NumericField::create($this->getSettingName('MinLength'), '', $min),
NumericField::create($this->getSettingName('MaxLength'), ' - ', $max)
), ),
new NumericField($this->getSettingName('Rows'), _t('EditableTextField.NUMBERROWS', NumericField::create(
'Number of rows'), $rows) 'Rows',
); _t('EditableTextField.NUMBERROWS', 'Number of rows'),
$rows
$fields->merge($extraFields); )
));
return $fields;
return $fields;
} }
/** /**
@ -57,6 +62,8 @@ class EditableTextField extends EditableFormField {
$field->setAttribute('data-rule-required', 'true'); $field->setAttribute('data-rule-required', 'true');
$field->setAttribute('data-msg-required', $errorMessage); $field->setAttribute('data-msg-required', $errorMessage);
} }
$field->setValue($this->Default);
return $field; return $field;
} }

View File

@ -16,20 +16,19 @@ class UserDefinedForm extends Page {
*/ */
private static $required_identifier = null; private static $required_identifier = null;
/**
* Prevent translatable module from attepmting to translate FieldEditor
*
* @var array
* @config
*/
private static $translate_excluded_fields = array(
'Fields'
);
/** /**
* @var string * @var string
*/ */
private static $email_template_directory = 'userforms/templates/email/'; private static $email_template_directory = 'userforms/templates/email/';
/**
* Built in extensions required by this page
* @config
* @var array
*/
private static $extensions = array(
'UserFormFieldEditorExtension'
);
/** /**
* @var array Fields on the user defined form page. * @var array Fields on the user defined form page.
@ -60,7 +59,6 @@ class UserDefinedForm extends Page {
* @var array * @var array
*/ */
private static $has_many = array( private static $has_many = array(
"Fields" => "EditableFormField",
"Submissions" => "SubmittedForm", "Submissions" => "SubmittedForm",
"EmailRecipients" => "UserDefinedForm_EmailRecipient" "EmailRecipients" => "UserDefinedForm_EmailRecipient"
); );
@ -98,14 +96,10 @@ class UserDefinedForm extends Page {
$this->beforeUpdateCMSFields(function($fields) use ($self) { $this->beforeUpdateCMSFields(function($fields) use ($self) {
// define tabs // define tabs
$fields->findOrMakeTab('Root.FormContent', _t('UserDefinedForm.FORM', 'Form'));
$fields->findOrMakeTab('Root.FormOptions', _t('UserDefinedForm.CONFIGURATION', 'Configuration')); $fields->findOrMakeTab('Root.FormOptions', _t('UserDefinedForm.CONFIGURATION', 'Configuration'));
$fields->findOrMakeTab('Root.Recipients', _t('UserDefinedForm.RECIPIENTS', 'Recipients')); $fields->findOrMakeTab('Root.Recipients', _t('UserDefinedForm.RECIPIENTS', 'Recipients'));
$fields->findOrMakeTab('Root.Submissions', _t('UserDefinedForm.SUBMISSIONS', 'Submissions')); $fields->findOrMakeTab('Root.Submissions', _t('UserDefinedForm.SUBMISSIONS', 'Submissions'));
// field editor
$fields->addFieldToTab('Root.FormContent', new FieldEditor('Fields', 'Fields', '', $self ));
// text to show on complete // text to show on complete
$onCompleteFieldSet = new CompositeField( $onCompleteFieldSet = new CompositeField(
$label = new LabelField('OnCompleteMessageLabel',_t('UserDefinedForm.ONCOMPLETELABEL', 'Show on completion')), $label = new LabelField('OnCompleteMessageLabel',_t('UserDefinedForm.ONCOMPLETELABEL', 'Show on completion')),
@ -209,125 +203,6 @@ SQL;
return $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} * Allow overriding the EmailRecipients on a {@link DataExtension}
@ -350,67 +225,6 @@ SQL;
return $recipients; 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 * Custom options for the form. You can extend the built in options by
* using {@link updateFormOptions()} * using {@link updateFormOptions()}
@ -436,36 +250,6 @@ SQL;
return $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;
}
/** /**
* Get the HTML id of the error container displayed above the form. * Get the HTML id of the error container displayed above the form.
@ -552,141 +336,23 @@ class UserDefinedForm_Controller extends Page_Controller {
* Get the form for the page. Form can be modified by calling {@link updateForm()} * Get the form for the page. Form can be modified by calling {@link updateForm()}
* on a UserDefinedForm extension. * on a UserDefinedForm extension.
* *
* @return Form|false * @return Forms
*/ */
public function Form() { public function Form() {
$fields = $this->getFormFields(); $form = UserForm::create($this);
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(); $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);
if($this->DisableCsrfSecurityToken) {
$form->disableSecurityToken();
}
$this->generateValidationJavascript($form); $this->generateValidationJavascript($form);
return $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();
$editableFields = $this->Fields();
if($editableFields) foreach($editableFields 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')) {
// Since this field expects raw html, safely escape the user data prior
$field->setRightTitle(Convert::raw2xml($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($extraClass = $editableField->getSetting('ExtraClass')) {
$field->addExtraClass(Convert::raw2att($extraClass));
}
// set the values passed by the url to the field
$request = $this->getRequest();
if($value = $request->getVar($field->getName())) {
$field->setValue($value);
}
$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.
*
* @return RequiredFields
*/
public function getRequiredFields() {
// Generate required field validator
$requiredNames = $this
->Fields()
->filter('Required', true)
->column('Name');
$required = new RequiredFields($requiredNames);
$this->extend('updateRequiredFields', $required);
return $required;
}
/** /**
* Build jQuery validation script and require as a custom script * Build jQuery validation script and require as a custom script
* *
* @param Form $form * @param UserForm $form
*/ */
public function generateValidationJavascript($form) { public function generateValidationJavascript(UserForm $form) {
// set the custom script for this form // set the custom script for this form
Requirements::customScript( Requirements::customScript(
$this $this
@ -711,139 +377,125 @@ class UserDefinedForm_Controller extends Page_Controller {
if($this->Fields()) { if($this->Fields()) {
foreach($this->Fields() as $field) { foreach($this->Fields() as $field) {
$fieldId = $field->Name; $fieldId = $field->Name;
if($field->ClassName == 'EditableFormHeading') { if($field->ClassName == 'EditableFormHeading') {
$fieldId = 'Form_Form_'.$field->Name; $fieldId = 'UserForm_Form_' . $field->Name;
} }
// Is this Field Show by Default // Is this Field Show by Default
if(!$field->getShowOnLoad()) { if(!$field->getShowOnLoad()) {
$default .= "$(\"#" . $fieldId . "\").hide();\n"; $default .= "$(\"#" . $fieldId . "\").hide();\n";
} }
// Check for field dependencies / default // Check for field dependencies / default
if($field->Dependencies()) { foreach($field->CustomRules() as $rule) {
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'))) { // Get the field which is effected
$action = "click"; $formFieldWatch = EditableFormField::get()->byId($rule->ConditionFieldID);
$checkboxField = true;
}
else if ($formFieldWatch->ClassName == "EditableRadioField") {
$radioField = true;
}
// Escape the values. if($formFieldWatch->RecordClassName == 'EditableDropdown') {
$dependency['Value'] = str_replace('"', '\"', $dependency['Value']); // watch out for multiselect options - radios and check boxes
$fieldToWatch = "$(\"select[name='" . $formFieldWatch->Name . "']\")";
// and what should we evaluate $fieldToWatchOnLoad = $fieldToWatch;
switch($dependency['ConditionOption']) { } else if($formFieldWatch->RecordClassName == 'EditableCheckboxGroupField') {
case 'IsNotBlank': // watch out for checkboxs as the inputs don't have values but are 'checked
$expression = ($checkboxField || $radioField) ? '$(this).prop("checked")' :'$(this).val() != ""'; $fieldToWatch = "$(\"input[name='" . $formFieldWatch->Name . "[" . $rule->FieldValue . "]']\")";
$fieldToWatchOnLoad = $fieldToWatch;
break; } else if($formFieldWatch->RecordClassName == 'EditableRadioField') {
case 'IsBlank': $fieldToWatch = "$(\"input[name='" . $formFieldWatch->Name . "']\")";
$expression = ($checkboxField || $radioField) ? '!($(this).prop("checked"))' : '$(this).val() == ""'; // We only want to trigger on load once for the radio group - hence we focus on the first option only.
$fieldToWatchOnLoad = "$(\"input[name='" . $formFieldWatch->Name . "']:first\")";
break; } else {
case 'HasValue': $fieldToWatch = "$(\"input[name='" . $formFieldWatch->Name . "']\")";
if ($checkboxField) { $fieldToWatchOnLoad = $fieldToWatch;
$expression = '$(this).prop("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).prop("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,
'action' => $action
);
$watchLoad[$fieldToWatchOnLoad] = true;
}
} }
// show or hide?
$view = ($rule->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;
}
// and what should we evaluate
switch($rule->ConditionOption) {
case 'IsNotBlank':
$expression = ($checkboxField || $radioField) ? '$(this).prop("checked")' :'$(this).val() != ""';
break;
case 'IsBlank':
$expression = ($checkboxField || $radioField) ? '!($(this).prop("checked"))' : '$(this).val() == ""';
break;
case 'HasValue':
if ($checkboxField) {
$expression = '$(this).prop("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()=="'. $rule->FieldValue .'"';
} else {
$expression = '$(this).val() == "'. $rule->FieldValue .'"';
}
break;
case 'ValueLessThan':
$expression = '$(this).val() < parseFloat("'. $rule->FieldValue .'")';
break;
case 'ValueLessThanEqual':
$expression = '$(this).val() <= parseFloat("'. $rule->FieldValue .'")';
break;
case 'ValueGreaterThan':
$expression = '$(this).val() > parseFloat("'. $rule->FieldValue .'")';
break;
case 'ValueGreaterThanEqual':
$expression = '$(this).val() >= parseFloat("'. $rule->FieldValue .'")';
break;
default: // ==HasNotValue
if ($checkboxField) {
$expression = '!$(this).prop("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()!="'. $rule->FieldValue .'"';
} else {
$expression = '$(this).val() != "'. $rule->FieldValue .'"';
}
break;
}
if(!isset($watch[$fieldToWatch])) {
$watch[$fieldToWatch] = array();
}
$watch[$fieldToWatch][] = array(
'expression' => $expression,
'field_id' => $fieldId,
'view' => $view,
'opposite' => $opposite,
'action' => $action
);
$watchLoad[$fieldToWatchOnLoad] = true;
} }
} }
} }
@ -902,27 +554,7 @@ JS
, 'UserFormsConditional'); , '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 * Process the form that is submitted through the site
* *
@ -934,7 +566,7 @@ JS
public function process($data, $form) { public function process($data, $form) {
Session::set("FormInfo.{$form->FormName()}.data",$data); Session::set("FormInfo.{$form->FormName()}.data",$data);
Session::clear("FormInfo.{$form->FormName()}.errors"); Session::clear("FormInfo.{$form->FormName()}.errors");
foreach($this->Fields() as $field) { foreach($this->Fields() as $field) {
$messages[$field->Name] = $field->getErrorMessage()->HTML(); $messages[$field->Name] = $field->getErrorMessage()->HTML();
$formField = $field->getFormField(); $formField = $field->getFormField();
@ -953,13 +585,13 @@ JS
} }
} }
} }
if(Session::get("FormInfo.{$form->FormName()}.errors")){ if(Session::get("FormInfo.{$form->FormName()}.errors")){
Controller::curr()->redirectBack(); Controller::curr()->redirectBack();
return; return;
} }
$submittedForm = Object::create('SubmittedForm'); $submittedForm = Object::create('SubmittedForm');
$submittedForm->SubmittedByID = ($id = Member::currentUserID()) ? $id : 0; $submittedForm->SubmittedByID = ($id = Member::currentUserID()) ? $id : 0;
$submittedForm->ParentID = $this->ID; $submittedForm->ParentID = $this->ID;
@ -968,22 +600,22 @@ JS
if(!$this->DisableSaveSubmissions) { if(!$this->DisableSaveSubmissions) {
$submittedForm->write(); $submittedForm->write();
} }
$values = array(); $values = array();
$attachments = array(); $attachments = array();
$submittedFields = new ArrayList(); $submittedFields = new ArrayList();
foreach($this->Fields() as $field) { foreach($this->Fields() as $field) {
if(!$field->showInReports()) { if(!$field->showInReports()) {
continue; continue;
} }
$submittedField = $field->getSubmittedFormField(); $submittedField = $field->getSubmittedFormField();
$submittedField->ParentID = $submittedForm->ID; $submittedField->ParentID = $submittedForm->ID;
$submittedField->Name = $field->Name; $submittedField->Name = $field->Name;
$submittedField->Title = $field->getField('Title'); $submittedField->Title = $field->getField('Title');
// save the value from the data // save the value from the data
if($field->hasMethod('getValueFromData')) { if($field->hasMethod('getValueFromData')) {
$submittedField->Value = $field->getValueFromData($data); $submittedField->Value = $field->getValueFromData($data);
@ -1021,23 +653,23 @@ JS
} }
} }
} }
$submittedField->extend('onPopulationFromField', $field); $submittedField->extend('onPopulationFromField', $field);
if(!$this->DisableSaveSubmissions) { if(!$this->DisableSaveSubmissions) {
$submittedField->write(); $submittedField->write();
} }
$submittedFields->push($submittedField); $submittedFields->push($submittedField);
} }
$emailData = array( $emailData = array(
"Sender" => Member::currentUser(), "Sender" => Member::currentUser(),
"Fields" => $submittedFields "Fields" => $submittedFields
); );
$this->extend('updateEmailData', $emailData, $attachments); $this->extend('updateEmailData', $emailData, $attachments);
// 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);
@ -1117,7 +749,7 @@ JS
} }
} }
} }
$submittedForm->extend('updateAfterProcess'); $submittedForm->extend('updateAfterProcess');
Session::clear("FormInfo.{$form->FormName()}.errors"); Session::clear("FormInfo.{$form->FormName()}.errors");
@ -1125,7 +757,6 @@ JS
$referrer = (isset($data['Referrer'])) ? '?referrer=' . urlencode($data['Referrer']) : ""; $referrer = (isset($data['Referrer'])) ? '?referrer=' . urlencode($data['Referrer']) : "";
// set a session variable from the security ID to stop people accessing // set a session variable from the security ID to stop people accessing
// the finished method directly. // the finished method directly.
if(!$this->DisableAuthenicatedFinishAction) { if(!$this->DisableAuthenicatedFinishAction) {
@ -1142,7 +773,7 @@ JS
} }
} }
} }
if(!$this->DisableSaveSubmissions) { if(!$this->DisableSaveSubmissions) {
Session::set('userformssubmission'. $this->ID, $submittedForm->ID); Session::set('userformssubmission'. $this->ID, $submittedForm->ID);
} }

View File

@ -0,0 +1,168 @@
<?php
/**
* Assists with upgrade of userforms to 3.0
*
* @author dmooyman
*/
class UserFormsUpgradeTask extends BuildTask {
protected $title = "UserForms 3.0 Migration Tool";
protected $description = "Upgrade tool for sites upgrading to userforms 3.0";
public function run($request) {
$this->log("Upgrading userforms module");
$this->upgradeRules();
$this->log("Done");
}
protected function log($message) {
if(Director::is_cli()) {
echo "{$message}\n";
} else {
echo "{$message}<br />";
}
}
protected function upgradeRules() {
$this->log("Upgrading formfield rules");
// Upgrade rules from EditableFormField.CustomRules into dataobjects
$fields = DB::fieldList('EditableFormField');
if(!isset($fields['CustomRules'])) {
return;
}
// List of rules that have been created in all stages
$fields = Versioned::get_including_deleted('EditableFormField');
foreach($fields as $field) {
$this->upgradeFieldRules($field);
}
}
/**
* Migrate a versioned field in all stages
*
* @param EditableFormField $field
*/
protected function upgradeFieldRules(EditableFormField $field) {
$this->log("Upgrading formfield ID = ".$field->ID);
// Check versions this field exists on
$filter = sprintf('"EditableFormField"."ID" = \'%d\'', $field->ID);
$stageField = Versioned::get_one_by_stage('EditableFormField', 'Stage', $filter);
$liveField = Versioned::get_one_by_stage('EditableFormField', 'Live', $filter);
if($stageField) {
$this->upgradeFieldRulesInStage($stageField, 'Stage');
}
if($liveField) {
$this->upgradeFieldRulesInStage($liveField, 'Live');
}
}
/**
* Migrate a versioned field in a single stage
*
* @param EditableFormField $field
* @param stage $stage
*/
protected function upgradeFieldRulesInStage(EditableFormField $field, $stage) {
Versioned::reading_stage($stage);
// Skip rules with empty data
$rulesData = $this->getRuleData($field->ID);
if(empty($rulesData)) {
return;
}
// Skip migrated records
if($field->CustomRules()->count()) {
return;
}
// Check value of this condition
foreach($rulesData as $ruleDataItem) {
if(empty($ruleDataItem['ConditionOption']) || empty($ruleDataItem['Display'])) {
continue;
}
// Get data for this rule
$conditionOption = $ruleDataItem['ConditionOption'];
$display = $ruleDataItem['Display'];
$conditionFieldName = empty($ruleDataItem['ConditionField']) ? null : $ruleDataItem['ConditionField'];
$value = isset($ruleDataItem['Value'])
? $ruleDataItem['Value']
: null;
// Create rule
$rule = $this->findOrCreateRule($field, $stage, $conditionOption, $display, $conditionFieldName, $value);
$this->log("Upgrading rule ID = " . $rule->ID);
}
}
/**
* Create or find an existing field with the matched specification
*
* @param EditableFormField $field
* @param string $stage
* @param string $conditionOption
* @param string $display
* @param string $conditionFieldName
* @param string $value
* @return EditableCustomRule
*/
protected function findOrCreateRule(EditableFormField $field, $stage, $conditionOption, $display, $conditionFieldName, $value) {
// Get id of field
$conditionField = $conditionFieldName
? EditableFormField::get()->filter('Name', $conditionFieldName)->first()
: null;
// If live, search stage record for matching one
if($stage === 'Live') {
$list = Versioned::get_by_stage('EditableCustomRule', 'Stage')
->filter(array(
'ParentID' => $field->ID,
'ConditionFieldID' => $conditionField ? $conditionField->ID : 0,
'Display' => $display,
'ConditionOption' => $conditionOption
));
if($value) {
$list = $list->filter('FieldValue', $value);
} else {
$list = $list->where('"FieldValue" IS NULL OR "FieldValue" = \'\'');
}
$rule = $list->first();
if($rule) {
$rule->write();
$rule->publish("Stage", "Live");
return $rule;
}
}
// If none found, or in stage, create new record
$rule = new EditableCustomRule();
$rule->ParentID = $field->ID;
$rule->ConditionFieldID = $conditionField ? $conditionField->ID : 0;
$rule->Display = $display;
$rule->ConditionOption = $conditionOption;
$rule->FieldValue = $value;
$rule->write();
return $rule;
}
/**
* Get deserialised rule data for a field
*
* @param type $id
*/
protected function getRuleData($id) {
$rules = DB::query(sprintf(
'SELECT "CustomRules" FROM "EditableFormField" WHERE "ID" = %d',
$id
))->value();
return $rules ? unserialize($rules) : array();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -53,8 +53,8 @@ puts the form at the end of all the content.
## Adding fields ## Adding fields
To add a field to the form, click on the "Form" tab under the "Content" tab in the To add a field to the form, click on the "Form" tab under the "Content" tab in the
Editing Pane. Select the type of field you want to add from the drop-down menu and Editing Pane. Click the "Add" button then select the type of field you want from the dropdown.
press the "Add" button. You can label any field by typing in the appropriate label field in the backend. Save or publish the form to start editing your new field's properties.
![Adding fields](_images/add-fields.png) ![Adding fields](_images/add-fields.png)

View File

@ -1,134 +0,0 @@
<!-- JS Relys on EditableFormField as a class - and the 3 ids in this order - do not change -->
<li class="$ClassName EditableFormField" id="$Name.ATT EditableItem_$Pos $Name">
<div class="fieldInfo">
<% if canEdit %>
<img class="fieldHandler" src="$ModulePath(framework)/images/drag.gif" alt="<% _t('EditableFormField.DRAG', 'Drag to rearrange order of fields') %>" />
<% else %>
<img class="fieldHandler" src="$ModulePath(framework)/images/drag_readonly.gif" alt="<% _t('EditableFormField.LOCKED', 'These fields cannot be modified') %>" />
<% end_if %>
<img class="icon" src="$Icon" alt="$ClassName" title="$singular_name" />
$TitleField
</div>
<div class="fieldActions">
<% if showExtraOptions %>
<a class="moreOptions" href="#" title="<% _t('EditableFormField.SHOWOPTIONS', 'Show Options') %>"><% _t('EditableFormField.SHOWOPTIONS','Show Options') %></a>
<% end_if %>
<% if canDelete %>
<a class="delete" href="#" title="<% _t('EditableFormField.DELETE', 'Delete') %>"><% _t('EditableFormField.DELETE', 'Delete') %></a>
<% end_if %>
</div>
<% if showExtraOptions %>
<div class="extraOptions hidden" id="$Name.ATT-extraOptions">
<% if HasAddableOptions %>
<fieldset class="fieldOptionsGroup">
<legend><% _t('EditableFormField.OPTIONS', 'Options') %></legend>
<ul class="editableOptions" id="$FieldName.ATT-list">
<% if canEdit %>
<% loop Options %>
$EditSegment
<% end_loop %>
<% if HasAddableOptions %>
<li class="{$ClassName}Option">
<a href="#" rel="$ID" class="addableOption" title="<% _t('EditableFormField.ADD', 'Add option to field') %>">
<% _t('EditableFormField.ADDLabel', 'Add option') %>
</a>
</li>
<% end_if %>
<% else %>
<% loop Options %>
$ReadonlyOption
<% end_loop %>
<% end_if %>
</ul>
</fieldset>
<% end_if %>
<% if FieldConfiguration %>
<fieldset class="fieldOptionsGroup">
<legend><% _t('EditableFormField.FIELDCONFIGURATION', 'Field Configuration') %></legend>
<% loop FieldConfiguration %>
$FieldHolder
<% end_loop %>
</fieldset>
<% end_if %>
<% if FieldValidationOptions %>
<fieldset class="fieldOptionsGroup">
<legend><% _t('EditableFormField.VALIDATION', 'Validation') %></legend>
<% loop FieldValidationOptions %>
$FieldHolder
<% end_loop %>
</fieldset>
<% end_if %>
<fieldset class="customRules fieldOptionsGroup">
<legend><% _t('EditableFormField.CUSTOMRULES', 'Custom Rules') %></legend>
<ul id="{$FieldName}-customRules">
<li>
<a href="#" class="addCondition" title="<% _t('EditableFormField.ADD', 'Add') %>"><% _t('EditableFormField.ADDRULE', 'Add Rule') %></a>
</li>
<li class="addCustomRule">
<select name="{$FieldName}[CustomSettings][ShowOnLoad]">
<option value="Show" <% if ShowOnLoad %>selected="selected"<% end_if %>><% _t('EditableFormField.SHOW', 'Show') %></option>
<option value="Hide" <% if ShowOnLoad %><% else %>selected="selected"<% end_if %>><% _t('EditableFormField.HIDE', 'Hide') %></option>
</select>
<label class="left"><% _t('EditableFormField.FIELDONDEFAULT', 'Field On Default') %></label>
</li>
<li class="hidden">
<select class="displayOption customRuleField" name="{$FieldName}[CustomRules][Display]">
<option value="Show"><% _t('EditableFormField.SHOWTHISFIELD', 'Show This Field') %></option>
<option value="Hide"><% _t('EditableFormField.HIDETHISFIELD', 'Hide This Field') %></option>
</select>
<label><% _t('EditableFormField.WHEN', 'When') %></label>
<select class="fieldOption customRuleField" name="{$FieldName}[CustomRules][ConditionField]">
<option></option>
<% loop Parent %>
<% if Fields %>
<% loop Fields %>
<option value="$Name"><% if Title %>$Title<% else %>$Name<% end_if %></option>
<% end_loop %>
<% end_if %>
<% end_loop %>
</select>
<label><% _t('EditableFormField.IS', 'Is') %></label>
<select class="conditionOption customRuleField" name="{$FieldName}[CustomRules][ConditionOption]">
<option value=""></option>
<option value="IsBlank"><% _t('EditableFormField.BLANK', 'Blank') %></option>
<option value="IsNotBlank"><% _t('EditableFormField.NOTBLANK', 'Not Blank') %></option>
<option value="HasValue"><% _t('EditableFormField.VALUE', 'Value') %></option>
<option value="ValueNot"><% _t('EditableFormField.NOTVALUE', 'Not Value') %></option>
<option value="ValueLessThan"><% _t('EditableFormField.LESSTHAN', 'Value Less Than') %></option>
<option value="ValueLessThanEqual"><% _t('EditableFormField.LESSTHANEQUAL', 'Value Less Than Or Equal') %></option>
<option value="ValueGreaterThan"><% _t('EditableFormField.GREATERTHAN', 'Value Greater Than') %></option>
<option value="ValueGreaterThanEqual"><% _t('EditableFormField.GREATERTHANEQUAL', 'Value Greater Than Or Equal') %></option>
</select>
<input type="text" class="ruleValue hidden customRuleField" name="{$FieldName}[CustomRules][Value]" />
<a href="#" class="deleteCondition" title="<% _t('EditableFormField.DELETE', 'Delete') %>"><img src="cms/images/delete.gif" alt="<% _t('EditableFormField.DELETE', 'Delete') %>" /></a>
</li>
<% if CustomRules %>
<% loop CustomRules %>
<li>
<% include CustomRule %>
</li>
<% end_loop %>
<% end_if %>
</ul>
</fieldset>
</div>
<% end_if %>
<!-- Hidden option Fields -->
<input type="hidden" class="typeHidden" name="{$FieldName}[Type]" value="$ClassName" />
<input type="hidden" class="sortHidden" name="{$FieldName}[Sort]" value="$Sort" />
</li>

View File

@ -1,28 +0,0 @@
<select class="displayOption customRuleField" name="{$FieldName}[CustomRules][$Pos][Display]">
<option value="Show" <% if Display = Show %>selected="selected"<% end_if %>><% _t('CustomRule.SHOWTHISFIELD', 'Show This Field') %></option>
<option value="Hide" <% if Display = Hide %>selected="selected"<% end_if %>><% _t('CustomRule.HIDETHISFIELD', 'Hide This Field') %></option>
</select>
<label><% _t('CustomRule.WHEN', 'When') %></label>
<select class="fieldOption customRuleField" name="{$FieldName}[CustomRules][$Pos][ConditionField]">
<option value="" selected="selected"></option>
<% loop Fields %>
<option value="$Name" <% if isSelected %>selected="selected"<% end_if %>>$Title</option>
<% end_loop %>
</select>
<label><% _t('CustomRule.IS', 'Is') %></label>
<select class="conditionOption customRuleField" name="{$FieldName}[CustomRules][$Pos][ConditionOption]">
<option value="IsBlank" <% if ConditionOption = IsBlank %>selected="selected"<% end_if %>><% _t('CustomRule.BLANK', 'Blank') %></option>
<option value="IsNotBlank" <% if ConditionOption = IsNotBlank %>selected="selected"<% end_if %>><% _t('CustomRule.NOTBLANK', 'Not Blank') %></option>
<option value="HasValue" <% if ConditionOption = HasValue %>selected="selected"<% end_if %>><% _t('CustomRule.VALUE', 'Value') %></option>
<option value="ValueNot" <% if ConditionOption = ValueNot %>selected="selected"<% end_if %>><% _t('CustomRule.NOTVALUE', 'Not Value') %></option>
<option value="ValueLessThan" <% if ConditionOption = ValueLessThan %>selected="selected"<% end_if %>><% _t('CustomRule.LESSTHAN', 'Value Less Than') %></option>
<option value="ValueLessThanEqual" <% if ConditionOption = ValueLessThanEqual %>selected="selected"<% end_if %>><% _t('CustomRule.LESSTHANEQUAL', 'Value Less Than Or Equal') %></option>
<option value="ValueGreaterThan" <% if ConditionOption = ValueGreaterThan %>selected="selected"<% end_if %>><% _t('CustomRule.GREATERTHAN', 'Value Greater Than') %></option>
<option value="ValueGreaterThanEqual" <% if ConditionOption = ValueGreaterThanEqual %>selected="selected"<% end_if %>><% _t('CustomRule.GREATERTHANEQUAL', 'Value Greater Than Or Equal') %></option>
</select>
<input type="text" class="ruleValue <% if ConditionOption %><% if ConditionOption = IsBlank %>hidden<% else_if ConditionOption = IsNotBlank %>hidden<% end_if %><% else %>hidden<% end_if %> customRuleField" name="{$FieldName}[CustomRules][$Pos][Value]" value="$Value" />
<a href="#" class="deleteCondition" title="<% _t('CustomRule.DELETE', 'Delete') %>"><img src="cms/images/delete.gif" alt="<% _t('CustomRule.DELETE', 'Delete') %>" /></a>

View File

@ -82,58 +82,6 @@ class EditableFormFieldTest extends FunctionalTest {
$text->setSetting('ShowOnLoad', ''); $text->setSetting('ShowOnLoad', '');
$this->assertTrue($text->getShowOnLoad()); $this->assertTrue($text->getShowOnLoad());
} }
function testPopulateFromPostData() {
$this->logInWithPermission('ADMIN');
$set = new ArrayList();
$field = new EditableFormField();
$data = array(
'Title' => 'Field Title',
'Default' => 'Default Value',
'Sort' => '2',
'Required' => 0,
'CustomErrorMessage' => 'Custom'
);
$field->populateFromPostData($data);
$set->push($field);
$this->assertDOSEquals(array($data), $set);
// test the custom settings
$data['CustomSettings'] = array(
'Foo' => 'Bar'
);
$checkbox = new EditableCheckbox();
$checkbox->write();
$checkbox->populateFromPostData(array('Title' => 'Checkbox'));
$field->populateFromPostData($data);
$this->assertEquals($field->getSettings(), array('Foo' => 'Bar'));
$rule = array(
'Display' => 'Hide',
'ConditionField' => $checkbox->Name,
'ConditionOption' => 'HasValue',
'Value' => 6
);
// test the custom rules
$data['CustomRules'] = array(
'Rule1' => $rule
);
$field->populateFromPostData($data);
$rules = unserialize($field->CustomRules);
$this->assertEquals($rules[0], $rule);
}
function testCustomRules() { function testCustomRules() {
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
@ -142,58 +90,18 @@ class EditableFormFieldTest extends FunctionalTest {
$checkbox = $form->Fields()->find('ClassName', 'EditableCheckbox'); $checkbox = $form->Fields()->find('ClassName', 'EditableCheckbox');
$field = $form->Fields()->find('ClassName', 'EditableTextField'); $field = $form->Fields()->find('ClassName', 'EditableTextField');
$rule = array( $rules = $checkbox->CustomRules();
'Display' => 'Hide',
'ConditionField' => $checkbox->Name,
'ConditionOption' => 'HasValue',
'Value' => 6
);
$data['CustomRules'] = array(
'Rule1' => $rule
);
$field->populateFromPostData($data);
$rules = $field->CustomRules();
// form has 2 fields - a checkbox and a text field // form has 2 fields - a checkbox and a text field
// it has 1 rule - when ticked the checkbox hides the text field // it has 1 rule - when ticked the checkbox hides the text field
$this->assertEquals($rules->Count(), 1); $this->assertEquals($rules->Count(), 1);
// rules are ArrayDatas not dataobjects
// $this->assertDOSEquals(array($rule), $rules);
$checkboxRule = $rules->First(); $checkboxRule = $rules->First();
$checkboxRule->ConditionFieldID = $field->ID;
$this->assertEquals($checkboxRule->Display, 'Hide'); $this->assertEquals($checkboxRule->Display, 'Hide');
$this->assertEquals($checkboxRule->ConditionField, $checkbox->Name);
$this->assertEquals($checkboxRule->ConditionOption, 'HasValue'); $this->assertEquals($checkboxRule->ConditionOption, 'HasValue');
$this->assertEquals($checkboxRule->Value, '6'); $this->assertEquals($checkboxRule->FieldValue, '6');
foreach($checkboxRule->Fields as $condition) {
if($checkbox->Name == $condition->Name) {
$this->assertTrue($condition->isSelected);
}
else {
$this->assertFalse($condition->isSelected);
}
}
$data['CustomRules'] = array(
'Rule2' => array(
'Display' => 'Hide',
'ConditionField' => $checkbox->Name,
'ConditionOption' => 'Blank'
)
);
$field->populateFromPostData($data);
$rules = $field->CustomRules();
// test that saving additional rules deletes the old one
$this->assertEquals($rules->Count(), 1);
} }
function testEditableDropdownField() { function testEditableDropdownField() {
@ -262,50 +170,6 @@ class EditableFormFieldTest extends FunctionalTest {
$this->assertEquals($orginal->Sort, $option->Sort); $this->assertEquals($orginal->Sort, $option->Sort);
} }
} }
function testMultipleOptionPopulateFromPostData() {
$dropdown = $this->objFromFixture('EditableDropdown','basic-dropdown');
$data = array();
foreach($dropdown->Options() as $option) {
$orginal[$option->ID] = array(
'Title' => $option->Title,
'Sort' => $option->Sort
);
$data[$option->ID] = array(
'Title' => 'New - '. $option->Title,
'Sort' => $option->Sort + 1
);
}
$dropdown->populateFromPostData($data);
$count = $dropdown->Options()->Count();
foreach($dropdown->Options() as $option) {
$this->assertEquals($option->Title, 'New - '. $orginal[$option->ID]['Title']);
$this->assertEquals($option->Sort, $orginal[$option->ID]['Sort'] + 1);
}
// remove the first one. can't assume by ID
foreach($data as $key => $value) {
unset($data[$key]);
break;
}
$dropdown->populateFromPostData($data);
$this->assertEquals($dropdown->Options()->Count(), $count-1);
}
function testEditableTextFieldConfiguration() {
// $text = $this->objFromFixture('EditableTextField', 'basic-text');
// $configuration = $text->getFieldConfiguration();
}
function testExtendedEditableFormField() { function testExtendedEditableFormField() {
/** @var ExtendedEditableFormField $field */ /** @var ExtendedEditableFormField $field */
@ -316,11 +180,6 @@ class EditableFormFieldTest extends FunctionalTest {
$this->assertTrue(array_key_exists('TestExtraField', $dbFields)); $this->assertTrue(array_key_exists('TestExtraField', $dbFields));
$this->assertTrue(array_key_exists('TestValidationField', $dbFields)); $this->assertTrue(array_key_exists('TestValidationField', $dbFields));
// Check Field Configuration
$fieldConfiguration = $field->getFieldConfiguration();
$extraField = $fieldConfiguration->dataFieldByName($field->getSettingName('TestExtraField'));
$this->assertNotNull($extraField);
// Check Validation Fields // Check Validation Fields
$fieldValidation = $field->getFieldValidationOptions(); $fieldValidation = $field->getFieldValidationOptions();
$validationField = $fieldValidation->dataFieldByName($field->getSettingName('TestValidationField')); $validationField = $fieldValidation->dataFieldByName($field->getSettingName('TestValidationField'));

View File

@ -1,8 +1,14 @@
EditableCustomRule:
rule-1:
Display: Hide
ConditionOption: HasValue
FieldValue: 6
EditableOption: EditableOption:
option-1: option-1:
Name: Option1 Name: Option1
Title: Option 1 Title: Option 1
option-2: option-2:
Name: Option2 Name: Option2
Title: Option 2 Title: Option 2
@ -30,79 +36,82 @@ EditableOption:
option-6: option-6:
Name: Option6 Name: Option6
Title: Option 6 Title: Option 6
UserDefinedForm_EmailRecipient: UserDefinedForm_EmailRecipient:
recipient-1: recipient-1:
EmailAddress: test@example.com EmailAddress: test@example.com
EmailSubject: Email Subject EmailSubject: Email Subject
EmailFrom: no-reply@example.com EmailFrom: no-reply@example.com
no-html: no-html:
EmailAddress: nohtml@example.com EmailAddress: nohtml@example.com
EmailSubject: Email Subject EmailSubject: Email Subject
EmailFrom: no-reply@example.com EmailFrom: no-reply@example.com
SendPlain: true SendPlain: true
no-data: no-data:
EmailAddress: nodata@example.com EmailAddress: nodata@example.com
EmailSubject: Email Subject EmailSubject: Email Subject
EmailFrom: no-reply@example.com EmailFrom: no-reply@example.com
HideFormData: true HideFormData: true
EditableTextField: EditableTextField:
basic-text: basic-text:
Name: basic-text-name Name: basic-text-name
Title: Basic Text Field Title: Basic Text Field
basic-text-2: basic-text-2:
Name: basic-text-name Name: basic-text-name
Title: Basic Text Field Title: Basic Text Field
required-text: required-text:
Name: required-text-field Name: required-text-field
Title: Required Text Field Title: Required Text Field
CustomErrorMessage: Custom Error Message CustomErrorMessage: Custom Error Message
Required: true Required: true
EditableDropdown: EditableDropdown:
basic-dropdown: basic-dropdown:
Name: basic-dropdown Name: basic-dropdown
Title: Basic Dropdown Field Title: Basic Dropdown Field
Options: =>EditableOption.option-1, =>EditableOption.option-2 Options: =>EditableOption.option-1, =>EditableOption.option-2
department-dropdown: department-dropdown:
Name: department Name: department
Title: Department Title: Department
Options: =>EditableOption.department-1, =>EditableOption.department-2 Options: =>EditableOption.department-1, =>EditableOption.department-2
EditableCheckbox: EditableCheckbox:
checkbox-1: checkbox-1:
Name: checkbox-1 Name: checkbox-1
Title: Checkbox 1 Title: Checkbox 1
checkbox-2: checkbox-2:
Name: checkbox-1 Name: checkbox-1
Title: Checkbox 1 Title: Checkbox 1
checkbox-with-rule:
Name: checkbox-with-rule
Title: Checkbox with rule
CustomRules: =>EditableCustomRule.rule-1
EditableCheckboxGroupField: EditableCheckboxGroupField:
checkbox-group: checkbox-group:
Name: check-box-group Name: check-box-group
Title: Check box group Title: Check box group
Options: =>EditableOption.option-3, =>EditableOption.option-4 Options: =>EditableOption.option-3, =>EditableOption.option-4
EditableEmailField: EditableEmailField:
email-field: email-field:
Name: email-field Name: email-field
Title: Email Title: Email
EditableRadioField: EditableRadioField:
radio-field: radio-field:
Name: radio-option Name: radio-option
Title: Radio Option Title: Radio Option
Options: =>EditableOption.option-5, =>EditableOption.option-6 Options: =>EditableOption.option-5, =>EditableOption.option-6
EditableFileField: EditableFileField:
file-field: file-field:
Name: file-uploader Name: file-uploader
@ -120,20 +129,18 @@ UserDefinedForm:
Title: User Defined Form Title: User Defined Form
Fields: =>EditableTextField.basic-text Fields: =>EditableTextField.basic-text
EmailRecipients: =>UserDefinedForm_EmailRecipient.recipient-1, =>UserDefinedForm_EmailRecipient.no-html, =>UserDefinedForm_EmailRecipient.no-data EmailRecipients: =>UserDefinedForm_EmailRecipient.recipient-1, =>UserDefinedForm_EmailRecipient.no-html, =>UserDefinedForm_EmailRecipient.no-data
form-with-reset-and-custom-action: form-with-reset-and-custom-action:
Title: Form with Reset Action Title: Form with Reset Action
SubmitButtonText: Custom Button SubmitButtonText: Custom Button
ShowClearButton: true ShowClearButton: true
validation-form: validation-form:
Title: Validation Form Title: Validation Form
Fields: =>EditableTextField.required-text Fields: =>EditableTextField.required-text
custom-rules-form: custom-rules-form:
Title: Custom Rules Form Title: Custom Rules Form
Fields: =>EditableCheckbox.checkbox-2, =>EditableTextField.basic-text-2 Fields: =>EditableCheckbox.checkbox-with-rule, =>EditableTextField.basic-text-2
empty-form: empty-form:
Title: Empty Form Title: Empty Form

View File

@ -1,46 +0,0 @@
<?php
/**
* Tests covering the form editor / builder and
* some of the user interface
*
* @package userforms
*/
class FieldEditorTest extends FunctionalTest {
static $fixture_file = 'userforms/tests/UserDefinedFormTest.yml';
protected $editor;
function setUp() {
parent::setUp();
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$controller = new FieldEditorTest_Controller($form);
$fields = $controller->Form()->Fields();
$this->editor = $fields->fieldByName('Fields');
}
function testSaveInto() {
$this->logInWithPermission('ADMIN');
// @todo
}
function testAddField() {
$this->logInWithPermission('ADMIN');
// Debug::show($this->editor->addfield());
}
}
class FieldEditorTest_Controller extends Controller {
public function Form() {
return new Form($this, 'Form', new FieldList(new FieldEditor('Fields')), new FieldList());
}
}

View File

@ -18,7 +18,7 @@ class UserDefinedFormControllerTest extends FunctionalTest {
// load the form // load the form
$this->get($form->URLSegment); $this->get($form->URLSegment);
$response = $this->submitForm('Form_Form', null, array('basic-text-name' => 'Basic Value')); $response = $this->submitForm('UserForm_Form', null, array('basic-text-name' => 'Basic Value'));
// should have a submitted form field now // should have a submitted form field now
$submitted = DataObject::get('SubmittedFormField', "\"Name\" = 'basic-text-name'"); $submitted = DataObject::get('SubmittedFormField', "\"Name\" = 'basic-text-name'");
@ -106,7 +106,7 @@ class UserDefinedFormControllerTest extends FunctionalTest {
$controller = new UserDefinedFormControllerTest_Controller($form); $controller = new UserDefinedFormControllerTest_Controller($form);
$fields = $controller->getFormFields(); $fields = $controller->Form()->getFormFields();
$this->assertEquals($fields->Count(), 1); $this->assertEquals($fields->Count(), 1);
@ -116,7 +116,7 @@ class UserDefinedFormControllerTest extends FunctionalTest {
UserDefinedForm::config()->required_identifier = "*"; UserDefinedForm::config()->required_identifier = "*";
$fields = $controller->getFormFields(); $fields = $controller->Form()->getFormFields();
$this->assertEquals($fields->First()->getCustomValidationMessage()->getValue(), 'Custom Error Message'); $this->assertEquals($fields->First()->getCustomValidationMessage()->getValue(), 'Custom Error Message');
$this->assertEquals($fields->First()->Title(), 'Required Text Field <span class=\'required-identifier\'>*</span>'); $this->assertEquals($fields->First()->Title(), 'Required Text Field <span class=\'required-identifier\'>*</span>');
@ -127,15 +127,15 @@ class UserDefinedFormControllerTest extends FunctionalTest {
$field->write(); $field->write();
$controller = new UserDefinedFormControllerTest_Controller($form); $controller = new UserDefinedFormControllerTest_Controller($form);
$fields = $controller->getFormFields(); $fields = $controller->Form()->getFormFields();
$this->assertEquals($fields->First()->RightTitle(), "Right Title"); $this->assertEquals($fields->First()->RightTitle(), "Right Title");
// test empty form // test empty form
$emptyForm = $this->objFromFixture('UserDefinedForm', 'empty-form'); $emptyForm = $this->objFromFixture('UserDefinedForm', 'empty-form');
$controller = new UserDefinedFormControllerTest_Controller($emptyForm); $controller = new UserDefinedFormControllerTest_Controller($emptyForm);
$this->assertFalse($controller->Form()); $this->assertFalse($controller->Form()->getFormFields()->exists());
} }
function testGetFormActions() { function testGetFormActions() {
@ -143,7 +143,7 @@ class UserDefinedFormControllerTest extends FunctionalTest {
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$controller = new UserDefinedFormControllerTest_Controller($form); $controller = new UserDefinedFormControllerTest_Controller($form);
$actions = $controller->getFormActions(); $actions = $controller->Form()->getFormActions();
// by default will have 1 submit button which links to process // by default will have 1 submit button which links to process
$expected = new FieldList(new FormAction('process', 'Submit')); $expected = new FieldList(new FormAction('process', 'Submit'));
@ -153,23 +153,14 @@ class UserDefinedFormControllerTest extends FunctionalTest {
// the custom popup should have a reset button and a custom text // the custom popup should have a reset button and a custom text
$custom = $this->objFromFixture('UserDefinedForm', 'form-with-reset-and-custom-action'); $custom = $this->objFromFixture('UserDefinedForm', 'form-with-reset-and-custom-action');
$controller = new UserDefinedFormControllerTest_Controller($custom); $controller = new UserDefinedFormControllerTest_Controller($custom);
$actions = $controller->Form()->getFormActions();
$actions = $controller->getFormActions();
$expected = new FieldList(new FormAction('process', 'Custom Button')); $expected = new FieldList(new FormAction('process', 'Custom Button'));
$expected->push(new ResetFormAction("clearForm", "Clear")); $expected->push(new ResetFormAction("clearForm", "Clear"));
$this->assertEquals($actions, $expected); $this->assertEquals($actions, $expected);
} }
function testArrayToJson() {
$array = array('1' => 'one', '2' => 'two');
$string = "{\n1:\"one\", 2:\"two\"\n}\n";
$form = new UserDefinedFormControllerTest_Controller();
$this->assertEquals($form->array2json($array), $string);
}
function testRenderingIntoFormTemplate() { function testRenderingIntoFormTemplate() {
$form = $this->setupFormFrontend(); $form = $this->setupFormFrontend();
@ -210,7 +201,7 @@ class UserDefinedFormControllerTest extends FunctionalTest {
} }
function checkTemplateIsCorrect($parser) { function checkTemplateIsCorrect($parser) {
$this->assertArrayHasKey(0, $parser->getBySelector('form#Form_Form')); $this->assertArrayHasKey(0, $parser->getBySelector('form#UserForm_Form'));
// check for the input // check for the input
$this->assertArrayHasKey(0, $parser->getBySelector('input.text')); $this->assertArrayHasKey(0, $parser->getBySelector('input.text'));