API Frontend form fields for nested composite fields

This commit is contained in:
Damian Mooyman 2015-08-12 16:08:32 +12:00
parent 03692c717f
commit 4adc698e0f
11 changed files with 249 additions and 38 deletions

View File

@ -40,19 +40,14 @@ class UserFormFieldEditorExtension extends DataExtension {
$fieldClasses = $this->getEditableFieldClasses();
$editableColumns->setDisplayFields(array(
'ClassName' => function($record, $column, $grid) use ($fieldClasses) {
if($record instanceof EditableFormStep) {
return new LabelField($column, _t('UserFormFieldEditorExtension.PAGE_BREAK', 'Page Break'));
} elseif($record instanceof EditableFieldGroup) {
return new LabelField($column, _t('UserFormFieldEditorExtension.FIELD_GROUP_START', 'Field Group (start)'));
} else if($record instanceof EditableFieldGroupEnd) {
return new LabelField($column, _t('UserFormFieldEditorExtension.FIELD_GROUP_END', 'Field Group (end)'));
} else {
return DropdownField::create($column, '', $fieldClasses);
if($record instanceof EditableFormField) {
return $record->getInlineClassnameField($column, $fieldClasses);
}
},
'Title' => function($record, $column, $grid) {
return TextField::create($column, ' ')
->setAttribute('placeholder', _t('UserDefinedForm.TITLE', 'Title'));
if($record instanceof EditableFormField) {
return $record->getInlineTitleField($column);
}
}
));

View File

@ -0,0 +1,47 @@
<?php
/**
* Represents a composite field group, which may contain other groups
*/
abstract class UserFormsCompositeField extends CompositeField implements UserFormsFieldContainer {
/**
* Parent field
*
* @var UserFormsFieldContainer
*/
protected $parent = null;
public function getParent() {
return $this->parent;
}
public function setParent(UserFormsFieldContainer $parent) {
$this->parent = $parent;
return $this;
}
public function processNext(EditableFormField $field) {
// When we find a step, bubble up to the top
if($field instanceof EditableFormStep) {
return $this->getParent()->processNext($field);
}
// Skip over fields that don't generate formfields
$formField = $field->getFormField();
if(!$formField) {
return $this;
}
// Save this field
$this->push($formField);
// Nest fields that are containers
if($formField instanceof UserFormsFieldContainer) {
return $formField->setParent($this);
}
// Add any subsequent fields to this
return $this;
}
}

View File

@ -0,0 +1,30 @@
<?php
/**
* Represents a field container which can iteratively process nested fields, converting it into a fieldset
*/
interface UserFormsFieldContainer {
/**
* Process the next field in the list, returning the container to add the next field to.
*
* @param EditableFormField $field
* @return EditableContainerField
*/
public function processNext(EditableFormField $field);
/**
* Set the parent
*
* @param UserFormsFieldContainer $parent
* @return $this
*/
public function setParent(UserFormsFieldContainer $parent);
/**
* Get the parent
*
* @return UserFormsFieldContainer
*/
public function getParent();
}

View File

@ -0,0 +1,32 @@
<?php
/**
* A list of formfields which allows for iterative processing of nested composite fields
*/
class UserFormsFieldList extends FieldList implements UserFormsFieldContainer {
public function processNext(EditableFormField $field) {
$formField = $field->getFormField();
if(!$formField) {
return $this;
}
$this->push($formField);
if($formField instanceof UserFormsFieldContainer) {
return $formField->setParent($this);
}
return $this;
}
public function getParent() {
// Field list does not have a parent
return null;
}
public function setParent(UserFormsFieldContainer $parent) {
return $this;
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* Front end composite field for userforms
*/
class UserFormsGroupField extends UserFormsCompositeField {
public function __construct($children = null) {
parent::__construct($children);
$this->setTag('fieldset');
}
public function getLegend() {
// Legend defaults to title
return parent::getLegend() ?: $this->Title();
}
public function processNext(EditableFormField $field) {
// When ending a group, jump up one level
if($field instanceof EditableFieldGroupEnd) {
return $this->getParent();
}
// Otherwise behave as per normal composite field
return parent::processNext($field);
}
}

View File

@ -0,0 +1,7 @@
<?php
/**
* Represents a page step in a form, which may contain form fields or other groups
*/
class UserFormsStepField extends UserFormsCompositeField {
}

View File

@ -75,31 +75,11 @@ class UserForm extends Form {
* @return FieldList
*/
public function getFormFields() {
$fields = new FieldList();
$emptyStep = null; // Last empty step, which may or may not later have children
$fields = new UserFormsFieldList();
$target = $fields;
foreach ($this->controller->Fields() as $field) {
// When we encounter a step, save it
if ($field instanceof EditableFormStep) {
$emptyStep = $field->getFormField();
continue;
}
// Ensure that the last field is a step
if($emptyStep) {
// When we reach the first non-step field, any empty step will no longer be empty
$fields->push($emptyStep);
$emptyStep = null;
} elseif(! $fields->last()) {
// If no steps have been saved yet, warn
trigger_error('Missing first step in form', E_USER_WARNING);
$fields->push(singleton('EditableFormStep')->getFormField());
}
$fields->last()->push($field->getFormField());
$target = $target->processNext($field);
}
$this->extend('updateFormFields', $fields);
return $fields;
}

View File

@ -12,5 +12,42 @@ class EditableFieldGroup extends EditableFormField {
* @var bool
*/
private static $hidden = true;
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
return $fields;
}
public function getInlineClassnameField($column, $fieldClasses) {
return new LabelField(
$column,
_t('EditableFieldGroup.FIELD_GROUP_START', 'Field Group (start)')
);
}
public function showInReports() {
return false;
}
public function getFormField() {
$field = UserFormsGroupField::create()
->setTitle($this->EscapedTitle ?: false);
$this->doUpdateFormField($field);
return $field;
}
protected function updateFormField($field) {
// set the right title on this field
if($this->RightTitle) {
// Since this field expects raw html, safely escape the user data prior
$field->setRightTitle(Convert::raw2xml($this->RightTitle));
}
// if this field has an extra class
if($field->ExtraClass) {
$field->addExtraClass($field->ExtraClass);
}
}
}

View File

@ -12,5 +12,34 @@ class EditableFieldGroupEnd extends EditableFormField {
* @var bool
*/
private static $hidden = true;
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
return $fields;
}
public function getInlineClassnameField($column, $fieldClasses) {
return new LabelField(
$column,
_t('EditableFieldGroupEnd.FIELD_GROUP_END', 'Field Group (end)')
);
}
public function getInlineTitleField($column) {
return HiddenField::create($column);
}
public function getFormField() {
return null;
}
public function showInReports() {
return false;
}
public function canEdit($member = null) {
return false;
}
}

View File

@ -624,4 +624,27 @@ class EditableFormField extends DataObject {
}
}
}
/**
* Get the formfield to use when editing this inline in gridfield
*
* @param string $column name of column
* @param array $fieldClasses List of allowed classnames if this formfield has a selectable class
* @return FormField
*/
public function getInlineClassnameField($column, $fieldClasses) {
return DropdownField::create($column, false, $fieldClasses);
}
/**
* Get the formfield to use when editing the title inline
*
* @param string $column
* @return FormField
*/
public function getInlineTitleField($column) {
return TextField::create($column, false)
->setAttribute('placeholder', _t('EditableFormField.TITLE', 'Title'))
->setAttribute('data-placeholder', _t('EditableFormField.TITLE', 'Title'));
}
}

View File

@ -32,10 +32,7 @@ class EditableFormStep extends EditableFormField {
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('MergeField');
$fields->removeByName('Default');
$fields->removeByName('Validation');
$fields->removeByName('CustomRules');
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
return $fields;
}
@ -44,7 +41,7 @@ class EditableFormStep extends EditableFormField {
* @return FormField
*/
public function getFormField() {
$field = CompositeField::create()
$field = UserFormsStepField::create()
->setTitle($this->EscapedTitle);
$this->doUpdateFormField($field);
return $field;
@ -63,4 +60,11 @@ class EditableFormStep extends EditableFormField {
public function showInReports() {
return false;
}
public function getInlineClassnameField($column, $fieldClasses) {
return new LabelField(
$column,
_t('EditableFieldGroupEnd.PAGE_BREAK', 'Page Break')
);
}
}