diff --git a/code/extensions/UserFormFieldEditorExtension.php b/code/extensions/UserFormFieldEditorExtension.php
index aa6cc15..8c0aa10 100644
--- a/code/extensions/UserFormFieldEditorExtension.php
+++ b/code/extensions/UserFormFieldEditorExtension.php
@@ -60,15 +60,13 @@ class UserFormFieldEditorExtension extends DataExtension {
->addComponents(
$editableColumns,
new GridFieldButtonRow(),
- GridFieldAddItemInlineButton::create('EditableFormField')
- ->setTitle(_t('UserFormFieldEditorExtension.ADD_FIELD', 'Add Field'))
+ GridFieldAddClassesButton::create('EditableFormField')
+ ->setButtonName(_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'),
+ GridFieldAddClassesButton::create('EditableFormStep')
+ ->setButtonName(_t('UserFormFieldEditorExtension.ADD_PAGE_BREAK', 'Add Page Break')),
+ GridFieldAddClassesButton::create(array('EditableFieldGroup', 'EditableFieldGroupEnd'))
+ ->setButtonName(_t('UserFormFieldEditorExtension.ADD_FIELD_GROUP', 'Add Field Group')),
new GridFieldEditButton(),
new GridFieldDeleteAction(),
new GridFieldToolbarHeader(),
diff --git a/code/forms/GridFieldAddClassesButton.php b/code/forms/GridFieldAddClassesButton.php
new file mode 100644
index 0000000..5810fe0
--- /dev/null
+++ b/code/forms/GridFieldAddClassesButton.php
@@ -0,0 +1,225 @@
+setClasses($classes);
+ $this->setFragment($targetFragment);
+ }
+
+ /**
+ * Change the button name
+ *
+ * @param string $name
+ * @return $this
+ */
+ public function setButtonName($name) {
+ $this->buttonName = $name;
+ return $this;
+ }
+
+ /**
+ * Get the button name
+ *
+ * @return string
+ */
+ public function getButtonName() {
+ return $this->buttonName;
+ }
+
+ /**
+ * Gets the fragment name this button is rendered into.
+ *
+ * @return string
+ */
+ public function getFragment() {
+ return $this->targetFragment;
+ }
+
+ /**
+ * Sets the fragment name this button is rendered into.
+ *
+ * @param string $fragment
+ * @return GridFieldAddNewInlineButton $this
+ */
+ public function setFragment($fragment) {
+ $this->targetFragment = $fragment;
+ return $this;
+ }
+
+ /**
+ * Get extra button class
+ *
+ * @return string
+ */
+ 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 the classes of the objects to create
+ *
+ * @return array
+ */
+ public function getClasses() {
+ return $this->modelClasses;
+ }
+
+ /**
+ * Gets the list of classes which can be created, with checks for permissions.
+ * Will fallback to the default model class for the given DataGrid
+ *
+ * @param DataGrid $grid
+ * @return array
+ */
+ public function getClassesCreate($grid) {
+ // Get explicit or fallback class list
+ $classes = $this->modelClasses;
+ if(empty($classes) && $grid) {
+ $classes = array($grid->getModelClass());
+ }
+
+ // Filter out classes without permission
+ return array_filter($classes, function($class) {
+ return singleton($class)->canCreate();
+ });
+ }
+
+ /**
+ * Specify the classes to create
+ *
+ * @param array $classes
+ */
+ public function setClasses($classes) {
+ if(!is_array($classes)) {
+ $classes = $classes ? array($classes) : array();
+ }
+ $this->modelClasses = $classes;
+ }
+
+ public function getHTMLFragments($grid) {
+ // Check create permission
+ $singleton = singleton($grid->getModelClass());
+ if(!$singleton->canCreate()) {
+ return array();
+ }
+
+ // Get button name
+ $buttonName = $this->getButtonName();
+ if(!$buttonName) {
+ // provide a default button name, can be changed by calling {@link setButtonName()} on this component
+ $objectName = $singleton->i18n_singular_name();
+ $buttonName = _t('GridField.Add', 'Add {name}', array('name' => $objectName));
+ }
+
+ $data = new ArrayData(array(
+ 'Title' => $this->getButtonName(),
+ 'ButtonClass' => $this->getButtonClass(),
+ 'Link' => Controller::join_links($grid->Link(), $this->getAction())
+ ));
+
+ return array(
+ $this->getFragment() => $data->renderWith(__CLASS__)
+ );
+ }
+
+ /**
+ * Get the action suburl for this component
+ *
+ * @return type
+ */
+ protected function getAction() {
+ $classes = implode('-', $this->getClasses());
+ return Controller::join_links('add-classes', $classes);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getURLHandlers($grid) {
+ return array(
+ $this->getAction() => 'handleAdd'
+ );
+ }
+
+ /**
+ * Handles adding a new instance of a selected class.
+ *
+ * @param GridField $grid
+ * @param SS_HTTPRequest $request
+ */
+ public function handleAdd($grid, $request) {
+ $classes = $this->getClassesCreate($grid);
+
+ if(empty($classes)) {
+ throw new SS_HTTPResponse_Exception(400);
+ }
+
+ // Add item to gridfield
+ $list = $grid->getList();
+ foreach($classes as $class) {
+ $item = $class::create();
+ $item->write();
+ $list->add($item);
+ }
+
+ // Return directly to the gridfield again
+ return $grid
+ ->getForm()
+ ->getController()
+ ->redirectBack();
+ }
+}
diff --git a/code/forms/gridfield/GridFieldAddItemInlineButton.php b/code/forms/gridfield/GridFieldAddItemInlineButton.php
deleted file mode 100644
index b8fe9cb..0000000
--- a/code/forms/gridfield/GridFieldAddItemInlineButton.php
+++ /dev/null
@@ -1,321 +0,0 @@
-setClasses($classes);
- $this->setFragment($fragment);
- $this->setTitle(_t('GridFieldExtensions.ADD', 'Add'));
- }
-
- /**
- * Gets the fragment name this button is rendered into.
- *
- * @return string
- */
- public function getFragment() {
- return $this->fragment;
- }
-
- /**
- * Sets the fragment name this button is rendered into.
- *
- * @param string $fragment
- * @return GridFieldAddNewInlineButton $this
- */
- public function setFragment($fragment) {
- $this->fragment = $fragment;
- return $this;
- }
-
- /**
- * Gets the button title text.
- *
- * @return string
- */
- public function getTitle() {
- return $this->title;
- }
-
- /**
- * Sets the button title text.
- *
- * @param string $title
- * @return GridFieldAddNewInlineButton $this
- */
- public function setTitle($title) {
- $this->title = $title;
- return $this;
- }
-
- public function getHTMLFragments($grid) {
- foreach($this->getClasses() as $class) {
- if(!singleton($class)->canCreate()) {
- return array();
- }
- }
-
- 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/GridFieldExtensions.js');
-
- // Build button content
- $data = new ArrayData(array(
- 'Title' => $this->getTitle(),
- '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(
- $this->getFragment() => $data->renderWith(__CLASS__),
- 'after' => $templates
- );
- }
-
-
-
- /**
- * Get the template for a given class
- *
- * @param GridField $grid
- * @param GridFieldEditableColumns $editable
- * @param string $class Name of class
- * @return type
- */
- protected function getItemRowTemplateFor(GridField $grid, GridFieldEditableColumns $editable, $class) {
- $columns = new ArrayList();
- $handled = array_keys($editable->getDisplayFields($grid));
-
- $record = Object::create($class);
-
- $fields = $editable->getFields($grid, $record);
-
- foreach($grid->getColumns() as $column) {
- $content = null;
- if(in_array($column, $handled)) {
- $field = $fields->dataFieldByName($column);
- if($field) {
- $field->setName(sprintf(
- '%s[%s][{%%=o.num%%}][%s][%s]', $grid->getName(), __CLASS__, $class, $field->getName()
- ));
- } else {
- $field = $fields->fieldByName($column);
- }
- if($field) {
- $content = $field->Field();
- }
- }
-
- $attrs = '';
-
- foreach($grid->getColumnAttributes($record, $column) as $attr => $val) {
- $attrs .= sprintf(' %s="%s"', $attr, Convert::raw2att($val));
- }
-
- $columns->push(new ArrayData(array(
- 'Content' => $content,
- 'Attributes' => $attrs,
- 'IsActions' => $column == 'Actions'
- )));
- }
-
- $data = new ArrayData(array(
- 'Columns' => $columns,
- 'ExtraClass' => $this->getExtraClass(),
- 'RecordClass' => $class,
- 'TemplateName' => $this->getRowTemplateNameFor($class)
- ));
- return $data->renderWith('GridFieldAddItemTemplate');
- }
-
- public function handleSave(GridField $grid, DataObjectInterface $record) {
- // Check that this submission relates to this component
- $value = $grid->Value();
- 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);
-
-
- // Process records matching the specified class
- $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();
-
- $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
- $form->saveInto($item);
-
- if($list instanceof ManyManyList) {
- $extra = array_intersect_key($form->getData(), (array) $list->getExtraFields());
- }
-
- $item->write();
- $list->add($item, $extra);
- }
- }
-
- /**
- * Get the classes of the objects to create
- *
- * @return array
- */
- public function getClasses() {
- return $this->modelClasses;
- }
-
- /**
- * Specify the classes to create
- *
- * @param array $classes
- */
- public function setClasses($classes) {
- if(!is_array($classes)) {
- $classes = array($classes);
- }
- $this->modelClasses = $classes;
- }
-
- /**
- * Get extra CSS classes for this row
- *
- * @return type
- */
- public function getExtraClass() {
- return $this->extraClass;
- }
-
- /**
- * Sets extra CSS classes for this row
- *
- * @param string $extraClass
- * @return $this
- */
- public function setExtraClass($extraClass) {
- $this->extraClass = $extraClass;
- return $this;
- }
-
- /**
- * Get extra button class
- *
- * @return string
- */
- 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/javascript/GridFieldExtensions.js b/javascript/GridFieldExtensions.js
deleted file mode 100644
index 21d1fb1..0000000
--- a/javascript/GridFieldExtensions.js
+++ /dev/null
@@ -1,32 +0,0 @@
-(function($) {
- $.entwine("ss", function($) {
- // See gridfieldextensions/javascript/GridFieldExtensions.js
-
- $(".ss-gridfield.ss-gridfield-editable").entwine({
- onaddnewiteminline: function(e, template) {
- var tmpl = window.tmpl;
- var row = this.find("." + template);
- var num = this.data("add-inline-num") || 1;
-
- tmpl.cache[template] = tmpl(row.html());
-
- this.find("tbody").append(tmpl(template, { num: num }));
- this.find(".ss-gridfield-no-items").hide();
- this.data("add-inline-num", num + 1);
- }
- });
-
- $(".ss-gridfield-add-new-item-inline").entwine({
- onclick: function() {
- // Create each template
- var gridfield = this.getGridField();
- $.each(this.data('template-names'), function(index, template) {
- console.log(template);
- gridfield.trigger("addnewiteminline", template);
- });
- return false;
- }
- });
- });
-
-})(jQuery);
diff --git a/templates/gridfield/GridFieldAddClassesButton.ss b/templates/gridfield/GridFieldAddClassesButton.ss
new file mode 100644
index 0000000..aabfe88
--- /dev/null
+++ b/templates/gridfield/GridFieldAddClassesButton.ss
@@ -0,0 +1,3 @@
+
+ $Title.XML
+
\ No newline at end of file
diff --git a/templates/gridfield/GridFieldAddItemInlineButton.ss b/templates/gridfield/GridFieldAddItemInlineButton.ss
deleted file mode 100644
index 6ce5103..0000000
--- a/templates/gridfield/GridFieldAddItemInlineButton.ss
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/templates/gridfield/GridFieldAddItemTemplate.ss b/templates/gridfield/GridFieldAddItemTemplate.ss
deleted file mode 100644
index fd345ac..0000000
--- a/templates/gridfield/GridFieldAddItemTemplate.ss
+++ /dev/null
@@ -1,13 +0,0 @@
-