PSR-2 code style guidelines

This commit is contained in:
Robbie Averill 2016-12-21 15:34:58 +13:00
parent 4ee047b591
commit 2acf6dee47
14 changed files with 1661 additions and 1550 deletions

View File

@ -11,95 +11,105 @@ use SilverStripe\View\ArrayData;
* A modal search dialog which uses search context to search for and add * A modal search dialog which uses search context to search for and add
* existing records to a grid field. * 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( private static $allowed_actions = array(
'handleSearch' 'handleSearch'
); );
protected $title; protected $title;
protected $fragment; protected $fragment;
protected $searchList; protected $searchList;
/** /**
* @param string $fragment * @param string $fragment
*/ */
public function __construct($fragment = 'buttons-before-left') { public function __construct($fragment = 'buttons-before-left')
$this->fragment = $fragment; {
$this->title = _t('GridFieldExtensions.ADDEXISTING', 'Add Existing'); $this->fragment = $fragment;
} $this->title = _t('GridFieldExtensions.ADDEXISTING', 'Add Existing');
}
/** /**
* @return string * @return string
*/ */
public function getTitle() { public function getTitle()
return $this->title; {
} return $this->title;
}
/** /**
* @param string $title * @param string $title
* @return GridFieldAddExistingSearchButton $this * @return GridFieldAddExistingSearchButton $this
*/ */
public function setTitle($title) { public function setTitle($title)
$this->title = $title; {
return $this; $this->title = $title;
} return $this;
}
/** /**
* @return string * @return string
*/ */
public function getFragment() { public function getFragment()
return $this->fragment; {
} return $this->fragment;
}
/** /**
* @param string $fragment * @param string $fragment
* @return GridFieldAddExistingSearchButton $this * @return GridFieldAddExistingSearchButton $this
*/ */
public function setFragment($fragment) { public function setFragment($fragment)
$this->fragment = $fragment; {
return $this; $this->fragment = $fragment;
} return $this;
}
/** /**
* Sets a custom list to use to provide the searchable items. * Sets a custom list to use to provide the searchable items.
* *
* @param SS_List $list * @param SS_List $list
* @return GridFieldAddExistingSearchButton $this * @return GridFieldAddExistingSearchButton $this
*/ */
public function setSearchList(SS_List $list) { public function setSearchList(SS_List $list)
$this->searchList = $list; {
return $this; $this->searchList = $list;
} return $this;
}
/** /**
* @return SS_List|null * @return SS_List|null
*/ */
public function getSearchList() { public function getSearchList()
return $this->searchList; {
} return $this->searchList;
}
public function getHTMLFragments($grid) { public function getHTMLFragments($grid)
GridFieldExtensions::include_requirements(); {
GridFieldExtensions::include_requirements();
$data = new ArrayData(array( $data = new ArrayData(array(
'Title' => $this->getTitle(), 'Title' => $this->getTitle(),
'Link' => $grid->Link('add-existing-search') 'Link' => $grid->Link('add-existing-search')
)); ));
return array( return array(
$this->fragment => $data->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddExistingSearchButton'), $this->fragment => $data->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddExistingSearchButton'),
); );
} }
public function getURLHandlers($grid) { public function getURLHandlers($grid)
return array( {
'add-existing-search' => 'handleSearch' return array(
); 'add-existing-search' => 'handleSearch'
} );
}
public function handleSearch($grid, $request) {
return new GridFieldAddExistingSearchHandler($grid, $this);
}
public function handleSearch($grid, $request)
{
return new GridFieldAddExistingSearchHandler($grid, $this);
}
} }

View File

@ -14,107 +14,115 @@ use SilverStripe\ORM\PaginatedList;
* Used by {@link GridFieldAddExistingSearchButton} to provide the searching * Used by {@link GridFieldAddExistingSearchButton} to provide the searching
* functionality. * functionality.
*/ */
class GridFieldAddExistingSearchHandler extends RequestHandler { class GridFieldAddExistingSearchHandler extends RequestHandler
{
private static $allowed_actions = array( private static $allowed_actions = array(
'index', 'index',
'add', 'add',
'SearchForm' 'SearchForm'
); );
/** /**
* @var GridField * @var GridField
*/ */
protected $grid; protected $grid;
/** /**
* @var GridFieldAddExistingSearchButton * @var GridFieldAddExistingSearchButton
*/ */
protected $button; protected $button;
/** /**
* @var SearchContext * @var SearchContext
*/ */
protected $context; protected $context;
public function __construct($grid, $button) { public function __construct($grid, $button)
$this->grid = $grid; {
$this->button = $button; $this->grid = $grid;
$this->context = singleton($grid->getModelClass())->getDefaultSearchContext(); $this->button = $button;
$this->context = singleton($grid->getModelClass())->getDefaultSearchContext();
parent::__construct(); parent::__construct();
} }
public function index() { public function index()
return $this->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddExistingSearchHandler'); {
} return $this->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddExistingSearchHandler');
}
public function add($request) { public function add($request)
if(!$id = $request->postVar('id')) { {
$this->httpError(400); if (!$id = $request->postVar('id')) {
} $this->httpError(400);
}
$list = $this->grid->getList(); $list = $this->grid->getList();
$item = DataList::create($list->dataClass())->byID($id); $item = DataList::create($list->dataClass())->byID($id);
if(!$item) { if (!$item) {
$this->httpError(400); $this->httpError(400);
} }
$list->add($item); $list->add($item);
} }
/** /**
* @return Form * @return Form
*/ */
public function SearchForm() { public function SearchForm()
$form = new Form( {
$this, $form = new Form(
'SilverStripe\\CMS\\Search\\SearchForm', $this,
$this->context->getFields(), 'SilverStripe\\CMS\\Search\\SearchForm',
new FieldList( $this->context->getFields(),
FormAction::create('doSearch', _t('GridFieldExtensions.SEARCH', 'Search')) new FieldList(
->setUseButtonTag(true) FormAction::create('doSearch', _t('GridFieldExtensions.SEARCH', 'Search'))
->addExtraClass('ss-ui-button') ->setUseButtonTag(true)
->setAttribute('data-icon', 'magnifier') ->addExtraClass('ss-ui-button')
) ->setAttribute('data-icon', 'magnifier')
); )
);
$form->addExtraClass('stacked add-existing-search-form'); $form->addExtraClass('stacked add-existing-search-form');
$form->setFormMethod('GET'); $form->setFormMethod('GET');
return $form; return $form;
} }
public function doSearch($data, $form) { public function doSearch($data, $form)
$list = $this->context->getQuery($data, false, false, $this->getSearchList()); {
$list = $list->subtract($this->grid->getList()); $list = $this->context->getQuery($data, false, false, $this->getSearchList());
$list = new PaginatedList($list, $this->request); $list = $list->subtract($this->grid->getList());
$list = new PaginatedList($list, $this->request);
$data = $this->customise(array( $data = $this->customise(array(
'SearchForm' => $form, 'SearchForm' => $form,
'Items' => $list 'Items' => $list
)); ));
return $data->index(); return $data->index();
} }
public function Items() { public function Items()
$list = $this->getSearchList(); {
$list = $list->subtract($this->grid->getList()); $list = $this->getSearchList();
$list = new PaginatedList($list, $this->request); $list = $list->subtract($this->grid->getList());
$list = new PaginatedList($list, $this->request);
return $list; return $list;
} }
public function Link($action = null) { public function Link($action = null)
return Controller::join_links($this->grid->Link(), 'add-existing-search', $action); {
} 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());
}
/**
* @return DataList
*/
protected function getSearchList()
{
return $this->button->getSearchList() ?: DataList::create($this->grid->getList()->dataClass());
}
} }

View File

