Merge pull request #21 from tractorcow/pulls/field-groups

API Formfield validation for nested groups and pages
This commit is contained in:
David Craig 2015-08-13 12:26:20 +12:00
commit e839d7eead
7 changed files with 212 additions and 24 deletions

View File

@ -115,7 +115,7 @@ class UserFormFieldEditorExtension extends DataExtension {
// Add step
$step = EditableFormStep::create();
$step->Title = _t('EditableFormStep.TITLE_FIRST', 'First Step');
$step->Title = _t('EditableFormStep.TITLE_FIRST', 'First Page');
$step->Sort = 1;
$step->write();
$fields->add($step);

View File

@ -0,0 +1,108 @@
<?php
class UserFormValidator extends RequiredFields {
public function php($data) {
if(!parent::php($data)) {
return false;
}
// Skip unsaved records
if(empty($data['ID']) || !is_numeric($data['ID'])) {
return true;
}
$fields = EditableFormField::get()->filter('ParentID', $data['ID'])->sort('"Sort" ASC');
// Current nesting
$stack = array();
foreach($fields as $field) {
if($field instanceof EditableFormStep) {
// Page at top level, or after another page is ok
if(empty($stack) || (count($stack) === 1 && $stack[0] instanceof EditableFormStep)) {
$stack = array($field);
continue;
}
$this->validationError(
'FormFields',
_t(
"UserFormValidator.UNEXPECTED_BREAK",
"Unexpected page break '{name}' inside nested field '{group}'",
array(
'name' => $field->CMSTitle,
'group' => end($stack)->CMSTitle
)
),
'error'
);
return false;
}
// Validate no pages
if(empty($stack)) {
$this->validationError(
'FormFields',
_t(
"UserFormValidator.NO_PAGE",
"Field '{name}' found before any pages",
array(
'name' => $field->CMSTitle
)
),
'error'
);
return false;
}
// Nest field group
if($field instanceof EditableFieldGroup) {
$stack[] = $field;
continue;
}
// Unnest field group
if($field instanceof EditableFieldGroupEnd) {
$top = end($stack);
// Check that the top is a group at all
if(!$top instanceof EditableFieldGroup) {
$this->validationError(
'FormFields',
_t(
"UserFormValidator.UNEXPECTED_GROUP_END",
"'{name}' found without a matching group",
array(
'name' => $field->CMSTitle
)
),
'error'
);
return false;
}
// Check that the top is the right group
if($top->EndID != $field->ID) {
$this->validationError(
'FormFields',
_t(
"UserFormValidator.WRONG_GROUP_END",
"'{name}' found closes the wrong group '{group}'",
array(
'name' => $field->CMSTitle,
'group' => $top->CMSTitle
)
),
'error'
);
return false;
}
// Unnest group
array_pop($stack);
}
}
return true;
}
}

View File

@ -286,6 +286,14 @@ SQL;
DB::alteration_message('Migrated userforms', 'changed');
}
/**
* Validate formfields
*/
public function getCMSValidator() {
return new UserFormValidator();
}
}
/**

View File

@ -5,6 +5,10 @@
*/
class EditableFieldGroup extends EditableFormField {
private static $has_one = array(
'End' => 'EditableFieldGroupEnd'
);
/**
* Disable selection of group class
*
@ -19,13 +23,20 @@ class EditableFieldGroup extends EditableFormField {
return $fields;
}
public function getInlineClassnameField($column, $fieldClasses) {
return new LabelField(
$column,
_t('EditableFieldGroup.FIELD_GROUP_START', 'Field Group (start)')
public function getCMSTitle() {
return _t(
'EditableFieldGroupEnd.FIELD_GROUP_START',
'Start of {group}',
array(
'group' => $this->Title ?: 'group'
)
);
}
public function getInlineClassnameField($column, $fieldClasses) {
return new LabelField($column, $this->CMSTitle);
}
public function showInReports() {
return false;
}
@ -50,4 +61,20 @@ class EditableFieldGroup extends EditableFormField {
}
}
protected function onBeforeDelete() {
parent::onBeforeDelete();
// Ensures EndID is lazy-loaded for onAfterDelete
$this->EndID;
}
protected function onAfterDelete() {
parent::onAfterDelete();
// Delete end
if(($end = $this->End()) && $end->exists()) {
$end->delete();
}
}
}

View File

@ -5,6 +5,10 @@
*/
class EditableFieldGroupEnd extends EditableFormField {
private static $belongs_to = array(
'Group' => 'EditableFieldGroup'
);
/**
* Disable selection of group class
*
@ -13,6 +17,17 @@ class EditableFieldGroupEnd extends EditableFormField {
*/
private static $hidden = true;
public function getCMSTitle() {
$group = $this->Group();
return _t(
'EditableFieldGroupEnd.FIELD_GROUP_END',
'End of {group}',
array(
'group' => ($group && $group->exists() && $group->Title) ? $group->Title : 'group'
)
);
}
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
@ -20,10 +35,7 @@ class EditableFieldGroupEnd extends EditableFormField {
}
public function getInlineClassnameField($column, $fieldClasses) {
return new LabelField(
$column,
_t('EditableFieldGroupEnd.FIELD_GROUP_END', 'Field Group (end)')
);
return new LabelField($column, $this->CMSTitle);
}
public function getInlineTitleField($column) {
@ -38,8 +50,37 @@ class EditableFieldGroupEnd extends EditableFormField {
return false;
}
public function canEdit($member = null) {
return false;
public function onAfterWrite() {
parent::onAfterWrite();
// If this is not attached to a group, find the first group prior to this
// with no end attached
$group = $this->Group();
if(!($group && $group->exists()) && $this->ParentID) {
$group = EditableFieldGroup::get()
->filter(array(
'ParentID' => $this->ParentID,
'Sort:LessThanOrEqual' => $this->Sort
))
->where('"EditableFieldGroup"."EndID" IS NULL OR "EditableFieldGroup"."EndID" = 0')
->sort('"Sort" DESC')
->first();
// When a group is found, attach it to this end
if($group) {
$group->EndID = $this->ID;
$group->write();
}
}
}
protected function onAfterDelete() {
parent::onAfterDelete();
// Delete group
if(($group = $this->Group()) && $group->exists()) {
$group->delete();
}
}
}

View File

@ -450,6 +450,10 @@ class EditableFormField extends DataObject {
return Convert::raw2xml($this->Title);
}
public function getCMSTitle() {
return $this->i18n_singular_name() . ' (' . $this->Title . ')';
}
/**
* @deprecated since version 4.0
*/

View File

@ -6,6 +6,10 @@
*/
class EditableFormStep extends EditableFormField {
private static $singular_name = 'Page Break';
private static $plural_name = 'Page Breaks';
/**
* Disable selection of step class
*
@ -14,18 +18,6 @@ class EditableFormStep extends EditableFormField {
*/
private static $hidden = true;
/**
* @config
* @var string
*/
private static $singular_name = 'Step';
/**
* @config
* @var string
*/
private static $plural_name = 'Steps';
/**
* @return FieldList
*/
@ -64,7 +56,15 @@ class EditableFormStep extends EditableFormField {
public function getInlineClassnameField($column, $fieldClasses) {
return new LabelField(
$column,
_t('EditableFieldGroupEnd.PAGE_BREAK', 'Page Break')
$this->CMSTitle
);
}
public function getCMSTitle() {
$title = $this->i18n_singular_name();
if($this->Title) {
$title .= ' (' . $this->Title . ')';
}
return $title;
}
}