From a8ee26ec505bce128cf79514431fe5e37614c34b Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Tue, 11 Aug 2015 18:21:43 +1200 Subject: [PATCH] API Add field group (unfinished) --- .../UserFormFieldEditorExtension.php | 44 +++-- .../GridFieldAddItemInlineButton.php | 163 +++++++++++++----- .../editableformfields/EditableFieldGroup.php | 16 ++ .../EditableFieldGroupEnd.php | 16 ++ .../editableformfields/EditableFormField.php | 8 + .../editableformfields/EditableFormStep.php | 8 + ...InlineButton.js => GridFieldExtensions.js} | 21 +-- .../gridfield/GridFieldAddItemInlineButton.ss | 2 +- ...ton_Row.ss => GridFieldAddItemTemplate.ss} | 0 9 files changed, 200 insertions(+), 78 deletions(-) create mode 100644 code/model/editableformfields/EditableFieldGroup.php create mode 100644 code/model/editableformfields/EditableFieldGroupEnd.php rename javascript/{GridFieldAddItemInlineButton.js => GridFieldExtensions.js} (61%) rename templates/gridfield/{GridFieldAddItemInlineButton_Row.ss => GridFieldAddItemTemplate.ss} (100%) diff --git a/code/extensions/UserFormFieldEditorExtension.php b/code/extensions/UserFormFieldEditorExtension.php index 7526216..aa6cc15 100644 --- a/code/extensions/UserFormFieldEditorExtension.php +++ b/code/extensions/UserFormFieldEditorExtension.php @@ -37,12 +37,17 @@ class UserFormFieldEditorExtension extends DataExtension { $this->createInitialFormStep(true); $editableColumns = new GridFieldEditableColumns(); + $fieldClasses = $this->getEditableFieldClasses(); $editableColumns->setDisplayFields(array( - 'ClassName' => function($record, $column, $grid) { + 'ClassName' => function($record, $column, $grid) use ($fieldClasses) { 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 { - return DropdownField::create($column, '', $this->getEditableFieldClasses()); + return DropdownField::create($column, '', $fieldClasses); } }, 'Title' => function($record, $column, $grid) { @@ -55,8 +60,15 @@ class UserFormFieldEditorExtension extends DataExtension { ->addComponents( $editableColumns, new GridFieldButtonRow(), - $addField = new GridFieldAddNewInlineButton(), - $addStep = new GridFieldAddItemInlineButton('EditableFormStep'), + GridFieldAddItemInlineButton::create('EditableFormField') + ->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 GridFieldDeleteAction(), new GridFieldToolbarHeader(), @@ -64,9 +76,6 @@ class UserFormFieldEditorExtension extends DataExtension { new GridState_Component(), new GridFieldDetailForm() ); - $addField->setTitle('Add Field'); - $addStep->setTitle('Add Page Break'); - $addStep->setExtraClass('uf-gridfield-steprow'); $fieldEditor = GridField::create( 'Fields', @@ -126,21 +135,20 @@ class UserFormFieldEditorExtension extends DataExtension { $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); - + foreach ($classes as $class) { + if(in_array($class, array('EditableFormField', 'EditableMultipleOptionField')) + || Config::inst()->get($class, 'hidden') + ) { + continue; + } + + $singleton = singleton($class); if(!$singleton->canCreate()) { continue; } - $editableFieldClasses[$className] = $singleton->i18n_singular_name(); + $editableFieldClasses[$class] = $singleton->i18n_singular_name(); } return $editableFieldClasses; diff --git a/code/forms/gridfield/GridFieldAddItemInlineButton.php b/code/forms/gridfield/GridFieldAddItemInlineButton.php index b3b196a..b8fe9cb 100644 --- a/code/forms/gridfield/GridFieldAddItemInlineButton.php +++ b/code/forms/gridfield/GridFieldAddItemInlineButton.php @@ -5,7 +5,7 @@ * * 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 @@ -14,6 +14,13 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_ */ protected $fragment; + /** + * Additonal CSS classes for the button + * + * @var string + */ + protected $buttonClass = null; + /** * Button title * @@ -22,11 +29,11 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_ protected $title; /** - * Class name + * Class names * - * @var string + * @var array */ - protected $modelClass = null; + protected $modelClasses = null; /** * Extra CSS classes for this row @@ -35,14 +42,13 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_ */ 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 */ - public function __construct($class, $fragment = 'buttons-before-left') { - $this->setClass($class); + public function __construct($classes, $fragment = 'buttons-before-left') { + $this->setClasses($classes); $this->setFragment($fragment); $this->setTitle(_t('GridFieldExtensions.ADD', 'Add')); } @@ -88,43 +94,54 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_ } public function getHTMLFragments($grid) { - if($grid->getList() && !singleton($grid->getModelClass())->canCreate()) { - return array(); + foreach($this->getClasses() as $class) { + if(!singleton($class)->canCreate()) { + return array(); + } } - $fragment = $this->getFragment(); - if(!$editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns')) { throw new Exception('Inline adding requires the editable columns component'); } Requirements::javascript(THIRDPARTY_DIR . '/javascript-templates/tmpl.js'); GridFieldExtensions::include_requirements(); - Requirements::javascript(USERFORMS_DIR . '/javascript/GridFieldAddItemInlineButton.js'); + Requirements::javascript(USERFORMS_DIR . '/javascript/GridFieldExtensions.js'); + // Build button content $data = new ArrayData(array( '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( - $fragment => $data->renderWith(__CLASS__), - 'after' => $this->getItemRowTemplate($grid, $editable) + $this->getFragment() => $data->renderWith(__CLASS__), + 'after' => $templates ); } + + /** - * Because getRowTemplate is private + * Get the template for a given class * * @param GridField $grid * @param GridFieldEditableColumns $editable + * @param string $class Name of class * @return type */ - protected function getItemRowTemplate(GridField $grid, GridFieldEditableColumns $editable) { + protected function getItemRowTemplateFor(GridField $grid, GridFieldEditableColumns $editable, $class) { $columns = new ArrayList(); $handled = array_keys($editable->getDisplayFields($grid)); - $record = Object::create($this->getClass()); + $record = Object::create($class); $fields = $editable->getFields($grid, $record); @@ -134,7 +151,7 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_ $field = $fields->dataFieldByName($column); if($field) { $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 { $field = $fields->fieldByName($column); @@ -160,30 +177,50 @@ class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_ $data = new ArrayData(array( 'Columns' => $columns, 'ExtraClass' => $this->getExtraClass(), - 'RecordClass' => $this->getClass(), - 'TemplateName' => $this->getRowTemplateName() + 'RecordClass' => $class, + 'TemplateName' => $this->getRowTemplateNameFor($class) )); - return $data->renderWith(__CLASS__ . '_Row'); + return $data->renderWith('GridFieldAddItemTemplate'); } public function handleSave(GridField $grid, DataObjectInterface $record) { - $list = $grid->getList(); + // Check that this submission relates to this component $value = $grid->Value(); - $class = $this->getClass(); - - if(!isset($value[__CLASS__][$class]) || !is_array($value[__CLASS__][$class])) { + if(empty($value[__CLASS__]) || !is_array($value[__CLASS__])) { 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'); - $form = $editable->getForm($grid, $record); + $form = $editable->getForm($grid, $record); - if(!singleton($class)->canCreate()) { - return; - } // 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(); $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() { - return $this->modelClass; + public function getClasses() { + return $this->modelClasses; } /** - * Specify the class to create + * Specify the classes to create * - * @param string $class + * @param array $classes */ - public function setClass($class) { - $this->modelClass = $class; + public function setClasses($classes) { + 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 * * @param string $extraClass + * @return $this */ public function setExtraClass($extraClass) { $this->extraClass = $extraClass; + return $this; } /** - * Get name of item template + * Get extra button class * * @return string */ - public function getRowTemplateName() { - return 'ss-gridfield-add-inline-template-' . $this->getClass(); + public function getButtonClass() { + 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}"; } } diff --git a/code/model/editableformfields/EditableFieldGroup.php b/code/model/editableformfields/EditableFieldGroup.php new file mode 100644 index 0000000..fe98f95 --- /dev/null +++ b/code/model/editableformfields/EditableFieldGroup.php @@ -0,0 +1,16 @@ + + diff --git a/templates/gridfield/GridFieldAddItemInlineButton_Row.ss b/templates/gridfield/GridFieldAddItemTemplate.ss similarity index 100% rename from templates/gridfield/GridFieldAddItemInlineButton_Row.ss rename to templates/gridfield/GridFieldAddItemTemplate.ss