API Add field group (unfinished)

This commit is contained in:
Damian Mooyman 2015-08-11 18:21:43 +12:00
parent b1c86f346f
commit a8ee26ec50
9 changed files with 200 additions and 78 deletions

View File

@ -37,12 +37,17 @@ class UserFormFieldEditorExtension extends DataExtension {
$this->createInitialFormStep(true); $this->createInitialFormStep(true);
$editableColumns = new GridFieldEditableColumns(); $editableColumns = new GridFieldEditableColumns();
$fieldClasses = $this->getEditableFieldClasses();
$editableColumns->setDisplayFields(array( $editableColumns->setDisplayFields(array(
'ClassName' => function($record, $column, $grid) { 'ClassName' => function($record, $column, $grid) use ($fieldClasses) {
if($record instanceof EditableFormStep) { if($record instanceof EditableFormStep) {
return new LabelField($column, "Page Break"); 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 { } else {
return DropdownField::create($column, '', $this->getEditableFieldClasses()); return DropdownField::create($column, '', $fieldClasses);
} }
}, },
'Title' => function($record, $column, $grid) { 'Title' => function($record, $column, $grid) {
@ -55,8 +60,15 @@ class UserFormFieldEditorExtension extends DataExtension {
->addComponents( ->addComponents(
$editableColumns, $editableColumns,
new GridFieldButtonRow(), new GridFieldButtonRow(),
$addField = new GridFieldAddNewInlineButton(), GridFieldAddItemInlineButton::create('EditableFormField')
$addStep = new GridFieldAddItemInlineButton('EditableFormStep'), ->setTitle(_t('UserFormFieldEditorExtension.ADD_FIELD', 'Add Field'))
->setButtonClass('ss-ui-action-constructive'),
GridFieldAddItemInlineButton::create('EditableFormStep')
->setTitle(_t('UserFormFieldEditorExtension.ADD_PAGE_BREAK', 'Add Page Break'))
->setExtraClass('uf-gridfield-steprow'),
GridFieldAddItemInlineButton::create(array('EditableFieldGroup', 'EditableFieldGroupEnd'))
->setTitle(_t('UserFormFieldEditorExtension.ADD_FIELD_GROUP', 'Add Field Group'))
->setExtraClass('uf-gridfield-grouprow'),
new GridFieldEditButton(), new GridFieldEditButton(),
new GridFieldDeleteAction(), new GridFieldDeleteAction(),
new GridFieldToolbarHeader(), new GridFieldToolbarHeader(),
@ -64,9 +76,6 @@ class UserFormFieldEditorExtension extends DataExtension {
new GridState_Component(), new GridState_Component(),
new GridFieldDetailForm() new GridFieldDetailForm()
); );
$addField->setTitle('Add Field');
$addStep->setTitle('Add Page Break');
$addStep->setExtraClass('uf-gridfield-steprow');
$fieldEditor = GridField::create( $fieldEditor = GridField::create(
'Fields', 'Fields',
@ -126,21 +135,20 @@ class UserFormFieldEditorExtension extends DataExtension {
$classes = ClassInfo::getValidSubClasses('EditableFormField'); $classes = ClassInfo::getValidSubClasses('EditableFormField');
// Remove classes we don't want to display in the dropdown. // Remove classes we don't want to display in the dropdown.
$classes = array_diff($classes, array(
'EditableFormField',
'EditableMultipleOptionField'
));
$editableFieldClasses = array(); $editableFieldClasses = array();
foreach ($classes as $class) {
foreach ($classes as $key => $className) { if(in_array($class, array('EditableFormField', 'EditableMultipleOptionField'))
$singleton = singleton($className); || Config::inst()->get($class, 'hidden')
) {
continue;
}
$singleton = singleton($class);
if(!$singleton->canCreate()) { if(!$singleton->canCreate()) {
continue; continue;
} }
$editableFieldClasses[$className] = $singleton->i18n_singular_name(); $editableFieldClasses[$class] = $singleton->i18n_singular_name();
} }
return $editableFieldClasses; return $editableFieldClasses;

View File

@ -5,7 +5,7 @@
* *
* Provides an alternative to GridFieldAddNewInlineButton, but allows you to set a classname * Provides an alternative to GridFieldAddNewInlineButton, but allows you to set a classname
*/ */
class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_SaveHandler { class GridFieldAddItemInlineButton extends Object implements GridField_HTMLProvider, GridField_SaveHandler {
/** /**
* Fragment id * Fragment id
@ -14,6 +14,13 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_
*/ */
protected $fragment; protected $fragment;
/**
* Additonal CSS classes for the button
*
* @var string
*/
protected $buttonClass = null;
/** /**
* Button title * Button title
* *
@ -22,11 +29,11 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_
protected $title; protected $title;
/** /**
* Class name * Class names
* *
* @var string * @var array
*/ */
protected $modelClass = null; protected $modelClasses = null;
/** /**
* Extra CSS classes for this row * Extra CSS classes for this row
@ -35,14 +42,13 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_
*/ */
protected $extraClass = null; protected $extraClass = null;
/** /**
* @param string $class Name of class to default to * @param array $classes Class or list of classes to create.
* If you enter more than one class, each click of the "add" button will create one of each
* @param string $fragment The fragment to render the button into * @param string $fragment The fragment to render the button into
*/ */
public function __construct($class, $fragment = 'buttons-before-left') { public function __construct($classes, $fragment = 'buttons-before-left') {
$this->setClass($class); $this->setClasses($classes);
$this->setFragment($fragment); $this->setFragment($fragment);
$this->setTitle(_t('GridFieldExtensions.ADD', 'Add')); $this->setTitle(_t('GridFieldExtensions.ADD', 'Add'));
} }
@ -88,43 +94,54 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_
} }
public function getHTMLFragments($grid) { public function getHTMLFragments($grid) {
if($grid->getList() && !singleton($grid->getModelClass())->canCreate()) { foreach($this->getClasses() as $class) {
return array(); if(!singleton($class)->canCreate()) {
return array();
}
} }
$fragment = $this->getFragment();
if(!$editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns')) { if(!$editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns')) {
throw new Exception('Inline adding requires the editable columns component'); throw new Exception('Inline adding requires the editable columns component');
} }
Requirements::javascript(THIRDPARTY_DIR . '/javascript-templates/tmpl.js'); Requirements::javascript(THIRDPARTY_DIR . '/javascript-templates/tmpl.js');
GridFieldExtensions::include_requirements(); GridFieldExtensions::include_requirements();
Requirements::javascript(USERFORMS_DIR . '/javascript/GridFieldAddItemInlineButton.js'); Requirements::javascript(USERFORMS_DIR . '/javascript/GridFieldExtensions.js');
// Build button content
$data = new ArrayData(array( $data = new ArrayData(array(
'Title' => $this->getTitle(), 'Title' => $this->getTitle(),
'TemplateName' => $this->getRowTemplateName() 'ButtonClass' => $this->getButtonClass(),
'TemplateNames' => json_encode($this->getRowTemplateNames())
)); ));
// Build template body
$templates = '';
foreach($this->getClasses() as $class) {
$templates .= $this->getItemRowTemplateFor($grid, $editable, $class);
}
return array( return array(
$fragment => $data->renderWith(__CLASS__), $this->getFragment() => $data->renderWith(__CLASS__),
'after' => $this->getItemRowTemplate($grid, $editable) 'after' => $templates
); );
} }
/** /**
* Because getRowTemplate is private * Get the template for a given class
* *
* @param GridField $grid * @param GridField $grid
* @param GridFieldEditableColumns $editable * @param GridFieldEditableColumns $editable
* @param string $class Name of class
* @return type * @return type
*/ */
protected function getItemRowTemplate(GridField $grid, GridFieldEditableColumns $editable) { protected function getItemRowTemplateFor(GridField $grid, GridFieldEditableColumns $editable, $class) {
$columns = new ArrayList(); $columns = new ArrayList();
$handled = array_keys($editable->getDisplayFields($grid)); $handled = array_keys($editable->getDisplayFields($grid));
$record = Object::create($this->getClass()); $record = Object::create($class);
$fields = $editable->getFields($grid, $record); $fields = $editable->getFields($grid, $record);
@ -134,7 +151,7 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_
$field = $fields->dataFieldByName($column); $field = $fields->dataFieldByName($column);
if($field) { if($field) {
$field->setName(sprintf( $field->setName(sprintf(
'%s[%s][%s][{%%=o.num%%}][%s]', $grid->getName(), __CLASS__, $this->getClass(), $field->getName() '%s[%s][{%%=o.num%%}][%s][%s]', $grid->getName(), __CLASS__, $class, $field->getName()
)); ));
} else { } else {
$field = $fields->fieldByName($column); $field = $fields->fieldByName($column);
@ -160,30 +177,50 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_
$data = new ArrayData(array( $data = new ArrayData(array(
'Columns' => $columns, 'Columns' => $columns,
'ExtraClass' => $this->getExtraClass(), 'ExtraClass' => $this->getExtraClass(),
'RecordClass' => $this->getClass(), 'RecordClass' => $class,
'TemplateName' => $this->getRowTemplateName() 'TemplateName' => $this->getRowTemplateNameFor($class)
)); ));
return $data->renderWith(__CLASS__ . '_Row'); return $data->renderWith('GridFieldAddItemTemplate');
} }
public function handleSave(GridField $grid, DataObjectInterface $record) { public function handleSave(GridField $grid, DataObjectInterface $record) {
$list = $grid->getList(); // Check that this submission relates to this component
$value = $grid->Value(); $value = $grid->Value();
$class = $this->getClass(); if(empty($value[__CLASS__]) || !is_array($value[__CLASS__])) {
if(!isset($value[__CLASS__][$class]) || !is_array($value[__CLASS__][$class])) {
return; return;
} }
// The way that this component works is that only the first component added to a form will
// be responsible for saving all records for each submission, in order to ensure creation
// and sort order is maintained
$addInlineComponents = $grid->getConfig()->getComponentsByType(__CLASS__);
if($this !== $addInlineComponents->first()) {
return;
}
// Get allowed classes
$classes = array();
foreach($addInlineComponents as $component) {
$classes = array_merge($classes, $component->getClasses());
}
$classes = array_filter($classes, function($class) {
return singleton($class)->canCreate();
});
// Get form
$editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns'); $editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns');
$form = $editable->getForm($grid, $record); $form = $editable->getForm($grid, $record);
if(!singleton($class)->canCreate()) {
return;
}
// Process records matching the specified class // Process records matching the specified class
foreach($value[__CLASS__][$class] as $fields) { $list = $grid->getList();
foreach($value[__CLASS__] as $row) {
$fields = reset($row);
$class = key($row);
if(!in_array($class, $classes)) {
continue;
}
$item = $class::create(); $item = $class::create();
$extra = array(); $extra = array();
@ -200,21 +237,24 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_
} }
/** /**
* Get the class of the object to create * Get the classes of the objects to create
* *
* @return string * @return array
*/ */
public function getClass() { public function getClasses() {
return $this->modelClass; return $this->modelClasses;
} }
/** /**
* Specify the class to create * Specify the classes to create
* *
* @param string $class * @param array $classes
*/ */
public function setClass($class) { public function setClasses($classes) {
$this->modelClass = $class; if(!is_array($classes)) {
$classes = array($classes);
}
$this->modelClasses = $classes;
} }
/** /**
@ -230,17 +270,52 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_
* Sets extra CSS classes for this row * Sets extra CSS classes for this row
* *
* @param string $extraClass * @param string $extraClass
* @return $this
*/ */
public function setExtraClass($extraClass) { public function setExtraClass($extraClass) {
$this->extraClass = $extraClass; $this->extraClass = $extraClass;
return $this;
} }
/** /**
* Get name of item template * Get extra button class
* *
* @return string * @return string
*/ */
public function getRowTemplateName() { public function getButtonClass() {
return 'ss-gridfield-add-inline-template-' . $this->getClass(); return $this->buttonClass;
}
/**
* Sets extra CSS classes for this button
*
* @param string $buttonClass
* @return $this
*/
public function setButtonClass($buttonClass) {
$this->buttonClass = $buttonClass;
return $this;
}
/**
* Get names of all item templates
*
* @return array
*/
public function getRowTemplateNames() {
$self = $this;
return array_map(function($class) use ($self) {
return $this->getRowTemplateNameFor($class);
}, $this->getClasses());
}
/**
* Get the template name for a single class
*
* @param string $class
* @return string
*/
public function getRowTemplateNameFor($class) {
return "ss-gridfield-add-inline-template-{$class}";
} }
} }

View File

@ -0,0 +1,16 @@
<?php
/**
* Specifies that this ends a group of fields
*/
class EditableFieldGroup extends EditableFormField {
/**
* Disable selection of group class
*
* @config
* @var bool
*/
private static $hidden = true;
}

View File

@ -0,0 +1,16 @@
<?php
/**
* Specifies that this ends a group of fields
*/
class EditableFieldGroupEnd extends EditableFormField {
/**
* Disable selection of group class
*
* @config
* @var bool
*/
private static $hidden = true;
}

View File

@ -8,6 +8,14 @@
*/ */
class EditableFormField extends DataObject { class EditableFormField extends DataObject {
/**
* Set to true to hide from class selector
*
* @config
* @var bool
*/
private static $hidden = false;
/** /**
* Default sort order * Default sort order
* *

View File

@ -6,6 +6,14 @@
*/ */
class EditableFormStep extends EditableFormField { class EditableFormStep extends EditableFormField {
/**
* Disable selection of step class
*
* @config
* @var bool
*/
private static $hidden = true;
/** /**
* @config * @config
* @var string * @var string

View File

@ -18,21 +18,12 @@
$(".ss-gridfield-add-new-item-inline").entwine({ $(".ss-gridfield-add-new-item-inline").entwine({
onclick: function() { onclick: function() {
// Get custom class from button // Create each template
var template = this.data('template'); var gridfield = this.getGridField();
this.getGridField().trigger("addnewiteminline", template); $.each(this.data('template-names'), function(index, template) {
return false; console.log(template);
} gridfield.trigger("addnewiteminline", template);
}); });
$(".ss-gridfield-delete-inline").entwine({
onclick: function() {
var msg = ss.i18n._t("GridFieldExtensions.CONFIRMDEL", "Are you sure you want to delete this?");
if(confirm(msg)) {
this.parents("tr").remove();
}
return false; return false;
} }
}); });

View File

@ -1,3 +1,3 @@
<button href="$Link" class="ss-gridfield-add-new-item-inline ss-ui-button" data-icon="add" data-template="$TemplateName.ATT"> <button href="$Link" class="ss-gridfield-add-new-item-inline ss-ui-button $ButtonClass" data-icon="add" data-template-names="$TemplateNames.ATT">
$Title $Title
</button> </button>