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 @@ -