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(); $fieldClasses = $this->getEditableFieldClasses();
$editableColumns->setDisplayFields(array( $editableColumns->setDisplayFields(array(
'ClassName' => function($record, $column, $grid) use ($fieldClasses) { 'ClassName' => function($record, $column, $grid) use ($fieldClasses) {
if($record instanceof EditableFormStep) { if($record instanceof EditableFormField) {
return new LabelField($column, _t('UserFormFieldEditorExtension.PAGE_BREAK', 'Page Break')); return $record->getInlineClassnameField($column, $fieldClasses);
} 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);
} }
}, },
'Title' => function($record, $column, $grid) { 'Title' => function($record, $column, $grid) {
return TextField::create($column, ' ') if($record instanceof EditableFormField) {
->setAttribute('placeholder', _t('UserDefinedForm.TITLE', 'Title')); 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 * @return FieldList
*/ */
public function getFormFields() { public function getFormFields() {
$fields = new FieldList(); $fields = new UserFormsFieldList();
$emptyStep = null; // Last empty step, which may or may not later have children $target = $fields;
foreach ($this->controller->Fields() as $field) { foreach ($this->controller->Fields() as $field) {
// When we encounter a step, save it $target = $target->processNext($field);
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());
} }
$this->extend('updateFormFields', $fields); $this->extend('updateFormFields', $fields);
return $fields; return $fields;
} }

View File

@ -12,5 +12,42 @@ class EditableFieldGroup extends EditableFormField {
* @var bool * @var bool
*/ */
private static $hidden = true; 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 * @var bool
*/ */
private static $hidden = true; 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() { public function getCMSFields() {
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
$fields->removeByName('MergeField'); $fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
$fields->removeByName('Default');
$fields->removeByName('Validation');
$fields->removeByName('CustomRules');
return $fields; return $fields;
} }
@ -44,7 +41,7 @@ class EditableFormStep extends EditableFormField {
* @return FormField * @return FormField
*/ */
public function getFormField() { public function getFormField() {
$field = CompositeField::create() $field = UserFormsStepField::create()
->setTitle($this->EscapedTitle); ->setTitle($this->EscapedTitle);
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
@ -63,4 +60,11 @@ class EditableFormStep extends EditableFormField {
public function showInReports() { public function showInReports() {
return false; return false;
} }
public function getInlineClassnameField($column, $fieldClasses) {
return new LabelField(
$column,
_t('EditableFieldGroupEnd.PAGE_BREAK', 'Page Break')
);
}
} }