@ -17,170 +17,181 @@ use SilverStripe\View\Requirements;
/** /**
* Builds on the {@link GridFieldEditableColumns} component to allow creating new records. * 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 * @param string $fragment the fragment to render the button in
*/ */
public function __construct($fragment = 'buttons-before-left') { public function __construct($fragment = 'buttons-before-left')
$this->setFragment($fragment); {
$this->setTitle(_t('GridFieldExtensions.ADD', 'Add')); $this->setFragment($fragment);
} $this->setTitle(_t('GridFieldExtensions.ADD', 'Add'));
}
/** /**
* Gets the fragment name this button is rendered into. * Gets the fragment name this button is rendered into.
* *
* @return string * @return string
*/ */
public function getFragment() { public function getFragment()
return $this->fragment; {
} return $this->fragment;
}
/** /**
* Sets the fragment name this button is rendered into. * Sets the fragment name this button is rendered into.
* *
* @param string $fragment * @param string $fragment
* @return GridFieldAddNewInlineButton $this * @return GridFieldAddNewInlineButton $this
*/ */
public function setFragment($fragment) { public function setFragment($fragment)
$this->fragment = $fragment; {
return $this; $this->fragment = $fragment;
} return $this;
}
/** /**
* Gets the button title text. * Gets the button title text.
* *
* @return string * @return string
*/ */
public function getTitle() { public function getTitle()
return $this->title; {
} return $this->title;
}
/** /**
* Sets the button title text. * Sets the button title text.
* *
* @param string $title * @param string $title
* @return GridFieldAddNewInlineButton $this * @return GridFieldAddNewInlineButton $this
*/ */
public function setTitle($title) { public function setTitle($title)
$this->title = $title; {
return $this; $this->title = $title;
} return $this;
}
public function getHTMLFragments($grid) { public function getHTMLFragments($grid)
if($grid->getList() && !singleton($grid->getModelClass())->canCreate()) { {
return array(); if ($grid->getList() && !singleton($grid->getModelClass())->canCreate()) {
} return array();
}
$fragment = $this->getFragment(); $fragment = $this->getFragment();
if(!$editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns')) { if (!$editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns')) {
throw new Exception('Inline adding requires the editable columns component'); throw new Exception('Inline adding requires the editable columns component');
} }
Requirements::javascript(THIRDPARTY_DIR . '/javascript-templates/tmpl.js'); Requirements::javascript(THIRDPARTY_DIR . '/javascript-templates/tmpl.js');
GridFieldExtensions::include_requirements(); GridFieldExtensions::include_requirements();
$data = new ArrayData(array( $data = new ArrayData(array(
'Title' => $this->getTitle(), 'Title' => $this->getTitle(),
)); ));
return array( return array(
$fragment => $data->renderWith(__CLASS__), $fragment => $data->renderWith(__CLASS__),
'after' => $this->getRowTemplate($grid, $editable) 'after' => $this->getRowTemplate($grid, $editable)
); );
} }
private function getRowTemplate(GridField $grid, GridFieldEditableColumns $editable) { private function getRowTemplate(GridField $grid, GridFieldEditableColumns $editable)
$columns = new ArrayList(); {
$handled = array_keys($editable->getDisplayFields($grid)); $columns = new ArrayList();
$handled = array_keys($editable->getDisplayFields($grid));
if($grid->getList()) { if ($grid->getList()) {
$record = Object::create($grid->getModelClass()); $record = Object::create($grid->getModelClass());
} else { } else {
$record = null; $record = null;
} }
$fields = $editable->getFields($grid, $record); $fields = $editable->getFields($grid, $record);
foreach($grid->getColumns() as $column) { foreach ($grid->getColumns() as $column) {
if(in_array($column, $handled)) { if (in_array($column, $handled)) {
$field = $fields->dataFieldByName($column); $field = $fields->dataFieldByName($column);
$field->setName(sprintf( $field->setName(sprintf(
'%s[%s][{%%=o.num%%}][%s]', $grid->getName(), __CLASS__, $field->getName() '%s[%s][{%%=o.num%%}][%s]',
)); $grid->getName(),
__CLASS__,
$field->getName()
));
$content = $field->Field(); $content = $field->Field();
} else { } else {
$content = $grid->getColumnContent($record, $column); $content = $grid->getColumnContent($record, $column);
// Convert GridFieldEditableColumns to the template format // Convert GridFieldEditableColumns to the template format
$content = str_replace( $content = str_replace(
'[GridFieldEditableColumns][0]', '[GridFieldEditableColumns][0]',
'[GridFieldAddNewInlineButton][{%=o.num%}]', '[GridFieldAddNewInlineButton][{%=o.num%}]',
$content $content
); );
} }
$attrs = ''; $attrs = '';
foreach($grid->getColumnAttributes($record, $column) as $attr => $val) { foreach ($grid->getColumnAttributes($record, $column) as $attr => $val) {
$attrs .= sprintf(' %s="%s"', $attr, Convert::raw2att($val)); $attrs .= sprintf(' %s="%s"', $attr, Convert::raw2att($val));
} }
$columns->push(new ArrayData(array( $columns->push(new ArrayData(array(
'Content' => $content, 'Content' => $content,
'Attributes' => $attrs, 'Attributes' => $attrs,
'IsActions' => $column == 'Actions' 'IsActions' => $column == 'Actions'
))); )));
} }
return $columns->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddNewInlineRow'); return $columns->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldAddNewInlineRow');
} }
public function handleSave(GridField $grid, DataObjectInterface $record) { public function handleSave(GridField $grid, DataObjectInterface $record)
$list = $grid->getList(); {
$value = $grid->Value(); $list = $grid->getList();
$value = $grid->Value();
if(!isset($value[__CLASS__]) || !is_array($value[__CLASS__])) { if (!isset($value[__CLASS__]) || !is_array($value[__CLASS__])) {
return; return;
} }
$class = $grid->getModelClass(); $class = $grid->getModelClass();
/** @var GridFieldEditableColumns $editable */ /** @var GridFieldEditableColumns $editable */
$editable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldEditableColumns'); $editable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldEditableColumns');
/** @var GridFieldOrderableRows $sortable */ /** @var GridFieldOrderableRows $sortable */
$sortable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRows'); $sortable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRows');
$form = $editable->getForm($grid, $record); $form = $editable->getForm($grid, $record);
if(!singleton($class)->canCreate()) { if (!singleton($class)->canCreate()) {
return; return;
} }
foreach($value[__CLASS__] as $fields) { foreach ($value[__CLASS__] as $fields) {
$item = $class::create(); $item = $class::create();
$extra = array(); $extra = array();
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
$form->saveInto($item); $form->saveInto($item);
// Check if we are also sorting these records // Check if we are also sorting these records
if ($sortable) { if ($sortable) {
$sortField = $sortable->getSortField(); $sortField = $sortable->getSortField();
$item->setField($sortField, $fields[$sortField]); $item->setField($sortField, $fields[$sortField]);
} }
if($list instanceof ManyManyList) { if ($list instanceof ManyManyList) {
$extra = array_intersect_key($form->getData(), (array) $list->getExtraFields()); $extra = array_intersect_key($form->getData(), (array) $list->getExtraFields());
} }
$item->write();
$list->add($item, $extra);
}
}
$item->write();
$list->add($item, $extra);
}
}
} }

View File

@ -20,217 +20,237 @@ use ReflectionClass;
* By default the list of classes that are createable is the grid field's model class, and any * 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()}. * 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( private static $allowed_actions = array(
'handleAdd' 'handleAdd'
); );
// Should we add an empty string to the add class dropdown? // Should we add an empty string to the add class dropdown?
private static $showEmptyString = true; private static $showEmptyString = true;
private $fragment; private $fragment;
private $title; private $title;
private $classes; private $classes;
private $defaultClass; private $defaultClass;
/** /**
* @var string * @var string
*/ */
protected $itemRequestClass = 'SilverStripe\\Forms\\GridField\\GridFieldAddNewMultiClassHandler'; protected $itemRequestClass = 'SilverStripe\\Forms\\GridField\\GridFieldAddNewMultiClassHandler';
/** /**
* @param string $fragment the fragment to render the button in * @param string $fragment the fragment to render the button in
*/ */
public function __construct($fragment = 'before') { public function __construct($fragment = 'before')
$this->setFragment($fragment); {
$this->setTitle(_t('GridFieldExtensions.ADD', 'Add')); $this->setFragment($fragment);
} $this->setTitle(_t('GridFieldExtensions.ADD', 'Add'));
}
/** /**
* Gets the fragment name this button is rendered into. * Gets the fragment name this button is rendered into.
* *
* @return string * @return string
*/ */
public function getFragment() { public function getFragment()
return $this->fragment; {
} return $this->fragment;
}
/** /**
* Sets the fragment name this button is rendered into. * Sets the fragment name this button is rendered into.
* *
* @param string $fragment * @param string $fragment
* @return GridFieldAddNewMultiClass $this * @return GridFieldAddNewMultiClass $this
*/ */
public function setFragment($fragment) { public function setFragment($fragment)
$this->fragment = $fragment; {
return $this; $this->fragment = $fragment;
} return $this;
}
/** /**
* Gets the button title text. * Gets the button title text.
* *
* @return string * @return string
*/ */
public function getTitle() { public function getTitle()
return $this->title; {
} return $this->title;
}
/** /**
* Sets the button title text. * Sets the button title text.
* *
* @param string $title * @param string $title
* @return GridFieldAddNewMultiClass $this * @return GridFieldAddNewMultiClass $this
*/ */
public function setTitle($title) { public function setTitle($title)
$this->title = $title; {
return $this; $this->title = $title;
} return $this;
}
/** /**
* Gets the classes that can be created using this button, defaulting to the model class and * Gets the classes that can be created using this button, defaulting to the model class and
* its subclasses. * its subclasses.
* *
* @param GridField $grid * @param GridField $grid
* @return array a map of class name to title * @return array a map of class name to title
*/ */
public function getClasses(GridField $grid) { public function getClasses(GridField $grid)
$result = array(); {
$result = array();
if(is_null($this->classes)) { if (is_null($this->classes)) {
$classes = array_values(ClassInfo::subclassesFor($grid->getModelClass())); $classes = array_values(ClassInfo::subclassesFor($grid->getModelClass()));
sort($classes); sort($classes);
} else { } else {
$classes = $this->classes; $classes = $this->classes;
} }
$kill_ancestors = array(); $kill_ancestors = array();
foreach($classes as $class => $title) { foreach ($classes as $class => $title) {
if(!is_string($class)) { if (!is_string($class)) {
$class = $title; $class = $title;
} }
if (!class_exists($class)) { if (!class_exists($class)) {
continue; continue;
} }
$is_abstract = (($reflection = new ReflectionClass($class)) && $reflection->isAbstract()); $is_abstract = (($reflection = new ReflectionClass($class)) && $reflection->isAbstract());
if (!$is_abstract && $class === $title) { if (!$is_abstract && $class === $title) {
$title = singleton($class)->i18n_singular_name(); $title = singleton($class)->i18n_singular_name();
} }
if ($ancestor_to_hide = Config::inst()->get($class, 'hide_ancestor', Config::FIRST_SET)) { if ($ancestor_to_hide = Config::inst()->get($class, 'hide_ancestor', Config::FIRST_SET)) {
$kill_ancestors[$ancestor_to_hide] = true; $kill_ancestors[$ancestor_to_hide] = true;
} }
if($is_abstract || !singleton($class)->canCreate()) { if ($is_abstract || !singleton($class)->canCreate()) {
continue; continue;
} }
$result[$class] = $title; $result[$class] = $title;
} }
if($kill_ancestors) { if ($kill_ancestors) {
foreach($kill_ancestors as $class => $bool) { foreach ($kill_ancestors as $class => $bool) {
unset($result[$class]); unset($result[$class]);
} }
} }
return $result; return $result;
} }
/** /**
* Sets the classes that can be created using this button. * Sets the classes that can be created using this button.
* *
* @param array $classes a set of class names, optionally mapped to titles * @param array $classes a set of class names, optionally mapped to titles
* @return GridFieldAddNewMultiClass $this * @return GridFieldAddNewMultiClass $this
*/ */
public function setClasses(array $classes, $default = null) { public function setClasses(array $classes, $default = null)
$this->classes = $classes; {
if($default) $this->defaultClass = $default; $this->classes = $classes;
return $this; if ($default) {
} $this->defaultClass = $default;
}
return $this;
}
/** /**
* Sets the default class that is selected automatically. * Sets the default class that is selected automatically.
* *
* @param string $default the class name to use as default * @param string $default the class name to use as default
* @return GridFieldAddNewMultiClass $this * @return GridFieldAddNewMultiClass $this
*/ */
public function setDefaultClass($default) { public function setDefaultClass($default)
$this->defaultClass = $default; {
return $this; $this->defaultClass = $default;
} return $this;
}
/** /**
* Handles adding a new instance of a selected class. * Handles adding a new instance of a selected class.
* *
* @param GridField $grid * @param GridField $grid
* @param SS_HTTPRequest $request * @param SS_HTTPRequest $request
* @return GridFieldAddNewMultiClassHandler * @return GridFieldAddNewMultiClassHandler
*/ */
public function handleAdd($grid, $request) { public function handleAdd($grid, $request)
$class = $request->param('ClassName'); {
$classes = $this->getClasses($grid); $class = $request->param('ClassName');
$component = $grid->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDetailForm'); $classes = $this->getClasses($grid);
$component = $grid->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDetailForm');
if(!$component) { if (!$component) {
throw new Exception('The add new multi class component requires the detail form component.'); throw new Exception('The add new multi class component requires the detail form component.');
} }
if(!$class || !array_key_exists($class, $classes)) { if (!$class || !array_key_exists($class, $classes)) {
throw new HTTPResponse_Exception(400); throw new HTTPResponse_Exception(400);
} }
$handler = Object::create($this->itemRequestClass, $handler = Object::create(
$grid, $component, new $class(), $grid->getForm()->getController(), 'add-multi-class' $this->itemRequestClass,
); $grid,
$handler->setTemplate($component->getTemplate()); $component,
new $class(),
$grid->getForm()->getController(),
'add-multi-class'
);
$handler->setTemplate($component->getTemplate());
return $handler; return $handler;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getHTMLFragments($grid) { public function getHTMLFragments($grid)
$classes = $this->getClasses($grid); {
$classes = $this->getClasses($grid);
if(!count($classes)) { if (!count($classes)) {
return array(); return array();
} }
GridFieldExtensions::include_requirements(); GridFieldExtensions::include_requirements();
$field = new DropdownField(sprintf('%s[ClassName]', __CLASS__), '', $classes, $this->defaultClass); $field = new DropdownField(sprintf('%s[ClassName]', __CLASS__), '', $classes, $this->defaultClass);
if (Config::inst()->get(__CLASS__, 'showEmptyString')) { if (Config::inst()->get(__CLASS__, 'showEmptyString')) {
$field->setEmptyString(_t('GridFieldExtensions.SELECTTYPETOCREATE', '(Select type to create)')); $field->setEmptyString(_t('GridFieldExtensions.SELECTTYPETOCREATE', '(Select type to create)'));
} }
$field->addExtraClass('no-change-track'); $field->addExtraClass('no-change-track');
$data = new ArrayData(array( $data = new ArrayData(array(
'Title' => $this->getTitle(), 'Title' => $this->getTitle(),
'Link' => Controller::join_links($grid->Link(), 'add-multi-class', '{class}'), 'Link' => Controller::join_links($grid->Link(), 'add-multi-class', '{class}'),
'ClassField' => $field 'ClassField' => $field
)); ));
return array( return array(
$this->getFragment() => $data->renderWith(__CLASS__) $this->getFragment() => $data->renderWith(__CLASS__)
); );
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getURLHandlers($grid) { public function getURLHandlers($grid)
return array( {
'add-multi-class/$ClassName!' => 'handleAdd' return array(
); 'add-multi-class/$ClassName!' => 'handleAdd'
} );
}
public function setItemRequestClass($class) { public function setItemRequestClass($class)
$this->itemRequestClass = $class; {
return $this; $this->itemRequestClass = $class;
} return $this;
}
} }

View File

@ -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. * A custom grid field request handler that allows interacting with form fields when adding records.
*/ */
class GridFieldAddNewMultiClassHandler extends GridFieldDetailForm_ItemRequest { 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)
);
}
}
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)
);
}
}
} }

