From f62493ffa943dcb531878b15c0800a22848eb91b Mon Sep 17 00:00:00 2001 From: ajshort Date: Thu, 7 Feb 2013 22:31:12 +1100 Subject: [PATCH] Add inline GridFieldEditableColumns component. --- README.md | 1 + code/GridFieldEditableColumns.php | 183 ++++++++++++++++++++++++++++++ composer.json | 2 +- css/GridFieldExtensions.css | 16 +++ javascript/GridFieldExtensions.js | 10 ++ 5 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 code/GridFieldEditableColumns.php diff --git a/README.md b/README.md index 5d3db52..c3b4543 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ This module provides a number of useful grid field components: * `GridFieldAddExistingSearchButton` - a more advanced search form for adding items. * `GridFieldAddNewMultiClass` - lets the user select from a list of classes to create a new record from. +* `GridFieldEditableColumns` - allows inline editing of records. * `GridFieldOrderableRows` - drag and drop re-ordering of rows. Maintainer Contacts diff --git a/code/GridFieldEditableColumns.php b/code/GridFieldEditableColumns.php new file mode 100644 index 0000000..e6f07a0 --- /dev/null +++ b/code/GridFieldEditableColumns.php @@ -0,0 +1,183 @@ +response specifying the field class to use. + */ +class GridFieldEditableColumns extends GridFieldDataColumns implements + GridField_HTMLProvider, + GridField_SaveHandler, + GridField_URLHandler { + + /** + * @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(); + $value = $grid->getDataFieldValue($record, $col); + $field = clone $fields->fieldByName($col); + + if(!$field) { + throw new Exception("Could not find the field '$col'"); + } + + 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); + + return $field->Field(); + } + + public function getHTMLFragments($grid) { + $grid->addExtraClass('ss-gridfield-editable'); + } + + public function handleSave(GridField $grid, DataObjectInterface $record) { + $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; + } + + $item = $grid->getList()->byID($id); + + if(!$item || !$item->canEdit()) { + continue; + } + + $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); + $form->saveInto($item); + + $item->write(); + } + } + + 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 + */ + protected function getFields(GridField $grid, DataObjectInterface $record) { + $cols = $this->getDisplayFields($grid); + $fields = new FieldList(); + $class = $grid->getList()->dataClass(); + + 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'])) { + $field = new $info['field']($col); + } + + if(!$field instanceof FormField) { + throw new Exception(sprintf( + 'The field for column "%s" is not a valid form field', + $col + )); + } + } + + if(!$field) { + if($obj = singleton($class)->dbObject($col)) { + $field = $obj->scaffoldFormField(); + } else { + $field = new ReadonlyField($col); + } + } + + if(!$field instanceof FormField) { + throw new Exception(sprintf( + 'Invalid form field instance for column "%s"', $col + )); + } + + $fields->push($field); + } + + return $fields; + } + + /** + * Gets the form instance for a record. + * + * @param GridField $grid + * @param DataObjectInterface $record + * @return Form + */ + protected function getForm(GridField $grid, DataObjectInterface $record) { + $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 + ); + } + +} diff --git a/composer.json b/composer.json index 1da1720..62aaf6c 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "silverstripe/framework": "3.*" + "silverstripe/framework": ">=3.1" }, "extra": { "installer-name": "gridfieldextensions" diff --git a/css/GridFieldExtensions.css b/css/GridFieldExtensions.css index df0e733..140158e 100644 --- a/css/GridFieldExtensions.css +++ b/css/GridFieldExtensions.css @@ -70,6 +70,22 @@ margin: 0 4px 0 0; } +/** + * GridFieldEditableColumns + */ + +.ss-gridfield-editable .readonly { + padding-top: 0 !important; +} + +.ss-gridfield-editable input.text, +.ss-gridfield-editable textarea, +.ss-gridfield-editable select, +.ss-gridfield-editable .TreeDropdownField { + margin: 0 !important; + max-width: none !important; +} + /** * GridFieldOrderableRows */ diff --git a/javascript/GridFieldExtensions.js b/javascript/GridFieldExtensions.js index 7642311..746f62d 100644 --- a/javascript/GridFieldExtensions.js +++ b/javascript/GridFieldExtensions.js @@ -107,6 +107,16 @@ } }); + /** + * GridFieldEditableColumns + */ + + $('.ss-gridfield.ss-gridfield-editable .ss-gridfield-item').entwine({ + onclick: function() { + // Stop the default click action when fields are clicked on. + } + }); + /** * GridFieldOrderableRows */