mirror of
https://github.com/silverstripe/silverstripe-userforms.git
synced 2024-10-22 17:05:42 +02:00
API Directly create editable fields on add
This commit is contained in:
parent
a8ee26ec50
commit
5ba3d9b2e0
@ -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(),
|
||||
|
225
code/forms/GridFieldAddClassesButton.php
Normal file
225
code/forms/GridFieldAddClassesButton.php
Normal file
@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A button which allows objects to be created with a specified classname(s)
|
||||
*/
|
||||
class GridFieldAddClassesButton extends Object implements GridField_HTMLProvider, GridField_URLHandler {
|
||||
|
||||
private static $allowed_actions = array(
|
||||
'handleAdd'
|
||||
);
|
||||
|
||||
/**
|
||||
* Name of fragment to insert into
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $targetFragment;
|
||||
|
||||
/**
|
||||
* Button title
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $buttonName;
|
||||
|
||||
/**
|
||||
* Additonal CSS classes for the button
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $buttonClass = null;
|
||||
|
||||
/**
|
||||
* Class names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modelClasses = null;
|
||||
|
||||
/**
|
||||
* @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 $targetFragment The fragment to render the button into
|
||||
*/
|
||||
public function __construct($classes, $targetFragment = 'buttons-before-left') {
|
||||
parent::__construct();
|
||||
$this->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();
|
||||
}
|
||||
}
|
@ -1,321 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Allows inline adding of classes with a default type
|
||||
*
|
||||
* Provides an alternative to GridFieldAddNewInlineButton, but allows you to set a classname
|
||||
*/
|
||||
class GridFieldAddItemInlineButton extends Object implements GridField_HTMLProvider, GridField_SaveHandler {
|
||||
|
||||
/**
|
||||
* Fragment id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fragment;
|
||||
|
||||
/**
|
||||
* Additonal CSS classes for the button
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $buttonClass = null;
|
||||
|
||||
/**
|
||||
* Button title
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* Class names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modelClasses = null;
|
||||
|
||||
/**
|
||||
* Extra CSS classes for this row
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $extraClass = null;
|
||||
|
||||
/**
|
||||
* @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($classes, $fragment = 'buttons-before-left') {
|
||||
$this->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}";
|
||||
}
|
||||
}
|
@ -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);
|
3
templates/gridfield/GridFieldAddClassesButton.ss
Normal file
3
templates/gridfield/GridFieldAddClassesButton.ss
Normal file
@ -0,0 +1,3 @@
|
||||
<a href="$Link.ATT" class="action action-detail ss-ui-button ui-button ui-widget ui-state-default ui-corner-all new new-link $ButtonClass.ATT" data-icon="add">
|
||||
$Title.XML
|
||||
</a>
|
@ -1,3 +0,0 @@
|
||||
<button href="$Link" class="ss-gridfield-add-new-item-inline ss-ui-button $ButtonClass" data-icon="add" data-template-names="$TemplateNames.ATT">
|
||||
$Title
|
||||
</button>
|
@ -1,13 +0,0 @@
|
||||
<script type="text/x-tmpl" class="$TemplateName">
|
||||
<tr class="ss-gridfield-inline-new $ExtraClass.ATT">
|
||||
<% loop $Columns %>
|
||||
<% if $IsActions %>
|
||||
<td$Attributes>
|
||||
<button class="ss-gridfield-delete-inline gridfield-button-delete ss-ui-button" data-icon="cross-circle"></button>
|
||||
</td>
|
||||
<% else %>
|
||||
<td$Attributes>$Content</td>
|
||||
<% end_if %>
|
||||
<% end_loop %>
|
||||
</tr>
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user