From ef354511d76ffb9a632ce6ea313bf0bb5f7366e6 Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Fri, 28 Jul 2017 16:09:48 +0100 Subject: [PATCH 01/20] FIX Forms now instantiate fields with correct record context on save --- code/GridFieldAddNewInlineButton.php | 2 +- code/GridFieldEditableColumns.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/GridFieldAddNewInlineButton.php b/code/GridFieldAddNewInlineButton.php index 3b776dd..47b6f17 100755 --- a/code/GridFieldAddNewInlineButton.php +++ b/code/GridFieldAddNewInlineButton.php @@ -147,7 +147,6 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S $editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns'); /** @var GridFieldOrderableRows $sortable */ $sortable = $grid->getConfig()->getComponentByType('GridFieldOrderableRows'); - $form = $editable->getForm($grid, $record); if(!singleton($class)->canCreate()) { return; @@ -157,6 +156,7 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S $item = $class::create(); $extra = array(); + $form = $editable->getForm($grid, $item); $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); $form->saveInto($item); diff --git a/code/GridFieldEditableColumns.php b/code/GridFieldEditableColumns.php index b69d4e6..996dbf9 100644 --- a/code/GridFieldEditableColumns.php +++ b/code/GridFieldEditableColumns.php @@ -94,8 +94,6 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements /** @var GridFieldOrderableRows $sortable */ $sortable = $grid->getConfig()->getComponentByType('GridFieldOrderableRows'); - $form = $this->getForm($grid, $record); - foreach($value[__CLASS__] as $id => $fields) { if(!is_numeric($id) || !is_array($fields)) { continue; @@ -107,6 +105,8 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements continue; } + $form = $this->getForm($grid, $item); + $extra = array(); $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); From e95c92a16829eac0d73a7ab4e431bf5fdf236e57 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Thu, 20 Jul 2017 11:51:24 +1200 Subject: [PATCH 02/20] NEW Add paginator with configurable page sizes --- code/GridFieldConfigurablePaginator.php | 442 +++++++++++++++++++ css/GridFieldExtensions.css | 390 ++++++++-------- javascript/GridFieldExtensions.js | 9 + templates/GridFieldConfigurablePaginator.ss | 30 ++ tests/GridFieldConfigurablePaginatorTest.php | 217 +++++++++ 5 files changed, 904 insertions(+), 184 deletions(-) create mode 100644 code/GridFieldConfigurablePaginator.php create mode 100644 templates/GridFieldConfigurablePaginator.ss create mode 100644 tests/GridFieldConfigurablePaginatorTest.php diff --git a/code/GridFieldConfigurablePaginator.php b/code/GridFieldConfigurablePaginator.php new file mode 100644 index 0000000..6c785f9 --- /dev/null +++ b/code/GridFieldConfigurablePaginator.php @@ -0,0 +1,442 @@ +setPageSizes($pageSizes ?: Config::inst()->get('GridFieldConfigurablePaginator', 'default_page_sizes')); + + if (!$itemsPerPage) { + $itemsPerPage = $this->pageSizes[0]; + } + + parent::__construct($itemsPerPage); + } + + /** + * Get the total number of records in the list + * + * @return int + */ + public function getTotalRecords() + { + return (int) $this->getGridField()->getList()->count(); + } + + /** + * Get the first shown record number + * + * @return int + */ + public function getFirstShown() + { + $firstShown = $this->getGridPagerState()->firstShown ?: 1; + // Prevent visiting a page with an offset higher than the total number of items + if ($firstShown > $this->getTotalRecords()) { + $this->getGridPagerState()->firstShown = $firstShown = 1; + } + return $firstShown; + } + + /** + * Set the first shown record number. Will be stored in the state. + * + * @param int $firstShown + * @return $this + */ + public function setFirstShown($firstShown = 1) + { + $this->getGridPagerState()->firstShown = (int) $firstShown; + return $this; + } + + /** + * Get the last shown record number + * + * @return int + */ + public function getLastShown() + { + return min($this->getTotalRecords(), $this->getFirstShown() + $this->getItemsPerPage() - 1); + } + + /** + * Get the total number of pages, given the current number of items per page. The total + * pages might be higher than / if the first shown record + * is half way through a standard page break point. + * + * @return int + */ + public function getTotalPages() + { + // Pages before + $pages = ceil(($this->getFirstShown() - 1) / $this->getItemsPerPage()); + + // Current page + $pages++; + + // Pages after + $pages += ceil(($this->getTotalRecords() - $this->getLastShown()) / $this->getItemsPerPage()); + + return (int) $pages; + } + + + /** + * Get the page currently active. This is calculated by adding one to the previous number + * of pages calculated via the "first shown record" position. + * + * @return int + */ + public function getCurrentPage() + { + return (int) ceil(($this->getFirstShown() - 1) / $this->getItemsPerPage()) + 1; + } + + /** + * Get the next page number + * + * @return int + */ + public function getNextPage() + { + return min($this->getTotalPages(), $this->getCurrentPage() + 1); + } + + /** + * Get the previous page number + * + * @return int + */ + public function getPreviousPage() + { + return max(1, $this->getCurrentPage() - 1); + } + + /** + * Set the page sizes to use in the "Show x" dropdown + * + * @param array $pageSizes + * @return $this + */ + public function setPageSizes(array $pageSizes) + { + $this->pageSizes = $pageSizes; + return $this; + } + + /** + * Get the sizes for the "Show x" dropdown + * + * @return array + */ + public function getPageSizes() + { + return $this->pageSizes; + } + + /** + * Gets a list of page sizes for use in templates as a dropdown + * + * @return ArrayList + */ + public function getPageSizesAsList() + { + $pageSizes = ArrayList::create(); + $perPage = $this->getItemsPerPage(); + foreach ($this->getPageSizes() as $pageSize) { + $pageSizes->push(array( + 'Size' => $pageSize, + 'Selected' => $pageSize == $perPage + )); + } + return $pageSizes; + } + + /** + * Get the GridField used in this request + * + * @return GridField + * @throws Exception If the GridField has not been previously set + */ + public function getGridField() + { + if ($this->gridField) { + return $this->gridField; + } + throw new Exception('No GridField available yet for this request!'); + } + + /** + * Set the GridField so it can be used in other parts of the component during this request + * + * @param GridField $gridField + * @return $this + */ + public function setGridField(GridField $gridField) + { + $this->gridField = $gridField; + return $this; + } + + public function handleAction(GridField $gridField, $actionName, $arguments, $data) + { + $this->setGridField($gridField); + + if ($actionName !== 'paginate') { + return; + } + $state = $this->getGridPagerState(); + + $state->firstShown = (int) $arguments['first-shown']; + $state->pageSize = $data[$gridField->getName()]['page-sizes']; + + $this->setItemsPerPage($state->pageSize); + } + + public function getManipulatedData(GridField $gridField, SS_List $dataList) + { + // Assign the GridField to the class so it can be used later in the request + $this->setGridField($gridField); + + if (!($dataList instanceof SS_Limitable) || ($dataList instanceof UnsavedRelationList)) { + return $dataList; + } + + return $dataList->limit($this->getItemsPerPage(), $this->getFirstShown() - 1); + } + + /** + * Add the configurable page size options to the template data + * + * {@inheritDoc} + * + * @param GridField $gridField + * @return ArrayList|null + */ + public function getTemplateParameters(GridField $gridField) + { + $state = $this->getGridPagerState(); + $arguments = $this->getPagerArguments(); + + // Figure out which page and record range we're on + if (!$arguments['total-rows']) { + return; + } + + // If there is only 1 page for all the records in list, we don't need to go further to sort out those + // first page, last page, pre and next pages, etc we are not render those in to the paginator. + if ($arguments['total-pages'] == 1) { + return ArrayData::create(array( + 'OnlyOnePage' => true, + 'FirstShownRecord' => $arguments['first-shown'], + 'LastShownRecord' => $arguments['last-shown'], + 'NumRecords' => $arguments['total-rows'], + 'NumPages' => $arguments['total-pages'] + )); + } + + // Define a list of the FormActions that should be generated for pager controls (see getPagerActions()) + $controls = array( + 'first' => array( + 'title' => 'First', + 'args' => array('first-shown' => 1), + 'extra-class' => 'ss-gridfield-firstpage', + 'disable-previous' => ($this->getCurrentPage() == 1) + ), + 'prev' => array( + 'title' => 'Previous', + 'args' => array('first-shown' => $arguments['first-shown'] - $this->getItemsPerPage()), + 'extra-class' => 'ss-gridfield-previouspage', + 'disable-previous' => ($this->getCurrentPage() == 1) + ), + 'next' => array( + 'title' => 'Next', + 'args' => array('first-shown' => $arguments['first-shown'] + $this->getItemsPerPage()), + 'extra-class' => 'ss-gridfield-nextpage', + 'disable-next' => ($this->getCurrentPage() == $arguments['total-pages']) + ), + 'last' => array( + 'title' => 'Last', + 'args' => array('first-shown' => ($this->getTotalPages() - 1) * $this->getItemsPerPage() + 1), + 'extra-class' => 'ss-gridfield-lastpage', + 'disable-next' => ($this->getCurrentPage() == $arguments['total-pages']) + ), + 'pagesize' => array( + 'title' => 'Page Size', + 'args' => array('first-shown' => $arguments['first-shown']), + 'extra-class' => 'ss-gridfield-pagesize-submit' + ), + ); + + if ($controls['prev']['args']['first-shown'] < 1) { + $controls['prev']['args']['first-shown'] = 1; + } + + $actions = $this->getPagerActions($controls, $gridField); + + // Render in template + return ArrayData::create(array( + 'OnlyOnePage' => false, + 'FirstPage' => $actions['first'], + 'PreviousPage' => $actions['prev'], + 'NextPage' => $actions['next'], + 'LastPage' => $actions['last'], + 'PageSizesSubmit' => $actions['pagesize'], + 'CurrentPageNum' => $this->getCurrentPage(), + 'NumPages' => $arguments['total-pages'], + 'FirstShownRecord' => $arguments['first-shown'], + 'LastShownRecord' => $arguments['last-shown'], + 'NumRecords' => $arguments['total-rows'], + 'PageSizes' => $this->getPageSizesAsList(), + 'PageSizesName' => $gridField->getName() . '[page-sizes]', + )); + } + + public function getHTMLFragments($gridField) + { + GridFieldExtensions::include_requirements(); + + $gridField->addExtraClass('ss-gridfield-configurable-paginator'); + + $forTemplate = $this->getTemplateParameters($gridField); + if ($forTemplate) { + return array( + 'footer' => $forTemplate->renderWith( + $this->itemClass, + array('Colspan' => count($gridField->getColumns())) + ) + ); + } + } + + /** + * Returns an array containing the arguments for the pagination: total rows, pages, first record etc + * + * @return array + */ + protected function getPagerArguments() + { + return array( + 'total-rows' => $this->getTotalRecords(), + 'total-pages' => $this->getTotalPages(), + 'items-per-page' => $this->getItemsPerPage(), + 'first-shown' => $this->getFirstShown(), + 'last-shown' => $this->getLastShown(), + ); + } + + /** + * Returns FormActions for each of the pagination actions, in an array + * + * @param array $controls + * @param GridField $gridField + * @return GridField_FormAction[] + */ + public function getPagerActions(array $controls, GridField $gridField) + { + $actions = array(); + + foreach ($controls as $key => $arguments) { + $action = GridField_FormAction::create( + $gridField, + 'pagination_' . $key, + $arguments['title'], + 'paginate', + $arguments['args'] + ); + + if (isset($arguments['extra-class'])) { + $action->addExtraClass($arguments['extra-class']); + } + + if (isset($arguments['disable-previous']) && $arguments['disable-previous']) { + $action = $action->performDisabledTransformation(); + } elseif (isset($arguments['disable-next']) && $arguments['disable-next']) { + $action = $action->performDisabledTransformation(); + } + + $actions[$key] = $action; + } + + return $actions; + } + + public function getActions($gridField) + { + return array('paginate'); + } + + /** + * Gets the state from the current request's GridField and sets some default values on it + * + * @param GridField $gridField Not used, but present for parent method compatibility + * @return GridState_Data + */ + protected function getGridPagerState(GridField $gridField = null) + { + if (!$this->gridFieldState) { + $state = $this->getGridField()->State->GridFieldConfigurablePaginator; + + // SS 3.1 compatibility (missing __call) + if (is_object($state->firstShown)) { + $state->firstShown = 1; + } + if (is_object($state->pageSize)) { + $state->pageSize = $this->getItemsPerPage(); + } + + // Handle free input in the page number field + $parentState = $this->getGridField()->State->GridFieldPaginator; + if (is_object($parentState->currentPage)) { + $parentState->currentPage = 0; + } + + if ($parentState->currentPage >= 1) { + $state->firstShown = ($parentState->currentPage - 1) * $this->getItemsPerPage() + 1; + $parentState->currentPage = null; + } + + $this->gridFieldState = $state; + } + + return $this->gridFieldState; + } +} diff --git a/css/GridFieldExtensions.css b/css/GridFieldExtensions.css index d2a3038..a3d11c5 100644 --- a/css/GridFieldExtensions.css +++ b/css/GridFieldExtensions.css @@ -1,184 +1,206 @@ -/** - * GridFieldAddExistingSearchButton - */ - -.add-existing-search-dialog { - min-width: inherit !important; -} - -.add-existing-search-dialog .add-existing-search-form .field { - border: none; - box-shadow: none; - margin-bottom: 0; - padding-bottom: 0; -} - -.add-existing-search-dialog .add-existing-search-form .field label { - padding-bottom: 4px; -} - -.add-existing-search-dialog .add-existing-search-form .Actions { - margin-top: 10px; - padding: 0; -} - -.add-existing-search-dialog .add-existing-search-items li a { - background: #FFF; - border-bottom-width: 1px; - border-color: #CCC; - border-left-width: 1px; - border-right-width: 1px; - border-style: solid; - display: block; - padding: 6px; -} - -.add-existing-search-dialog .add-existing-search-items li:first-child a { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-top-width: 1px; -} - -.add-existing-search-dialog .add-existing-search-items li:last-child a { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -.add-existing-search-dialog .add-existing-search-items li a:hover { - background: #F4F4F4; -} - -.add-existing-search-dialog .add-existing-search-pagination li { - background: #FFF; - display: block; - float: left; - margin-right: 2px; - margin-top: 12px; - padding: 6px; -} - -/** - * GridFieldAddNewInlineButton - */ - -.ss-gridfield-inline-new { - background: #EFE; -} - -.ss-gridfield-inline-new:nth-child(2n) { - background: #DFD; -} - -/** - * GridFieldAddNewMultiClass - */ - -.ss-gridfield-add-new-multi-class { - margin-bottom: 8px !important; -} - -.ss-gridfield-add-new-multi-class .field { - border: none; - box-shadow: none; - float: left; - margin: 0 4px 0 0; -} - -/** - * GridFieldEditableColumns - */ - -.ss-gridfield-editable .readonly { - padding-top: 0 !important; -} - -.ss-gridfield-editable input.text, -.ss-gridfield-editable textarea, -.ss-gridfield-editable select, -.ss-gridfield-editable .TreeDropdownField { - margin: 0 !important; - max-width: none !important; -} - -.ss-gridfield-editable select.dropdown { - border: 1px solid #b3b3b3; - background-color: #fff; - padding: 7px 7px; - padding-left: 4px; - line-height: 16px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; -} - -.ss-gridfield-add-new-inline { - margin-bottom: 12px; -} - -.ss-gridfield-add-new-inline span.readonly { - color: #FFF !important; -} - -.ss-gridfield-add-new-inline .col-buttons { - text-align: right; -} - -/** - * GridFieldOrderableRows - */ - -.ss-gridfield-orderable thead tr th.col-Reorder span { - padding: 0 !important; - margin-left: 8px; -} - -.ss-gridfield-orderable .col-reorder { - position: relative; - padding: 0 !important; - width: 16px !important; -} - -.ss-gridfield-orderable .col-reorder .handle { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - cursor: move; -} - -.ss-gridfield-orderable .col-reorder .handle .icon { - position: absolute; - top: 50%; - left: 50%; - width: 5px; - height: 11px; - margin: -5px 0 0 -2px; - background-image: url('../../framework/thirdparty/jquery-ui-themes/smoothness/images/ui-icons_222222_256x240.png'); - background-position: -5px -227px; -} - -.ss-gridfield-orderhelper { - border-bottom: 1px solid rgba(0, 0, 0, .1); - border-top: 1px solid rgba(0, 0, 0, .1); - box-shadow: 0 0 8px rgba(0, 0, 0, .4); -} - -.ss-gridfield-orderable tfoot .ui-droppable { - padding-left: 12px; - padding-right: 12px; -} - -.ss-gridfield-orderable tfoot .ui-droppable-active { - background-color: #D4CF90 !important; -} - -.ss-gridfield-orderable tfoot .ss-gridfield-previouspage { - background-position: -16px 9px !important; - margin-left: 0; -} - -.ss-gridfield-orderable tfoot .ss-gridfield-nextpage { - background-position: -40px 9px !important; - margin-right: 0; -} +/** + * GridFieldAddExistingSearchButton + */ + +.add-existing-search-dialog { + min-width: inherit !important; +} + +.add-existing-search-dialog .add-existing-search-form .field { + border: none; + box-shadow: none; + margin-bottom: 0; + padding-bottom: 0; +} + +.add-existing-search-dialog .add-existing-search-form .field label { + padding-bottom: 4px; +} + +.add-existing-search-dialog .add-existing-search-form .Actions { + margin-top: 10px; + padding: 0; +} + +.add-existing-search-dialog .add-existing-search-items li a { + background: #FFF; + border-bottom-width: 1px; + border-color: #CCC; + border-left-width: 1px; + border-right-width: 1px; + border-style: solid; + display: block; + padding: 6px; +} + +.add-existing-search-dialog .add-existing-search-items li:first-child a { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-top-width: 1px; +} + +.add-existing-search-dialog .add-existing-search-items li:last-child a { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} + +.add-existing-search-dialog .add-existing-search-items li a:hover { + background: #F4F4F4; +} + +.add-existing-search-dialog .add-existing-search-pagination li { + background: #FFF; + display: block; + float: left; + margin-right: 2px; + margin-top: 12px; + padding: 6px; +} + +/** + * GridFieldAddNewInlineButton + */ + +.ss-gridfield-inline-new { + background: #EFE; +} + +.ss-gridfield-inline-new:nth-child(2n) { + background: #DFD; +} + +/** + * GridFieldAddNewMultiClass + */ + +.ss-gridfield-add-new-multi-class { + margin-bottom: 8px !important; +} + +.ss-gridfield-add-new-multi-class .field { + border: none; + box-shadow: none; + float: left; + margin: 0 4px 0 0; +} + +/** + * GridFieldEditableColumns + */ + +.ss-gridfield-editable .readonly { + padding-top: 0 !important; +} + +.ss-gridfield-editable input.text, +.ss-gridfield-editable textarea, +.ss-gridfield-editable select, +.ss-gridfield-editable .TreeDropdownField { + margin: 0 !important; + max-width: none !important; +} + +.ss-gridfield-editable select.dropdown { + border: 1px solid #b3b3b3; + background-color: #fff; + padding: 7px 7px; + padding-left: 4px; + line-height: 16px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} + +.ss-gridfield-add-new-inline { + margin-bottom: 12px; +} + +.ss-gridfield-add-new-inline span.readonly { + color: #FFF !important; +} + +.ss-gridfield-add-new-inline .col-buttons { + text-align: right; +} + +/** + * GridFieldOrderableRows + */ + +.ss-gridfield-orderable thead tr th.col-Reorder span { + padding: 0 !important; + margin-left: 8px; +} + +.ss-gridfield-orderable .col-reorder { + position: relative; + padding: 0 !important; + width: 16px !important; +} + +.ss-gridfield-orderable .col-reorder .handle { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + cursor: move; +} + +.ss-gridfield-orderable .col-reorder .handle .icon { + position: absolute; + top: 50%; + left: 50%; + width: 5px; + height: 11px; + margin: -5px 0 0 -2px; + background-image: url('../../framework/thirdparty/jquery-ui-themes/smoothness/images/ui-icons_222222_256x240.png'); + background-position: -5px -227px; +} + +.ss-gridfield-orderhelper { + border-bottom: 1px solid rgba(0, 0, 0, .1); + border-top: 1px solid rgba(0, 0, 0, .1); + box-shadow: 0 0 8px rgba(0, 0, 0, .4); +} + +.ss-gridfield-orderable tfoot .ui-droppable { + padding-left: 12px; + padding-right: 12px; +} + +.ss-gridfield-orderable tfoot .ui-droppable-active { + background-color: #D4CF90 !important; +} + +.ss-gridfield-orderable tfoot .ss-gridfield-previouspage { + background-position: -16px 9px !important; + margin-left: 0; +} + +.ss-gridfield-orderable tfoot .ss-gridfield-nextpage { + background-position: -40px 9px !important; + margin-right: 0; +} + +/** + * GridFieldConfigurablePaginator + */ +.ss-gridfield-configurable-paginator .pagination-page-size { + color: #fff; + float: left; + padding: 6px 0; +} + +.ss-gridfield-configurable-paginator .pagination-page-size-select { + margin-left: 0; + width: auto; +} + +.ss-gridfield-configurable-paginator .ss-gridfield-pagesize-submit { + display: none; +} + +.ss-gridfield-configurable-paginator .pagination-page-number input { + text-align: center; +} diff --git a/javascript/GridFieldExtensions.js b/javascript/GridFieldExtensions.js index 435cdb6..ff2f230 100644 --- a/javascript/GridFieldExtensions.js +++ b/javascript/GridFieldExtensions.js @@ -387,5 +387,14 @@ if(this.hasClass("ui-droppable")) this.droppable("destroy"); } }); + + /** + * GridFieldConfigurablePaginator + */ + $('.ss-gridfield-configurable-paginator .pagination-page-size-select').entwine({ + onchange: function () { + this.parent().find('.ss-gridfield-pagesize-submit').trigger('click'); + } + }); }); })(jQuery); diff --git a/templates/GridFieldConfigurablePaginator.ss b/templates/GridFieldConfigurablePaginator.ss new file mode 100644 index 0000000..f1229b2 --- /dev/null +++ b/templates/GridFieldConfigurablePaginator.ss @@ -0,0 +1,30 @@ + + + <% if not $OnlyOnePage %> + + <%t GridFieldConfigurablePaginator.SHOW 'Show' %> + + $PageSizesSubmit + +
+ $FirstPage $PreviousPage + + <%t Pagination.Page 'Page' %> + + <%t TableListField_PageControls_ss.OF 'of' is 'Example: View 1 of 2' %> + $NumPages + + $NextPage $LastPage +
+ <% end_if %> + + {$FirstShownRecord}–{$LastShownRecord} + <%t TableListField_PageControls_ss.OF 'of' is 'Example: View 1 of 2' %> + $NumRecords + + + diff --git a/tests/GridFieldConfigurablePaginatorTest.php b/tests/GridFieldConfigurablePaginatorTest.php new file mode 100644 index 0000000..cd58440 --- /dev/null +++ b/tests/GridFieldConfigurablePaginatorTest.php @@ -0,0 +1,217 @@ +push(array('ID' => $i)); + } + + $this->gridField = GridField::create('Mock', null, $data); + } + + public function testGetTotalRecords() + { + $paginator = new GridFieldConfigurablePaginator; + $paginator->setGridField($this->gridField); + + $this->assertSame(130, $paginator->getTotalRecords()); + } + + public function testGetFirstShown() + { + $paginator = new GridFieldConfigurablePaginator; + $paginator->setGridField($this->gridField); + + // No state + $this->assertSame(1, $paginator->getFirstShown()); + + // With a state + $paginator->setFirstShown(123); + $this->assertSame(123, $paginator->getFirstShown()); + + // Too high! + $paginator->setFirstShown(234); + $this->assertSame(1, $paginator->getFirstShown()); + } + + public function testGetLastShown() + { + $paginator = new GridFieldConfigurablePaginator(20, array(10, 20, 30)); + $paginator->setGridField($this->gridField); + + $this->assertSame(20, $paginator->getLastShown()); + + $paginator->setFirstShown(5); + $this->assertSame(24, $paginator->getLastShown()); + } + + public function testGetTotalPages() + { + $paginator = new GridFieldConfigurablePaginator(20, array(20, 40, 60)); + $paginator->setGridField($this->gridField); + + // Default calculation + $this->assertSame(7, $paginator->getTotalPages()); + + // With a standard "first shown" record number, e.g. page 2 + $paginator->setFirstShown(21); + $this->assertSame(7, $paginator->getTotalPages()); + + // Non-standard "first shown", e.g. when a page size is changed at page 3. In this case the first page is + // 20 records, the second page is 7 records, third page 20 records, etc + $paginator->setFirstShown(27); + $this->assertSame(8, $paginator->getTotalPages()); + + // ... and when the page size has also been changed. In this case the first page is 57 records, second page + // 60 records and last page is 13 records + $paginator->setFirstShown(57); + $paginator->setItemsPerPage(60); + $this->assertSame(3, $paginator->getTotalPages()); + } + + public function testGetCurrentPreviousAndNextPages() + { + $paginator = new GridFieldConfigurablePaginator(20, array(20, 40, 60)); + $paginator->setGridField($this->gridField); + + // No page selected (first page) + $this->assertSame(1, $paginator->getCurrentPage()); + $this->assertSame(1, $paginator->getPreviousPage()); + $this->assertSame(2, $paginator->getNextPage()); + + // Second page + $paginator->setFirstShown(21); + $this->assertSame(2, $paginator->getCurrentPage()); + $this->assertSame(1, $paginator->getPreviousPage()); + $this->assertSame(3, $paginator->getNextPage()); + + // Third page + $paginator->setFirstShown(41); + $this->assertSame(3, $paginator->getCurrentPage()); + $this->assertSame(2, $paginator->getPreviousPage()); + $this->assertSame(4, $paginator->getNextPage()); + + // Fourth page, partial record count + $paginator->setFirstShown(42); + $this->assertSame(4, $paginator->getCurrentPage()); + $this->assertSame(3, $paginator->getPreviousPage()); + $this->assertSame(5, $paginator->getNextPage()); + + // Last page (default paging) + $paginator->setFirstShown(121); + $this->assertSame(7, $paginator->getCurrentPage()); + $this->assertSame(6, $paginator->getPreviousPage()); + $this->assertSame(7, $paginator->getNextPage()); + + // Non-standard page size should recalculate the page numbers to be relative to the page size + $paginator->setFirstShown(121); + $paginator->setItemsPerPage(60); + $this->assertSame(3, $paginator->getCurrentPage()); + $this->assertSame(2, $paginator->getPreviousPage()); + $this->assertSame(3, $paginator->getNextPage()); + } + + public function testPageSizesAreConfigurable() + { + // Via constructor + $paginator = new GridFieldConfigurablePaginator(3, array(2, 4, 6)); + $this->assertSame(3, $paginator->getItemsPerPage()); + $this->assertSame(array(2, 4, 6), $paginator->getPageSizes()); + + // Via public API + $paginator->setPageSizes(array(10, 20, 30)); + $this->assertSame(array(10, 20, 30), $paginator->getPageSizes()); + + // Via default configuration + $paginator = new GridFieldConfigurablePaginator; + $default = Config::inst()->get('GridFieldConfigurablePaginator', 'default_page_sizes'); + $this->assertSame($default, $paginator->getPageSizes()); + } + + public function testGetPageSizesAsList() + { + $paginator = new GridFieldConfigurablePaginator(10, array(10, 20, 30)); + $this->assertDOSEquals(array( + array('Size' => '10', 'Selected' => true), + array('Size' => '20', 'Selected' => false), + array('Size' => '30', 'Selected' => false), + ), $paginator->getPageSizesAsList()); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage No GridField available yet for this request! + */ + public function testGetGridFieldThrowsExceptionWhenNotSet() + { + $paginator = new GridFieldConfigurablePaginator; + $paginator->getGridField(); + } + + public function testGetPagerActions() + { + $controls = array( + 'prev' => array( + 'title' => 'Previous', + 'args' => array( + 'next-page' => 123, + 'first-shown' => 234 + ), + 'extra-class' => 'ss-gridfield-previouspage', + 'disable-previous' => false + ), + 'next' => array( + 'title' => 'Next', + 'args' => array( + 'next-page' => 234, + 'first-shown' => 123 + ), + 'extra-class' => 'ss-gridfield-nextpage', + 'disable-next' => true + ) + ); + + $gridField = $this->getMockBuilder('GridField')->disableOriginalConstructor()->getMock(); + $paginator = new GridFieldConfigurablePaginator; + $result = $paginator->getPagerActions($controls, $gridField); + + $this->assertCount(2, $result); + $this->assertArrayHasKey('next', $result); + $this->assertContainsOnlyInstancesOf('GridField_FormAction', $result); + + $this->assertFalse($result['prev']->isDisabled()); + + $this->assertTrue((bool) $result['next']->hasClass('ss-gridfield-nextpage')); + $this->assertTrue($result['next']->isDisabled()); + } + + public function testSinglePageWithLotsOfItems() + { + $paginator = new GridFieldConfigurablePaginator(null, array(100, 200, 300)); + $this->assertSame(100, $paginator->getItemsPerPage()); + } + + /** + * Set something to the GridField's paginator state data + * + * @param string $key + * @param mixed $value + * @return $this + */ + protected function setState($key, $value) + { + $this->gridField->State->GridFieldConfigurablePaginator->$key = $value; + return $this; + } +} From f0d2ec773395a71bd8ca90c72d596b89a6103027 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Tue, 1 Aug 2017 16:41:17 +1200 Subject: [PATCH 03/20] FIX Always show page size dropdown when page size is greater than number of records --- code/GridFieldConfigurablePaginator.php | 14 +------------- templates/GridFieldConfigurablePaginator.ss | 18 +++++++++--------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/code/GridFieldConfigurablePaginator.php b/code/GridFieldConfigurablePaginator.php index 6c785f9..817685c 100644 --- a/code/GridFieldConfigurablePaginator.php +++ b/code/GridFieldConfigurablePaginator.php @@ -261,18 +261,6 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator return; } - // If there is only 1 page for all the records in list, we don't need to go further to sort out those - // first page, last page, pre and next pages, etc we are not render those in to the paginator. - if ($arguments['total-pages'] == 1) { - return ArrayData::create(array( - 'OnlyOnePage' => true, - 'FirstShownRecord' => $arguments['first-shown'], - 'LastShownRecord' => $arguments['last-shown'], - 'NumRecords' => $arguments['total-rows'], - 'NumPages' => $arguments['total-pages'] - )); - } - // Define a list of the FormActions that should be generated for pager controls (see getPagerActions()) $controls = array( 'first' => array( @@ -314,7 +302,7 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator // Render in template return ArrayData::create(array( - 'OnlyOnePage' => false, + 'OnlyOnePage' => ($arguments['total-pages'] == 1), 'FirstPage' => $actions['first'], 'PreviousPage' => $actions['prev'], 'NextPage' => $actions['next'], diff --git a/templates/GridFieldConfigurablePaginator.ss b/templates/GridFieldConfigurablePaginator.ss index f1229b2..adb2490 100644 --- a/templates/GridFieldConfigurablePaginator.ss +++ b/templates/GridFieldConfigurablePaginator.ss @@ -1,15 +1,15 @@ + + <%t GridFieldConfigurablePaginator.SHOW 'Show' %> + + $PageSizesSubmit + <% if not $OnlyOnePage %> - - <%t GridFieldConfigurablePaginator.SHOW 'Show' %> - - $PageSizesSubmit -
$FirstPage $PreviousPage From 403900f1fd86e8896c04586c42e454e7d4de0aa3 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Wed, 2 Aug 2017 09:28:31 +1200 Subject: [PATCH 04/20] FIX Ensure selected page size is retained when deleting records --- code/GridFieldConfigurablePaginator.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/code/GridFieldConfigurablePaginator.php b/code/GridFieldConfigurablePaginator.php index 817685c..c147e09 100644 --- a/code/GridFieldConfigurablePaginator.php +++ b/code/GridFieldConfigurablePaginator.php @@ -236,6 +236,12 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator // Assign the GridField to the class so it can be used later in the request $this->setGridField($gridField); + // Retain page sizes during actions provided by other components + $state = $this->getGridPagerState(); + if (is_numeric($state->pageSize)) { + $this->setItemsPerPage($state->pageSize); + } + if (!($dataList instanceof SS_Limitable) || ($dataList instanceof UnsavedRelationList)) { return $dataList; } @@ -254,6 +260,9 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator public function getTemplateParameters(GridField $gridField) { $state = $this->getGridPagerState(); + if (is_numeric($state->pageSize)) { + $this->setItemsPerPage($state->pageSize); + } $arguments = $this->getPagerArguments(); // Figure out which page and record range we're on From 36d9711a4704320666cb849f920fcfad194480cf Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Wed, 2 Aug 2017 10:03:00 +1200 Subject: [PATCH 05/20] Use precise distro for Travis builds to allow PHP 5.3 to work --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8425684..fceb781 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ sudo: false language: php +dist: precise + php: - 5.3 - 5.4 From 719b906460c80e476c882af6104d6a3918223881 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Wed, 23 Aug 2017 14:07:42 +1200 Subject: [PATCH 06/20] DOCS Add use examples for GridFieldConfigurablePaginator --- README.md | 51 ++++++++++++++++++++++++------------------------ docs/en/index.md | 33 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index fda41b7..b5589c1 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,26 @@ -SilverStripe Grid Field Extensions Module -========================================= - -[![Build Status](https://travis-ci.org/silverstripe-australia/silverstripe-gridfieldextensions.svg?branch=master)](https://travis-ci.org/silverstripe-australia/silverstripe-gridfieldextensions) -[![Latest Stable Version](https://poser.pugx.org/silverstripe-australia/gridfieldextensions/version.svg)](https://github.com/silverstripe-australia/silverstripe-gridfieldextensions/releases) -[![Latest Unstable Version](https://poser.pugx.org/silverstripe-australia/gridfieldextensions/v/unstable.svg)](https://packagist.org/packages/silverstripe-australia/gridfieldextensions) -[![Total Downloads](https://poser.pugx.org/silverstripe-australia/gridfieldextensions/downloads.svg)](https://packagist.org/packages/silverstripe-australia/gridfieldextensions) -[![License](https://poser.pugx.org/silverstripe-australia/gridfieldextensions/license.svg)](https://github.com/silverstripe-australia/silverstripe-gridfieldextensions/blob/master/LICENSE.md) - -This module provides a number of useful grid field components: - -* `GridFieldAddExistingSearchButton` - a more advanced search form for adding - items. -* `GridFieldAddNewInlineButton` - builds on `GridFieldEditableColumns` to allow - inline creation of records. -* `GridFieldAddNewMultiClass` - lets the user select from a list of classes to - create a new record from. -* `GridFieldEditableColumns` - allows inline editing of records. -* `GridFieldOrderableRows` - drag and drop re-ordering of rows. -* `GridFieldRequestHandler` - a basic utility class which can be used to build - custom grid field detail views including tabs, breadcrumbs and other CMS - features. -* `GridFieldTitleHeader` - a simple header which displays column titles. - -See [docs/en/index.md](docs/en/index.md) for documentation and examples. +SilverStripe Grid Field Extensions Module +========================================= + +[![Build Status](https://travis-ci.org/silverstripe-australia/silverstripe-gridfieldextensions.svg?branch=master)](https://travis-ci.org/silverstripe-australia/silverstripe-gridfieldextensions) +[![Latest Stable Version](https://poser.pugx.org/silverstripe-australia/gridfieldextensions/version.svg)](https://github.com/silverstripe-australia/silverstripe-gridfieldextensions/releases) +[![Latest Unstable Version](https://poser.pugx.org/silverstripe-australia/gridfieldextensions/v/unstable.svg)](https://packagist.org/packages/silverstripe-australia/gridfieldextensions) +[![Total Downloads](https://poser.pugx.org/silverstripe-australia/gridfieldextensions/downloads.svg)](https://packagist.org/packages/silverstripe-australia/gridfieldextensions) +[![License](https://poser.pugx.org/silverstripe-australia/gridfieldextensions/license.svg)](https://github.com/silverstripe-australia/silverstripe-gridfieldextensions/blob/master/LICENSE.md) + +This module provides a number of useful grid field components: + +* `GridFieldAddExistingSearchButton` - a more advanced search form for adding + items. +* `GridFieldAddNewInlineButton` - builds on `GridFieldEditableColumns` to allow + inline creation of records. +* `GridFieldAddNewMultiClass` - lets the user select from a list of classes to + create a new record from. +* `GridFieldEditableColumns` - allows inline editing of records. +* `GridFieldOrderableRows` - drag and drop re-ordering of rows. +* `GridFieldRequestHandler` - a basic utility class which can be used to build + custom grid field detail views including tabs, breadcrumbs and other CMS + features. +* `GridFieldTitleHeader` - a simple header which displays column titles. +* `GridFieldConfigurablePaginator` - a paginator for GridField that allows customisable page sizes. + +See [docs/en/index.md](docs/en/index.md) for documentation and examples. diff --git a/docs/en/index.md b/docs/en/index.md index fb1e3be..5afe558 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -106,3 +106,36 @@ class Item extends DataObject { **Please NOTE:** There is a limitation when using `GridFieldOrderableRows` on unsaved data objects; namely, that it doesn't work as without data being saved, the list of related objects has no context. Please check `$this->ID` before adding the `GridFieldOrderableRows` component to the grid field config (or even, before adding the gridfield at all). +Configurable Paginator +---------------------- + +The `GridFieldConfigurablePaginator` component allows you to have a page size dropdown added to your GridField +pagination controls. The page sizes are configurable via the configuration system, or at call time using the public API. +To use this component you should remove the original paginator component first: + +```php +$gridField->getConfig() + ->removeComponentsByType('GridFieldPaginator') + ->addComponent(new GridFieldConfigurablePaginator()); +``` + +You can configure the page sizes with the configuration system. Note that merging is the default strategy, so to replace +the default sizes with your own you will need to unset the original first, for example: + +```php +# File: mysite/_config.php +Config::inst()->remove('GridFieldConfigurablePaginator', 'default_page_sizes'); +Config::inst()->update('GridFieldConfigurablePaginator', 'default_page_sizes', array(100, 200, 500)); +``` + +You can also override these at call time: + +```php +$paginator = new GridFieldConfigurablePaginator(100, array(100, 200, 500)); + +$paginator->setPageSizes(array(200, 500, 1000)); +$paginator->setItemsPerPage(500); +``` + +The first shown record will be maintained across page size changes, and the number of pages and current page will be +recalculated on each request, based on the current first shown record and page size. From e14ed0b47aec0b8deb34f83653ac4dc089d07a34 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Mon, 28 Aug 2017 14:04:20 +1200 Subject: [PATCH 07/20] Update vendor and create new major 2.x branch --- composer.json | 75 ++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/composer.json b/composer.json index 4f171c4..5b0b50b 100644 --- a/composer.json +++ b/composer.json @@ -1,37 +1,38 @@ -{ - "name": "silverstripe-australia/gridfieldextensions", - "description": "A collection of useful grid field components", - "type": "silverstripe-module", - "homepage": "http://github.com/silverstripe-australia/silverstripe-gridfieldextensions", - "keywords": ["silverstripe", "gridfield"], - "license": "BSD-3-Clause", - "authors": [ - { - "name": "Andrew Short", - "email": "andrewjshort@gmail.com" - }, - { - "name": "Marcus Nyeholt", - "email": "marcus@silverstripe.com.au" - } - ], - "support": { - "issues": "http://github.com/silverstripe-australia/silverstripe-gridfieldextensions/issues" - }, - "require": { - "silverstripe/framework": "~3.1" - }, - "extra": { - "installer-name": "gridfieldextensions", - "branch-alias": { - "dev-master": "1.4.x-dev" - }, - "screenshots": [ - "docs/en/_images/editable-rows.png", - "docs/en/_images/add-existing-search.png" - ] - }, - "replace": { - "ajshort/silverstripe-gridfieldextensions": "self.version" - } -} +{ + "name": "symbiote/silverstripe-gridfieldextensions", + "description": "A collection of useful grid field components", + "type": "silverstripe-module", + "homepage": "http://github.com/silverstripe-australia/silverstripe-gridfieldextensions", + "keywords": ["silverstripe", "gridfield"], + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Andrew Short", + "email": "andrewjshort@gmail.com" + }, + { + "name": "Marcus Nyeholt", + "email": "marcus@silverstripe.com.au" + } + ], + "support": { + "issues": "http://github.com/symbiote/silverstripe-gridfieldextensions/issues" + }, + "require": { + "silverstripe/framework": "~3.1" + }, + "extra": { + "installer-name": "gridfieldextensions", + "branch-alias": { + "2.x-dev": "2.0.x-dev" + }, + "screenshots": [ + "docs/en/_images/editable-rows.png", + "docs/en/_images/add-existing-search.png" + ] + }, + "replace": { + "silverstripe-australia/gridfieldextensions": "self.version", + "ajshort/silverstripe-gridfieldextensions": "self.version" + } +} From 320a6198e5955522c915ebb232223d5358fb4f31 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Thu, 31 Aug 2017 14:35:52 +1200 Subject: [PATCH 08/20] FIX When setting the page sizes, reset items per page to the first value --- code/GridFieldConfigurablePaginator.php | 4 ++++ tests/GridFieldConfigurablePaginatorTest.php | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/code/GridFieldConfigurablePaginator.php b/code/GridFieldConfigurablePaginator.php index c147e09..6ddfd07 100644 --- a/code/GridFieldConfigurablePaginator.php +++ b/code/GridFieldConfigurablePaginator.php @@ -159,6 +159,10 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator public function setPageSizes(array $pageSizes) { $this->pageSizes = $pageSizes; + + // Reset items per page + $this->setItemsPerPage(current($pageSizes)); + return $this; } diff --git a/tests/GridFieldConfigurablePaginatorTest.php b/tests/GridFieldConfigurablePaginatorTest.php index cd58440..b16235a 100644 --- a/tests/GridFieldConfigurablePaginatorTest.php +++ b/tests/GridFieldConfigurablePaginatorTest.php @@ -80,6 +80,20 @@ class GridFieldConfigurablePaginatorTest extends SapphireTest $this->assertSame(3, $paginator->getTotalPages()); } + public function testItemsPerPageIsSetToFirstInPageSizesListWhenChanged() + { + $paginator = new GridFieldConfigurablePaginator(20, array(20, 40, 60)); + $paginator->setGridField($this->gridField); + + // Initial state, should be what was provided to the constructor + $this->assertSame(20, $paginator->getItemsPerPage()); + + $paginator->setPageSizes(array(50, 100, 200)); + + // Set via public API, should now be set to 50 + $this->assertSame(50, $paginator->getItemsPerPage()); + } + public function testGetCurrentPreviousAndNextPages() { $paginator = new GridFieldConfigurablePaginator(20, array(20, 40, 60)); From a36c96e692cfa9313751fb507535064e18a09690 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Thu, 31 Aug 2017 14:44:40 +1200 Subject: [PATCH 09/20] Remove obsolete branch alias --- composer.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/composer.json b/composer.json index 5b0b50b..0421c76 100644 --- a/composer.json +++ b/composer.json @@ -23,9 +23,6 @@ }, "extra": { "installer-name": "gridfieldextensions", - "branch-alias": { - "2.x-dev": "2.0.x-dev" - }, "screenshots": [ "docs/en/_images/editable-rows.png", "docs/en/_images/add-existing-search.png" From a34d6242a5100ac9b74632b47eb68a25b4e589f1 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Thu, 31 Aug 2017 14:45:42 +1200 Subject: [PATCH 10/20] Update branch alias --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5b0b50b..4dd634c 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "extra": { "installer-name": "gridfieldextensions", "branch-alias": { - "2.x-dev": "2.0.x-dev" + "2.x-dev": "2.1.x-dev" }, "screenshots": [ "docs/en/_images/editable-rows.png", From 58200f847fbdeeae6f593274846e939e482cbdd5 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Thu, 31 Aug 2017 14:35:52 +1200 Subject: [PATCH 11/20] FIX When setting the page sizes, reset items per page to the first value --- code/GridFieldConfigurablePaginator.php | 4 ++++ tests/GridFieldConfigurablePaginatorTest.php | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/code/GridFieldConfigurablePaginator.php b/code/GridFieldConfigurablePaginator.php index c147e09..6ddfd07 100644 --- a/code/GridFieldConfigurablePaginator.php +++ b/code/GridFieldConfigurablePaginator.php @@ -159,6 +159,10 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator public function setPageSizes(array $pageSizes) { $this->pageSizes = $pageSizes; + + // Reset items per page + $this->setItemsPerPage(current($pageSizes)); + return $this; } diff --git a/tests/GridFieldConfigurablePaginatorTest.php b/tests/GridFieldConfigurablePaginatorTest.php index cd58440..b16235a 100644 --- a/tests/GridFieldConfigurablePaginatorTest.php +++ b/tests/GridFieldConfigurablePaginatorTest.php @@ -80,6 +80,20 @@ class GridFieldConfigurablePaginatorTest extends SapphireTest $this->assertSame(3, $paginator->getTotalPages()); } + public function testItemsPerPageIsSetToFirstInPageSizesListWhenChanged() + { + $paginator = new GridFieldConfigurablePaginator(20, array(20, 40, 60)); + $paginator->setGridField($this->gridField); + + // Initial state, should be what was provided to the constructor + $this->assertSame(20, $paginator->getItemsPerPage()); + + $paginator->setPageSizes(array(50, 100, 200)); + + // Set via public API, should now be set to 50 + $this->assertSame(50, $paginator->getItemsPerPage()); + } + public function testGetCurrentPreviousAndNextPages() { $paginator = new GridFieldConfigurablePaginator(20, array(20, 40, 60)); From 04a07d505f32f3256f35c7653389198e18e0bccd Mon Sep 17 00:00:00 2001 From: Jake B Date: Mon, 4 Sep 2017 14:59:38 +1000 Subject: [PATCH 12/20] FIX Backport and sanitiseClassName for the "Save" action URL --- code/GridFieldAddNewMultiClassHandler.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/code/GridFieldAddNewMultiClassHandler.php b/code/GridFieldAddNewMultiClassHandler.php index cf99b5b..8eb41d0 100644 --- a/code/GridFieldAddNewMultiClassHandler.php +++ b/code/GridFieldAddNewMultiClassHandler.php @@ -9,9 +9,16 @@ class GridFieldAddNewMultiClassHandler extends GridFieldDetailForm_ItemRequest { return parent::Link($action); } else { return Controller::join_links( - $this->gridField->Link(), 'add-multi-class', get_class($this->record) + $this->gridField->Link(), 'add-multi-class', $this->sanitiseClassName(get_class($this->record)) ); } } + /** + * Sanitise a model class' name for inclusion in a link + * @return string + */ + protected function sanitiseClassName($class) { + return str_replace('\\', '-', $class); + } } From b7b3678b26594e798ca61396981815f64ba3e688 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Fri, 15 Sep 2017 17:03:20 +1200 Subject: [PATCH 13/20] =?UTF-8?q?BUG=20Fix=20casting=20for=20=E2=80=98$Att?= =?UTF-8?q?ributes=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/GridFieldAddNewInlineButton.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/GridFieldAddNewInlineButton.php b/src/GridFieldAddNewInlineButton.php index 6e7d6e6..3baf298 100755 --- a/src/GridFieldAddNewInlineButton.php +++ b/src/GridFieldAddNewInlineButton.php @@ -144,8 +144,10 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S sprintf('[%s][{%%=o.num%%}]', self::POST_KEY), $content ); + } - // Cast content as HTML + // Cast content + if (! $content instanceof DBField) { $content = DBField::create_field('HTMLFragment', $content); } @@ -157,7 +159,7 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S $columns->push(new ArrayData(array( 'Content' => $content, - 'Attributes' => $attrs, + 'Attributes' => DBField::create_field('HTMLFragment', $attrs), 'IsActions' => $column == 'Actions' ))); } From 3ee4ff4cb7d4f4cf88cd8c7d4ffe0eb592b9de57 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 1 Sep 2017 12:53:38 +1200 Subject: [PATCH 14/20] Update Travis configuration, add codecov.io and PHPUnit config --- .travis.yml | 45 ++++++++++++++++++++++++--------------------- codecov.yml | 1 + composer.json | 4 +++- phpunit.xml.dist | 14 ++++++++++++++ 4 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 codecov.yml create mode 100644 phpunit.xml.dist diff --git a/.travis.yml b/.travis.yml index cc29166..d858917 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,31 @@ -# See https://github.com/silverstripe/silverstripe-travis-support for setup details - -sudo: false - language: php -dist: precise - -php: - - 5.6 - - 7.0 - - 7.1 - env: - - DB=MYSQL CORE_RELEASE=4 - - DB=PGSQL CORE_RELEASE=4 + global: + - COMPOSER_ROOT_VERSION="4.0.x-dev" + +matrix: + include: + - php: 5.6 + env: DB=MYSQL PHPCS_TEST=1 PHPUNIT_TEST=1 + - php: 7.0 + env: DB=PGSQL PHPUNIT_TEST=1 + - php: 7.1 + env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1 before_script: - - composer self-update || true - - git clone git://github.com/silverstripe/silverstripe-travis-support.git ~/travis-support - - php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss - - cd ~/builds/ss - - composer install --prefer-dist --no-progress --no-suggest - - composer require silverstripe/versioned:1.x-dev --prefer-dist --no-progress --no-suggest - - composer dump-autoload --optimize + - phpenv rehash + - phpenv config-rm xdebug.ini + + - composer validate + - composer require silverstripe/recipe-core 1.0.x-dev --no-update + - if [[ $DB == PGSQL ]]; then composer require silverstripe/postgresql:2.0.x-dev --no-update; fi + - composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile script: - - vendor/bin/phpunit gridfieldextensions/tests + - if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit tests/; fi + - if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi + - if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs --standard=framework/phpcs.xml.dist src/ tests/ ; fi + +after_success: + - if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..69cb760 --- /dev/null +++ b/codecov.yml @@ -0,0 +1 @@ +comment: false diff --git a/composer.json b/composer.json index 0f5133c..ecb98fa 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,9 @@ "silverstripe/framework": "~4.0" }, "require-dev": { - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^5.7", + "squizlabs/php_codesniffer": "^3.0", + "silverstripe/versioned": "^1@dev" }, "extra": { "installer-name": "gridfieldextensions", diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..d08e57c --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + tests + + + + + src/ + + tests/ + + + + From 0ad1fc367ebd53bcf88a5f40b0eff25bced1e882 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 1 Sep 2017 13:02:18 +1200 Subject: [PATCH 15/20] MINOR Apply PSR-2 linting --- src/GridFieldAddNewMultiClass.php | 2 +- src/GridFieldOrderableRows.php | 12 +-- ...ieldAddNewMultiClassWithNamespacesTest.php | 73 ++++++++++--------- 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/GridFieldAddNewMultiClass.php b/src/GridFieldAddNewMultiClass.php index ad2f490..9afc7cd 100755 --- a/src/GridFieldAddNewMultiClass.php +++ b/src/GridFieldAddNewMultiClass.php @@ -158,7 +158,7 @@ class GridFieldAddNewMultiClass implements GridField_HTMLProvider, GridField_URL } $sanitised = array(); - foreach($result as $class=>$title) { + foreach ($result as $class => $title) { $sanitised[$this->sanitiseClassName($class)] = $title; } diff --git a/src/GridFieldOrderableRows.php b/src/GridFieldOrderableRows.php index 2d86084..91cb6d3 100755 --- a/src/GridFieldOrderableRows.php +++ b/src/GridFieldOrderableRows.php @@ -516,11 +516,11 @@ class GridFieldOrderableRows extends RequestHandler implements $sortTable = $this->getSortTable($list); $additionalSQL = ''; $baseTable = $sortTable; - if(class_exists($sortTable)) { + if (class_exists($sortTable)) { $baseTable = singleton($sortTable)->baseTable(); } $isBaseTable = ($baseTable == $sortTable); - if(!$list instanceof ManyManyList && $isBaseTable){ + if (!$list instanceof ManyManyList && $isBaseTable) { $additionalSQL = ', "LastEdited" = NOW()'; } @@ -535,7 +535,7 @@ class GridFieldOrderableRows extends RequestHandler implements $this->getSortTableClauseForIds($list, $id) )); - if(!$isBaseTable) { + if (!$isBaseTable) { DB::query(sprintf( 'UPDATE "%s" SET "LastEdited" = NOW() WHERE %s', $baseTable, @@ -570,11 +570,11 @@ class GridFieldOrderableRows extends RequestHandler implements $additionalSQL = ''; $baseTable = $table; - if(class_exists($table)) { + if (class_exists($table)) { $baseTable = singleton($table)->baseTable(); } $isBaseTable = ($baseTable == $table); - if(!$list instanceof ManyManyList && $isBaseTable){ + if (!$list instanceof ManyManyList && $isBaseTable) { $additionalSQL = ', "LastEdited" = NOW()'; } @@ -591,7 +591,7 @@ class GridFieldOrderableRows extends RequestHandler implements $this->getSortTableClauseForIds($list, $id) )); - if(!$isBaseTable) { + if (!$isBaseTable) { DB::query(sprintf( 'UPDATE "%s" SET "LastEdited" = NOW() WHERE %s', $baseTable, diff --git a/tests/GridFieldAddNewMultiClassWithNamespacesTest.php b/tests/GridFieldAddNewMultiClassWithNamespacesTest.php index bb00d5d..e7187e2 100644 --- a/tests/GridFieldAddNewMultiClassWithNamespacesTest.php +++ b/tests/GridFieldAddNewMultiClassWithNamespacesTest.php @@ -13,54 +13,59 @@ use SilverStripe\Forms\GridField\GridFieldDetailForm; use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass; use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClassHandler; -class GridFieldAddNewMultiClassWithNamespacesTest extends SapphireTest { +class GridFieldAddNewMultiClassWithNamespacesTest extends SapphireTest +{ - public function testGetClassesWithNamespaces() { - $grid = new GridField('TestGridField'); - $grid->setModelClass('Symbiote\\Test\\NamespacedClass'); + public function testGetClassesWithNamespaces() + { + $grid = new GridField('TestGridField'); + $grid->setModelClass('Symbiote\\Test\\NamespacedClass'); - $component = new GridFieldAddNewMultiClass(); + $component = new GridFieldAddNewMultiClass(); - $this->assertEquals( - array( - 'Symbiote-Test-NamespacedClass' => 'NamespacedClass' - ), - $component->getClasses($grid), - 'Namespaced classes are sanitised' - ); - } + $this->assertEquals( + array( + 'Symbiote-Test-NamespacedClass' => 'NamespacedClass' + ), + $component->getClasses($grid), + 'Namespaced classes are sanitised' + ); + } - public function testHandleAddWithNamespaces() { - $grid = new GridField('TestGridField'); - $grid->getConfig()->addComponent(new GridFieldDetailForm()); - $grid->setModelClass('Symbiote\\Test\\NamespacedClass'); - $grid->setForm(Form::create(Controller::create(), 'test', FieldList::create(), FieldList::create())); + public function testHandleAddWithNamespaces() + { + $grid = new GridField('TestGridField'); + $grid->getConfig()->addComponent(new GridFieldDetailForm()); + $grid->setModelClass('Symbiote\\Test\\NamespacedClass'); + $grid->setForm(Form::create(Controller::create(), 'test', FieldList::create(), FieldList::create())); - $request = new HTTPRequest('POST', 'test'); - $request->setRouteParams(array('ClassName' => 'Symbiote-Test-NamespacedClass')); + $request = new HTTPRequest('POST', 'test'); + $request->setRouteParams(array('ClassName' => 'Symbiote-Test-NamespacedClass')); - $component = new GridFieldAddNewMultiClass(); - $response = $component->handleAdd($grid, $request); - - $record = new \ReflectionProperty(GridFieldAddNewMultiClassHandler::class, 'record'); - $record->setAccessible(true); - $this->assertInstanceOf('Symbiote\\Test\\NamespacedClass', $record->getValue($response)); - } + $component = new GridFieldAddNewMultiClass(); + $response = $component->handleAdd($grid, $request); + $record = new \ReflectionProperty(GridFieldAddNewMultiClassHandler::class, 'record'); + $record->setAccessible(true); + $this->assertInstanceOf('Symbiote\\Test\\NamespacedClass', $record->getValue($response)); + } } /**#@+ * @ignore */ -class NamespacedClass implements TestOnly { - public function i18n_singular_name() { - return 'NamespacedClass'; - } +class NamespacedClass implements TestOnly +{ + public function i18n_singular_name() + { + return 'NamespacedClass'; + } - public function canCreate() { - return true; - } + public function canCreate() + { + return true; + } } /**#@-*/ From cb94bbe5e0721f6ad4ae89c266cea70c138b5255 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 1 Sep 2017 13:33:54 +1200 Subject: [PATCH 16/20] FIX Update remaining unit tests, separate stubs, add PSR-4 autoloader --- composer.json | 3 +- tests/GridFieldAddNewMultiClassTest.php | 48 +++-------- ...ieldAddNewMultiClassWithNamespacesTest.php | 32 ++------ tests/GridFieldConfigurablePaginatorTest.php | 2 + tests/GridFieldOrderableRowsTest.php | 79 ++++--------------- tests/GridFieldOrderableRowsTest.yml | 23 ++++-- tests/Stub/NamespacedClass.php | 18 +++++ tests/Stub/StubA.php | 19 +++++ tests/Stub/StubB.php | 9 +++ tests/Stub/StubC.php | 9 +++ tests/Stub/StubOrdered.php | 23 ++++++ tests/Stub/StubParent.php | 24 ++++++ tests/Stub/StubSubclass.php | 11 +++ 13 files changed, 169 insertions(+), 131 deletions(-) create mode 100644 tests/Stub/NamespacedClass.php create mode 100644 tests/Stub/StubA.php create mode 100644 tests/Stub/StubB.php create mode 100644 tests/Stub/StubC.php create mode 100644 tests/Stub/StubOrdered.php create mode 100644 tests/Stub/StubParent.php create mode 100644 tests/Stub/StubSubclass.php diff --git a/composer.json b/composer.json index ecb98fa..f8bca21 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,8 @@ }, "autoload": { "psr-4": { - "Symbiote\\GridFieldExtensions\\": "src/" + "Symbiote\\GridFieldExtensions\\": "src/", + "Symbiote\\GridFieldExtensions\\Tests\\": "tests/" } }, "prefer-stable": true, diff --git a/tests/GridFieldAddNewMultiClassTest.php b/tests/GridFieldAddNewMultiClassTest.php index e997053..1741f08 100644 --- a/tests/GridFieldAddNewMultiClassTest.php +++ b/tests/GridFieldAddNewMultiClassTest.php @@ -1,9 +1,14 @@ setModelClass('GridFieldAddNewMultiClassTest_A'); + $grid->setModelClass(StubA::class); $component = new GridFieldAddNewMultiClass(); $this->assertEquals( array( - 'GridFieldAddNewMultiClassTest_A' => 'A', - 'GridFieldAddNewMultiClassTest_B' => 'B', - 'GridFieldAddNewMultiClassTest_C' => 'C' + 'Symbiote-GridFieldExtensions-Tests-Stub-StubA' => 'A', + 'Symbiote-GridFieldExtensions-Tests-Stub-StubB' => 'B', + 'Symbiote-GridFieldExtensions-Tests-Stub-StubC' => 'C' ), $component->getClasses($grid), 'Subclasses are populated by default and sorted' ); $component->setClasses(array( - 'GridFieldAddNewMultiClassTest_B' => 'Custom Title', - 'GridFieldAddNewMultiClassTest_A' + StubB::class => 'Custom Title', + StubA::class )); $this->assertEquals( array( - 'GridFieldAddNewMultiClassTest_B' => 'Custom Title', - 'GridFieldAddNewMultiClassTest_A' => 'A' + 'Symbiote-GridFieldExtensions-Tests-Stub-StubB' => 'Custom Title', + 'Symbiote-GridFieldExtensions-Tests-Stub-StubA' => '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); - } - - public function canCreate() - { - return true; - } -} - -class GridFieldAddNewMultiClassTest_B extends GridFieldAddNewMultiClassTest_A implements TestOnly -{ -} -class GridFieldAddNewMultiClassTest_C extends GridFieldAddNewMultiClassTest_A implements TestOnly -{ -} - -/**#@-*/ diff --git a/tests/GridFieldAddNewMultiClassWithNamespacesTest.php b/tests/GridFieldAddNewMultiClassWithNamespacesTest.php index e7187e2..f683379 100644 --- a/tests/GridFieldAddNewMultiClassWithNamespacesTest.php +++ b/tests/GridFieldAddNewMultiClassWithNamespacesTest.php @@ -1,6 +1,6 @@ setModelClass('Symbiote\\Test\\NamespacedClass'); + $grid->setModelClass(NamespacedClass::class); $component = new GridFieldAddNewMultiClass(); $this->assertEquals( array( - 'Symbiote-Test-NamespacedClass' => 'NamespacedClass' + 'Symbiote-GridFieldExtensions-Tests-Stub-NamespacedClass' => 'NamespacedClass' ), $component->getClasses($grid), 'Namespaced classes are sanitised' @@ -36,36 +37,17 @@ class GridFieldAddNewMultiClassWithNamespacesTest extends SapphireTest { $grid = new GridField('TestGridField'); $grid->getConfig()->addComponent(new GridFieldDetailForm()); - $grid->setModelClass('Symbiote\\Test\\NamespacedClass'); + $grid->setModelClass(NamespacedClass::class); $grid->setForm(Form::create(Controller::create(), 'test', FieldList::create(), FieldList::create())); $request = new HTTPRequest('POST', 'test'); - $request->setRouteParams(array('ClassName' => 'Symbiote-Test-NamespacedClass')); + $request->setRouteParams(array('ClassName' => 'Symbiote-GridFieldExtensions-Tests-Stub-NamespacedClass')); $component = new GridFieldAddNewMultiClass(); $response = $component->handleAdd($grid, $request); $record = new \ReflectionProperty(GridFieldAddNewMultiClassHandler::class, 'record'); $record->setAccessible(true); - $this->assertInstanceOf('Symbiote\\Test\\NamespacedClass', $record->getValue($response)); + $this->assertInstanceOf(NamespacedClass::class, $record->getValue($response)); } } - -/**#@+ - * @ignore - */ - -class NamespacedClass implements TestOnly -{ - public function i18n_singular_name() - { - return 'NamespacedClass'; - } - - public function canCreate() - { - return true; - } -} - -/**#@-*/ diff --git a/tests/GridFieldConfigurablePaginatorTest.php b/tests/GridFieldConfigurablePaginatorTest.php index 7681d78..a8b4418 100644 --- a/tests/GridFieldConfigurablePaginatorTest.php +++ b/tests/GridFieldConfigurablePaginatorTest.php @@ -1,5 +1,7 @@ markTestSkipped('Upgrade to 4.0: Needs to be re-implemented.'); - } - public function testReorderItems() { $orderable = new GridFieldOrderableRows('ManyManySort'); $reflection = new ReflectionMethod($orderable, 'executeReorder'); $reflection->setAccessible(true); - $parent = $this->objFromFixture('GridFieldOrderableRowsTest_Parent', 'parent'); + $parent = $this->objFromFixture(StubParent::class, 'parent'); $config = new GridFieldConfig_RelationEditor(); $config->addComponent($orderable); @@ -71,70 +69,27 @@ class GridFieldOrderableRowsTest extends SapphireTest { $orderable = new GridFieldOrderableRows(); - $parent = new GridFieldOrderableRowsTest_Parent(); + $parent = new StubParent(); $parent->write(); $this->assertEquals( - 'GridFieldOrderableRowsTest_Ordered', + 'StubOrdered', $orderable->getSortTable($parent->MyHasMany()) ); $this->assertEquals( - 'GridFieldOrderableRowsTest_Ordered', + 'StubOrdered', $orderable->getSortTable($parent->MyHasManySubclass()) ); $this->assertEquals( - 'GridFieldOrderableRowsTest_Ordered', + 'StubOrdered', $orderable->getSortTable($parent->MyManyMany()) ); $this->assertEquals( - 'GridFieldOrderableRowsTest_Parent_MyManyMany', + 'StubParent_MyManyMany', $orderable->setSortField('ManyManySort')->getSortTable($parent->MyManyMany()) ); } } - -/**#@+ - * @ignore - */ - -class GridFieldOrderableRowsTest_Parent extends DataObject implements TestOnly -{ - - 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') - ); -} - -class GridFieldOrderableRowsTest_Ordered extends DataObject implements TestOnly -{ - - private static $db = array( - 'Sort' => 'Int' - ); - - 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 -{ -} - -/**#@-*/ diff --git a/tests/GridFieldOrderableRowsTest.yml b/tests/GridFieldOrderableRowsTest.yml index 30b7e79..c40f34c 100644 --- a/tests/GridFieldOrderableRowsTest.yml +++ b/tests/GridFieldOrderableRowsTest.yml @@ -1,22 +1,29 @@ -GridFieldOrderableRowsTest_Ordered: +Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered: item1: + Sort: 1 item2: + Sort: 2 item3: + Sort: 3 item4: + Sort: 4 item5: + Sort: 5 item6: -GridFieldOrderableRowsTest_Parent: + Sort: 6 + +Symbiote\GridFieldExtensions\Tests\Stub\StubParent: parent: MyManyMany: - - 0: =>GridFieldOrderableRowsTest_Ordered.item1 + - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item1: ManyManySort: 1 - - 1: =>GridFieldOrderableRowsTest_Ordered.item2 + - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item2: ManyManySort: 1 - - 2: =>GridFieldOrderableRowsTest_Ordered.item3 + - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item3: ManyManySort: 2 - - 3: =>GridFieldOrderableRowsTest_Ordered.item4 + - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item4: ManyManySort: 2 - - 4: =>GridFieldOrderableRowsTest_Ordered.item5 + - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item5: ManyManySort: 108 - - 5: =>GridFieldOrderableRowsTest_Ordered.item6 + - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item6: ManyManySort: 108 diff --git a/tests/Stub/NamespacedClass.php b/tests/Stub/NamespacedClass.php new file mode 100644 index 0000000..4c79128 --- /dev/null +++ b/tests/Stub/NamespacedClass.php @@ -0,0 +1,18 @@ + 'Int' + ); + + private static $has_one = array( + 'Parent' => StubParent::class + ); + + private static $belongs_many_many =array( + 'MyManyMany' => StubParent::class, + ); + + private static $table_name = 'StubOrdered'; +} diff --git a/tests/Stub/StubParent.php b/tests/Stub/StubParent.php new file mode 100644 index 0000000..7d60a11 --- /dev/null +++ b/tests/Stub/StubParent.php @@ -0,0 +1,24 @@ + StubOrdered::class, + 'MyHasManySubclass' => StubSubclass::class + ); + + private static $many_many = array( + 'MyManyMany' => StubOrdered::class + ); + + private static $many_many_extraFields = array( + 'MyManyMany' => array('ManyManySort' => 'Int') + ); + + private static $table_name = 'StubParent'; +} diff --git a/tests/Stub/StubSubclass.php b/tests/Stub/StubSubclass.php new file mode 100644 index 0000000..90434f0 --- /dev/null +++ b/tests/Stub/StubSubclass.php @@ -0,0 +1,11 @@ + Date: Wed, 23 Aug 2017 16:01:20 +1200 Subject: [PATCH 17/20] FIX Update selector for editable columns Javascript handler to match GridField.js in core --- javascript/GridFieldExtensions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/GridFieldExtensions.js b/javascript/GridFieldExtensions.js index f7f416b..d961716 100644 --- a/javascript/GridFieldExtensions.js +++ b/javascript/GridFieldExtensions.js @@ -250,7 +250,7 @@ * GridFieldEditableColumns */ - $('.ss-gridfield.ss-gridfield-editable .ss-gridfield-item td').entwine({ + $('.grid-field .ss-gridfield-item').entwine({ onclick: function(e) { // Prevent the default row click action when clicking a cell that contains a field if (this.find('.editable-column-field').length) { From e98226fe3d884530bccbc50d7f888634ce55867b Mon Sep 17 00:00:00 2001 From: Dylan Wagstaff Date: Thu, 5 Oct 2017 10:07:55 +1300 Subject: [PATCH 18/20] FIX convert CI bootstrap references to new their new locations in vendor --- .travis.yml | 2 +- phpunit.xml.dist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d858917..a8d8436 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ before_script: script: - if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit tests/; fi - if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi - - if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs --standard=framework/phpcs.xml.dist src/ tests/ ; fi + - if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs --standard=vendor/silverstripe/framework/phpcs.xml.dist src/ tests/ ; fi after_success: - if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d08e57c..0ed0311 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,4 +1,4 @@ - + tests From efdf9dcc13c4ff8cd21d48f529150f554ee39a5a Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Wed, 11 Oct 2017 09:31:30 +1300 Subject: [PATCH 19/20] API Install GridFieldExtensions to vendor folder, remove get_module_dir --- composer.json | 7 ++++++- src/GridFieldExtensions.php | 11 ++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index f8bca21..ac758d0 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "symbiote/silverstripe-gridfieldextensions", "description": "A collection of useful grid field components", - "type": "silverstripe-module", + "type": "silverstripe-vendormodule", "homepage": "http://github.com/symbiote/silverstripe-gridfieldextensions", "keywords": ["silverstripe", "gridfield"], "license": "BSD-3-Clause", @@ -19,6 +19,7 @@ "issues": "http://github.com/symbiote/silverstripe-gridfieldextensions/issues" }, "require": { + "silverstripe/vendor-plugin": "^1.0", "silverstripe/framework": "~4.0" }, "require-dev": { @@ -34,6 +35,10 @@ "screenshots": [ "docs/en/_images/editable-rows.png", "docs/en/_images/add-existing-search.png" + ], + "expose": [ + "css", + "javascript" ] }, "replace": { diff --git a/src/GridFieldExtensions.php b/src/GridFieldExtensions.php index 4c1b529..2c14f59 100644 --- a/src/GridFieldExtensions.php +++ b/src/GridFieldExtensions.php @@ -9,16 +9,9 @@ use SilverStripe\View\Requirements; */ 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__)); + Requirements::css('symbiote/silverstripe-gridfieldextensions:css/GridFieldExtensions.css'); + Requirements::javascript('symbiote/silverstripe-gridfieldextensions:javascript/GridFieldExtensions.js'); } } From 931e014a0e536bc75828142f749569e95cc70dd5 Mon Sep 17 00:00:00 2001 From: Martin D Date: Wed, 25 Oct 2017 13:22:35 -0400 Subject: [PATCH 20/20] Fixed link to SS3 compatible branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab0fe47..20a4e06 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ This module provides a number of useful grid field components: This branch will aim for compatibility with SilverStripe 4.x. -For SilverStripe 3.x, please see the [compatible branch](https://github.com/symbiote/silverstripe-gridfieldextensions/tree/1). +For SilverStripe 3.x, please see the [compatible branch](https://github.com/symbiote/silverstripe-gridfieldextensions/tree/2). See [docs/en/index.md](docs/en/index.md) for documentation and examples.