2013-02-07 12:31:12 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Allows inline editing of grid field records without having to load a separate
|
|
|
|
* edit interface.
|
|
|
|
*
|
|
|
|
* The form fields used can be configured by setting the value in {@link setDisplayFields()} to one
|
|
|
|
* of the following forms:
|
|
|
|
* - A Closure which returns the field instance.
|
|
|
|
* - An array with a `callback` key pointing to a function which returns the field.
|
|
|
|
* - An array with a `field` key->response specifying the field class to use.
|
|
|
|
*/
|
|
|
|
class GridFieldEditableColumns extends GridFieldDataColumns implements
|
|
|
|
GridField_HTMLProvider,
|
|
|
|
GridField_SaveHandler,
|
|
|
|
GridField_URLHandler {
|
|
|
|
|
2013-07-11 04:40:13 +02:00
|
|
|
private static $allowed_actions = array(
|
|
|
|
'handleForm'
|
|
|
|
);
|
|
|
|
|
2013-02-07 12:31:12 +01:00
|
|
|
/**
|
|
|
|
* @var Form[]
|
|
|
|
*/
|
|
|
|
protected $forms = array();
|
|
|
|
|
|
|
|
public function getColumnContent($grid, $record, $col) {
|
|
|
|
if(!$record->canEdit()) {
|
|
|
|
return parent::getColumnContent($grid, $record, $col);
|
|
|
|
}
|
|
|
|
|
|
|
|
$fields = $this->getForm($grid, $record)->Fields();
|
|
|
|
|
2016-01-25 05:10:35 +01:00
|
|
|
if (!$this->displayFields)
|
|
|
|
{
|
|
|
|
// If setDisplayFields() not used, utilize $summary_fields
|
|
|
|
// in a way similar to base class
|
|
|
|
$colRelation = explode('.', $col);
|
|
|
|
$value = $grid->getDataFieldValue($record, $colRelation[0]);
|
|
|
|
$field = $fields->fieldByName($colRelation[0]);
|
|
|
|
if (!$field || $field->isReadonly() || $field->isDisabled()) {
|
|
|
|
return parent::getColumnContent($grid, $record, $col);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure this field is available to edit on the record
|
|
|
|
// (ie. Maybe its readonly due to certain circumstances, or removed and not editable)
|
|
|
|
$cmsFields = $record->getCMSFields();
|
|
|
|
$cmsField = $cmsFields->dataFieldByName($colRelation[0]);
|
|
|
|
if (!$cmsField || $cmsField->isReadonly() || $cmsField->isDisabled())
|
|
|
|
{
|
|
|
|
return parent::getColumnContent($grid, $record, $col);
|
|
|
|
}
|
|
|
|
$field = clone $field;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$value = $grid->getDataFieldValue($record, $col);
|
|
|
|
$rel = (strpos($col,'.') === false); // field references a relation value
|
|
|
|
$field = ($rel) ? clone $fields->fieldByName($col) : new ReadonlyField($col);
|
|
|
|
|
|
|
|
if(!$field) {
|
|
|
|
throw new Exception("Could not find the field '$col'");
|
|
|
|
}
|
2013-02-07 12:31:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(array_key_exists($col, $this->fieldCasting)) {
|
|
|
|
$value = $grid->getCastedValue($value, $this->fieldCasting[$col]);
|
|
|
|
}
|
|
|
|
|
|
|
|
$value = $this->formatValue($grid, $record, $col, $value);
|
|
|
|
|
|
|
|
$field->setName($this->getFieldName($field->getName(), $grid, $record));
|
|
|
|
$field->setValue($value);
|
|
|
|
|
2014-12-28 11:03:04 +01:00
|
|
|
return $field->forTemplate();
|
2013-02-07 12:31:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getHTMLFragments($grid) {
|
2013-02-08 15:42:48 +01:00
|
|
|
GridFieldExtensions::include_requirements();
|
2013-02-07 12:31:12 +01:00
|
|
|
$grid->addExtraClass('ss-gridfield-editable');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function handleSave(GridField $grid, DataObjectInterface $record) {
|
2013-02-10 06:59:06 +01:00
|
|
|
$list = $grid->getList();
|
2013-02-07 12:31:12 +01:00
|
|
|
$value = $grid->Value();
|
|
|
|
|
|
|
|
if(!isset($value[__CLASS__]) || !is_array($value[__CLASS__])) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$form = $this->getForm($grid, $record);
|
|
|
|
|
|
|
|
foreach($value[__CLASS__] as $id => $fields) {
|
|
|
|
if(!is_numeric($id) || !is_array($fields)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-02-10 06:59:06 +01:00
|
|
|
$item = $list->byID($id);
|
2013-02-07 12:31:12 +01:00
|
|
|
|
|
|
|
if(!$item || !$item->canEdit()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-02-10 06:59:06 +01:00
|
|
|
$extra = array();
|
|
|
|
|
2013-02-07 12:31:12 +01:00
|
|
|
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
|
|
|
|
$form->saveInto($item);
|
|
|
|
|
2014-02-26 04:02:07 +01:00
|
|
|
if($list instanceof ManyManyList) {
|
|
|
|
$extra = array_intersect_key($form->getData(), (array) $list->getExtraFields());
|
|
|
|
}
|
|
|
|
|
2013-02-07 12:31:12 +01:00
|
|
|
$item->write();
|
2013-02-10 06:59:06 +01:00
|
|
|
$list->add($item, $extra);
|
2013-02-07 12:31:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function handleForm(GridField $grid, $request) {
|
|
|
|
$id = $request->param('ID');
|
|
|
|
$list = $grid->getList();
|
|
|
|
|
|
|
|
if(!ctype_digit($id)) {
|
|
|
|
throw new SS_HTTPResponse_Exception(null, 400);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!$record = $list->byID($id)) {
|
|
|
|
throw new SS_HTTPResponse_Exception(null, 404);
|
|
|
|
}
|
|
|
|
|
|
|
|
$form = $this->getForm($grid, $record);
|
|
|
|
|
|
|
|
foreach($form->Fields() as $field) {
|
|
|
|
$field->setName($this->getFieldName($field->getName(), $grid, $record));
|
|
|
|
}
|
|
|
|
|
|
|
|
return $form;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getURLHandlers($grid) {
|
|
|
|
return array(
|
|
|
|
'editable/form/$ID' => 'handleForm'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the field list for a record.
|
|
|
|
*
|
|
|
|
* @param GridField $grid
|
|
|
|
* @param DataObjectInterface $record
|
|
|
|
* @return FieldList
|
|
|
|
*/
|
2013-02-08 16:19:30 +01:00
|
|
|
public function getFields(GridField $grid, DataObjectInterface $record) {
|
2013-02-07 12:31:12 +01:00
|
|
|
$cols = $this->getDisplayFields($grid);
|
|
|
|
$fields = new FieldList();
|
2013-02-10 06:59:06 +01:00
|
|
|
|
|
|
|
$list = $grid->getList();
|
2013-06-22 13:38:29 +02:00
|
|
|
$class = $list ? $list->dataClass() : null;
|
2013-02-07 12:31:12 +01:00
|
|
|
|
|
|
|
foreach($cols as $col => $info) {
|
|
|
|
$field = null;
|
|
|
|
|
|
|
|
if($info instanceof Closure) {
|
|
|
|
$field = call_user_func($info, $record, $col, $grid);
|
|
|
|
} elseif(is_array($info)) {
|
|
|
|
if(isset($info['callback'])) {
|
|
|
|
$field = call_user_func($info['callback'], $record, $col, $grid);
|
|
|
|
} elseif(isset($info['field'])) {
|
2014-06-02 06:46:01 +02:00
|
|
|
if ($info['field'] == 'LiteralField') {
|
|
|
|
$field = new $info['field']($col, null);
|
2015-10-06 09:14:13 +02:00
|
|
|
} else {
|
2014-06-02 06:46:01 +02:00
|
|
|
$field = new $info['field']($col);
|
|
|
|
}
|
2013-02-07 12:31:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!$field instanceof FormField) {
|
|
|
|
throw new Exception(sprintf(
|
|
|
|
'The field for column "%s" is not a valid form field',
|
|
|
|
$col
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-10 06:59:06 +01:00
|
|
|
if(!$field && $list instanceof ManyManyList) {
|
|
|
|
$extra = $list->getExtraFields();
|
|
|
|
|
2013-03-03 09:21:42 +01:00
|
|
|
if($extra && array_key_exists($col, $extra)) {
|
2013-02-10 06:59:06 +01:00
|
|
|
$field = Object::create_from_string($extra[$col], $col)->scaffoldFormField();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-07 12:31:12 +01:00
|
|
|
if(!$field) {
|
2016-01-25 05:10:35 +01:00
|
|
|
if (!$this->displayFields)
|
|
|
|
{
|
|
|
|
// If setDisplayFields() not used, utilize $summary_fields
|
|
|
|
// in a way similar to base class
|
|
|
|
//
|
|
|
|
// Allows use of 'MyBool.Nice' and 'MyHTML.NoHTML' so that
|
|
|
|
// GridFields not using inline editing still look good or
|
|
|
|
// revert to looking good in cases where the field isn't
|
|
|
|
// available or is readonly
|
|
|
|
//
|
|
|
|
$colRelation = explode('.', $col);
|
|
|
|
if($class && $obj = singleton($class)->dbObject($colRelation[0])) {
|
|
|
|
$field = $obj->scaffoldFormField();
|
|
|
|
} else {
|
|
|
|
$field = new ReadonlyField($colRelation[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if($class && $obj = singleton($class)->dbObject($col)) {
|
2013-02-07 12:31:12 +01:00
|
|
|
$field = $obj->scaffoldFormField();
|
|
|
|
} else {
|
|
|
|
$field = new ReadonlyField($col);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!$field instanceof FormField) {
|
|
|
|
throw new Exception(sprintf(
|
|
|
|
'Invalid form field instance for column "%s"', $col
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2016-03-12 02:50:52 +01:00
|
|
|
// Add CSS class for interactive fields
|
|
|
|
if (!($field->isReadOnly() || $field instanceof LiteralField)) $field->addExtraClass('editable-column-field');
|
|
|
|
|
2013-02-07 12:31:12 +01:00
|
|
|
$fields->push($field);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $fields;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the form instance for a record.
|
|
|
|
*
|
|
|
|
* @param GridField $grid
|
|
|
|
* @param DataObjectInterface $record
|
|
|
|
* @return Form
|
|
|
|
*/
|
2013-02-08 16:19:30 +01:00
|
|
|
public function getForm(GridField $grid, DataObjectInterface $record) {
|
2013-02-07 12:31:12 +01:00
|
|
|
$fields = $this->getFields($grid, $record);
|
|
|
|
|
|
|
|
$form = new Form($this, null, $fields, new FieldList());
|
|
|
|
$form->loadDataFrom($record);
|
|
|
|
|
|
|
|
$form->setFormAction(Controller::join_links(
|
|
|
|
$grid->Link(), 'editable/form', $record->ID
|
|
|
|
));
|
|
|
|
|
|
|
|
return $form;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getFieldName($name, GridField $grid, DataObjectInterface $record) {
|
|
|
|
return sprintf(
|
|
|
|
'%s[%s][%s][%s]', $grid->getName(), __CLASS__, $record->ID, $name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|