View File

@ -29,260 +29,271 @@ use SilverStripe\ORM\ManyManyList;
* - An array with a `field` key->response specifying the field class to use. * - An array with a `field` key->response specifying the field class to use.
*/ */
class GridFieldEditableColumns extends GridFieldDataColumns implements class GridFieldEditableColumns extends GridFieldDataColumns implements
GridField_HTMLProvider, GridField_HTMLProvider,
GridField_SaveHandler, GridField_SaveHandler,
GridField_URLHandler { GridField_URLHandler
{
private static $allowed_actions = array( private static $allowed_actions = array(
'handleForm' 'handleForm'
); );
/** /**
* @var Form[] * @var Form[]
*/ */
protected $forms = array(); protected $forms = array();
public function getColumnContent($grid, $record, $col) { public function getColumnContent($grid, $record, $col)
if(!$record->canEdit()) { {
return parent::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 (!$this->displayFields) {
{ // If setDisplayFields() not used, utilize $summary_fields
// If setDisplayFields() not used, utilize $summary_fields // in a way similar to base class
// in a way similar to base class $colRelation = explode('.', $col);
$colRelation = explode('.', $col); $value = $grid->getDataFieldValue($record, $colRelation[0]);
$value = $grid->getDataFieldValue($record, $colRelation[0]); $field = $fields->fieldByName($colRelation[0]);
$field = $fields->fieldByName($colRelation[0]); if (!$field || $field->isReadonly() || $field->isDisabled()) {
if (!$field || $field->isReadonly() || $field->isDisabled()) { return parent::getColumnContent($grid, $record, $col);
return parent::getColumnContent($grid, $record, $col); }
}
// Ensure this field is available to edit on the record // Ensure this field is available to edit on the record
// (ie. Maybe its readonly due to certain circumstances, or removed and not editable) // (ie. Maybe its readonly due to certain circumstances, or removed and not editable)
$cmsFields = $record->getCMSFields(); $cmsFields = $record->getCMSFields();
$cmsField = $cmsFields->dataFieldByName($colRelation[0]); $cmsField = $cmsFields->dataFieldByName($colRelation[0]);
if (!$cmsField || $cmsField->isReadonly() || $cmsField->isDisabled()) if (!$cmsField || $cmsField->isReadonly() || $cmsField->isDisabled()) {
{ return parent::getColumnContent($grid, $record, $col);
return parent::getColumnContent($grid, $record, $col); }
} $field = clone $field;
$field = clone $field; } else {
} $value = $grid->getDataFieldValue($record, $col);
else $rel = (strpos($col, '.') === false); // field references a relation value
{ $field = ($rel) ? clone $fields->fieldByName($col) : new ReadonlyField($col);
$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) { if (!$field) {
throw new Exception("Could not find the field '$col'"); throw new Exception("Could not find the field '$col'");
} }
} }
if(array_key_exists($col, $this->fieldCasting)) { if (array_key_exists($col, $this->fieldCasting)) {
$value = $grid->getCastedValue($value, $this->fieldCasting[$col]); $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->setName($this->getFieldName($field->getName(), $grid, $record));
$field->setValue($value); $field->setValue($value);
if ($field instanceof HtmlEditorField) { if ($field instanceof HtmlEditorField) {
return $field->FieldHolder(); return $field->FieldHolder();
} }
return $field->forTemplate(); return $field->forTemplate();
} }
public function getHTMLFragments($grid) { public function getHTMLFragments($grid)
GridFieldExtensions::include_requirements(); {
$grid->addExtraClass('ss-gridfield-editable'); GridFieldExtensions::include_requirements();
} $grid->addExtraClass('ss-gridfield-editable');
}
public function handleSave(GridField $grid, DataObjectInterface $record) { public function handleSave(GridField $grid, DataObjectInterface $record)
$list = $grid->getList(); {
$value = $grid->Value(); $list = $grid->getList();
$value = $grid->Value();
if(!isset($value[__CLASS__]) || !is_array($value[__CLASS__])) { if (!isset($value[__CLASS__]) || !is_array($value[__CLASS__])) {
return; return;
} }
/** @var GridFieldOrderableRows $sortable */ /** @var GridFieldOrderableRows $sortable */
$sortable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRows'); $sortable = $grid->getConfig()->getComponentByType('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRows');
$form = $this->getForm($grid, $record); $form = $this->getForm($grid, $record);
foreach($value[__CLASS__] as $id => $fields) { foreach ($value[__CLASS__] as $id => $fields) {
if(!is_numeric($id) || !is_array($fields)) { if (!is_numeric($id) || !is_array($fields)) {
continue; continue;
} }
$item = $list->byID($id); $item = $list->byID($id);
if(!$item || !$item->canEdit()) { if (!$item || !$item->canEdit()) {
continue; continue;
} }
$extra = array(); $extra = array();
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
$form->saveInto($item); $form->saveInto($item);
// Check if we are also sorting these records // Check if we are also sorting these records
if ($sortable) { if ($sortable) {
$sortField = $sortable->getSortField(); $sortField = $sortable->getSortField();
$item->setField($sortField, $fields[$sortField]); $item->setField($sortField, $fields[$sortField]);
} }
if($list instanceof ManyManyList) { if ($list instanceof ManyManyList) {
$extra = array_intersect_key($form->getData(), (array) $list->getExtraFields()); $extra = array_intersect_key($form->getData(), (array) $list->getExtraFields());
} }
$item->write(); $item->write();
$list->add($item, $extra); $list->add($item, $extra);
} }
} }
public function handleForm(GridField $grid, $request) { public function handleForm(GridField $grid, $request)
$id = $request->param('ID'); {
$list = $grid->getList(); $id = $request->param('ID');
$list = $grid->getList();
if(!ctype_digit($id)) { if (!ctype_digit($id)) {
throw new HTTPResponse_Exception(null, 400); throw new HTTPResponse_Exception(null, 400);
} }
if(!$record = $list->byID($id)) { if (!$record = $list->byID($id)) {
throw new HTTPResponse_Exception(null, 404); throw new HTTPResponse_Exception(null, 404);
} }
$form = $this->getForm($grid, $record); $form = $this->getForm($grid, $record);
foreach($form->Fields() as $field) { foreach ($form->Fields() as $field) {
$field->setName($this->getFieldName($field->getName(), $grid, $record)); $field->setName($this->getFieldName($field->getName(), $grid, $record));
} }
return $form; return $form;
} }
public function getURLHandlers($grid) { public function getURLHandlers($grid)
return array( {
'editable/form/$ID' => 'handleForm' return array(
); 'editable/form/$ID' => 'handleForm'
} );
}
/** /**
* Gets the field list for a record. * Gets the field list for a record.
* *
* @param GridField $grid * @param GridField $grid
* @param DataObjectInterface $record * @param DataObjectInterface $record
* @return FieldList * @return FieldList
*/ */
public function getFields(GridField $grid, DataObjectInterface $record) { public function getFields(GridField $grid, DataObjectInterface $record)
$cols = $this->getDisplayFields($grid); {
$fields = new FieldList(); $cols = $this->getDisplayFields($grid);
$fields = new FieldList();
$list = $grid->getList(); $list = $grid->getList();
$class = $list ? $list->dataClass() : null; $class = $list ? $list->dataClass() : null;
foreach($cols as $col => $info) { foreach ($cols as $col => $info) {
$field = null; $field = null;
if($info instanceof Closure) { if ($info instanceof Closure) {
$field = call_user_func($info, $record, $col, $grid); $field = call_user_func($info, $record, $col, $grid);
} elseif(is_array($info)) { } elseif (is_array($info)) {
if(isset($info['callback'])) { if (isset($info['callback'])) {
$field = call_user_func($info['callback'], $record, $col, $grid); $field = call_user_func($info['callback'], $record, $col, $grid);
} elseif(isset($info['field'])) { } elseif (isset($info['field'])) {
if ($info['field'] == 'SilverStripe\\Forms\\LiteralField') { if ($info['field'] == 'SilverStripe\\Forms\\LiteralField') {
$field = new $info['field']($col, null); $field = new $info['field']($col, null);
} else { } else {
$field = new $info['field']($col); $field = new $info['field']($col);
} }
} }
if(!$field instanceof FormField) { if (!$field instanceof FormField) {
throw new Exception(sprintf( throw new Exception(sprintf(
'The field for column "%s" is not a valid form field', 'The field for column "%s" is not a valid form field',
$col $col
)); ));
} }
} }
if(!$field && $list instanceof ManyManyList) { if (!$field && $list instanceof ManyManyList) {
$extra = $list->getExtraFields(); $extra = $list->getExtraFields();
if($extra && array_key_exists($col, $extra)) { if ($extra && array_key_exists($col, $extra)) {
$field = Object::create_from_string($extra[$col], $col)->scaffoldFormField(); $field = Object::create_from_string($extra[$col], $col)->scaffoldFormField();
} }
} }
if(!$field) { if (!$field) {
if (!$this->displayFields) if (!$this->displayFields) {
{ // If setDisplayFields() not used, utilize $summary_fields
// If setDisplayFields() not used, utilize $summary_fields // in a way similar to base class
// in a way similar to base class //
// // Allows use of 'MyBool.Nice' and 'MyHTML.NoHTML' so that
// Allows use of 'MyBool.Nice' and 'MyHTML.NoHTML' so that // GridFields not using inline editing still look good or
// GridFields not using inline editing still look good or // revert to looking good in cases where the field isn't
// revert to looking good in cases where the field isn't // available or is readonly
// available or is readonly //
// $colRelation = explode('.', $col);
$colRelation = explode('.', $col); if ($class && $obj = singleton($class)->dbObject($colRelation[0])) {
if($class && $obj = singleton($class)->dbObject($colRelation[0])) { $field = $obj->scaffoldFormField();
$field = $obj->scaffoldFormField(); } else {
} else { $field = new ReadonlyField($colRelation[0]);
$field = new ReadonlyField($colRelation[0]); }
} } elseif ($class && $obj = singleton($class)->dbObject($col)) {
} $field = $obj->scaffoldFormField();
else if($class && $obj = singleton($class)->dbObject($col)) { } else {
$field = $obj->scaffoldFormField(); $field = new ReadonlyField($col);
} else { }
$field = new ReadonlyField($col); }
}
}
if(!$field instanceof FormField) { if (!$field instanceof FormField) {
throw new Exception(sprintf( throw new Exception(sprintf(
'Invalid form field instance for column "%s"', $col 'Invalid form field instance for column "%s"',
)); $col
} ));
}
// Add CSS class for interactive fields // Add CSS class for interactive fields
if (!($field->isReadOnly() || $field instanceof LiteralField)) $field->addExtraClass('editable-column-field'); 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. * Gets the form instance for a record.
* *
* @param GridField $grid * @param GridField $grid
* @param DataObjectInterface $record * @param DataObjectInterface $record
* @return Form * @return Form
*/ */
public function getForm(GridField $grid, DataObjectInterface $record) { public function getForm(GridField $grid, DataObjectInterface $record)
$fields = $this->getFields($grid, $record); {
$fields = $this->getFields($grid, $record);
$form = new Form($this, null, $fields, new FieldList()); $form = new Form($this, null, $fields, new FieldList());
$form->loadDataFrom($record); $form->loadDataFrom($record);
$form->setFormAction(Controller::join_links( $form->setFormAction(Controller::join_links(
$grid->Link(), 'editable/form', $record->ID $grid->Link(),
)); 'editable/form',
$record->ID
));
return $form; return $form;
} }
protected function getFieldName($name, GridField $grid, DataObjectInterface $record) {
return sprintf(
'%s[%s][%s][%s]', $grid->getName(), __CLASS__, $record->ID, $name
);
}
protected function getFieldName($name, GridField $grid, DataObjectInterface $record)
{
return sprintf(
'%s[%s][%s][%s]',
$grid->getName(),
__CLASS__,
$record->ID,
$name
);
}
} }

View File

@ -7,16 +7,18 @@ use SilverStripe\View\Requirements;
/** /**
* Utility functions for the grid fields extension module. * Utility functions for the grid fields extension module.
*/ */
class GridFieldExtensions { class GridFieldExtensions
{
public static function include_requirements() { public static function include_requirements()
$moduleDir = self::get_module_dir(); {
Requirements::css($moduleDir.'/css/GridFieldExtensions.css'); $moduleDir = self::get_module_dir();
Requirements::javascript($moduleDir.'/javascript/GridFieldExtensions.js'); 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 get_module_dir()
{
return basename(dirname(__DIR__));
}
} }

View File

@ -8,67 +8,75 @@ use SilverStripe\View\ArrayData;
/** /**
* Displays a link to an external source referenced 'external link' * Displays a link to an external source referenced 'external link'
*/ */
class GridFieldExternalLink extends GridFieldDataColumns { class GridFieldExternalLink extends GridFieldDataColumns
{
/** /**
* Add a column for the actions * Add a column for the actions
* *
* @param type $gridField * @param type $gridField
* @param array $columns * @param array $columns
*/ */
public function augmentColumns($gridField, &$columns) { public function augmentColumns($gridField, &$columns)
if(!in_array('Actions', $columns)) $columns[] = 'Actions'; {
} if (!in_array('Actions', $columns)) {
$columns[] = 'Actions';
}
}
/** /**
* Return any special attributes that will be used for FormField::create_tag() * Return any special attributes that will be used for FormField::create_tag()
* *
* @param GridField $gridField * @param GridField $gridField
* @param DataObject $record * @param DataObject $record
* @param string $columnName * @param string $columnName
* @return array * @return array
*/ */
public function getColumnAttributes($gridField, $record, $columnName) { public function getColumnAttributes($gridField, $record, $columnName)
return array('class' => 'col-buttons'); {
} return array('class' => 'col-buttons');
}
/** /**
* Add the title * Add the title
* *
* @param GridField $gridField * @param GridField $gridField
* @param string $columnName * @param string $columnName
* @return array * @return array
*/ */
public function getColumnMetadata($gridField, $columnName) { public function getColumnMetadata($gridField, $columnName)
if($columnName == 'Actions') { {
return array('title' => ''); if ($columnName == 'Actions') {
} return array('title' => '');
return array(); }
} return array();
}
/** /**
* Which columns are handled by this component * Which columns are handled by this component
* *
* @param type $gridField * @param type $gridField
* @return type * @return type
*/ */
public function getColumnsHandled($gridField) { public function getColumnsHandled($gridField)
return array('Actions'); {
} return array('Actions');
}
/** /**
* @param GridField $gridField * @param GridField $gridField
* @param DataObject $record * @param DataObject $record
* @param string $columnName * @param string $columnName
* *
* @return string - the HTML for the column * @return string - the HTML for the column
*/ */
public function getColumnContent($gridField, $record, $columnName) { public function getColumnContent($gridField, $record, $columnName)
$data = new ArrayData(array( {
'Link' => $record->hasMethod('getExternalLink') ? $record->getExternalLink() : $record->ExternalLink, $data = new ArrayData(array(
'Text' => $record->hasMethod('getExternalLinkText') ? $record->getExternalLinkText() : 'External Link' 'Link' => $record->hasMethod('getExternalLink') ? $record->getExternalLink() : $record->ExternalLink,
)); 'Text' => $record->hasMethod('getExternalLinkText') ? $record->getExternalLinkText() : 'External Link'
));
return $data->renderWith('GridFieldExternalLink'); return $data->renderWith('GridFieldExternalLink');
} }
} }

View File

@ -29,544 +29,567 @@ use SilverStripe\View\ViewableData;
* the sort field. * the sort field.
*/ */
class GridFieldOrderableRows extends RequestHandler implements class GridFieldOrderableRows extends RequestHandler implements
GridField_ColumnProvider, GridField_ColumnProvider,
GridField_DataManipulator, GridField_DataManipulator,
GridField_HTMLProvider, GridField_HTMLProvider,
GridField_URLHandler, GridField_URLHandler,
GridField_SaveHandler { GridField_SaveHandler
{
/**
* @see $immediateUpdate /**
* @var boolean * @see $immediateUpdate
*/ * @var boolean
private static $default_immediate_update = true; */
private static $default_immediate_update = true;
private static $allowed_actions = array(
'handleReorder', private static $allowed_actions = array(
'handleMoveToPage' 'handleReorder',
); 'handleMoveToPage'
);
/**
* The database field which specifies the sort, defaults to "Sort". /**
* * The database field which specifies the sort, defaults to "Sort".
* @see setSortField() *
* @var string * @see setSortField()
*/ * @var string
protected $sortField; */
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 * If set to true, when an item is re-ordered, it will update on the
* update the sort order when the record is saved. * database and refresh the gridfield. When set to false, it will only
* * update the sort order when the record is saved.
* @var boolean *
*/ * @var boolean
protected $immediateUpdate; */
protected $immediateUpdate;
/**
* Extra sort fields to apply before the sort field. /**
* * Extra sort fields to apply before the sort field.
* @see setExtraSortFields() *
* @var string|array * @see setExtraSortFields()
*/ * @var string|array
protected $extraSortFields = null; */
protected $extraSortFields = null;
/**
* The number of the column containing the reorder handles /**
* * The number of the column containing the reorder handles
* @see setReorderColumnNumber() *
* @var int * @see setReorderColumnNumber()
*/ * @var int
protected $reorderColumnNumber = 0; */
protected $reorderColumnNumber = 0;
/**
* @param string $sortField /**
*/ * @param string $sortField
public function __construct($sortField = 'Sort') { */
parent::__construct(); public function __construct($sortField = 'Sort')
$this->sortField = $sortField; {
$this->immediateUpdate = $this->config()->default_immediate_update; parent::__construct();
} $this->sortField = $sortField;
$this->immediateUpdate = $this->config()->default_immediate_update;
/** }
* @return string
*/ /**
public function getSortField() { * @return string
return $this->sortField; */
} public function getSortField()
{
/** return $this->sortField;
* Sets the field used to specify the sort. }
*
* @param string $sortField /**
* @return GridFieldOrderableRows $this * Sets the field used to specify the sort.
*/ *
public function setSortField($field) { * @param string $sortField
$this->sortField = $field; * @return GridFieldOrderableRows $this
return $this; */
} public function setSortField($field)
{
/** $this->sortField = $field;
* @return boolean return $this;
*/ }
public function getImmediateUpdate() {
return $this->immediateUpdate; /**
} * @return boolean
*/
/** public function getImmediateUpdate()
* @see $immediateUpdate {
* @param boolean $immediateUpdate return $this->immediateUpdate;
* @return GridFieldOrderableRows $this }
*/
public function setImmediateUpdate($bool) { /**
$this->immediateUpdate = $bool; * @see $immediateUpdate
return $this; * @param boolean $immediateUpdate
} * @return GridFieldOrderableRows $this
*/
/** public function setImmediateUpdate($bool)
* @return string|array {
*/ $this->immediateUpdate = $bool;
public function getExtraSortFields() { return $this;
return $this->extraSortFields; }
}
/**
/** * @return string|array
* Sets extra sort fields to apply before the sort field. */
* public function getExtraSortFields()
* @param string|array $fields {
* @return GridFieldOrderableRows $this return $this->extraSortFields;
*/ }
public function setExtraSortFields($fields) {
$this->extraSortFields = $fields; /**
return $this; * Sets extra sort fields to apply before the sort field.
} *
* @param string|array $fields
/** * @return GridFieldOrderableRows $this
* @return int */
*/ public function setExtraSortFields($fields)
public function getReorderColumnNumber() { {
return $this->reorderColumnNumber; $this->extraSortFields = $fields;
} return $this;
}
/**
* Sets the number of the column containing the reorder handles. /**
* * @return int
* @param int $colno */
* @return GridFieldOrderableRows $this public function getReorderColumnNumber()
*/ {
public function setReorderColumnNumber($colno) { return $this->reorderColumnNumber;
$this->reorderColumnNumber = $colno; }
return $this;
} /**
* Sets the number of the column containing the reorder handles.
/** *
* Gets the table which contains the sort field. * @param int $colno
* * @return GridFieldOrderableRows $this
* @param DataList $list */
* @return string public function setReorderColumnNumber($colno)
*/ {
public function getSortTable(SS_List $list) { $this->reorderColumnNumber = $colno;
$field = $this->getSortField(); return $this;
}
if($list instanceof ManyManyList) {
$extra = $list->getExtraFields(); /**
$table = $list->getJoinTable(); * Gets the table which contains the sort field.
*
if($extra && array_key_exists($field, $extra)) { * @param DataList $list
return $table; * @return string
} */
} public function getSortTable(SS_List $list)
{
$classes = ClassInfo::dataClassesFor($list->dataClass()); $field = $this->getSortField();
foreach($classes as $class) { if ($list instanceof ManyManyList) {
if(singleton($class)->hasDataBaseField($field)) { $extra = $list->getExtraFields();
return $class; $table = $list->getJoinTable();
}
} if ($extra && array_key_exists($field, $extra)) {
return $table;
throw new Exception("Couldn't find the sort field '$field'"); }
} }
public function getURLHandlers($grid) { $classes = ClassInfo::dataClassesFor($list->dataClass());
return array(
'POST reorder' => 'handleReorder', foreach ($classes as $class) {
'POST movetopage' => 'handleMoveToPage' if (singleton($class)->hasDataBaseField($field)) {
); return $class;
} }
}
/**
* @param GridField $field throw new Exception("Couldn't find the sort field '$field'");
*/ }
public function getHTMLFragments($field) {
GridFieldExtensions::include_requirements(); public function getURLHandlers($grid)
{
$field->addExtraClass('ss-gridfield-orderable'); return array(
$field->setAttribute('data-immediate-update', (string)(int)$this->immediateUpdate); 'POST reorder' => 'handleReorder',
$field->setAttribute('data-url-reorder', $field->Link('reorder')); 'POST movetopage' => 'handleMoveToPage'
$field->setAttribute('data-url-movetopage', $field->Link('movetopage')); );
} }
public function augmentColumns($grid, &$cols) { /**
if(!in_array('Reorder', $cols) && $grid->getState()->GridFieldOrderableRows->enabled) { * @param GridField $field
array_splice($cols, $this->reorderColumnNumber, 0, 'Reorder'); */
} public function getHTMLFragments($field)
} {
GridFieldExtensions::include_requirements();
public function getColumnsHandled($grid) {
return array('Reorder'); $field->addExtraClass('ss-gridfield-orderable');
} $field->setAttribute('data-immediate-update', (string)(int)$this->immediateUpdate);
$field->setAttribute('data-url-reorder', $field->Link('reorder'));
public function getColumnContent($grid, $record, $col) { $field->setAttribute('data-url-movetopage', $field->Link('movetopage'));
// 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. public function augmentColumns($grid, &$cols)
$sortFieldName = sprintf( {
'%s[GridFieldEditableColumns][%s][%s]', if (!in_array('Reorder', $cols) && $grid->getState()->GridFieldOrderableRows->enabled) {
$grid->getName(), array_splice($cols, $this->reorderColumnNumber, 0, 'Reorder');
$record->ID, }
$this->getSortField() }
);
$sortField = new HiddenField($sortFieldName, false, $record->getField($this->getSortField())); public function getColumnsHandled($grid)
$sortField->addExtraClass('ss-orderable-hidden-sort'); {
$sortField->setForm($grid->getForm()); return array('Reorder');
}
return ViewableData::create()->customise(array(
'SortField' => $sortField public function getColumnContent($grid, $record, $col)
))->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRowsDragHandle'); {
} // In case you are using GridFieldEditableColumns, this ensures that
// the correct sort order is saved. If you are not using that component,
public function getColumnAttributes($grid, $record, $col) { // this will be ignored by other components, but will still work for this.
return array('class' => 'col-reorder'); $sortFieldName = sprintf(
} '%s[GridFieldEditableColumns][%s][%s]',
$grid->getName(),
public function getColumnMetadata($grid, $col) { $record->ID,
if ($fieldLabels = singleton($grid->getModelClass())->fieldLabels()) { $this->getSortField()
return array('title' => isset($fieldLabels['Reorder']) ? $fieldLabels['Reorder'] : ''); );
} $sortField = new HiddenField($sortFieldName, false, $record->getField($this->getSortField()));
$sortField->addExtraClass('ss-orderable-hidden-sort');
return array('title' => ''); $sortField->setForm($grid->getForm());
}
return ViewableData::create()->customise(array(
public function getManipulatedData(GridField $grid, SS_List $list) { 'SortField' => $sortField
$state = $grid->getState(); ))->renderWith('SilverStripe\\GridFieldExtensions\\GridFieldOrderableRowsDragHandle');
$sorted = (bool) ((string) $state->GridFieldSortableHeader->SortColumn); }
// If the data has not been sorted by the user, then sort it by the public function getColumnAttributes($grid, $record, $col)
// sort column, otherwise disable reordering. {
$state->GridFieldOrderableRows->enabled = !$sorted; return array('class' => 'col-reorder');
}
if(!$sorted) {
$sortterm = ''; public function getColumnMetadata($grid, $col)
if ($this->extraSortFields) { {
if (is_array($this->extraSortFields)) { if ($fieldLabels = singleton($grid->getModelClass())->fieldLabels()) {
foreach($this->extraSortFields as $col => $dir) { return array('title' => isset($fieldLabels['Reorder']) ? $fieldLabels['Reorder'] : '');
$sortterm .= "$col $dir, "; }
}
} else { return array('title' => '');
$sortterm = $this->extraSortFields.', '; }
}
} public function getManipulatedData(GridField $grid, SS_List $list)
if ($list instanceof ArrayList) { {
// Fix bug in 3.1.3+ where ArrayList doesn't account for quotes $state = $grid->getState();
$sortterm .= $this->getSortTable($list).'.'.$this->getSortField(); $sorted = (bool) ((string) $state->GridFieldSortableHeader->SortColumn);
} else {
$sortterm .= '"'.$this->getSortTable($list).'"."'.$this->getSortField().'"'; // If the data has not been sorted by the user, then sort it by the
} // sort column, otherwise disable reordering.
return $list->sort($sortterm); $state->GridFieldOrderableRows->enabled = !$sorted;
} else {
return $list; if (!$sorted) {
} $sortterm = '';
} if ($this->extraSortFields) {
if (is_array($this->extraSortFields)) {
/** foreach ($this->extraSortFields as $col => $dir) {
* Handles requests to reorder a set of IDs in a specific order. $sortterm .= "$col $dir, ";
* }
* @param GridField $grid } else {
* @param SS_HTTPRequest $request $sortterm = $this->extraSortFields.', ';
* @return SS_HTTPResponse }
*/ }
public function handleReorder($grid, $request) { if ($list instanceof ArrayList) {
if (!$this->immediateUpdate) // Fix bug in 3.1.3+ where ArrayList doesn't account for quotes
{ $sortterm .= $this->getSortTable($list).'.'.$this->getSortField();
$this->httpError(400); } else {
} $sortterm .= '"'.$this->getSortTable($list).'"."'.$this->getSortField().'"';
$list = $grid->getList(); }
$modelClass = $grid->getModelClass(); return $list->sort($sortterm);
if ($list instanceof ManyManyList && !singleton($modelClass)->canView()) { } else {
$this->httpError(403); return $list;
} else if(!($list instanceof ManyManyList) && !singleton($modelClass)->canEdit()) { }
$this->httpError(403); }
}
/**
// Save any un-committed changes to the gridfield * Handles requests to reorder a set of IDs in a specific order.
if(($form = $grid->getForm()) && ($record = $form->getRecord()) ) { *
$form->loadDataFrom($request->requestVars(), true); * @param GridField $grid
$grid->saveInto($record); * @param SS_HTTPRequest $request
} * @return SS_HTTPResponse
*/
// Get records from the `GridFieldEditableColumns` column public function handleReorder($grid, $request)
$data = $request->postVar($grid->getName()); {
$sortedIDs = $this->getSortedIDs($data); if (!$this->immediateUpdate) {
if (!$this->executeReorder($grid, $sortedIDs)) $this->httpError(400);
{ }
$this->httpError(400); $list = $grid->getList();
} $modelClass = $grid->getModelClass();
if ($list instanceof ManyManyList && !singleton($modelClass)->canView()) {
Controller::curr()->getResponse()->addHeader('X-Status', rawurlencode('Records reordered.')); $this->httpError(403);
return $grid->FieldHolder(); } elseif (!($list instanceof ManyManyList) && !singleton($modelClass)->canEdit()) {
} $this->httpError(403);
}
/**
* Get mapping of sort value to ID from posted data // Save any un-committed changes to the gridfield
* if (($form = $grid->getForm()) && ($record = $form->getRecord())) {
* @param array $data Raw posted data $form->loadDataFrom($request->requestVars(), true);
* @return array $grid->saveInto($record);
*/ }
protected function getSortedIDs($data) {
if (empty($data['GridFieldEditableColumns'])) { // Get records from the `GridFieldEditableColumns` column
return array(); $data = $request->postVar($grid->getName());
} $sortedIDs = $this->getSortedIDs($data);
if (!$this->executeReorder($grid, $sortedIDs)) {
$sortedIDs = array(); $this->httpError(400);
foreach($data['GridFieldEditableColumns'] as $id => $recordData) { }
$sortValue = $recordData[$this->sortField];
$sortedIDs[$sortValue] = $id; Controller::curr()->getResponse()->addHeader('X-Status', rawurlencode('Records reordered.'));
} return $grid->FieldHolder();
ksort($sortedIDs); }
return $sortedIDs;
} /**
* Get mapping of sort value to ID from posted data
/** *
* Handles requests to move an item to the previous or next page. * @param array $data Raw posted data
*/ * @return array
public function handleMoveToPage(GridField $grid, $request) { */
if(!$paginator = $grid->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldPaginator')) { protected function getSortedIDs($data)
$this->httpError(404, 'Paginator component not found'); {
} if (empty($data['GridFieldEditableColumns'])) {
return array();
$move = $request->postVar('move'); }
$field = $this->getSortField();
$sortedIDs = array();
$list = $grid->getList(); foreach ($data['GridFieldEditableColumns'] as $id => $recordData) {
$manip = $grid->getManipulatedList(); $sortValue = $recordData[$this->sortField];
$sortedIDs[$sortValue] = $id;
$existing = $manip->map('ID', $field)->toArray(); }
$values = $existing; ksort($sortedIDs);
$order = array(); return $sortedIDs;
}
$id = isset($move['id']) ? (int) $move['id'] : null;
$to = isset($move['page']) ? $move['page'] : null; /**
* Handles requests to move an item to the previous or next page.
if(!isset($values[$id])) { */
$this->httpError(400, 'Invalid item ID'); public function handleMoveToPage(GridField $grid, $request)
} {
if (!$paginator = $grid->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldPaginator')) {
$this->populateSortValues($list); $this->httpError(404, 'Paginator component not found');
}
$page = ((int) $grid->getState()->GridFieldPaginator->currentPage) ?: 1;
$per = $paginator->getItemsPerPage(); $move = $request->postVar('move');
$field = $this->getSortField();
if($to == 'prev') {
$swap = $list->limit(1, ($page - 1) * $per - 1)->first(); $list = $grid->getList();
$values[$swap->ID] = $swap->$field; $manip = $grid->getManipulatedList();
$order[] = $id; $existing = $manip->map('ID', $field)->toArray();
$order[] = $swap->ID; $values = $existing;
$order = array();
foreach($existing as $_id => $sort) {
if($id != $_id) $order[] = $_id; $id = isset($move['id']) ? (int) $move['id'] : null;
} $to = isset($move['page']) ? $move['page'] : null;
} elseif($to == 'next') {
$swap = $list->limit(1, $page * $per)->first(); if (!isset($values[$id])) {
$values[$swap->ID] = $swap->$field; $this->httpError(400, 'Invalid item ID');
}
foreach($existing as $_id => $sort) {
if($id != $_id) $order[] = $_id; $this->populateSortValues($list);
}
$page = ((int) $grid->getState()->GridFieldPaginator->currentPage) ?: 1;
$order[] = $swap->ID; $per = $paginator->getItemsPerPage();
$order[] = $id;
} else { if ($to == 'prev') {
$this->httpError(400, 'Invalid page target'); $swap = $list->limit(1, ($page - 1) * $per - 1)->first();
} $values[$swap->ID] = $swap->$field;
$this->reorderItems($list, $values, $order); $order[] = $id;
$order[] = $swap->ID;
return $grid->FieldHolder();
} foreach ($existing as $_id => $sort) {
if ($id != $_id) {
/** $order[] = $_id;
* Handle saving when 'immediateUpdate' is disabled, otherwise this isn't }
* necessary for the default sort mode. }
*/ } elseif ($to == 'next') {
public function handleSave(GridField $grid, DataObjectInterface $record) { $swap = $list->limit(1, $page * $per)->first();
if (!$this->immediateUpdate) $values[$swap->ID] = $swap->$field;
{
$value = $grid->Value(); foreach ($existing as $_id => $sort) {
$sortedIDs = $this->getSortedIDs($value); if ($id != $_id) {
if ($sortedIDs) { $order[] = $_id;
$this->executeReorder($grid, $sortedIDs); }
} }
}
} $order[] = $swap->ID;
$order[] = $id;
/** } else {
* @param GridField $grid $this->httpError(400, 'Invalid page target');
* @param array $sortedIDs List of IDS, where the key is the sort field value to save }
* @return bool
*/ $this->reorderItems($list, $values, $order);
protected function executeReorder(GridField $grid, $sortedIDs) {
if(!is_array($sortedIDs)) { return $grid->FieldHolder();
return false; }
}
$field = $this->getSortField(); /**
* Handle saving when 'immediateUpdate' is disabled, otherwise this isn't
$sortterm = ''; * necessary for the default sort mode.
if ($this->extraSortFields) { */
if (is_array($this->extraSortFields)) { public function handleSave(GridField $grid, DataObjectInterface $record)
foreach($this->extraSortFields as $col => $dir) { {
$sortterm .= "$col $dir, "; if (!$this->immediateUpdate) {
} $value = $grid->Value();
} else { $sortedIDs = $this->getSortedIDs($value);
$sortterm = $this->extraSortFields.', '; if ($sortedIDs) {
} $this->executeReorder($grid, $sortedIDs);
} }
$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. * @param GridField $grid
if(count($items) != count($sortedIDs)) { * @param array $sortedIDs List of IDS, where the key is the sort field value to save
return false; * @return bool
} */
protected function executeReorder(GridField $grid, $sortedIDs)
// Populate each object we are sorting with a sort value. {
$this->populateSortValues($items); if (!is_array($sortedIDs)) {
return false;
// Generate the current sort values. }
if ($items instanceof ManyManyList) $field = $this->getSortField();
{
$current = array(); $sortterm = '';
foreach ($items->toArray() as $record) if ($this->extraSortFields) {
{ if (is_array($this->extraSortFields)) {
// NOTE: _SortColumn0 is the first ->sort() field foreach ($this->extraSortFields as $col => $dir) {
// used by SS when functions are detected in a SELECT $sortterm .= "$col $dir, ";
// or CASE WHEN. }
if (isset($record->_SortColumn0)) { } else {
$current[$record->ID] = $record->_SortColumn0; $sortterm = $this->extraSortFields.', ';
} else { }
$current[$record->ID] = $record->$field; }
} $list = $grid->getList();
} $sortterm .= '"'.$this->getSortTable($list).'"."'.$field.'"';
} $items = $list->filter('ID', $sortedIDs)->sort($sortterm);
else
{ // Ensure that each provided ID corresponded to an actual object.
$current = $items->map('ID', $field)->toArray(); if (count($items) != count($sortedIDs)) {
} return false;
}
// Perform the actual re-ordering.
$this->reorderItems($list, $current, $sortedIDs); // Populate each object we are sorting with a sort value.
return true; $this->populateSortValues($items);
}
// Generate the current sort values.
protected function reorderItems($list, array $values, array $sortedIDs) { if ($items instanceof ManyManyList) {
$sortField = $this->getSortField(); $current = array();
/** @var SS_List $map */ foreach ($items->toArray() as $record) {
$map = $list->map('ID', $sortField); // NOTE: _SortColumn0 is the first ->sort() field
//fix for versions of SS that return inconsistent types for `map` function // used by SS when functions are detected in a SELECT
if ($map instanceof SS_Map) { // or CASE WHEN.
$map = $map->toArray(); if (isset($record->_SortColumn0)) {
} $current[$record->ID] = $record->_SortColumn0;
} else {
// If not a ManyManyList and using versioning, detect it. $current[$record->ID] = $record->$field;
$isVersioned = false; }
$class = $list->dataClass(); }
if ($class == $this->getSortTable($list)) { } else {
$isVersioned = $class::has_extension('SilverStripe\\ORM\\Versioning\\Versioned'); $current = $items->map('ID', $field)->toArray();
} }
// Loop through each item, and update the sort values which do not // Perform the actual re-ordering.
// match to order the objects. $this->reorderItems($list, $current, $sortedIDs);
if (!$isVersioned) { return true;
$sortTable = $this->getSortTable($list); }
$additionalSQL = (!$list instanceof ManyManyList) ? ', "LastEdited" = NOW()' : '';
foreach($sortedIDs as $sortValue => $id) { protected function reorderItems($list, array $values, array $sortedIDs)
if($map[$id] != $sortValue) { {
DB::query(sprintf( $sortField = $this->getSortField();
'UPDATE "%s" SET "%s" = %d%s WHERE %s', /** @var SS_List $map */
$sortTable, $map = $list->map('ID', $sortField);
$sortField, //fix for versions of SS that return inconsistent types for `map` function
$sortValue, if ($map instanceof SS_Map) {
$additionalSQL, $map = $map->toArray();
$this->getSortTableClauseForIds($list, $id) }
));
} // If not a ManyManyList and using versioning, detect it.
} $isVersioned = false;
} else { $class = $list->dataClass();
// For versioned objects, modify them with the ORM so that the if ($class == $this->getSortTable($list)) {
// *_versions table is updated. This ensures re-ordering works $isVersioned = $class::has_extension('SilverStripe\\ORM\\Versioning\\Versioned');
// similar to the SiteTree where you change the position, and then }
// you go into the record and publish it.
foreach($sortedIDs as $sortValue => $id) { // Loop through each item, and update the sort values which do not
if($map[$id] != $sortValue) { // match to order the objects.
$record = $class::get()->byID($id); if (!$isVersioned) {
$record->$sortField = $sortValue; $sortTable = $this->getSortTable($list);
$record->write(); $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',
$this->extend('onAfterReorderItems', $list); $sortTable,
} $sortField,
$sortValue,
protected function populateSortValues(DataList $list) { $additionalSQL,
$list = clone $list; $this->getSortTableClauseForIds($list, $id)
$field = $this->getSortField(); ));
$table = $this->getSortTable($list); }
$clause = sprintf('"%s"."%s" = 0', $table, $this->getSortField()); }
$additionalSQL = (!$list instanceof ManyManyList) ? ', "LastEdited" = NOW()' : ''; } else {
// For versioned objects, modify them with the ORM so that the
foreach($list->where($clause)->column('ID') as $id) { // *_versions table is updated. This ensures re-ordering works
$max = DB::query(sprintf('SELECT MAX("%s") + 1 FROM "%s"', $field, $table)); // similar to the SiteTree where you change the position, and then
$max = $max->value(); // you go into the record and publish it.
foreach ($sortedIDs as $sortValue => $id) {
DB::query(sprintf( if ($map[$id] != $sortValue) {
'UPDATE "%s" SET "%s" = %d%s WHERE %s', $record = $class::get()->byID($id);
$table, $record->$sortField = $sortValue;
$field, $record->write();
$max, }
$additionalSQL, }
$this->getSortTableClauseForIds($list, $id) }
));
} $this->extend('onAfterReorderItems', $list);
} }
protected function getSortTableClauseForIds(DataList $list, $ids) { protected function populateSortValues(DataList $list)
if(is_array($ids)) { {
$value = 'IN (' . implode(', ', array_map('intval', $ids)) . ')'; $list = clone $list;
} else { $field = $this->getSortField();
$value = '= ' . (int) $ids; $table = $this->getSortTable($list);
} $clause = sprintf('"%s"."%s" = 0', $table, $this->getSortField());
$additionalSQL = (!$list instanceof ManyManyList) ? ', "LastEdited" = NOW()' : '';
if($list instanceof ManyManyList) {
$extra = $list->getExtraFields(); foreach ($list->where($clause)->column('ID') as $id) {
$key = $list->getLocalKey(); $max = DB::query(sprintf('SELECT MAX("%s") + 1 FROM "%s"', $field, $table));
$foreignKey = $list->getForeignKey(); $max = $max->value();
$foreignID = (int) $list->getForeignID();
DB::query(sprintf(
if($extra && array_key_exists($this->getSortField(), $extra)) { 'UPDATE "%s" SET "%s" = %d%s WHERE %s',
return sprintf( $table,
'"%s" %s AND "%s" = %d', $field,
$key, $max,
$value, $additionalSQL,
$foreignKey, $this->getSortTableClauseForIds($list, $id)
$foreignID ));
); }
} }
}
protected function getSortTableClauseForIds(DataList $list, $ids)
return "\"ID\" $value"; {
} 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";
}
} }

View File

@ -22,145 +22,155 @@ use SilverStripe\ORM\ArrayList;
* as tabs, breadcrumbs and a back link. Much of this code is extracted from the * as tabs, breadcrumbs and a back link. Much of this code is extracted from the
* detail form. * detail form.
*/ */
abstract class GridFieldRequestHandler extends RequestHandler { abstract class GridFieldRequestHandler extends RequestHandler
{
private static $allowed_actions = array( private static $allowed_actions = array(
'Form' 'Form'
); );
/** /**
* @var GridField * @var GridField
*/ */
protected $grid; protected $grid;
/** /**
* @var GridFieldComponent * @var GridFieldComponent
*/ */
protected $component; protected $component;
/** /**
* @var string * @var string
*/ */
protected $name; protected $name;
/** /**
* @var string * @var string
*/ */
protected $template = __CLASS__; protected $template = __CLASS__;
public function __construct(GridField $grid, GridFieldComponent $component, $name) { public function __construct(GridField $grid, GridFieldComponent $component, $name)
$this->grid = $grid; {
$this->component = $component; $this->grid = $grid;
$this->name = $name; $this->component = $component;
$this->name = $name;
parent::__construct(); parent::__construct();
} }
public function index($request) { public function index($request)
$result = $this->renderWith($this->template); {
$result = $this->renderWith($this->template);
if($request->isAjax()) { if ($request->isAjax()) {
return $result; return $result;
} else { } else {
return $this->getTopLevelController()->customise(array( return $this->getTopLevelController()->customise(array(
'Content' => $result 'Content' => $result
)); ));
} }
} }
public function Link($action = null) { public function Link($action = null)
return Controller::join_links($this->grid->Link(), $this->name, $action); {
} return Controller::join_links($this->grid->Link(), $this->name, $action);
}
/** /**
* This method should be overloaded to build out the detail form. * This method should be overloaded to build out the detail form.
* *
* @return Form * @return Form
*/ */
public function Form() { public function Form()
$form = new Form( {
$this, $form = new Form(
'SilverStripe\\Forms\\Form', $this,
new FieldList($root = new TabSet('Root', new Tab('Main'))), 'SilverStripe\\Forms\\Form',
new FieldList() new FieldList($root = new TabSet('Root', new Tab('Main'))),
); new FieldList()
);
if($this->getTopLevelController() instanceof LeftAndMain) { if ($this->getTopLevelController() instanceof LeftAndMain) {
$form->setTemplate('LeftAndMain_EditForm'); $form->setTemplate('LeftAndMain_EditForm');
$form->addExtraClass('cms-content cms-edit-form cms-tabset center'); $form->addExtraClass('cms-content cms-edit-form cms-tabset center');
$form->setAttribute('data-pjax-fragment', 'CurrentForm Content'); $form->setAttribute('data-pjax-fragment', 'CurrentForm Content');
$root->setTemplate('CMSTabSet'); $root->setTemplate('CMSTabSet');
$form->Backlink = $this->getBackLink(); $form->Backlink = $this->getBackLink();
} }
return $form; return $form;
} }
/** /**
* @return Controller * @return Controller
*/ */
public function getController() { public function getController()
return $this->grid->getForm()->getController(); {
} return $this->grid->getForm()->getController();
}
/** /**
* @param string $template * @param string $template
*/ */
public function setTemplate($template) { public function setTemplate($template)
$this->template = $template; {
} $this->template = $template;
}
/** /**
* @return string * @return string
*/ */
public function getTemplate() { public function getTemplate()
return $this->template; {
} return $this->template;
}
/** /**
* @return ArrayList * @return ArrayList
*/ */
public function getBreadcrumbs() { public function getBreadcrumbs()
$controller = $this->getController(); {
$controller = $this->getController();
if($controller->hasMethod('Breadcrumbs')) { if ($controller->hasMethod('Breadcrumbs')) {
return $controller->Breadcrumbs(); return $controller->Breadcrumbs();
} else { } else {
return new ArrayList(); return new ArrayList();
} }
} }
/** /**
* @return string * @return string
*/ */
protected function getBackLink() { protected function getBackLink()
$controller = $this->getTopLevelController(); {
$controller = $this->getTopLevelController();
if($controller->hasMethod('Backlink')) { if ($controller->hasMethod('Backlink')) {
return $controller->Backlink(); return $controller->Backlink();
} else { } else {
return $controller->Link(); return $controller->Link();
} }
} }
/** /**
* @return Controller * @return Controller
*/ */
protected function getTopLevelController() { protected function getTopLevelController()
$controller = $this->getController(); {
$controller = $this->getController();
while($controller) { while ($controller) {
if($controller instanceof GridFieldRequestHandler) { if ($controller instanceof GridFieldRequestHandler) {
$controller = $controller->getController(); $controller = $controller->getController();
} elseif($controller instanceof GridFieldDetailForm_ItemRequest) { } elseif ($controller instanceof GridFieldDetailForm_ItemRequest) {
$controller = $controller->getController(); $controller = $controller->getController();
} else { } else {
break; break;
} }
} }
return $controller;
}
return $controller;
}
} }

View File

@ -9,23 +9,24 @@ use SilverStripe\View\ArrayData;
/** /**
* A simple header which displays column titles. * A simple header which displays column titles.
*/ */
class GridFieldTitleHeader implements GridField_HTMLProvider { class GridFieldTitleHeader implements GridField_HTMLProvider
{
public function getHTMLFragments($grid) { public function getHTMLFragments($grid)
$cols = new ArrayList(); {
$cols = new ArrayList();
foreach ($grid->getColumns() as $name) { foreach ($grid->getColumns() as $name) {
$meta = $grid->getColumnMetadata($name); $meta = $grid->getColumnMetadata($name);
$cols->push(new ArrayData(array( $cols->push(new ArrayData(array(
'Name' => $name, 'Name' => $name,
'Title' => $meta['title'] 'Title' => $meta['title']
))); )));
} }
return array(
'header' => $cols->renderWith(__CLASS__)
);
}
return array(
'header' => $cols->renderWith(__CLASS__)
);
}
} }

View File

@ -8,57 +8,65 @@ use SilverStripe\GridFieldExtensions\GridFieldAddNewMultiClass;
/** /**
* Tests for {@link GridFieldAddNewMultiClass}. * Tests for {@link GridFieldAddNewMultiClass}.
*/ */
class GridFieldAddNewMultiClassTest extends SapphireTest { class GridFieldAddNewMultiClassTest extends SapphireTest
{
public function testGetClasses() { public function testGetClasses()
$grid = new GridField('TestGridField'); {
$grid->setModelClass('GridFieldAddNewMultiClassTest_A'); $grid = new GridField('TestGridField');
$grid->setModelClass('GridFieldAddNewMultiClassTest_A');
$component = new GridFieldAddNewMultiClass(); $component = new GridFieldAddNewMultiClass();
$this->assertEquals( $this->assertEquals(
array( array(
'GridFieldAddNewMultiClassTest_A' => 'A', 'GridFieldAddNewMultiClassTest_A' => 'A',
'GridFieldAddNewMultiClassTest_B' => 'B', 'GridFieldAddNewMultiClassTest_B' => 'B',
'GridFieldAddNewMultiClassTest_C' => 'C' 'GridFieldAddNewMultiClassTest_C' => 'C'
), ),
$component->getClasses($grid), $component->getClasses($grid),
'Subclasses are populated by default and sorted' 'Subclasses are populated by default and sorted'
); );
$component->setClasses(array( $component->setClasses(array(
'GridFieldAddNewMultiClassTest_B' => 'Custom Title', 'GridFieldAddNewMultiClassTest_B' => 'Custom Title',
'GridFieldAddNewMultiClassTest_A' 'GridFieldAddNewMultiClassTest_A'
)); ));
$this->assertEquals(
array(
'GridFieldAddNewMultiClassTest_B' => 'Custom Title',
'GridFieldAddNewMultiClassTest_A' => 'A'
),
$component->getClasses($grid),
'Sorting and custom titles can be specified'
);
}
$this->assertEquals(
array(
'GridFieldAddNewMultiClassTest_B' => 'Custom Title',
'GridFieldAddNewMultiClassTest_A' => 'A'
),
$component->getClasses($grid),
'Sorting and custom titles can be specified'
);
}
} }
/**#@+ /**#@+
* @ignore * @ignore
*/ */
class GridFieldAddNewMultiClassTest_A implements TestOnly { class GridFieldAddNewMultiClassTest_A implements TestOnly
public function i18n_singular_name() { {
$class = get_class($this); public function i18n_singular_name()
return substr($class, strpos($class, '_') + 1); {
} $class = get_class($this);
return substr($class, strpos($class, '_') + 1);
}
public function canCreate() { public function canCreate()
return true; {
} return true;
}
} }
class GridFieldAddNewMultiClassTest_B extends GridFieldAddNewMultiClassTest_A implements TestOnly {} class GridFieldAddNewMultiClassTest_B extends GridFieldAddNewMultiClassTest_A implements TestOnly
class GridFieldAddNewMultiClassTest_C extends GridFieldAddNewMultiClassTest_A implements TestOnly {} {
}
class GridFieldAddNewMultiClassTest_C extends GridFieldAddNewMultiClassTest_A implements TestOnly
{
}
/**#@-*/ /**#@-*/

View File

@ -10,17 +10,18 @@ use SilverStripe\ORM\DataObject;
/** /**
* Tests for the {@link GridFieldOrderableRows} component. * 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( protected $extraDataObjects = array(
'GridFieldOrderableRowsTest_Parent', 'GridFieldOrderableRowsTest_Parent',
'GridFieldOrderableRowsTest_Ordered', 'GridFieldOrderableRowsTest_Ordered',
'GridFieldOrderableRowsTest_Subclass', 'GridFieldOrderableRowsTest_Subclass',
); );
public function setUp() public function setUp()
{ {
@ -28,111 +29,112 @@ class GridFieldOrderableRowsTest extends SapphireTest {
$this->markTestSkipped('Upgrade to 4.0: Needs to be re-implemented.'); $this->markTestSkipped('Upgrade to 4.0: Needs to be re-implemented.');
} }
public function testReorderItems() { public function testReorderItems()
{
$orderable = new GridFieldOrderableRows('ManyManySort'); $orderable = new GridFieldOrderableRows('ManyManySort');
$reflection = new ReflectionMethod($orderable, 'executeReorder'); $reflection = new ReflectionMethod($orderable, 'executeReorder');
$reflection->setAccessible(true); $reflection->setAccessible(true);
$parent = $this->objFromFixture('GridFieldOrderableRowsTest_Parent', 'parent'); $parent = $this->objFromFixture('GridFieldOrderableRowsTest_Parent', 'parent');
$config = new GridFieldConfig_RelationEditor(); $config = new GridFieldConfig_RelationEditor();
$config->addComponent($orderable); $config->addComponent($orderable);
$grid = new GridField( $grid = new GridField(
'MyManyMany', 'MyManyMany',
'My Many Many', 'My Many Many',
$parent->MyManyMany()->sort('ManyManySort'), $parent->MyManyMany()->sort('ManyManySort'),
$config $config
); );
$originalOrder = $parent->MyManyMany()->sort('ManyManySort')->column('ID'); $originalOrder = $parent->MyManyMany()->sort('ManyManySort')->column('ID');
$desiredOrder = array(); $desiredOrder = array();
// Make order non-contiguous, and 1-based // Make order non-contiguous, and 1-based
foreach(array_reverse($originalOrder) as $index => $id) { foreach (array_reverse($originalOrder) as $index => $id) {
$desiredOrder[$index * 2 + 1] = $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
/** */
* @covers GridFieldOrderableRows::getSortTable public function testGetSortTable()
*/ {
public function testGetSortTable() {
$orderable = new GridFieldOrderableRows(); $orderable = new GridFieldOrderableRows();
$parent = new GridFieldOrderableRowsTest_Parent(); $parent = new GridFieldOrderableRowsTest_Parent();
$parent->write(); $parent->write();
$this->assertEquals( $this->assertEquals(
'GridFieldOrderableRowsTest_Ordered', 'GridFieldOrderableRowsTest_Ordered',
$orderable->getSortTable($parent->MyHasMany()) $orderable->getSortTable($parent->MyHasMany())
); );
$this->assertEquals( $this->assertEquals(
'GridFieldOrderableRowsTest_Ordered', 'GridFieldOrderableRowsTest_Ordered',
$orderable->getSortTable($parent->MyHasManySubclass()) $orderable->getSortTable($parent->MyHasManySubclass())
); );
$this->assertEquals( $this->assertEquals(
'GridFieldOrderableRowsTest_Ordered', 'GridFieldOrderableRowsTest_Ordered',
$orderable->getSortTable($parent->MyManyMany()) $orderable->getSortTable($parent->MyManyMany())
); );
$this->assertEquals(
'GridFieldOrderableRowsTest_Parent_MyManyMany',
$orderable->setSortField('ManyManySort')->getSortTable($parent->MyManyMany())
);
}
$this->assertEquals(
'GridFieldOrderableRowsTest_Parent_MyManyMany',
$orderable->setSortField('ManyManySort')->getSortTable($parent->MyManyMany())
);
}
} }
/**#@+ /**#@+
* @ignore * @ignore
*/ */
class GridFieldOrderableRowsTest_Parent extends DataObject implements TestOnly { class GridFieldOrderableRowsTest_Parent extends DataObject implements TestOnly
{
private static $has_many = array( private static $has_many = array(
'MyHasMany' => 'GridFieldOrderableRowsTest_Ordered', 'MyHasMany' => 'GridFieldOrderableRowsTest_Ordered',
'MyHasManySubclass' => 'GridFieldOrderableRowsTest_Subclass' 'MyHasManySubclass' => 'GridFieldOrderableRowsTest_Subclass'
); );
private static $many_many = array( private static $many_many = array(
'MyManyMany' => 'GridFieldOrderableRowsTest_Ordered' 'MyManyMany' => 'GridFieldOrderableRowsTest_Ordered'
); );
private static $many_many_extraFields = array(
'MyManyMany' => array('ManyManySort' => 'Int')
);
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( private static $db = array(
'Sort' => 'Int' 'Sort' => 'Int'
); );
private static $has_one = array( private static $has_one = array(
'Parent' => 'GridFieldOrderableRowsTest_Parent' 'Parent' => 'GridFieldOrderableRowsTest_Parent'
); );
private static $belongs_many_many =array(
'MyManyMany' => '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
{
} }
/**#@-*/ /**#@-*/

View File

@ -1,16 +1,10 @@
GridFieldOrderableRowsTest_Ordered: GridFieldOrderableRowsTest_Ordered:
item1: item1:
Sort: 0
item2: item2:
Sort: 0
item3: item3:
Sort: 0
item4: item4:
Sort: 0
item5: item5:
Sort: 0
item6: item6:
Sort: 0
GridFieldOrderableRowsTest_Parent: GridFieldOrderableRowsTest_Parent:
parent: parent:
MyManyMany: MyManyMany: