From 2acf6dee471d30ddf3959adcc168c91a2285036c Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Wed, 21 Dec 2016 15:34:58 +1300 Subject: [PATCH] PSR-2 code style guidelines --- src/GridFieldAddExistingSearchButton.php | 164 +-- src/GridFieldAddExistingSearchHandler.php | 174 ++-- src/GridFieldAddNewInlineButton.php | 279 +++--- src/GridFieldAddNewMultiClass.php | 376 +++---- src/GridFieldAddNewMultiClassHandler.php | 25 +- src/GridFieldEditableColumns.php | 419 ++++---- src/GridFieldExtensions.php | 22 +- src/GridFieldExternalLink.php | 122 +-- src/GridFieldOrderableRows.php | 1103 +++++++++++---------- src/GridFieldRequestHandler.php | 244 ++--- src/GridFieldTitleHeader.php | 31 +- tests/GridFieldAddNewMultiClassTest.php | 84 +- tests/GridFieldOrderableRowsTest.php | 162 +-- tests/GridFieldOrderableRowsTest.yml | 6 - 14 files changed, 1661 insertions(+), 1550 deletions(-) diff --git a/src/GridFieldAddExistingSearchButton.php b/src/GridFieldAddExistingSearchButton.php index 77788a9..ea5dc9c 100755 --- a/src/GridFieldAddExistingSearchButton.php +++ b/src/GridFieldAddExistingSearchButton.php @@ -11,95 +11,105 @@ use SilverStripe\View\ArrayData; * A modal search dialog which uses search context to search for and add * existing records to a grid field. */ -class GridFieldAddExistingSearchButton implements GridField_HTMLProvider, GridField_URLHandler { +class GridFieldAddExistingSearchButton implements GridField_HTMLProvider, GridField_URLHandler +{ - private static $allowed_actions = array( - 'handleSearch' - ); + private static $allowed_actions = array( + 'handleSearch' + ); - protected $title; - protected $fragment; - protected $searchList; + protected $title; + protected $fragment; + protected $searchList; - /** - * @param string $fragment - */ - public function __construct($fragment = 'buttons-before-left') { - $this->fragment = $fragment; - $this->title = _t('GridFieldExtensions.ADDEXISTING', 'Add Existing'); - } + /** + * @param string $fragment + */ + public function __construct($fragment = 'buttons-before-left') + { + $this->fragment = $fragment; + $this->title = _t('GridFieldExtensions.ADDEXISTING', 'Add Existing'); + } - /** - * @return string - */ - public function getTitle() { - return $this->title; - } + /** + * @return string + */ + public function getTitle() + { + return $this->title; + } - /** - * @param string $title - * @return GridFieldAddExistingSearchButton $this - */ - public function setTitle($title) { - $this->title = $title; - return $this; - } + /** + * @param string $title + * @return GridFieldAddExistingSearchButton $this + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } - /** - * @return string - */ - public function getFragment() { - return $this->fragment; - } + /** + * @return string + */ + public function getFragment() + { + return $this->fragment; + } - /** - * @param string $fragment - * @return GridFieldAddExistingSearchButton $this - */ - public function setFragment($fragment) { - $this->fragment = $fragment; - return $this; - } + /** + * @param string $fragment + * @return GridFieldAddExistingSearchButton $this + */ + public function setFragment($fragment) + { + $this->fragment = $fragment; + return $this; + } - /** - * Sets a custom list to use to provide the searchable items. - * - * @param SS_List $list - * @return GridFieldAddExistingSearchButton $this - */ - public function setSearchList(SS_List $list) { - $this->searchList = $list; - return $this; - } + /** + * Sets a custom list to use to provide the searchable items. + * + * @param SS_List $list + * @return GridFieldAddExistingSearchButton $this + */ + public function setSearchList(SS_List $list) + { + $this->searchList = $list; + return $this; + } - /** - * @return SS_List|null - */ - public function getSearchList() { - return $this->searchList; - } + /** + * @return SS_List|null + */ + public function getSearchList() + { + return $this->searchList; + } - public function getHTMLFragments($grid) { - GridFieldExtensions::include_requirements(); + public function getHTMLFragments($grid) + { + GridFieldExtensions::include_requirements(); - $data = new ArrayData(array( - 'Title' => $this->getTitle(), - 'Link' => $grid->Link('add-existing-search') - )); + $data = new ArrayData(array( + 'Title' => $this->getTitle(), + 'Link' => $grid->Link('add-existing-search') + )); - return array( - $this->fragment => $data->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddExistingSearchButton'), - ); - } + return array( + $this->fragment => $data->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddExistingSearchButton'), + ); + } - public function getURLHandlers($grid) { - return array( - 'add-existing-search' => 'handleSearch' - ); - } - - public function handleSearch($grid, $request) { - return new GridFieldAddExistingSearchHandler($grid, $this); - } + public function getURLHandlers($grid) + { + return array( + 'add-existing-search' => 'handleSearch' + ); + } + public function handleSearch($grid, $request) + { + return new GridFieldAddExistingSearchHandler($grid, $this); + } } diff --git a/src/GridFieldAddExistingSearchHandler.php b/src/GridFieldAddExistingSearchHandler.php index e6ec15f..a4ef7cf 100644 --- a/src/GridFieldAddExistingSearchHandler.php +++ b/src/GridFieldAddExistingSearchHandler.php @@ -14,107 +14,115 @@ use SilverStripe\ORM\PaginatedList; * Used by {@link GridFieldAddExistingSearchButton} to provide the searching * functionality. */ -class GridFieldAddExistingSearchHandler extends RequestHandler { +class GridFieldAddExistingSearchHandler extends RequestHandler +{ - private static $allowed_actions = array( - 'index', - 'add', - 'SearchForm' - ); + private static $allowed_actions = array( + 'index', + 'add', + 'SearchForm' + ); - /** - * @var GridField - */ - protected $grid; + /** + * @var GridField + */ + protected $grid; - /** - * @var GridFieldAddExistingSearchButton - */ - protected $button; + /** + * @var GridFieldAddExistingSearchButton + */ + protected $button; - /** - * @var SearchContext - */ - protected $context; + /** + * @var SearchContext + */ + protected $context; - public function __construct($grid, $button) { - $this->grid = $grid; - $this->button = $button; - $this->context = singleton($grid->getModelClass())->getDefaultSearchContext(); + public function __construct($grid, $button) + { + $this->grid = $grid; + $this->button = $button; + $this->context = singleton($grid->getModelClass())->getDefaultSearchContext(); - parent::__construct(); - } + parent::__construct(); + } - public function index() { - return $this->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddExistingSearchHandler'); - } + public function index() + { + return $this->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddExistingSearchHandler'); + } - public function add($request) { - if(!$id = $request->postVar('id')) { - $this->httpError(400); - } + public function add($request) + { + if (!$id = $request->postVar('id')) { + $this->httpError(400); + } - $list = $this->grid->getList(); - $item = DataList::create($list->dataClass())->byID($id); + $list = $this->grid->getList(); + $item = DataList::create($list->dataClass())->byID($id); - if(!$item) { - $this->httpError(400); - } + if (!$item) { + $this->httpError(400); + } - $list->add($item); - } + $list->add($item); + } - /** - * @return Form - */ - public function SearchForm() { - $form = new Form( - $this, - 'SilverStripe\\CMS\\Search\\SearchForm', - $this->context->getFields(), - new FieldList( - FormAction::create('doSearch', _t('GridFieldExtensions.SEARCH', 'Search')) - ->setUseButtonTag(true) - ->addExtraClass('ss-ui-button') - ->setAttribute('data-icon', 'magnifier') - ) - ); + /** + * @return Form + */ + public function SearchForm() + { + $form = new Form( + $this, + 'SilverStripe\\CMS\\Search\\SearchForm', + $this->context->getFields(), + new FieldList( + FormAction::create('doSearch', _t('GridFieldExtensions.SEARCH', 'Search')) + ->setUseButtonTag(true) + ->addExtraClass('ss-ui-button') + ->setAttribute('data-icon', 'magnifier') + ) + ); - $form->addExtraClass('stacked add-existing-search-form'); - $form->setFormMethod('GET'); + $form->addExtraClass('stacked add-existing-search-form'); + $form->setFormMethod('GET'); - return $form; - } + return $form; + } - public function doSearch($data, $form) { - $list = $this->context->getQuery($data, false, false, $this->getSearchList()); - $list = $list->subtract($this->grid->getList()); - $list = new PaginatedList($list, $this->request); + public function doSearch($data, $form) + { + $list = $this->context->getQuery($data, false, false, $this->getSearchList()); + $list = $list->subtract($this->grid->getList()); + $list = new PaginatedList($list, $this->request); - $data = $this->customise(array( - 'SearchForm' => $form, - 'Items' => $list - )); - return $data->index(); - } + $data = $this->customise(array( + 'SearchForm' => $form, + 'Items' => $list + )); + return $data->index(); + } - public function Items() { - $list = $this->getSearchList(); - $list = $list->subtract($this->grid->getList()); - $list = new PaginatedList($list, $this->request); + public function Items() + { + $list = $this->getSearchList(); + $list = $list->subtract($this->grid->getList()); + $list = new PaginatedList($list, $this->request); - return $list; - } + return $list; + } - public function Link($action = null) { - return Controller::join_links($this->grid->Link(), 'add-existing-search', $action); - } - - /** - * @return DataList - */ - protected function getSearchList() { - return $this->button->getSearchList() ?: DataList::create($this->grid->getList()->dataClass()); - } + public function Link($action = null) + { + return Controller::join_links($this->grid->Link(), 'add-existing-search', $action); + } + /** + * @return DataList + */ + protected function getSearchList() + { + return $this->button->getSearchList() ?: DataList::create($this->grid->getList()->dataClass()); + } } diff --git a/src/GridFieldAddNewInlineButton.php b/src/GridFieldAddNewInlineButton.php index aa2cc38..72a1904 100755 --- a/src/GridFieldAddNewInlineButton.php +++ b/src/GridFieldAddNewInlineButton.php @@ -17,170 +17,181 @@ use SilverStripe\View\Requirements; /** * Builds on the {@link GridFieldEditableColumns} component to allow creating new records. */ -class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_SaveHandler { +class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_SaveHandler +{ - private $fragment; + private $fragment; - private $title; + private $title; - /** - * @param string $fragment the fragment to render the button in - */ - public function __construct($fragment = 'buttons-before-left') { - $this->setFragment($fragment); - $this->setTitle(_t('GridFieldExtensions.ADD', 'Add')); - } + /** + * @param string $fragment the fragment to render the button in + */ + public function __construct($fragment = 'buttons-before-left') + { + $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; - } + /** + * 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; - } + /** + * 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; - } + /** + * 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; - } + /** + * Sets the button title text. + * + * @param string $title + * @return GridFieldAddNewInlineButton $this + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } - public function getHTMLFragments($grid) { - if($grid->getList() && !singleton($grid->getModelClass())->canCreate()) { - return array(); - } + public function getHTMLFragments($grid) + { + if ($grid->getList() && !singleton($grid->getModelClass())->canCreate()) { + return array(); + } - $fragment = $this->getFragment(); + $fragment = $this->getFragment(); - if(!$editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns')) { - throw new Exception('Inline adding requires the editable columns component'); - } + 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(THIRDPARTY_DIR . '/javascript-templates/tmpl.js'); + GridFieldExtensions::include_requirements(); - $data = new ArrayData(array( - 'Title' => $this->getTitle(), - )); + $data = new ArrayData(array( + 'Title' => $this->getTitle(), + )); - return array( - $fragment => $data->renderWith(__CLASS__), - 'after' => $this->getRowTemplate($grid, $editable) - ); - } + return array( + $fragment => $data->renderWith(__CLASS__), + 'after' => $this->getRowTemplate($grid, $editable) + ); + } - private function getRowTemplate(GridField $grid, GridFieldEditableColumns $editable) { - $columns = new ArrayList(); - $handled = array_keys($editable->getDisplayFields($grid)); + private function getRowTemplate(GridField $grid, GridFieldEditableColumns $editable) + { + $columns = new ArrayList(); + $handled = array_keys($editable->getDisplayFields($grid)); - if($grid->getList()) { - $record = Object::create($grid->getModelClass()); - } else { - $record = null; - } + if ($grid->getList()) { + $record = Object::create($grid->getModelClass()); + } else { + $record = null; + } - $fields = $editable->getFields($grid, $record); + $fields = $editable->getFields($grid, $record); - foreach($grid->getColumns() as $column) { - if(in_array($column, $handled)) { - $field = $fields->dataFieldByName($column); - $field->setName(sprintf( - '%s[%s][{%%=o.num%%}][%s]', $grid->getName(), __CLASS__, $field->getName() - )); + foreach ($grid->getColumns() as $column) { + if (in_array($column, $handled)) { + $field = $fields->dataFieldByName($column); + $field->setName(sprintf( + '%s[%s][{%%=o.num%%}][%s]', + $grid->getName(), + __CLASS__, + $field->getName() + )); - $content = $field->Field(); - } else { - $content = $grid->getColumnContent($record, $column); + $content = $field->Field(); + } else { + $content = $grid->getColumnContent($record, $column); - // Convert GridFieldEditableColumns to the template format - $content = str_replace( - '[GridFieldEditableColumns][0]', - '[GridFieldAddNewInlineButton][{%=o.num%}]', - $content - ); - } + // Convert GridFieldEditableColumns to the template format + $content = str_replace( + '[GridFieldEditableColumns][0]', + '[GridFieldAddNewInlineButton][{%=o.num%}]', + $content + ); + } - $attrs = ''; + $attrs = ''; - foreach($grid->getColumnAttributes($record, $column) as $attr => $val) { - $attrs .= sprintf(' %s="%s"', $attr, Convert::raw2att($val)); - } + 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' - ))); - } + $columns->push(new ArrayData(array( + 'Content' => $content, + 'Attributes' => $attrs, + 'IsActions' => $column == 'Actions' + ))); + } - return $columns->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddNewInlineRow'); - } + return $columns->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddNewInlineRow'); + } - public function handleSave(GridField $grid, DataObjectInterface $record) { - $list = $grid->getList(); - $value = $grid->Value(); + public function handleSave(GridField $grid, DataObjectInterface $record) + { + $list = $grid->getList(); + $value = $grid->Value(); - if(!isset($value[__CLASS__]) || !is_array($value[__CLASS__])) { - return; - } + if (!isset($value[__CLASS__]) || !is_array($value[__CLASS__])) { + return; + } - $class = $grid->getModelClass(); - /** @var GridFieldEditableColumns $editable */ - $editable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldEditableColumns'); - /** @var GridFieldOrderableRows $sortable */ - $sortable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRows'); - $form = $editable->getForm($grid, $record); + $class = $grid->getModelClass(); + /** @var GridFieldEditableColumns $editable */ + $editable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldEditableColumns'); + /** @var GridFieldOrderableRows $sortable */ + $sortable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRows'); + $form = $editable->getForm($grid, $record); - if(!singleton($class)->canCreate()) { - return; - } + if (!singleton($class)->canCreate()) { + return; + } - foreach($value[__CLASS__] as $fields) { - $item = $class::create(); - $extra = array(); + foreach ($value[__CLASS__] as $fields) { + $item = $class::create(); + $extra = array(); - $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); - $form->saveInto($item); + $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); + $form->saveInto($item); - // Check if we are also sorting these records - if ($sortable) { - $sortField = $sortable->getSortField(); - $item->setField($sortField, $fields[$sortField]); - } + // Check if we are also sorting these records + if ($sortable) { + $sortField = $sortable->getSortField(); + $item->setField($sortField, $fields[$sortField]); + } - if($list instanceof ManyManyList) { - $extra = array_intersect_key($form->getData(), (array) $list->getExtraFields()); - } - - $item->write(); - $list->add($item, $extra); - } - } + if ($list instanceof ManyManyList) { + $extra = array_intersect_key($form->getData(), (array) $list->getExtraFields()); + } + $item->write(); + $list->add($item, $extra); + } + } } diff --git a/src/GridFieldAddNewMultiClass.php b/src/GridFieldAddNewMultiClass.php index 55cd1b1..506faf2 100755 --- a/src/GridFieldAddNewMultiClass.php +++ b/src/GridFieldAddNewMultiClass.php @@ -20,217 +20,237 @@ use ReflectionClass; * By default the list of classes that are createable is the grid field's model class, and any * subclasses. This can be customised using {@link setClasses()}. */ -class GridFieldAddNewMultiClass implements GridField_HTMLProvider, GridField_URLHandler { +class GridFieldAddNewMultiClass implements GridField_HTMLProvider, GridField_URLHandler +{ - private static $allowed_actions = array( - 'handleAdd' - ); + private static $allowed_actions = array( + 'handleAdd' + ); - // Should we add an empty string to the add class dropdown? - private static $showEmptyString = true; + // Should we add an empty string to the add class dropdown? + private static $showEmptyString = true; - private $fragment; + private $fragment; - private $title; + private $title; - private $classes; + private $classes; - private $defaultClass; + private $defaultClass; - /** - * @var string - */ - protected $itemRequestClass = 'SilverStripe\\Forms\\GridField\\GridFieldAddNewMultiClassHandler'; + /** + * @var string + */ + protected $itemRequestClass = 'SilverStripe\\Forms\\GridField\\GridFieldAddNewMultiClassHandler'; - /** - * @param string $fragment the fragment to render the button in - */ - public function __construct($fragment = 'before') { - $this->setFragment($fragment); - $this->setTitle(_t('GridFieldExtensions.ADD', 'Add')); - } + /** + * @param string $fragment the fragment to render the button in + */ + public function __construct($fragment = 'before') + { + $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; - } + /** + * 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 GridFieldAddNewMultiClass $this - */ - public function setFragment($fragment) { - $this->fragment = $fragment; - return $this; - } + /** + * Sets the fragment name this button is rendered into. + * + * @param string $fragment + * @return GridFieldAddNewMultiClass $this + */ + public function setFragment($fragment) + { + $this->fragment = $fragment; + return $this; + } - /** - * Gets the button title text. - * - * @return string - */ - public function getTitle() { - return $this->title; - } + /** + * Gets the button title text. + * + * @return string + */ + public function getTitle() + { + return $this->title; + } - /** - * Sets the button title text. - * - * @param string $title - * @return GridFieldAddNewMultiClass $this - */ - public function setTitle($title) { - $this->title = $title; - return $this; - } + /** + * Sets the button title text. + * + * @param string $title + * @return GridFieldAddNewMultiClass $this + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } - /** - * Gets the classes that can be created using this button, defaulting to the model class and - * its subclasses. - * - * @param GridField $grid - * @return array a map of class name to title - */ - public function getClasses(GridField $grid) { - $result = array(); + /** + * Gets the classes that can be created using this button, defaulting to the model class and + * its subclasses. + * + * @param GridField $grid + * @return array a map of class name to title + */ + public function getClasses(GridField $grid) + { + $result = array(); - if(is_null($this->classes)) { - $classes = array_values(ClassInfo::subclassesFor($grid->getModelClass())); - sort($classes); - } else { - $classes = $this->classes; - } + if (is_null($this->classes)) { + $classes = array_values(ClassInfo::subclassesFor($grid->getModelClass())); + sort($classes); + } else { + $classes = $this->classes; + } - $kill_ancestors = array(); - foreach($classes as $class => $title) { - if(!is_string($class)) { - $class = $title; - } - if (!class_exists($class)) { - continue; - } - $is_abstract = (($reflection = new ReflectionClass($class)) && $reflection->isAbstract()); - if (!$is_abstract && $class === $title) { - $title = singleton($class)->i18n_singular_name(); - } + $kill_ancestors = array(); + foreach ($classes as $class => $title) { + if (!is_string($class)) { + $class = $title; + } + if (!class_exists($class)) { + continue; + } + $is_abstract = (($reflection = new ReflectionClass($class)) && $reflection->isAbstract()); + if (!$is_abstract && $class === $title) { + $title = singleton($class)->i18n_singular_name(); + } - if ($ancestor_to_hide = Config::inst()->get($class, 'hide_ancestor', Config::FIRST_SET)) { - $kill_ancestors[$ancestor_to_hide] = true; - } + if ($ancestor_to_hide = Config::inst()->get($class, 'hide_ancestor', Config::FIRST_SET)) { + $kill_ancestors[$ancestor_to_hide] = true; + } - if($is_abstract || !singleton($class)->canCreate()) { - continue; - } + if ($is_abstract || !singleton($class)->canCreate()) { + continue; + } - $result[$class] = $title; - } + $result[$class] = $title; + } - if($kill_ancestors) { - foreach($kill_ancestors as $class => $bool) { - unset($result[$class]); - } - } + if ($kill_ancestors) { + foreach ($kill_ancestors as $class => $bool) { + unset($result[$class]); + } + } - return $result; - } + return $result; + } - /** - * Sets the classes that can be created using this button. - * - * @param array $classes a set of class names, optionally mapped to titles - * @return GridFieldAddNewMultiClass $this - */ - public function setClasses(array $classes, $default = null) { - $this->classes = $classes; - if($default) $this->defaultClass = $default; - return $this; - } + /** + * Sets the classes that can be created using this button. + * + * @param array $classes a set of class names, optionally mapped to titles + * @return GridFieldAddNewMultiClass $this + */ + public function setClasses(array $classes, $default = null) + { + $this->classes = $classes; + if ($default) { + $this->defaultClass = $default; + } + return $this; + } - /** - * Sets the default class that is selected automatically. - * - * @param string $default the class name to use as default - * @return GridFieldAddNewMultiClass $this - */ - public function setDefaultClass($default) { - $this->defaultClass = $default; - return $this; - } + /** + * Sets the default class that is selected automatically. + * + * @param string $default the class name to use as default + * @return GridFieldAddNewMultiClass $this + */ + public function setDefaultClass($default) + { + $this->defaultClass = $default; + return $this; + } - /** - * Handles adding a new instance of a selected class. - * - * @param GridField $grid - * @param SS_HTTPRequest $request - * @return GridFieldAddNewMultiClassHandler - */ - public function handleAdd($grid, $request) { - $class = $request->param('ClassName'); - $classes = $this->getClasses($grid); - $component = $grid->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDetailForm'); + /** + * Handles adding a new instance of a selected class. + * + * @param GridField $grid + * @param SS_HTTPRequest $request + * @return GridFieldAddNewMultiClassHandler + */ + public function handleAdd($grid, $request) + { + $class = $request->param('ClassName'); + $classes = $this->getClasses($grid); + $component = $grid->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDetailForm'); - if(!$component) { - throw new Exception('The add new multi class component requires the detail form component.'); - } + if (!$component) { + throw new Exception('The add new multi class component requires the detail form component.'); + } - if(!$class || !array_key_exists($class, $classes)) { - throw new HTTPResponse_Exception(400); - } + if (!$class || !array_key_exists($class, $classes)) { + throw new HTTPResponse_Exception(400); + } - $handler = Object::create($this->itemRequestClass, - $grid, $component, new $class(), $grid->getForm()->getController(), 'add-multi-class' - ); - $handler->setTemplate($component->getTemplate()); + $handler = Object::create( + $this->itemRequestClass, + $grid, + $component, + new $class(), + $grid->getForm()->getController(), + 'add-multi-class' + ); + $handler->setTemplate($component->getTemplate()); - return $handler; - } + return $handler; + } - /** - * {@inheritDoc} - */ - public function getHTMLFragments($grid) { - $classes = $this->getClasses($grid); + /** + * {@inheritDoc} + */ + public function getHTMLFragments($grid) + { + $classes = $this->getClasses($grid); - if(!count($classes)) { - return array(); - } + if (!count($classes)) { + return array(); + } - GridFieldExtensions::include_requirements(); + GridFieldExtensions::include_requirements(); - $field = new DropdownField(sprintf('%s[ClassName]', __CLASS__), '', $classes, $this->defaultClass); - if (Config::inst()->get(__CLASS__, 'showEmptyString')) { - $field->setEmptyString(_t('GridFieldExtensions.SELECTTYPETOCREATE', '(Select type to create)')); - } - $field->addExtraClass('no-change-track'); + $field = new DropdownField(sprintf('%s[ClassName]', __CLASS__), '', $classes, $this->defaultClass); + if (Config::inst()->get(__CLASS__, 'showEmptyString')) { + $field->setEmptyString(_t('GridFieldExtensions.SELECTTYPETOCREATE', '(Select type to create)')); + } + $field->addExtraClass('no-change-track'); - $data = new ArrayData(array( - 'Title' => $this->getTitle(), - 'Link' => Controller::join_links($grid->Link(), 'add-multi-class', '{class}'), - 'ClassField' => $field - )); + $data = new ArrayData(array( + 'Title' => $this->getTitle(), + 'Link' => Controller::join_links($grid->Link(), 'add-multi-class', '{class}'), + 'ClassField' => $field + )); - return array( - $this->getFragment() => $data->renderWith(__CLASS__) - ); - } + return array( + $this->getFragment() => $data->renderWith(__CLASS__) + ); + } - /** - * {@inheritDoc} - */ - public function getURLHandlers($grid) { - return array( - 'add-multi-class/$ClassName!' => 'handleAdd' - ); - } + /** + * {@inheritDoc} + */ + public function getURLHandlers($grid) + { + return array( + 'add-multi-class/$ClassName!' => 'handleAdd' + ); + } - public function setItemRequestClass($class) { - $this->itemRequestClass = $class; - return $this; - } + public function setItemRequestClass($class) + { + $this->itemRequestClass = $class; + return $this; + } } diff --git a/src/GridFieldAddNewMultiClassHandler.php b/src/GridFieldAddNewMultiClassHandler.php index b554ac8..45b6729 100644 --- a/src/GridFieldAddNewMultiClassHandler.php +++ b/src/GridFieldAddNewMultiClassHandler.php @@ -8,16 +8,19 @@ use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest; /** * A custom grid field request handler that allows interacting with form fields when adding records. */ -class GridFieldAddNewMultiClassHandler extends GridFieldDetailForm_ItemRequest { - - public function Link($action = null) { - if($this->record->ID) { - return parent::Link($action); - } else { - return Controller::join_links( - $this->gridField->Link(), 'add-multi-class', get_class($this->record) - ); - } - } +class GridFieldAddNewMultiClassHandler extends GridFieldDetailForm_ItemRequest +{ + public function Link($action = null) + { + if ($this->record->ID) { + return parent::Link($action); + } else { + return Controller::join_links( + $this->gridField->Link(), + 'add-multi-class', + get_class($this->record) + ); + } + } } diff --git a/src/GridFieldEditableColumns.php b/src/GridFieldEditableColumns.php index 537140d..c3ba772 100644 --- a/src/GridFieldEditableColumns.php +++ b/src/GridFieldEditableColumns.php @@ -29,260 +29,271 @@ use SilverStripe\ORM\ManyManyList; * - An array with a `field` key->response specifying the field class to use. */ class GridFieldEditableColumns extends GridFieldDataColumns implements - GridField_HTMLProvider, - GridField_SaveHandler, - GridField_URLHandler { + GridField_HTMLProvider, + GridField_SaveHandler, + GridField_URLHandler +{ - private static $allowed_actions = array( - 'handleForm' - ); + private static $allowed_actions = array( + 'handleForm' + ); - /** - * @var Form[] - */ - protected $forms = array(); + /** + * @var Form[] + */ + protected $forms = array(); - public function getColumnContent($grid, $record, $col) { - if(!$record->canEdit()) { - return parent::getColumnContent($grid, $record, $col); - } + public function getColumnContent($grid, $record, $col) + { + if (!$record->canEdit()) { + return parent::getColumnContent($grid, $record, $col); + } - $fields = $this->getForm($grid, $record)->Fields(); + $fields = $this->getForm($grid, $record)->Fields(); - 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); - } + 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); + // 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'"); - } - } + 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]); - } + if (array_key_exists($col, $this->fieldCasting)) { + $value = $grid->getCastedValue($value, $this->fieldCasting[$col]); + } - $value = $this->formatValue($grid, $record, $col, $value); + $value = $this->formatValue($grid, $record, $col, $value); - $field->setName($this->getFieldName($field->getName(), $grid, $record)); - $field->setValue($value); + $field->setName($this->getFieldName($field->getName(), $grid, $record)); + $field->setValue($value); if ($field instanceof HtmlEditorField) { return $field->FieldHolder(); } - return $field->forTemplate(); - } + return $field->forTemplate(); + } - public function getHTMLFragments($grid) { - GridFieldExtensions::include_requirements(); - $grid->addExtraClass('ss-gridfield-editable'); - } + public function getHTMLFragments($grid) + { + GridFieldExtensions::include_requirements(); + $grid->addExtraClass('ss-gridfield-editable'); + } - public function handleSave(GridField $grid, DataObjectInterface $record) { - $list = $grid->getList(); - $value = $grid->Value(); + public function handleSave(GridField $grid, DataObjectInterface $record) + { + $list = $grid->getList(); + $value = $grid->Value(); - if(!isset($value[__CLASS__]) || !is_array($value[__CLASS__])) { - return; - } + if (!isset($value[__CLASS__]) || !is_array($value[__CLASS__])) { + return; + } - /** @var GridFieldOrderableRows $sortable */ - $sortable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRows'); + /** @var GridFieldOrderableRows $sortable */ + $sortable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRows'); - $form = $this->getForm($grid, $record); + $form = $this->getForm($grid, $record); - foreach($value[__CLASS__] as $id => $fields) { - if(!is_numeric($id) || !is_array($fields)) { - continue; - } + foreach ($value[__CLASS__] as $id => $fields) { + if (!is_numeric($id) || !is_array($fields)) { + continue; + } - $item = $list->byID($id); + $item = $list->byID($id); - if(!$item || !$item->canEdit()) { - continue; - } + if (!$item || !$item->canEdit()) { + continue; + } - $extra = array(); + $extra = array(); - $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); - $form->saveInto($item); + $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); + $form->saveInto($item); - // Check if we are also sorting these records - if ($sortable) { - $sortField = $sortable->getSortField(); - $item->setField($sortField, $fields[$sortField]); - } + // Check if we are also sorting these records + if ($sortable) { + $sortField = $sortable->getSortField(); + $item->setField($sortField, $fields[$sortField]); + } - if($list instanceof ManyManyList) { - $extra = array_intersect_key($form->getData(), (array) $list->getExtraFields()); - } + if ($list instanceof ManyManyList) { + $extra = array_intersect_key($form->getData(), (array) $list->getExtraFields()); + } - $item->write(); - $list->add($item, $extra); - } - } + $item->write(); + $list->add($item, $extra); + } + } - public function handleForm(GridField $grid, $request) { - $id = $request->param('ID'); - $list = $grid->getList(); + public function handleForm(GridField $grid, $request) + { + $id = $request->param('ID'); + $list = $grid->getList(); - if(!ctype_digit($id)) { - throw new HTTPResponse_Exception(null, 400); - } + if (!ctype_digit($id)) { + throw new HTTPResponse_Exception(null, 400); + } - if(!$record = $list->byID($id)) { - throw new HTTPResponse_Exception(null, 404); - } + if (!$record = $list->byID($id)) { + throw new HTTPResponse_Exception(null, 404); + } - $form = $this->getForm($grid, $record); + $form = $this->getForm($grid, $record); - foreach($form->Fields() as $field) { - $field->setName($this->getFieldName($field->getName(), $grid, $record)); - } + foreach ($form->Fields() as $field) { + $field->setName($this->getFieldName($field->getName(), $grid, $record)); + } - return $form; - } + return $form; + } - public function getURLHandlers($grid) { - return array( - 'editable/form/$ID' => 'handleForm' - ); - } + 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 - */ - public function getFields(GridField $grid, DataObjectInterface $record) { - $cols = $this->getDisplayFields($grid); - $fields = new FieldList(); + /** + * Gets the field list for a record. + * + * @param GridField $grid + * @param DataObjectInterface $record + * @return FieldList + */ + public function getFields(GridField $grid, DataObjectInterface $record) + { + $cols = $this->getDisplayFields($grid); + $fields = new FieldList(); - $list = $grid->getList(); - $class = $list ? $list->dataClass() : null; + $list = $grid->getList(); + $class = $list ? $list->dataClass() : null; - foreach($cols as $col => $info) { - $field = null; + 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'])) { - if ($info['field'] == 'SilverStripe\\Forms\\LiteralField') { - $field = new $info['field']($col, null); - } else { - $field = new $info['field']($col); - } - } + 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'])) { + if ($info['field'] == 'SilverStripe\\Forms\\LiteralField') { + $field = new $info['field']($col, null); + } else { + $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 instanceof FormField) { + throw new Exception(sprintf( + 'The field for column "%s" is not a valid form field', + $col + )); + } + } - if(!$field && $list instanceof ManyManyList) { - $extra = $list->getExtraFields(); + if (!$field && $list instanceof ManyManyList) { + $extra = $list->getExtraFields(); - if($extra && array_key_exists($col, $extra)) { - $field = Object::create_from_string($extra[$col], $col)->scaffoldFormField(); - } - } + if ($extra && array_key_exists($col, $extra)) { + $field = Object::create_from_string($extra[$col], $col)->scaffoldFormField(); + } + } - if(!$field) { - 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)) { - $field = $obj->scaffoldFormField(); - } else { - $field = new ReadonlyField($col); - } - } + if (!$field) { + 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]); + } + } elseif ($class && $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 - )); - } + if (!$field instanceof FormField) { + throw new Exception(sprintf( + 'Invalid form field instance for column "%s"', + $col + )); + } - // Add CSS class for interactive fields - if (!($field->isReadOnly() || $field instanceof LiteralField)) $field->addExtraClass('editable-column-field'); + // Add CSS class for interactive fields + if (!($field->isReadOnly() || $field instanceof LiteralField)) { + $field->addExtraClass('editable-column-field'); + } - $fields->push($field); - } + $fields->push($field); + } - return $fields; - } + return $fields; + } - /** - * Gets the form instance for a record. - * - * @param GridField $grid - * @param DataObjectInterface $record - * @return Form - */ - public function getForm(GridField $grid, DataObjectInterface $record) { - $fields = $this->getFields($grid, $record); + /** + * Gets the form instance for a record. + * + * @param GridField $grid + * @param DataObjectInterface $record + * @return Form + */ + public function getForm(GridField $grid, DataObjectInterface $record) + { + $fields = $this->getFields($grid, $record); - $form = new Form($this, null, $fields, new FieldList()); - $form->loadDataFrom($record); + $form = new Form($this, null, $fields, new FieldList()); + $form->loadDataFrom($record); - $form->setFormAction(Controller::join_links( - $grid->Link(), 'editable/form', $record->ID - )); + $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 - ); - } + 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/src/GridFieldExtensions.php b/src/GridFieldExtensions.php index ed6bc95..c3535f5 100644 --- a/src/GridFieldExtensions.php +++ b/src/GridFieldExtensions.php @@ -7,16 +7,18 @@ use SilverStripe\View\Requirements; /** * Utility functions for the grid fields extension module. */ -class GridFieldExtensions { +class GridFieldExtensions +{ - public static function include_requirements() { - $moduleDir = self::get_module_dir(); - Requirements::css($moduleDir.'/css/GridFieldExtensions.css'); - Requirements::javascript($moduleDir.'/javascript/GridFieldExtensions.js'); - } - - public static function get_module_dir() { - return basename(dirname(__DIR__)); - } + public static function include_requirements() + { + $moduleDir = self::get_module_dir(); + Requirements::css($moduleDir.'/css/GridFieldExtensions.css'); + Requirements::javascript($moduleDir.'/javascript/GridFieldExtensions.js'); + } + public static function get_module_dir() + { + return basename(dirname(__DIR__)); + } } diff --git a/src/GridFieldExternalLink.php b/src/GridFieldExternalLink.php index 3771f01..e9ebded 100644 --- a/src/GridFieldExternalLink.php +++ b/src/GridFieldExternalLink.php @@ -8,67 +8,75 @@ use SilverStripe\View\ArrayData; /** * Displays a link to an external source referenced 'external link' */ -class GridFieldExternalLink extends GridFieldDataColumns { +class GridFieldExternalLink extends GridFieldDataColumns +{ - /** - * Add a column for the actions - * - * @param type $gridField - * @param array $columns - */ - public function augmentColumns($gridField, &$columns) { - if(!in_array('Actions', $columns)) $columns[] = 'Actions'; - } + /** + * Add a column for the actions + * + * @param type $gridField + * @param array $columns + */ + public function augmentColumns($gridField, &$columns) + { + if (!in_array('Actions', $columns)) { + $columns[] = 'Actions'; + } + } - /** - * Return any special attributes that will be used for FormField::create_tag() - * - * @param GridField $gridField - * @param DataObject $record - * @param string $columnName - * @return array - */ - public function getColumnAttributes($gridField, $record, $columnName) { - return array('class' => 'col-buttons'); - } + /** + * Return any special attributes that will be used for FormField::create_tag() + * + * @param GridField $gridField + * @param DataObject $record + * @param string $columnName + * @return array + */ + public function getColumnAttributes($gridField, $record, $columnName) + { + return array('class' => 'col-buttons'); + } - /** - * Add the title - * - * @param GridField $gridField - * @param string $columnName - * @return array - */ - public function getColumnMetadata($gridField, $columnName) { - if($columnName == 'Actions') { - return array('title' => ''); - } - return array(); - } + /** + * Add the title + * + * @param GridField $gridField + * @param string $columnName + * @return array + */ + public function getColumnMetadata($gridField, $columnName) + { + if ($columnName == 'Actions') { + return array('title' => ''); + } + return array(); + } - /** - * Which columns are handled by this component - * - * @param type $gridField - * @return type - */ - public function getColumnsHandled($gridField) { - return array('Actions'); - } + /** + * Which columns are handled by this component + * + * @param type $gridField + * @return type + */ + public function getColumnsHandled($gridField) + { + return array('Actions'); + } - /** - * @param GridField $gridField - * @param DataObject $record - * @param string $columnName - * - * @return string - the HTML for the column - */ - public function getColumnContent($gridField, $record, $columnName) { - $data = new ArrayData(array( - 'Link' => $record->hasMethod('getExternalLink') ? $record->getExternalLink() : $record->ExternalLink, - 'Text' => $record->hasMethod('getExternalLinkText') ? $record->getExternalLinkText() : 'External Link' - )); + /** + * @param GridField $gridField + * @param DataObject $record + * @param string $columnName + * + * @return string - the HTML for the column + */ + public function getColumnContent($gridField, $record, $columnName) + { + $data = new ArrayData(array( + 'Link' => $record->hasMethod('getExternalLink') ? $record->getExternalLink() : $record->ExternalLink, + 'Text' => $record->hasMethod('getExternalLinkText') ? $record->getExternalLinkText() : 'External Link' + )); - return $data->renderWith('GridFieldExternalLink'); - } + return $data->renderWith('GridFieldExternalLink'); + } } diff --git a/src/GridFieldOrderableRows.php b/src/GridFieldOrderableRows.php index 4bb6e11..949ee68 100755 --- a/src/GridFieldOrderableRows.php +++ b/src/GridFieldOrderableRows.php @@ -29,544 +29,567 @@ use SilverStripe\View\ViewableData; * the sort field. */ class GridFieldOrderableRows extends RequestHandler implements - GridField_ColumnProvider, - GridField_DataManipulator, - GridField_HTMLProvider, - GridField_URLHandler, - GridField_SaveHandler { - - /** - * @see $immediateUpdate - * @var boolean - */ - private static $default_immediate_update = true; - - private static $allowed_actions = array( - 'handleReorder', - 'handleMoveToPage' - ); - - /** - * The database field which specifies the sort, defaults to "Sort". - * - * @see setSortField() - * @var string - */ - protected $sortField; - - /** - * If set to true, when an item is re-ordered, it will update on the - * database and refresh the gridfield. When set to false, it will only - * update the sort order when the record is saved. - * - * @var boolean - */ - protected $immediateUpdate; - - /** - * Extra sort fields to apply before the sort field. - * - * @see setExtraSortFields() - * @var string|array - */ - protected $extraSortFields = null; - - /** - * The number of the column containing the reorder handles - * - * @see setReorderColumnNumber() - * @var int - */ - protected $reorderColumnNumber = 0; - - /** - * @param string $sortField - */ - public function __construct($sortField = 'Sort') { - parent::__construct(); - $this->sortField = $sortField; - $this->immediateUpdate = $this->config()->default_immediate_update; - } - - /** - * @return string - */ - public function getSortField() { - return $this->sortField; - } - - /** - * Sets the field used to specify the sort. - * - * @param string $sortField - * @return GridFieldOrderableRows $this - */ - public function setSortField($field) { - $this->sortField = $field; - return $this; - } - - /** - * @return boolean - */ - public function getImmediateUpdate() { - return $this->immediateUpdate; - } - - /** - * @see $immediateUpdate - * @param boolean $immediateUpdate - * @return GridFieldOrderableRows $this - */ - public function setImmediateUpdate($bool) { - $this->immediateUpdate = $bool; - return $this; - } - - /** - * @return string|array - */ - public function getExtraSortFields() { - return $this->extraSortFields; - } - - /** - * Sets extra sort fields to apply before the sort field. - * - * @param string|array $fields - * @return GridFieldOrderableRows $this - */ - public function setExtraSortFields($fields) { - $this->extraSortFields = $fields; - return $this; - } - - /** - * @return int - */ - public function getReorderColumnNumber() { - return $this->reorderColumnNumber; - } - - /** - * Sets the number of the column containing the reorder handles. - * - * @param int $colno - * @return GridFieldOrderableRows $this - */ - public function setReorderColumnNumber($colno) { - $this->reorderColumnNumber = $colno; - return $this; - } - - /** - * Gets the table which contains the sort field. - * - * @param DataList $list - * @return string - */ - public function getSortTable(SS_List $list) { - $field = $this->getSortField(); - - if($list instanceof ManyManyList) { - $extra = $list->getExtraFields(); - $table = $list->getJoinTable(); - - if($extra && array_key_exists($field, $extra)) { - return $table; - } - } - - $classes = ClassInfo::dataClassesFor($list->dataClass()); - - foreach($classes as $class) { - if(singleton($class)->hasDataBaseField($field)) { - return $class; - } - } - - throw new Exception("Couldn't find the sort field '$field'"); - } - - public function getURLHandlers($grid) { - return array( - 'POST reorder' => 'handleReorder', - 'POST movetopage' => 'handleMoveToPage' - ); - } - - /** - * @param GridField $field - */ - public function getHTMLFragments($field) { - GridFieldExtensions::include_requirements(); - - $field->addExtraClass('ss-gridfield-orderable'); - $field->setAttribute('data-immediate-update', (string)(int)$this->immediateUpdate); - $field->setAttribute('data-url-reorder', $field->Link('reorder')); - $field->setAttribute('data-url-movetopage', $field->Link('movetopage')); - } - - public function augmentColumns($grid, &$cols) { - if(!in_array('Reorder', $cols) && $grid->getState()->GridFieldOrderableRows->enabled) { - array_splice($cols, $this->reorderColumnNumber, 0, 'Reorder'); - } - } - - public function getColumnsHandled($grid) { - return array('Reorder'); - } - - public function getColumnContent($grid, $record, $col) { - // In case you are using GridFieldEditableColumns, this ensures that - // the correct sort order is saved. If you are not using that component, - // this will be ignored by other components, but will still work for this. - $sortFieldName = sprintf( - '%s[GridFieldEditableColumns][%s][%s]', - $grid->getName(), - $record->ID, - $this->getSortField() - ); - $sortField = new HiddenField($sortFieldName, false, $record->getField($this->getSortField())); - $sortField->addExtraClass('ss-orderable-hidden-sort'); - $sortField->setForm($grid->getForm()); - - return ViewableData::create()->customise(array( - 'SortField' => $sortField - ))->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRowsDragHandle'); - } - - public function getColumnAttributes($grid, $record, $col) { - return array('class' => 'col-reorder'); - } - - public function getColumnMetadata($grid, $col) { - if ($fieldLabels = singleton($grid->getModelClass())->fieldLabels()) { - return array('title' => isset($fieldLabels['Reorder']) ? $fieldLabels['Reorder'] : ''); - } - - return array('title' => ''); - } - - public function getManipulatedData(GridField $grid, SS_List $list) { - $state = $grid->getState(); - $sorted = (bool) ((string) $state->GridFieldSortableHeader->SortColumn); - - // If the data has not been sorted by the user, then sort it by the - // sort column, otherwise disable reordering. - $state->GridFieldOrderableRows->enabled = !$sorted; - - if(!$sorted) { - $sortterm = ''; - if ($this->extraSortFields) { - if (is_array($this->extraSortFields)) { - foreach($this->extraSortFields as $col => $dir) { - $sortterm .= "$col $dir, "; - } - } else { - $sortterm = $this->extraSortFields.', '; - } - } - if ($list instanceof ArrayList) { - // Fix bug in 3.1.3+ where ArrayList doesn't account for quotes - $sortterm .= $this->getSortTable($list).'.'.$this->getSortField(); - } else { - $sortterm .= '"'.$this->getSortTable($list).'"."'.$this->getSortField().'"'; - } - return $list->sort($sortterm); - } else { - return $list; - } - } - - /** - * Handles requests to reorder a set of IDs in a specific order. - * - * @param GridField $grid - * @param SS_HTTPRequest $request - * @return SS_HTTPResponse - */ - public function handleReorder($grid, $request) { - if (!$this->immediateUpdate) - { - $this->httpError(400); - } - $list = $grid->getList(); - $modelClass = $grid->getModelClass(); - if ($list instanceof ManyManyList && !singleton($modelClass)->canView()) { - $this->httpError(403); - } else if(!($list instanceof ManyManyList) && !singleton($modelClass)->canEdit()) { - $this->httpError(403); - } - - // Save any un-committed changes to the gridfield - if(($form = $grid->getForm()) && ($record = $form->getRecord()) ) { - $form->loadDataFrom($request->requestVars(), true); - $grid->saveInto($record); - } - - // Get records from the `GridFieldEditableColumns` column - $data = $request->postVar($grid->getName()); - $sortedIDs = $this->getSortedIDs($data); - if (!$this->executeReorder($grid, $sortedIDs)) - { - $this->httpError(400); - } - - Controller::curr()->getResponse()->addHeader('X-Status', rawurlencode('Records reordered.')); - return $grid->FieldHolder(); - } - - /** - * Get mapping of sort value to ID from posted data - * - * @param array $data Raw posted data - * @return array - */ - protected function getSortedIDs($data) { - if (empty($data['GridFieldEditableColumns'])) { - return array(); - } - - $sortedIDs = array(); - foreach($data['GridFieldEditableColumns'] as $id => $recordData) { - $sortValue = $recordData[$this->sortField]; - $sortedIDs[$sortValue] = $id; - } - ksort($sortedIDs); - return $sortedIDs; - } - - /** - * Handles requests to move an item to the previous or next page. - */ - public function handleMoveToPage(GridField $grid, $request) { - if(!$paginator = $grid->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldPaginator')) { - $this->httpError(404, 'Paginator component not found'); - } - - $move = $request->postVar('move'); - $field = $this->getSortField(); - - $list = $grid->getList(); - $manip = $grid->getManipulatedList(); - - $existing = $manip->map('ID', $field)->toArray(); - $values = $existing; - $order = array(); - - $id = isset($move['id']) ? (int) $move['id'] : null; - $to = isset($move['page']) ? $move['page'] : null; - - if(!isset($values[$id])) { - $this->httpError(400, 'Invalid item ID'); - } - - $this->populateSortValues($list); - - $page = ((int) $grid->getState()->GridFieldPaginator->currentPage) ?: 1; - $per = $paginator->getItemsPerPage(); - - if($to == 'prev') { - $swap = $list->limit(1, ($page - 1) * $per - 1)->first(); - $values[$swap->ID] = $swap->$field; - - $order[] = $id; - $order[] = $swap->ID; - - foreach($existing as $_id => $sort) { - if($id != $_id) $order[] = $_id; - } - } elseif($to == 'next') { - $swap = $list->limit(1, $page * $per)->first(); - $values[$swap->ID] = $swap->$field; - - foreach($existing as $_id => $sort) { - if($id != $_id) $order[] = $_id; - } - - $order[] = $swap->ID; - $order[] = $id; - } else { - $this->httpError(400, 'Invalid page target'); - } - - $this->reorderItems($list, $values, $order); - - return $grid->FieldHolder(); - } - - /** - * Handle saving when 'immediateUpdate' is disabled, otherwise this isn't - * necessary for the default sort mode. - */ - public function handleSave(GridField $grid, DataObjectInterface $record) { - if (!$this->immediateUpdate) - { - $value = $grid->Value(); - $sortedIDs = $this->getSortedIDs($value); - if ($sortedIDs) { - $this->executeReorder($grid, $sortedIDs); - } - } - } - - /** - * @param GridField $grid - * @param array $sortedIDs List of IDS, where the key is the sort field value to save - * @return bool - */ - protected function executeReorder(GridField $grid, $sortedIDs) { - if(!is_array($sortedIDs)) { - return false; - } - $field = $this->getSortField(); - - $sortterm = ''; - if ($this->extraSortFields) { - if (is_array($this->extraSortFields)) { - foreach($this->extraSortFields as $col => $dir) { - $sortterm .= "$col $dir, "; - } - } else { - $sortterm = $this->extraSortFields.', '; - } - } - $list = $grid->getList(); - $sortterm .= '"'.$this->getSortTable($list).'"."'.$field.'"'; - $items = $list->filter('ID', $sortedIDs)->sort($sortterm); - - // Ensure that each provided ID corresponded to an actual object. - if(count($items) != count($sortedIDs)) { - return false; - } - - // Populate each object we are sorting with a sort value. - $this->populateSortValues($items); - - // Generate the current sort values. - if ($items instanceof ManyManyList) - { - $current = array(); - foreach ($items->toArray() as $record) - { - // NOTE: _SortColumn0 is the first ->sort() field - // used by SS when functions are detected in a SELECT - // or CASE WHEN. - if (isset($record->_SortColumn0)) { - $current[$record->ID] = $record->_SortColumn0; - } else { - $current[$record->ID] = $record->$field; - } - } - } - else - { - $current = $items->map('ID', $field)->toArray(); - } - - // Perform the actual re-ordering. - $this->reorderItems($list, $current, $sortedIDs); - return true; - } - - protected function reorderItems($list, array $values, array $sortedIDs) { - $sortField = $this->getSortField(); - /** @var SS_List $map */ - $map = $list->map('ID', $sortField); - //fix for versions of SS that return inconsistent types for `map` function - if ($map instanceof SS_Map) { - $map = $map->toArray(); - } - - // If not a ManyManyList and using versioning, detect it. - $isVersioned = false; - $class = $list->dataClass(); - if ($class == $this->getSortTable($list)) { - $isVersioned = $class::has_extension('SilverStripe\\ORM\\Versioning\\Versioned'); - } - - // Loop through each item, and update the sort values which do not - // match to order the objects. - if (!$isVersioned) { - $sortTable = $this->getSortTable($list); - $additionalSQL = (!$list instanceof ManyManyList) ? ', "LastEdited" = NOW()' : ''; - foreach($sortedIDs as $sortValue => $id) { - if($map[$id] != $sortValue) { - DB::query(sprintf( - 'UPDATE "%s" SET "%s" = %d%s WHERE %s', - $sortTable, - $sortField, - $sortValue, - $additionalSQL, - $this->getSortTableClauseForIds($list, $id) - )); - } - } - } else { - // For versioned objects, modify them with the ORM so that the - // *_versions table is updated. This ensures re-ordering works - // similar to the SiteTree where you change the position, and then - // you go into the record and publish it. - foreach($sortedIDs as $sortValue => $id) { - if($map[$id] != $sortValue) { - $record = $class::get()->byID($id); - $record->$sortField = $sortValue; - $record->write(); - } - } - } - - $this->extend('onAfterReorderItems', $list); - } - - protected function populateSortValues(DataList $list) { - $list = clone $list; - $field = $this->getSortField(); - $table = $this->getSortTable($list); - $clause = sprintf('"%s"."%s" = 0', $table, $this->getSortField()); - $additionalSQL = (!$list instanceof ManyManyList) ? ', "LastEdited" = NOW()' : ''; - - foreach($list->where($clause)->column('ID') as $id) { - $max = DB::query(sprintf('SELECT MAX("%s") + 1 FROM "%s"', $field, $table)); - $max = $max->value(); - - DB::query(sprintf( - 'UPDATE "%s" SET "%s" = %d%s WHERE %s', - $table, - $field, - $max, - $additionalSQL, - $this->getSortTableClauseForIds($list, $id) - )); - } - } - - protected function getSortTableClauseForIds(DataList $list, $ids) { - if(is_array($ids)) { - $value = 'IN (' . implode(', ', array_map('intval', $ids)) . ')'; - } else { - $value = '= ' . (int) $ids; - } - - if($list instanceof ManyManyList) { - $extra = $list->getExtraFields(); - $key = $list->getLocalKey(); - $foreignKey = $list->getForeignKey(); - $foreignID = (int) $list->getForeignID(); - - if($extra && array_key_exists($this->getSortField(), $extra)) { - return sprintf( - '"%s" %s AND "%s" = %d', - $key, - $value, - $foreignKey, - $foreignID - ); - } - } - - return "\"ID\" $value"; - } - + GridField_ColumnProvider, + GridField_DataManipulator, + GridField_HTMLProvider, + GridField_URLHandler, + GridField_SaveHandler +{ + + /** + * @see $immediateUpdate + * @var boolean + */ + private static $default_immediate_update = true; + + private static $allowed_actions = array( + 'handleReorder', + 'handleMoveToPage' + ); + + /** + * The database field which specifies the sort, defaults to "Sort". + * + * @see setSortField() + * @var string + */ + protected $sortField; + + /** + * If set to true, when an item is re-ordered, it will update on the + * database and refresh the gridfield. When set to false, it will only + * update the sort order when the record is saved. + * + * @var boolean + */ + protected $immediateUpdate; + + /** + * Extra sort fields to apply before the sort field. + * + * @see setExtraSortFields() + * @var string|array + */ + protected $extraSortFields = null; + + /** + * The number of the column containing the reorder handles + * + * @see setReorderColumnNumber() + * @var int + */ + protected $reorderColumnNumber = 0; + + /** + * @param string $sortField + */ + public function __construct($sortField = 'Sort') + { + parent::__construct(); + $this->sortField = $sortField; + $this->immediateUpdate = $this->config()->default_immediate_update; + } + + /** + * @return string + */ + public function getSortField() + { + return $this->sortField; + } + + /** + * Sets the field used to specify the sort. + * + * @param string $sortField + * @return GridFieldOrderableRows $this + */ + public function setSortField($field) + { + $this->sortField = $field; + return $this; + } + + /** + * @return boolean + */ + public function getImmediateUpdate() + { + return $this->immediateUpdate; + } + + /** + * @see $immediateUpdate + * @param boolean $immediateUpdate + * @return GridFieldOrderableRows $this + */ + public function setImmediateUpdate($bool) + { + $this->immediateUpdate = $bool; + return $this; + } + + /** + * @return string|array + */ + public function getExtraSortFields() + { + return $this->extraSortFields; + } + + /** + * Sets extra sort fields to apply before the sort field. + * + * @param string|array $fields + * @return GridFieldOrderableRows $this + */ + public function setExtraSortFields($fields) + { + $this->extraSortFields = $fields; + return $this; + } + + /** + * @return int + */ + public function getReorderColumnNumber() + { + return $this->reorderColumnNumber; + } + + /** + * Sets the number of the column containing the reorder handles. + * + * @param int $colno + * @return GridFieldOrderableRows $this + */ + public function setReorderColumnNumber($colno) + { + $this->reorderColumnNumber = $colno; + return $this; + } + + /** + * Gets the table which contains the sort field. + * + * @param DataList $list + * @return string + */ + public function getSortTable(SS_List $list) + { + $field = $this->getSortField(); + + if ($list instanceof ManyManyList) { + $extra = $list->getExtraFields(); + $table = $list->getJoinTable(); + + if ($extra && array_key_exists($field, $extra)) { + return $table; + } + } + + $classes = ClassInfo::dataClassesFor($list->dataClass()); + + foreach ($classes as $class) { + if (singleton($class)->hasDataBaseField($field)) { + return $class; + } + } + + throw new Exception("Couldn't find the sort field '$field'"); + } + + public function getURLHandlers($grid) + { + return array( + 'POST reorder' => 'handleReorder', + 'POST movetopage' => 'handleMoveToPage' + ); + } + + /** + * @param GridField $field + */ + public function getHTMLFragments($field) + { + GridFieldExtensions::include_requirements(); + + $field->addExtraClass('ss-gridfield-orderable'); + $field->setAttribute('data-immediate-update', (string)(int)$this->immediateUpdate); + $field->setAttribute('data-url-reorder', $field->Link('reorder')); + $field->setAttribute('data-url-movetopage', $field->Link('movetopage')); + } + + public function augmentColumns($grid, &$cols) + { + if (!in_array('Reorder', $cols) && $grid->getState()->GridFieldOrderableRows->enabled) { + array_splice($cols, $this->reorderColumnNumber, 0, 'Reorder'); + } + } + + public function getColumnsHandled($grid) + { + return array('Reorder'); + } + + public function getColumnContent($grid, $record, $col) + { + // In case you are using GridFieldEditableColumns, this ensures that + // the correct sort order is saved. If you are not using that component, + // this will be ignored by other components, but will still work for this. + $sortFieldName = sprintf( + '%s[GridFieldEditableColumns][%s][%s]', + $grid->getName(), + $record->ID, + $this->getSortField() + ); + $sortField = new HiddenField($sortFieldName, false, $record->getField($this->getSortField())); + $sortField->addExtraClass('ss-orderable-hidden-sort'); + $sortField->setForm($grid->getForm()); + + return ViewableData::create()->customise(array( + 'SortField' => $sortField + ))->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRowsDragHandle'); + } + + public function getColumnAttributes($grid, $record, $col) + { + return array('class' => 'col-reorder'); + } + + public function getColumnMetadata($grid, $col) + { + if ($fieldLabels = singleton($grid->getModelClass())->fieldLabels()) { + return array('title' => isset($fieldLabels['Reorder']) ? $fieldLabels['Reorder'] : ''); + } + + return array('title' => ''); + } + + public function getManipulatedData(GridField $grid, SS_List $list) + { + $state = $grid->getState(); + $sorted = (bool) ((string) $state->GridFieldSortableHeader->SortColumn); + + // If the data has not been sorted by the user, then sort it by the + // sort column, otherwise disable reordering. + $state->GridFieldOrderableRows->enabled = !$sorted; + + if (!$sorted) { + $sortterm = ''; + if ($this->extraSortFields) { + if (is_array($this->extraSortFields)) { + foreach ($this->extraSortFields as $col => $dir) { + $sortterm .= "$col $dir, "; + } + } else { + $sortterm = $this->extraSortFields.', '; + } + } + if ($list instanceof ArrayList) { + // Fix bug in 3.1.3+ where ArrayList doesn't account for quotes + $sortterm .= $this->getSortTable($list).'.'.$this->getSortField(); + } else { + $sortterm .= '"'.$this->getSortTable($list).'"."'.$this->getSortField().'"'; + } + return $list->sort($sortterm); + } else { + return $list; + } + } + + /** + * Handles requests to reorder a set of IDs in a specific order. + * + * @param GridField $grid + * @param SS_HTTPRequest $request + * @return SS_HTTPResponse + */ + public function handleReorder($grid, $request) + { + if (!$this->immediateUpdate) { + $this->httpError(400); + } + $list = $grid->getList(); + $modelClass = $grid->getModelClass(); + if ($list instanceof ManyManyList && !singleton($modelClass)->canView()) { + $this->httpError(403); + } elseif (!($list instanceof ManyManyList) && !singleton($modelClass)->canEdit()) { + $this->httpError(403); + } + + // Save any un-committed changes to the gridfield + if (($form = $grid->getForm()) && ($record = $form->getRecord())) { + $form->loadDataFrom($request->requestVars(), true); + $grid->saveInto($record); + } + + // Get records from the `GridFieldEditableColumns` column + $data = $request->postVar($grid->getName()); + $sortedIDs = $this->getSortedIDs($data); + if (!$this->executeReorder($grid, $sortedIDs)) { + $this->httpError(400); + } + + Controller::curr()->getResponse()->addHeader('X-Status', rawurlencode('Records reordered.')); + return $grid->FieldHolder(); + } + + /** + * Get mapping of sort value to ID from posted data + * + * @param array $data Raw posted data + * @return array + */ + protected function getSortedIDs($data) + { + if (empty($data['GridFieldEditableColumns'])) { + return array(); + } + + $sortedIDs = array(); + foreach ($data['GridFieldEditableColumns'] as $id => $recordData) { + $sortValue = $recordData[$this->sortField]; + $sortedIDs[$sortValue] = $id; + } + ksort($sortedIDs); + return $sortedIDs; + } + + /** + * Handles requests to move an item to the previous or next page. + */ + public function handleMoveToPage(GridField $grid, $request) + { + if (!$paginator = $grid->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldPaginator')) { + $this->httpError(404, 'Paginator component not found'); + } + + $move = $request->postVar('move'); + $field = $this->getSortField(); + + $list = $grid->getList(); + $manip = $grid->getManipulatedList(); + + $existing = $manip->map('ID', $field)->toArray(); + $values = $existing; + $order = array(); + + $id = isset($move['id']) ? (int) $move['id'] : null; + $to = isset($move['page']) ? $move['page'] : null; + + if (!isset($values[$id])) { + $this->httpError(400, 'Invalid item ID'); + } + + $this->populateSortValues($list); + + $page = ((int) $grid->getState()->GridFieldPaginator->currentPage) ?: 1; + $per = $paginator->getItemsPerPage(); + + if ($to == 'prev') { + $swap = $list->limit(1, ($page - 1) * $per - 1)->first(); + $values[$swap->ID] = $swap->$field; + + $order[] = $id; + $order[] = $swap->ID; + + foreach ($existing as $_id => $sort) { + if ($id != $_id) { + $order[] = $_id; + } + } + } elseif ($to == 'next') { + $swap = $list->limit(1, $page * $per)->first(); + $values[$swap->ID] = $swap->$field; + + foreach ($existing as $_id => $sort) { + if ($id != $_id) { + $order[] = $_id; + } + } + + $order[] = $swap->ID; + $order[] = $id; + } else { + $this->httpError(400, 'Invalid page target'); + } + + $this->reorderItems($list, $values, $order); + + return $grid->FieldHolder(); + } + + /** + * Handle saving when 'immediateUpdate' is disabled, otherwise this isn't + * necessary for the default sort mode. + */ + public function handleSave(GridField $grid, DataObjectInterface $record) + { + if (!$this->immediateUpdate) { + $value = $grid->Value(); + $sortedIDs = $this->getSortedIDs($value); + if ($sortedIDs) { + $this->executeReorder($grid, $sortedIDs); + } + } + } + + /** + * @param GridField $grid + * @param array $sortedIDs List of IDS, where the key is the sort field value to save + * @return bool + */ + protected function executeReorder(GridField $grid, $sortedIDs) + { + if (!is_array($sortedIDs)) { + return false; + } + $field = $this->getSortField(); + + $sortterm = ''; + if ($this->extraSortFields) { + if (is_array($this->extraSortFields)) { + foreach ($this->extraSortFields as $col => $dir) { + $sortterm .= "$col $dir, "; + } + } else { + $sortterm = $this->extraSortFields.', '; + } + } + $list = $grid->getList(); + $sortterm .= '"'.$this->getSortTable($list).'"."'.$field.'"'; + $items = $list->filter('ID', $sortedIDs)->sort($sortterm); + + // Ensure that each provided ID corresponded to an actual object. + if (count($items) != count($sortedIDs)) { + return false; + } + + // Populate each object we are sorting with a sort value. + $this->populateSortValues($items); + + // Generate the current sort values. + if ($items instanceof ManyManyList) { + $current = array(); + foreach ($items->toArray() as $record) { + // NOTE: _SortColumn0 is the first ->sort() field + // used by SS when functions are detected in a SELECT + // or CASE WHEN. + if (isset($record->_SortColumn0)) { + $current[$record->ID] = $record->_SortColumn0; + } else { + $current[$record->ID] = $record->$field; + } + } + } else { + $current = $items->map('ID', $field)->toArray(); + } + + // Perform the actual re-ordering. + $this->reorderItems($list, $current, $sortedIDs); + return true; + } + + protected function reorderItems($list, array $values, array $sortedIDs) + { + $sortField = $this->getSortField(); + /** @var SS_List $map */ + $map = $list->map('ID', $sortField); + //fix for versions of SS that return inconsistent types for `map` function + if ($map instanceof SS_Map) { + $map = $map->toArray(); + } + + // If not a ManyManyList and using versioning, detect it. + $isVersioned = false; + $class = $list->dataClass(); + if ($class == $this->getSortTable($list)) { + $isVersioned = $class::has_extension('SilverStripe\\ORM\\Versioning\\Versioned'); + } + + // Loop through each item, and update the sort values which do not + // match to order the objects. + if (!$isVersioned) { + $sortTable = $this->getSortTable($list); + $additionalSQL = (!$list instanceof ManyManyList) ? ', "LastEdited" = NOW()' : ''; + foreach ($sortedIDs as $sortValue => $id) { + if ($map[$id] != $sortValue) { + DB::query(sprintf( + 'UPDATE "%s" SET "%s" = %d%s WHERE %s', + $sortTable, + $sortField, + $sortValue, + $additionalSQL, + $this->getSortTableClauseForIds($list, $id) + )); + } + } + } else { + // For versioned objects, modify them with the ORM so that the + // *_versions table is updated. This ensures re-ordering works + // similar to the SiteTree where you change the position, and then + // you go into the record and publish it. + foreach ($sortedIDs as $sortValue => $id) { + if ($map[$id] != $sortValue) { + $record = $class::get()->byID($id); + $record->$sortField = $sortValue; + $record->write(); + } + } + } + + $this->extend('onAfterReorderItems', $list); + } + + protected function populateSortValues(DataList $list) + { + $list = clone $list; + $field = $this->getSortField(); + $table = $this->getSortTable($list); + $clause = sprintf('"%s"."%s" = 0', $table, $this->getSortField()); + $additionalSQL = (!$list instanceof ManyManyList) ? ', "LastEdited" = NOW()' : ''; + + foreach ($list->where($clause)->column('ID') as $id) { + $max = DB::query(sprintf('SELECT MAX("%s") + 1 FROM "%s"', $field, $table)); + $max = $max->value(); + + DB::query(sprintf( + 'UPDATE "%s" SET "%s" = %d%s WHERE %s', + $table, + $field, + $max, + $additionalSQL, + $this->getSortTableClauseForIds($list, $id) + )); + } + } + + protected function getSortTableClauseForIds(DataList $list, $ids) + { + if (is_array($ids)) { + $value = 'IN (' . implode(', ', array_map('intval', $ids)) . ')'; + } else { + $value = '= ' . (int) $ids; + } + + if ($list instanceof ManyManyList) { + $extra = $list->getExtraFields(); + $key = $list->getLocalKey(); + $foreignKey = $list->getForeignKey(); + $foreignID = (int) $list->getForeignID(); + + if ($extra && array_key_exists($this->getSortField(), $extra)) { + return sprintf( + '"%s" %s AND "%s" = %d', + $key, + $value, + $foreignKey, + $foreignID + ); + } + } + + return "\"ID\" $value"; + } } diff --git a/src/GridFieldRequestHandler.php b/src/GridFieldRequestHandler.php index a6a74c5..fd92966 100644 --- a/src/GridFieldRequestHandler.php +++ b/src/GridFieldRequestHandler.php @@ -22,145 +22,155 @@ use SilverStripe\ORM\ArrayList; * as tabs, breadcrumbs and a back link. Much of this code is extracted from the * detail form. */ -abstract class GridFieldRequestHandler extends RequestHandler { +abstract class GridFieldRequestHandler extends RequestHandler +{ - private static $allowed_actions = array( - 'Form' - ); + private static $allowed_actions = array( + 'Form' + ); - /** - * @var GridField - */ - protected $grid; + /** + * @var GridField + */ + protected $grid; - /** - * @var GridFieldComponent - */ - protected $component; + /** + * @var GridFieldComponent + */ + protected $component; - /** - * @var string - */ - protected $name; + /** + * @var string + */ + protected $name; - /** - * @var string - */ - protected $template = __CLASS__; + /** + * @var string + */ + protected $template = __CLASS__; - public function __construct(GridField $grid, GridFieldComponent $component, $name) { - $this->grid = $grid; - $this->component = $component; - $this->name = $name; + public function __construct(GridField $grid, GridFieldComponent $component, $name) + { + $this->grid = $grid; + $this->component = $component; + $this->name = $name; - parent::__construct(); - } + parent::__construct(); + } - public function index($request) { - $result = $this->renderWith($this->template); + public function index($request) + { + $result = $this->renderWith($this->template); - if($request->isAjax()) { - return $result; - } else { - return $this->getTopLevelController()->customise(array( - 'Content' => $result - )); - } - } + if ($request->isAjax()) { + return $result; + } else { + return $this->getTopLevelController()->customise(array( + 'Content' => $result + )); + } + } - public function Link($action = null) { - return Controller::join_links($this->grid->Link(), $this->name, $action); - } + public function Link($action = null) + { + return Controller::join_links($this->grid->Link(), $this->name, $action); + } - /** - * This method should be overloaded to build out the detail form. - * - * @return Form - */ - public function Form() { - $form = new Form( - $this, - 'SilverStripe\\Forms\\Form', - new FieldList($root = new TabSet('Root', new Tab('Main'))), - new FieldList() - ); + /** + * This method should be overloaded to build out the detail form. + * + * @return Form + */ + public function Form() + { + $form = new Form( + $this, + 'SilverStripe\\Forms\\Form', + new FieldList($root = new TabSet('Root', new Tab('Main'))), + new FieldList() + ); - if($this->getTopLevelController() instanceof LeftAndMain) { - $form->setTemplate('LeftAndMain_EditForm'); - $form->addExtraClass('cms-content cms-edit-form cms-tabset center'); - $form->setAttribute('data-pjax-fragment', 'CurrentForm Content'); + if ($this->getTopLevelController() instanceof LeftAndMain) { + $form->setTemplate('LeftAndMain_EditForm'); + $form->addExtraClass('cms-content cms-edit-form cms-tabset center'); + $form->setAttribute('data-pjax-fragment', 'CurrentForm Content'); - $root->setTemplate('CMSTabSet'); - $form->Backlink = $this->getBackLink(); - } + $root->setTemplate('CMSTabSet'); + $form->Backlink = $this->getBackLink(); + } - return $form; - } + return $form; + } - /** - * @return Controller - */ - public function getController() { - return $this->grid->getForm()->getController(); - } + /** + * @return Controller + */ + public function getController() + { + return $this->grid->getForm()->getController(); + } - /** - * @param string $template - */ - public function setTemplate($template) { - $this->template = $template; - } + /** + * @param string $template + */ + public function setTemplate($template) + { + $this->template = $template; + } - /** - * @return string - */ - public function getTemplate() { - return $this->template; - } + /** + * @return string + */ + public function getTemplate() + { + return $this->template; + } - /** - * @return ArrayList - */ - public function getBreadcrumbs() { - $controller = $this->getController(); + /** + * @return ArrayList + */ + public function getBreadcrumbs() + { + $controller = $this->getController(); - if($controller->hasMethod('Breadcrumbs')) { - return $controller->Breadcrumbs(); - } else { - return new ArrayList(); - } - } + if ($controller->hasMethod('Breadcrumbs')) { + return $controller->Breadcrumbs(); + } else { + return new ArrayList(); + } + } - /** - * @return string - */ - protected function getBackLink() { - $controller = $this->getTopLevelController(); + /** + * @return string + */ + protected function getBackLink() + { + $controller = $this->getTopLevelController(); - if($controller->hasMethod('Backlink')) { - return $controller->Backlink(); - } else { - return $controller->Link(); - } - } + if ($controller->hasMethod('Backlink')) { + return $controller->Backlink(); + } else { + return $controller->Link(); + } + } - /** - * @return Controller - */ - protected function getTopLevelController() { - $controller = $this->getController(); + /** + * @return Controller + */ + protected function getTopLevelController() + { + $controller = $this->getController(); - while($controller) { - if($controller instanceof GridFieldRequestHandler) { - $controller = $controller->getController(); - } elseif($controller instanceof GridFieldDetailForm_ItemRequest) { - $controller = $controller->getController(); - } else { - break; - } - } - - return $controller; - } + while ($controller) { + if ($controller instanceof GridFieldRequestHandler) { + $controller = $controller->getController(); + } elseif ($controller instanceof GridFieldDetailForm_ItemRequest) { + $controller = $controller->getController(); + } else { + break; + } + } + return $controller; + } } diff --git a/src/GridFieldTitleHeader.php b/src/GridFieldTitleHeader.php index d1a9d4f..e6f8804 100644 --- a/src/GridFieldTitleHeader.php +++ b/src/GridFieldTitleHeader.php @@ -9,23 +9,24 @@ use SilverStripe\View\ArrayData; /** * A simple header which displays column titles. */ -class GridFieldTitleHeader implements GridField_HTMLProvider { +class GridFieldTitleHeader implements GridField_HTMLProvider +{ - public function getHTMLFragments($grid) { - $cols = new ArrayList(); + public function getHTMLFragments($grid) + { + $cols = new ArrayList(); - foreach ($grid->getColumns() as $name) { - $meta = $grid->getColumnMetadata($name); + foreach ($grid->getColumns() as $name) { + $meta = $grid->getColumnMetadata($name); - $cols->push(new ArrayData(array( - 'Name' => $name, - 'Title' => $meta['title'] - ))); - } - - return array( - 'header' => $cols->renderWith(__CLASS__) - ); - } + $cols->push(new ArrayData(array( + 'Name' => $name, + 'Title' => $meta['title'] + ))); + } + return array( + 'header' => $cols->renderWith(__CLASS__) + ); + } } diff --git a/tests/GridFieldAddNewMultiClassTest.php b/tests/GridFieldAddNewMultiClassTest.php index 34198b5..33e6943 100644 --- a/tests/GridFieldAddNewMultiClassTest.php +++ b/tests/GridFieldAddNewMultiClassTest.php @@ -8,57 +8,65 @@ use SilverStripe\GridFieldExtensions\GridFieldAddNewMultiClass; /** * Tests for {@link GridFieldAddNewMultiClass}. */ -class GridFieldAddNewMultiClassTest extends SapphireTest { +class GridFieldAddNewMultiClassTest extends SapphireTest +{ - public function testGetClasses() { - $grid = new GridField('TestGridField'); - $grid->setModelClass('GridFieldAddNewMultiClassTest_A'); + public function testGetClasses() + { + $grid = new GridField('TestGridField'); + $grid->setModelClass('GridFieldAddNewMultiClassTest_A'); - $component = new GridFieldAddNewMultiClass(); + $component = new GridFieldAddNewMultiClass(); - $this->assertEquals( - array( - 'GridFieldAddNewMultiClassTest_A' => 'A', - 'GridFieldAddNewMultiClassTest_B' => 'B', - 'GridFieldAddNewMultiClassTest_C' => 'C' - ), - $component->getClasses($grid), - 'Subclasses are populated by default and sorted' - ); + $this->assertEquals( + array( + 'GridFieldAddNewMultiClassTest_A' => 'A', + 'GridFieldAddNewMultiClassTest_B' => 'B', + 'GridFieldAddNewMultiClassTest_C' => 'C' + ), + $component->getClasses($grid), + 'Subclasses are populated by default and sorted' + ); - $component->setClasses(array( - 'GridFieldAddNewMultiClassTest_B' => 'Custom Title', - 'GridFieldAddNewMultiClassTest_A' - )); - - $this->assertEquals( - array( - 'GridFieldAddNewMultiClassTest_B' => 'Custom Title', - 'GridFieldAddNewMultiClassTest_A' => 'A' - ), - $component->getClasses($grid), - 'Sorting and custom titles can be specified' - ); - } + $component->setClasses(array( + 'GridFieldAddNewMultiClassTest_B' => 'Custom Title', + 'GridFieldAddNewMultiClassTest_A' + )); + $this->assertEquals( + array( + 'GridFieldAddNewMultiClassTest_B' => 'Custom Title', + 'GridFieldAddNewMultiClassTest_A' => 'A' + ), + $component->getClasses($grid), + 'Sorting and custom titles can be specified' + ); + } } /**#@+ * @ignore */ -class GridFieldAddNewMultiClassTest_A implements TestOnly { - public function i18n_singular_name() { - $class = get_class($this); - return substr($class, strpos($class, '_') + 1); - } +class GridFieldAddNewMultiClassTest_A implements TestOnly +{ + public function i18n_singular_name() + { + $class = get_class($this); + return substr($class, strpos($class, '_') + 1); + } - public function canCreate() { - return true; - } + public function canCreate() + { + return true; + } } -class GridFieldAddNewMultiClassTest_B extends GridFieldAddNewMultiClassTest_A implements TestOnly {} -class GridFieldAddNewMultiClassTest_C extends GridFieldAddNewMultiClassTest_A implements TestOnly {} +class GridFieldAddNewMultiClassTest_B extends GridFieldAddNewMultiClassTest_A implements TestOnly +{ +} +class GridFieldAddNewMultiClassTest_C extends GridFieldAddNewMultiClassTest_A implements TestOnly +{ +} /**#@-*/ diff --git a/tests/GridFieldOrderableRowsTest.php b/tests/GridFieldOrderableRowsTest.php index ac080f6..6343a72 100644 --- a/tests/GridFieldOrderableRowsTest.php +++ b/tests/GridFieldOrderableRowsTest.php @@ -10,17 +10,18 @@ use SilverStripe\ORM\DataObject; /** * Tests for the {@link GridFieldOrderableRows} component. */ -class GridFieldOrderableRowsTest extends SapphireTest { +class GridFieldOrderableRowsTest extends SapphireTest +{ - protected $usesDatabase = true; + protected $usesDatabase = true; - // protected static $fixture_file = 'GridFieldOrderableRowsTest.yml'; + // protected static $fixture_file = 'GridFieldOrderableRowsTest.yml'; - protected $extraDataObjects = array( - 'GridFieldOrderableRowsTest_Parent', - 'GridFieldOrderableRowsTest_Ordered', - 'GridFieldOrderableRowsTest_Subclass', - ); + protected $extraDataObjects = array( + 'GridFieldOrderableRowsTest_Parent', + 'GridFieldOrderableRowsTest_Ordered', + 'GridFieldOrderableRowsTest_Subclass', + ); public function setUp() { @@ -28,111 +29,112 @@ class GridFieldOrderableRowsTest extends SapphireTest { $this->markTestSkipped('Upgrade to 4.0: Needs to be re-implemented.'); } - public function testReorderItems() { + public function testReorderItems() + { $orderable = new GridFieldOrderableRows('ManyManySort'); - $reflection = new ReflectionMethod($orderable, 'executeReorder'); - $reflection->setAccessible(true); + $reflection = new ReflectionMethod($orderable, 'executeReorder'); + $reflection->setAccessible(true); - $parent = $this->objFromFixture('GridFieldOrderableRowsTest_Parent', 'parent'); + $parent = $this->objFromFixture('GridFieldOrderableRowsTest_Parent', 'parent'); - $config = new GridFieldConfig_RelationEditor(); - $config->addComponent($orderable); + $config = new GridFieldConfig_RelationEditor(); + $config->addComponent($orderable); - $grid = new GridField( - 'MyManyMany', - 'My Many Many', - $parent->MyManyMany()->sort('ManyManySort'), - $config - ); + $grid = new GridField( + 'MyManyMany', + 'My Many Many', + $parent->MyManyMany()->sort('ManyManySort'), + $config + ); - $originalOrder = $parent->MyManyMany()->sort('ManyManySort')->column('ID'); - $desiredOrder = array(); + $originalOrder = $parent->MyManyMany()->sort('ManyManySort')->column('ID'); + $desiredOrder = array(); - // Make order non-contiguous, and 1-based - foreach(array_reverse($originalOrder) as $index => $id) { - $desiredOrder[$index * 2 + 1] = $id; - } + // Make order non-contiguous, and 1-based + foreach (array_reverse($originalOrder) as $index => $id) { + $desiredOrder[$index * 2 + 1] = $id; + } - $this->assertNotEquals($originalOrder, $desiredOrder); + $this->assertNotEquals($originalOrder, $desiredOrder); - $reflection->invoke($orderable, $grid, $desiredOrder); + $reflection->invoke($orderable, $grid, $desiredOrder); - $newOrder = $parent->MyManyMany()->sort('ManyManySort')->map('ManyManySort', 'ID')->toArray(); + $newOrder = $parent->MyManyMany()->sort('ManyManySort')->map('ManyManySort', 'ID')->toArray(); - $this->assertEquals($desiredOrder, $newOrder); + $this->assertEquals($desiredOrder, $newOrder); + } - } - - /** - * @covers GridFieldOrderableRows::getSortTable - */ - public function testGetSortTable() { + /** + * @covers GridFieldOrderableRows::getSortTable + */ + public function testGetSortTable() + { $orderable = new GridFieldOrderableRows(); - $parent = new GridFieldOrderableRowsTest_Parent(); - $parent->write(); + $parent = new GridFieldOrderableRowsTest_Parent(); + $parent->write(); - $this->assertEquals( - 'GridFieldOrderableRowsTest_Ordered', - $orderable->getSortTable($parent->MyHasMany()) - ); + $this->assertEquals( + 'GridFieldOrderableRowsTest_Ordered', + $orderable->getSortTable($parent->MyHasMany()) + ); - $this->assertEquals( - 'GridFieldOrderableRowsTest_Ordered', - $orderable->getSortTable($parent->MyHasManySubclass()) - ); + $this->assertEquals( + 'GridFieldOrderableRowsTest_Ordered', + $orderable->getSortTable($parent->MyHasManySubclass()) + ); - $this->assertEquals( - 'GridFieldOrderableRowsTest_Ordered', - $orderable->getSortTable($parent->MyManyMany()) - ); - - $this->assertEquals( - 'GridFieldOrderableRowsTest_Parent_MyManyMany', - $orderable->setSortField('ManyManySort')->getSortTable($parent->MyManyMany()) - ); - } + $this->assertEquals( + 'GridFieldOrderableRowsTest_Ordered', + $orderable->getSortTable($parent->MyManyMany()) + ); + $this->assertEquals( + 'GridFieldOrderableRowsTest_Parent_MyManyMany', + $orderable->setSortField('ManyManySort')->getSortTable($parent->MyManyMany()) + ); + } } /**#@+ * @ignore */ -class GridFieldOrderableRowsTest_Parent extends DataObject implements TestOnly { +class GridFieldOrderableRowsTest_Parent extends DataObject implements TestOnly +{ - private static $has_many = array( - 'MyHasMany' => 'GridFieldOrderableRowsTest_Ordered', - 'MyHasManySubclass' => 'GridFieldOrderableRowsTest_Subclass' - ); + private static $has_many = array( + 'MyHasMany' => 'GridFieldOrderableRowsTest_Ordered', + 'MyHasManySubclass' => 'GridFieldOrderableRowsTest_Subclass' + ); - private static $many_many = array( - 'MyManyMany' => 'GridFieldOrderableRowsTest_Ordered' - ); - - private static $many_many_extraFields = array( - 'MyManyMany' => array('ManyManySort' => 'Int') - ); + private static $many_many = array( + 'MyManyMany' => 'GridFieldOrderableRowsTest_Ordered' + ); + private static $many_many_extraFields = array( + 'MyManyMany' => array('ManyManySort' => 'Int') + ); } -class GridFieldOrderableRowsTest_Ordered extends DataObject implements TestOnly { +class GridFieldOrderableRowsTest_Ordered extends DataObject implements TestOnly +{ - private static $db = array( - 'Sort' => 'Int' - ); + private static $db = array( + 'Sort' => 'Int' + ); - private static $has_one = array( - 'Parent' => 'GridFieldOrderableRowsTest_Parent' - ); - - private static $belongs_many_many =array( - 'MyManyMany' => 'GridFieldOrderableRowsTest_Parent', - ); + private static $has_one = array( + 'Parent' => 'GridFieldOrderableRowsTest_Parent' + ); + private static $belongs_many_many =array( + 'MyManyMany' => 'GridFieldOrderableRowsTest_Parent', + ); } -class GridFieldOrderableRowsTest_Subclass extends GridFieldOrderableRowsTest_Ordered implements TestOnly { +class GridFieldOrderableRowsTest_Subclass extends GridFieldOrderableRowsTest_Ordered implements TestOnly +{ } /**#@-*/ diff --git a/tests/GridFieldOrderableRowsTest.yml b/tests/GridFieldOrderableRowsTest.yml index fef3ea7..30b7e79 100644 --- a/tests/GridFieldOrderableRowsTest.yml +++ b/tests/GridFieldOrderableRowsTest.yml @@ -1,16 +1,10 @@ GridFieldOrderableRowsTest_Ordered: item1: - Sort: 0 item2: - Sort: 0 item3: - Sort: 0 item4: - Sort: 0 item5: - Sort: 0 item6: - Sort: 0 GridFieldOrderableRowsTest_Parent: parent: MyManyMany: