From 735cc0127005a0485826a29228a8770ffb8cd258 Mon Sep 17 00:00:00 2001 From: Mojmir Fendek Date: Mon, 18 Dec 2017 08:18:33 +1300 Subject: [PATCH 1/8] SS4 upgrade fixes. --- javascript/GridFieldExtensions.js | 78 ++++++++++++++++++++++++++++++- src/GridFieldOrderableRows.php | 36 ++++++++++++-- 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/javascript/GridFieldExtensions.js b/javascript/GridFieldExtensions.js index d961716..1381f10 100644 --- a/javascript/GridFieldExtensions.js +++ b/javascript/GridFieldExtensions.js @@ -264,6 +264,82 @@ */ $(".ss-gridfield-orderable tbody").entwine({ + // reload the gridfield without triggering the change event + // this is because the change has already been saved by reorder action + reload: function (ajaxOpts, successCallback) { + var self = this.getGridField(), form = this.closest('form'), + focusedElName = this.find(':input:focus').attr('name'), // Save focused element for restoring after refresh + data = form.find(':input').serializeArray(); + + if (!ajaxOpts) { + ajaxOpts = {}; + } + if (!ajaxOpts.data) { + ajaxOpts.data = []; + } + ajaxOpts.data = ajaxOpts.data.concat(data); + + // Include any GET parameters from the current URL, as the view state might depend on it. + // For example, a list prefiltered through external search criteria might be passed to GridField. + if (window.location.search) { + ajaxOpts.data = window.location.search.replace(/^\?/, '') + '&' + $.param(ajaxOpts.data); + } + + form.addClass('loading'); + + $.ajax($.extend({}, { + headers: {"X-Pjax": 'CurrentField'}, + type: "POST", + url: this.data('url'), + dataType: 'html', + success: function (data) { + // Replace the grid field with response, not the form. + // TODO Only replaces all its children, to avoid replacing the current scope + // of the executing method. Means that it doesn't retrigger the onmatch() on the main container. + self.empty().append($(data).children()); + + // Refocus previously focused element. Useful e.g. for finding+adding + // multiple relationships via keyboard. + if (focusedElName) self.find(':input[name="' + focusedElName + '"]').focus(); + + // Update filter + if (self.find('.grid-field__filter-header').length) { + var content; + if (ajaxOpts.data[0].filter == "show") { + content = ''; + self.addClass('show-filter').find('.grid-field__filter-header').show(); + } else { + content = ''; + self.removeClass('show-filter').find('.grid-field__filter-header').hide(); + } + + self.find('.sortable-header th:last').html(content); + } + + form.removeClass('loading'); + if (successCallback) { + successCallback.apply(this, arguments); + } + self.trigger('reload', self); + + // update publish button if necessary + const publish = $('#Form_EditForm_action_publish'); + + // button needs to be updated only if it's in published state + if (publish.length > 0 && publish.hasClass('btn-outline-primary')) { + publish.removeClass('btn-outline-primary'); + publish.removeClass('font-icon-tick'); + publish.addClass('btn-primary'); + publish.addClass('font-icon-rocket'); + publish.find('.btn__title').html('Save & publish'); + } + }, + error: function (e) { + alert(i18n._t('Admin.ERRORINTRANSACTION')); + form.removeClass('loading'); + } + }, ajaxOpts)); + }, rebuildSort: function() { var grid = this.getGridField(); @@ -320,7 +396,7 @@ var grid = self.getGridField(); if (grid.data("immediate-update") && postback) { - grid.reload({ + self.reload({ url: grid.data("url-reorder") }); } diff --git a/src/GridFieldOrderableRows.php b/src/GridFieldOrderableRows.php index 91cb6d3..8b9a314 100755 --- a/src/GridFieldOrderableRows.php +++ b/src/GridFieldOrderableRows.php @@ -20,6 +20,7 @@ use SilverStripe\ORM\DataObjectInterface; use SilverStripe\ORM\ManyManyList; use SilverStripe\ORM\SS_List; use SilverStripe\ORM\SS_Map; +use SilverStripe\Versioned\Versioned; use SilverStripe\View\ViewableData; use Exception; @@ -171,6 +172,35 @@ class GridFieldOrderableRows extends RequestHandler implements return $this; } + /** + * Validates sortable list + * + * @param SS_List $list + * @throws Exception + */ + public function validateSortField(SS_List $list) + { + $field = $this->getSortField(); + + if ($list instanceof ManyManyList) { + $extra = $list->getExtraFields(); + + if ($extra && array_key_exists($field, $extra)) { + return; + } + } + + $classes = ClassInfo::dataClassesFor($list->dataClass()); + + foreach ($classes as $class) { + if (singleton($class)->hasDataBaseField($field)) { + return; + } + } + + throw new \Exception("Couldn't find the sort field '" . $field . "'"); + } + /** * Gets the table which contains the sort field. * @@ -504,11 +534,9 @@ class GridFieldOrderableRows extends RequestHandler implements } // If not a ManyManyList and using versioning, detect it. - $isVersioned = false; + $this->validateSortField($list); $class = $list->dataClass(); - if ($class == $this->getSortTable($list)) { - $isVersioned = $class::has_extension('SilverStripe\\ORM\\Versioning\\Versioned'); - } + $isVersioned = $class::has_extension(Versioned::class); // Loop through each item, and update the sort values which do not // match to order the objects. From 8ba5c3435e071d4cae157aad08d74dc43472f321 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 26 Jan 2018 11:01:24 +1300 Subject: [PATCH 2/8] FIX Switch Add Existing from jQuery UI to use Bootstrap styles --- src/GridFieldAddExistingSearchButton.php | 13 +++++++------ .../GridFieldAddExistingSearchButton.ss | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/GridFieldAddExistingSearchButton.php b/src/GridFieldAddExistingSearchButton.php index 7e991cb..630a2c3 100755 --- a/src/GridFieldAddExistingSearchButton.php +++ b/src/GridFieldAddExistingSearchButton.php @@ -91,14 +91,15 @@ class GridFieldAddExistingSearchButton implements GridField_HTMLProvider, GridFi { GridFieldExtensions::include_requirements(); - $data = new ArrayData(array( + $data = ArrayData::create([ 'Title' => $this->getTitle(), - 'Link' => $grid->Link('add-existing-search') - )); + 'Classes' => 'action btn btn-primary font-icon-search add-existing-search', + 'Link' => $grid->Link('add-existing-search'), + ]); - return array( - $this->fragment => $data->renderWith('Symbiote\\GridFieldExtensions\\GridFieldAddExistingSearchButton'), - ); + return [ + $this->fragment => $data->renderWith(__CLASS__), + ]; } public function getURLHandlers($grid) diff --git a/templates/Symbiote/GridFieldExtensions/GridFieldAddExistingSearchButton.ss b/templates/Symbiote/GridFieldExtensions/GridFieldAddExistingSearchButton.ss index 3efa1ec..ddebae0 100644 --- a/templates/Symbiote/GridFieldExtensions/GridFieldAddExistingSearchButton.ss +++ b/templates/Symbiote/GridFieldExtensions/GridFieldAddExistingSearchButton.ss @@ -1,3 +1,3 @@ - - $Title - + + $Title + From 0a1656647112f7d2a6f98dec13c023d4453d7f21 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 26 Jan 2018 11:53:40 +1300 Subject: [PATCH 3/8] FIX Convert broken add-existing-search-form styles to use Bootstrap lists and pagination --- css/GridFieldExtensions.css | 34 +------ javascript/GridFieldExtensions.js | 9 ++ src/GridFieldAddExistingSearchHandler.php | 13 +-- .../GridFieldAddExistingSearchHandler.ss | 92 +++++++++++-------- 4 files changed, 76 insertions(+), 72 deletions(-) diff --git a/css/GridFieldExtensions.css b/css/GridFieldExtensions.css index f49c814..45d3ed6 100644 --- a/css/GridFieldExtensions.css +++ b/css/GridFieldExtensions.css @@ -14,7 +14,7 @@ } .add-existing-search-dialog .add-existing-search-form .field label { - padding-bottom: 4px; + padding: 4px 0; } .add-existing-search-dialog .add-existing-search-form .Actions { @@ -22,38 +22,12 @@ padding: 0; } -.add-existing-search-dialog .add-existing-search-items li a { - background: #FFF; - border: solid #CCC; - border-right-width: 1px; - border-bottom-width: 1px; - border-left-width: 1px; - display: block; - padding: 6px; +.add-existing-search-dialog .list-group-item { + min-height: 32px; } -.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; +.add-existing-search-dialog .btn-toolbar { margin-top: 12px; - padding: 6px; } /** diff --git a/javascript/GridFieldExtensions.js b/javascript/GridFieldExtensions.js index d961716..5158790 100644 --- a/javascript/GridFieldExtensions.js +++ b/javascript/GridFieldExtensions.js @@ -44,6 +44,15 @@ } }); + // Allow the list item to be clickable as well as the anchor + $('.add-existing-search-dialog .add-existing-search-items .list-group-item-action').entwine({ + onclick: function() { + if (this.children('a').length > 0) { + this.children('a').first().trigger('click'); + } + } + }); + $(".add-existing-search-dialog .add-existing-search-items a").entwine({ onclick: function() { var link = this.closest(".add-existing-search-items").data("add-link"); diff --git a/src/GridFieldAddExistingSearchHandler.php b/src/GridFieldAddExistingSearchHandler.php index 8f025fd..58de381 100644 --- a/src/GridFieldAddExistingSearchHandler.php +++ b/src/GridFieldAddExistingSearchHandler.php @@ -7,8 +7,10 @@ use SilverStripe\Control\RequestHandler; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\Form; use SilverStripe\Forms\FormAction; +use SilverStripe\Forms\GridField\GridField; use SilverStripe\ORM\DataList; use SilverStripe\ORM\PaginatedList; +use SilverStripe\ORM\Search\SearchContext; /** * Used by {@link GridFieldAddExistingSearchButton} to provide the searching @@ -49,7 +51,7 @@ class GridFieldAddExistingSearchHandler extends RequestHandler public function index() { - return $this->renderWith('Symbiote\\GridFieldExtensions\\GridFieldAddExistingSearchHandler'); + return $this->renderWith(__CLASS__); } public function add($request) @@ -73,19 +75,18 @@ class GridFieldAddExistingSearchHandler extends RequestHandler */ public function SearchForm() { - $form = new Form( + $form = Form::create( $this, 'SearchForm', $this->context->getFields(), - new FieldList( + FieldList::create( FormAction::create('doSearch', _t('GridFieldExtensions.SEARCH', 'Search')) ->setUseButtonTag(true) - ->addExtraClass('ss-ui-button') - ->setAttribute('data-icon', 'magnifier') + ->addExtraClass('btn btn-primary font-icon-search') ) ); - $form->addExtraClass('stacked add-existing-search-form'); + $form->addExtraClass('stacked add-existing-search-form form--no-dividers'); $form->setFormMethod('GET'); return $form; diff --git a/templates/Symbiote/GridFieldExtensions/GridFieldAddExistingSearchHandler.ss b/templates/Symbiote/GridFieldExtensions/GridFieldAddExistingSearchHandler.ss index eaa531a..969fc94 100644 --- a/templates/Symbiote/GridFieldExtensions/GridFieldAddExistingSearchHandler.ss +++ b/templates/Symbiote/GridFieldExtensions/GridFieldAddExistingSearchHandler.ss @@ -1,36 +1,56 @@ -$SearchForm - -

<% _t("RESULTS", "Results") %>

-
- <% if $Items %> -
    - <% loop $Items %> -
  • $Title
  • - <% end_loop %> -
- <% else %> -

<% _t("NOITEMS", "There are no items.") %>

- <% end_if %> - - <% if $Items.MoreThanOnePage %> -
    - <% if $Items.NotFirstPage %> -
  • «
  • - <% end_if %> - - <% loop $Items.PaginationSummary(4) %> - <% if $CurrentBool %> -
  • $PageNum
  • - <% else_if $Link %> -
  • $PageNum
  • - <% else %> -
  • - <% end_if %> - <% end_loop %> - - <% if $Items.NotLastPage %> -
  • »
  • - <%end_if %> -
- <% end_if %> -
+$SearchForm + +

<%t GridFieldExtensions.RESULTS "Results" %>

+
+ <% if $Items %> +
    + <% loop $Items %> +
  • + $Title +
  • + <% end_loop %> +
+ <% else %> +

<%t GridFieldExtensions.NOITEMS "There are no items." %>

+ <% end_if %> + + <% if $Items.MoreThanOnePage %> + + <% end_if %> +
From eb9093a3ac9e9279217cfcc7c72a995b63fc1ec5 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 26 Jan 2018 11:53:55 +1300 Subject: [PATCH 4/8] Update translations --- lang/en.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lang/en.yml diff --git a/lang/en.yml b/lang/en.yml new file mode 100644 index 0000000..5792708 --- /dev/null +++ b/lang/en.yml @@ -0,0 +1,14 @@ +en: + GridFieldExtensions: + ADD: Add + ADDEXISTING: 'Add Existing' + BACK: Back + CURRENT: (current) + NOITEMS: 'There are no items.' + Next: Next + PREVIOUS: Previous + RESULTS: Results + SEARCH: Search + SELECTTYPETOCREATE: '(Select type to create)' + Symbiote\GridFieldExtensions\GridFieldConfigurablePaginator: + SHOW: Show From b29db7a4e7c68b7f8088e2d36b389c6dea5faf3b Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 26 Jan 2018 13:37:44 +1300 Subject: [PATCH 5/8] Add PHP 7.2 to Travis build matrix, add individual phpcs ruleset --- .travis.yml | 4 +++- phpcs.xml.dist | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 phpcs.xml.dist diff --git a/.travis.yml b/.travis.yml index a8d8436..4e18a3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,8 @@ matrix: env: DB=PGSQL PHPUNIT_TEST=1 - php: 7.1 env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1 + - php: 7.2 + env: DB=MYSQL PHPUNIT_TEST=1 before_script: - phpenv rehash @@ -25,7 +27,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=vendor/silverstripe/framework/phpcs.xml.dist src/ tests/ ; fi + - if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs src/ tests/ *.php; fi after_success: - if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..1b984f8 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,9 @@ + + + CodeSniffer ruleset for SilverStripe coding conventions. + + + + + + From 24db0da047081af098e6e6e556577b29ba789fc1 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 26 Jan 2018 13:40:06 +1300 Subject: [PATCH 6/8] Fix PSR-2 rule violations and incorrect docblock return type --- src/GridFieldConfigurablePaginator.php | 15 +++++++++------ src/GridFieldOrderableRows.php | 8 ++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/GridFieldConfigurablePaginator.php b/src/GridFieldConfigurablePaginator.php index f3af8d5..3655ae9 100644 --- a/src/GridFieldConfigurablePaginator.php +++ b/src/GridFieldConfigurablePaginator.php @@ -5,8 +5,8 @@ namespace Symbiote\GridFieldExtensions; use Exception; use SilverStripe\Core\Config\Configurable; use SilverStripe\Forms\GridField\GridField; -use SilverStripe\Forms\GridField\GridFieldPaginator; use SilverStripe\Forms\GridField\GridField_FormAction; +use SilverStripe\Forms\GridField\GridFieldPaginator; use SilverStripe\Forms\GridField\GridState_Data; use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\Limitable; @@ -269,7 +269,7 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator * {@inheritDoc} * * @param GridField $gridField - * @return ArrayList|null + * @return ArrayData|null */ public function getTemplateParameters(GridField $gridField) { @@ -281,7 +281,7 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator // Figure out which page and record range we're on if (!$arguments['total-rows']) { - return; + return null; } // Define a list of the FormActions that should be generated for pager controls (see getPagerActions()) @@ -289,13 +289,15 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator 'first' => array( 'title' => 'First', 'args' => array('first-shown' => 1), - 'extra-class' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-double-left ss-gridfield-firstpage', + 'extra-class' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-double-left ' + . 'ss-gridfield-firstpage', 'disable-previous' => ($this->getCurrentPage() == 1) ), 'prev' => array( 'title' => 'Previous', 'args' => array('first-shown' => $arguments['first-shown'] - $this->getItemsPerPage()), - 'extra-class' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-left ss-gridfield-previouspage', + 'extra-class' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-left ' + . 'ss-gridfield-previouspage', 'disable-previous' => ($this->getCurrentPage() == 1) ), 'next' => array( @@ -307,7 +309,8 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator 'last' => array( 'title' => 'Last', 'args' => array('first-shown' => ($this->getTotalPages() - 1) * $this->getItemsPerPage() + 1), - 'extra-class' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-double-right ss-gridfield-lastpage', + 'extra-class' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-double-right ' + . 'ss-gridfield-lastpage', 'disable-next' => ($this->getCurrentPage() == $arguments['total-pages']) ), 'pagesize' => array( diff --git a/src/GridFieldOrderableRows.php b/src/GridFieldOrderableRows.php index 91cb6d3..8e52a33 100755 --- a/src/GridFieldOrderableRows.php +++ b/src/GridFieldOrderableRows.php @@ -11,17 +11,17 @@ use SilverStripe\Forms\GridField\GridField_DataManipulator; use SilverStripe\Forms\GridField\GridField_HTMLProvider; use SilverStripe\Forms\GridField\GridField_SaveHandler; use SilverStripe\Forms\GridField\GridField_URLHandler; +use SilverStripe\Forms\GridField\GridFieldPaginator; use SilverStripe\Forms\HiddenField; use SilverStripe\ORM\ArrayList; -use SilverStripe\ORM\DataObject; -use SilverStripe\ORM\DB; use SilverStripe\ORM\DataList; +use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObjectInterface; +use SilverStripe\ORM\DB; use SilverStripe\ORM\ManyManyList; use SilverStripe\ORM\SS_List; use SilverStripe\ORM\SS_Map; use SilverStripe\View\ViewableData; -use Exception; /** * Allows grid field rows to be re-ordered via drag and drop. Both normal data @@ -363,7 +363,7 @@ class GridFieldOrderableRows extends RequestHandler implements */ public function handleMoveToPage(GridField $grid, $request) { - if (!$paginator = $grid->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldPaginator')) { + if (!$paginator = $grid->getConfig()->getComponentByType(GridFieldPaginator::class)) { $this->httpError(404, 'Paginator component not found'); } From cd1fdada5d2b750769539aa4b473e52d48820beb Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 26 Jan 2018 14:03:23 +1300 Subject: [PATCH 7/8] FIX Remove excessive width on add new multi class dropdown This removes a width definition added in da2e75f --- css/GridFieldExtensions.css | 1 - 1 file changed, 1 deletion(-) diff --git a/css/GridFieldExtensions.css b/css/GridFieldExtensions.css index f49c814..f8bd5a8 100644 --- a/css/GridFieldExtensions.css +++ b/css/GridFieldExtensions.css @@ -97,7 +97,6 @@ display: inline-block; margin: 0; min-width: 150px; - width: calc(100% - 20px); } .ss-gridfield-add-new-multi-class .form-group:after { From 6d8b41ee5a0b0f1b07215f5bf146c07a78ef61fe Mon Sep 17 00:00:00 2001 From: Priyashantha Date: Wed, 31 Jan 2018 02:10:59 +0530 Subject: [PATCH 8/8] Fixed GridFieldOrderableRows issue when data class is Versioned and relation is has_many (#243) * Fixed GridFieldOrderableRows issue when data class is Versioned and relation is has_many * just compare table names rather than updating existing getSortTable() func --- src/GridFieldOrderableRows.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GridFieldOrderableRows.php b/src/GridFieldOrderableRows.php index 0b2dadf..79f1b24 100755 --- a/src/GridFieldOrderableRows.php +++ b/src/GridFieldOrderableRows.php @@ -19,8 +19,8 @@ use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObjectInterface; use SilverStripe\ORM\DB; use SilverStripe\ORM\ManyManyList; +use SilverStripe\ORM\Map; use SilverStripe\ORM\SS_List; -use SilverStripe\ORM\SS_Map; use SilverStripe\Versioned\Versioned; use SilverStripe\View\ViewableData; @@ -529,7 +529,7 @@ class GridFieldOrderableRows extends RequestHandler implements /** @var SS_List $map */ $map = $list->map('ID', $sortField); //fix for versions of SS that return inconsistent types for `map` function - if ($map instanceof SS_Map) { + if ($map instanceof Map) { $map = $map->toArray(); }