Compare commits

...

210 Commits

Author SHA1 Message Date
UndefinedOffset f6ce5741ab
Added error when a many_many relationship could not be found 2024-05-13 11:21:18 -03:00
UndefinedOffset b1febae3d8
Updated description 2024-01-30 14:45:48 -04:00
UndefinedOffset 9e5908c4ef
Updated description 2024-01-30 14:44:55 -04:00
UndefinedOffset 09f7be61ae
Removed Silverstripe supported badge from the readme 2023-06-16 09:30:10 -03:00
UndefinedOffset 402dab8faa
Updated calls to set-output in the github actions workflow 2023-05-08 10:51:31 -03:00
UndefinedOffset bb65d01748
Removed PHPCS test's plugin enabling 2023-05-08 10:40:38 -03:00
UndefinedOffset e3d92e774a
Changed mysql engine for tests 2023-05-08 10:38:12 -03:00
UndefinedOffset 237716571f
Added missing allow plugins for composer 2023-05-08 10:36:11 -03:00
UndefinedOffset 3940a7475f
Updated changelog and license 2023-05-08 10:32:33 -03:00
UndefinedOffset 29957be873
Enabled Silverstripe 5 testing
Adjusted composer requirements to allow Silverstripe 5

Replaced some deprecated api calls
2023-05-08 10:27:50 -03:00
UndefinedOffset c3915cc2b8
Updated changelog 2022-07-06 14:53:04 -03:00
UndefinedOffset 255347b755
Added allow plugin configs for composer 2 in ci 2022-07-06 14:50:18 -03:00
UndefinedOffset eabbe33342
Adjusted ci workflow to only run against 4.11 2022-07-06 14:46:33 -03:00
Ed Chipman f5a5346349
Merge pull request #134 from GuySartorelli/enh/injectable
NEW Extend new AbstractGridFieldComponent class
2022-07-06 14:42:50 -03:00
UndefinedOffset 66dfcc25ad
Added new badges
Updated the license
2022-02-16 16:29:10 -04:00
UndefinedOffset 6f44c555cd
Renamed tests to have simpler cleaner names 2022-02-16 16:23:08 -04:00
UndefinedOffset 8ed1621f8b
Split unit test classes into separate files 2022-02-16 16:15:33 -04:00
UndefinedOffset 5f37c8a0b4
Added phpunit/phpunit and squizlabs/php_codesniffer as dev dependencies
Coding style update
2022-02-16 15:35:10 -04:00
UndefinedOffset e1b1bbf8b3
Updated PHPUnit config
Added phpcs config

Added GitHub Actions badge

Re-organized tests folder
2022-02-16 15:25:43 -04:00
UndefinedOffset c965eb0c2e
Attempt at getting GitHub actions working 2022-02-16 15:19:42 -04:00
Guy Sartorelli e374c93ae3 NEW Extend new AbstractGridFieldComponent class
This makes the `GridFieldSortableRows` component `Injectable`, and
allows any future enhancements in the new abstract class to
automatically apply without requiring additional changes in this module.

The class is introduced in silverstripe/framework 4.11.0 so the
dependency constraint needs to be updated.
2022-02-12 19:24:44 +13:00
UndefinedOffset fff3f08901
Updated localizations from transifex 2021-07-19 09:51:48 -03:00
Robbie Averill 7bdeebb6cd
Merge pull request #130 from samandeggs/patch-1
Update ModelAdminExample.md
2020-09-17 00:07:24 -07:00
Samuel Mulliss 6a14bfbcc1
Update ModelAdminExample.md
adding semicolon, readability.
2020-09-17 14:14:35 +12:00
Ed Chipman 51c43642ff
Merge pull request #129 from creative-commoners/pulls/master/travis
Update travis
2020-06-24 09:41:38 -03:00
Steve Boyd c3d202d4a0 Update travis, allow dev packages to be installed 2020-06-24 14:48:06 +12:00
Ed Chipman ee5f10e1e7
Merge pull request #128 from sunnysideup/patch-3
MINOR: better example
2020-06-01 11:43:04 -03:00
Nicolaas 902fe50c96
MINOR: better example (#127) 2020-06-01 11:42:19 -03:00
Nicolaas a0d1272238
Update HasManyExample.md 2020-05-30 13:17:03 +12:00
Nicolaas f1a9c92784
MINOR: better example 2020-05-30 13:16:11 +12:00
UndefinedOffset 74756cad52
Re-worked unit tests for Silverstripe 4.4+ compatibility 2020-05-29 10:44:04 -03:00
UndefinedOffset dd955ff1bb
Updated travis config 2020-05-29 08:55:24 -03:00
Robbie Averill 30b50a0597
Merge pull request #126 from sunnysideup/patch-1
MINOR: better keywords
2020-05-28 17:40:34 -07:00
Nicolaas 2e9d0e6b33
MINOR: better keywords 2020-05-29 12:26:04 +12:00
UndefinedOffset af492d0267 Updated changelog
Removed helpfulbot badge

Updated license year
2019-01-08 12:23:14 -04:00
UndefinedOffset 35775ad2ce More adjustments to how ManyManyThroughLists are handled
Fixed issue causing the alert to be empty when an error occurs on the server
2019-01-08 12:21:33 -04:00
UndefinedOffset aef0a8acc1 Updated changelog
Changed Travis postgres and sqlite requirements
2018-12-13 14:38:11 -04:00
UndefinedOffset 3ab5aadf45 Attempt to correct the core release used 2018-12-13 14:31:34 -04:00
UndefinedOffset a622fd4d6f Fixed syntax error in travis config 2018-12-13 14:26:24 -04:00
UndefinedOffset cd86fe2fd4 Fixed composer requirement in travis for versioned 2018-12-13 14:21:06 -04:00
UndefinedOffset fc83d6813e Adjust Travis to use 4.3 and 4.2 2018-12-13 14:15:20 -04:00
UndefinedOffset 8a2417a750 Fixed functionality when using ManyManyThroughList 2018-12-13 14:05:12 -04:00
UndefinedOffset 504387d711 Switched Travis to test against 4.1 2018-07-04 14:06:01 -03:00
UndefinedOffset bc633e151d Fixed issue where versioned objects in would not be flagged as modified preventing the sort order from publishing to the live state when the owner is published 2018-07-04 14:01:37 -03:00
UndefinedOffset ae12e74aa8 Added templates for github (fixes #117) 2018-06-07 09:57:03 -03:00
UndefinedOffset 97b526ea87 Updated changelog 2018-06-07 09:51:29 -03:00
ChrissiQ 118838fb79 Fixes "undefined offset" error when moving a many_many item to previous page (#121)
When viewing the last page of a list of data objects, trying to move one of them to the previous page triggers an "undefined offset" error.
2018-05-31 14:56:51 -03:00
UndefinedOffset 5e3e5cac3f Fixed issue #120 where 'Int' as a datatype would not work 2018-03-07 09:46:08 -04:00
UndefinedOffset dc11841e11 Fixed test errors 2018-03-01 10:58:02 -04:00
UndefinedOffset 6cfc1b7338 Fixed test failures related to legacy code in the test objects 2018-03-01 10:52:12 -04:00
UndefinedOffset 20da27f7dd Fixed potential issue with DBInt 2018-03-01 10:45:44 -04:00
UndefinedOffset e53e3622a4 Prep for 2.0.2 tag 2017-12-11 12:29:59 -04:00
UndefinedOffset ecbe9b904a Fixed issue related to checking if the base data class has the versioned extension
Updated translations
2017-12-11 12:27:42 -04:00
UndefinedOffset 12cd5776c1 Fixed issue where the table name could not be determined properly for namespaced classes (fixes #113)
Fixed issue sorting to page in 4.0
2017-12-11 11:49:30 -04:00
UndefinedOffset e6eefdffd3 Prep for 2.0.1 tag 2017-11-29 10:08:59 -04:00
Daniel Hensby d8c379879f Merge 1.0.x into master (#112)
* Fixed travis build failure related to phpunit/php-file-iterator

* DOCS Update docs examples to be 3.1+ relevant (#111)
2017-11-29 10:05:37 -04:00
UndefinedOffset 8254ce7fe7 Updated change log 2017-11-27 10:19:25 -04:00
UndefinedOffset f0110c11e2 Fixed issue #110 where many many lists would cause a crash 2017-11-27 10:17:17 -04:00
UndefinedOffset 0c66fa0ec6 Added no-update to travis builds for postgresql and sqlite3 2017-11-24 17:04:04 -04:00
UndefinedOffset fe104efad8 Added phpunit.xml.dist to the export ignore 2017-11-24 16:42:02 -04:00
UndefinedOffset 21289b42f5 Added phpunit to travis builds 2017-11-24 16:38:32 -04:00
UndefinedOffset 2bde7362cd Adjusted requirements 2017-11-24 16:35:26 -04:00
UndefinedOffset b70ec00945 Removed prefer-dist from composer in travis builds 2017-11-24 16:29:29 -04:00
UndefinedOffset 386fa1053d Fixed recipe name in .travis.yml 2017-11-24 16:24:59 -04:00
UndefinedOffset 710b94e25b Attempt at moving away from silverstripe/silverstripe-travis-support 2017-11-24 16:21:12 -04:00
UndefinedOffset 7e8cdd9c2b Prep for 2.0.0 tag 2017-11-24 14:52:31 -04:00
UndefinedOffset 9ea5caa91c Removed unused constant and config option 2017-11-01 09:35:04 -03:00
UndefinedOffset b38d7f6f04 Revert "Revert "Moved to a vendor module #103""
This reverts commit 1925d5151e.
2017-11-01 09:34:29 -03:00
UndefinedOffset c537c86567 Attempt to fix travis builds 2017-10-31 09:51:22 -03:00
UndefinedOffset bf1f27e075 Removed additional php 5.6 mysql test 2017-10-27 16:31:14 -03:00
UndefinedOffset fc150881f2 Configured travis to test 2.0 against sqlite and php 5.6 mysql 2017-10-27 16:23:06 -03:00
UndefinedOffset c0c0518ae9 Merge branch '2.0.x' 2017-10-27 16:16:24 -03:00
UndefinedOffset 0571a60875 Prep for 1.0.0 tag 2017-10-27 16:14:27 -03:00
UndefinedOffset 1925d5151e Revert "Moved to a vendor module #103"
This reverts commit 08a0781d56.
2017-10-27 16:09:42 -03:00
UndefinedOffset 5dc5bbc908 Fixed paths to yaml fixture files in unit tests 2017-10-20 16:37:30 -03:00
UndefinedOffset d3fd38e54c Merge branch 'master' into 2.0.x
# Conflicts:
#	.travis.yml
#	code/forms/GridFieldSortableRows.php
#	composer.json
2017-10-20 16:26:33 -03:00
UndefinedOffset 775067ff93 Adjusted travis to test against 3.6 and one test against 3.5 (fixes #97) 2017-10-20 16:19:08 -03:00
UndefinedOffset 08a0781d56 Moved to a vendor module #103 2017-10-20 16:07:01 -03:00
UndefinedOffset 21a6f3c8e8 Updated documentation and examples to correctly state namespaces used. 2017-09-11 11:37:22 -03:00
UndefinedOffset 2b03ab81f7 Updated change log 2017-08-14 09:38:34 -03:00
UndefinedOffset 684f0c8df6 Adjusted composer version constraint 2017-08-14 09:33:40 -03:00
UndefinedOffset e5d442cabe Fixed test failures related to changes in the session api 2017-07-04 10:58:25 -03:00
UndefinedOffset f0fbbd5650 Adjusted silverstripe/framework dependency
Removed silverstripe/admin dependency

Aliased 2.0 branch as 2.0.x-dev

Set travis to 4.0 instead of just 4
2017-07-04 10:34:17 -03:00
UndefinedOffset 72d458c819 Style improvements 2017-07-04 10:20:29 -03:00
Robbie Averill 36dcdcef6b Remove closing PHP tag (#107)
To prevent whitespace being sent after the closing tag
2017-06-07 07:06:41 -03:00
Reece 23da810b19 Fixes bug
JS formatting

Updated README.md

SS4 Compliance & Updated Tests

Namespaced templates

Update .travis.yml, .scrutinizer.yml

Update composer.json
2017-06-05 09:41:56 -03:00
Reece 1862700761 Rename forms to Forms 2017-05-26 20:34:57 +10:00
Reece d88af0c513 Renamed code to src 2017-05-26 20:32:10 +10:00
UndefinedOffset 2d8cd6540f Added missing new translations 2017-05-11 12:17:40 -03:00
UndefinedOffset 2950efe6a6 Updated translations 2017-05-11 12:12:42 -03:00
UndefinedOffset 1750b7b45a Updated change log 2017-04-21 09:32:43 -03:00
UndefinedOffset d5fb2aaa9d Fixed issue where queries could fail when using append to top and the base table does not contain the parent relationship (Fixes #101) 2017-04-20 10:45:11 -03:00
UndefinedOffset 8ba6c2f3f6 Updated change log 2017-04-18 09:35:21 -03:00
UndefinedOffset 09b295d10d Fixed bug causing the ancestry to not be looked at correctly when trying to find the sort column (Fixes #100) 2017-04-18 09:31:47 -03:00
UndefinedOffset 06517fc3af Updated change log 2017-04-13 10:22:49 -03:00
UndefinedOffset 5f9610046e Changed travis config to test against 3.5 for PGSQL, SQLITE and MYSQL, and additionally MYSQL for 3.1 2017-04-13 10:19:52 -03:00
UndefinedOffset 1fecff9cc7 Switched order of the is_subclass_of check (Fixes #99) 2017-04-13 10:17:22 -03:00
UndefinedOffset e8eb7994fc Swapped .cms prefix for styles with form to fix styles when used with webbuilders-group/silverstripe-frontendgridfield 2017-04-12 16:40:57 -03:00
UndefinedOffset 94e916b4d9 Fixed issue where the headers could be missing even when sorting is off
Fixed issue where the sort direction would not apply when sorting using the headers
2016-11-24 10:01:29 -04:00
UndefinedOffset 9acc93b361 Updated changelog 2016-11-22 09:36:49 -04:00
UndefinedOffset 5d2aa717f3 Fix crash when the sort state is empty or not set 2016-11-22 09:34:35 -04:00
UndefinedOffset 29e8996b4a Updated changelog 2016-11-18 14:16:06 -04:00
Matthias Schelling d3cfda5fe1 Use Sort of GridField Header (#96)
When not in 'Drag&Drop reordering' mode ('sortableToggle' is false) and a SortColumn is set by GridField's header, this last one should be used; otherwise, when the user clicks on a sortable header, the list is reloaded and the header shows a up/down sorting arrow, but this doesn't reflect the actual sorting.
2016-11-18 14:13:54 -04:00
UndefinedOffset bc38ff5693 Removed notes from contributing 2016-06-28 11:36:06 -03:00
UndefinedOffset 05637b8216 Updated changelog for 0.6.2 2016-06-09 14:24:20 -03:00
UndefinedOffset 6847bf2a42 Added changelog and helpfulrobot badge 2016-06-08 16:07:21 -03:00
Damian Mooyman 92dfcd0447 BUG Remove redundant leading underscore in strings (#95) 2016-05-18 20:13:03 -03:00
Ingo Schommer 372819a57a Test against 3.3 core and PHP 5.6 (#93)
* Removed builds for unsupported 3.0 release line

* Added 3.3 release testing

Note that we're trying to make this smarter going forward, just testing against `3@stable` (see https://github.com/silverstripe-labs/silverstripe-travis-support/issues/33)

* Testing on PHP 5.6, reduced variations

Testing every release line against every DB driver and every PHP version tends to increase the complexity of maintaining the builds (more chance of a false negative). It also strains the free resources provided by Travis CI
2016-05-16 21:22:10 -03:00
UndefinedOffset 0270e32c83 Fixed php doc typo 2016-05-03 12:18:40 -03:00
UndefinedOffset fc2e8e5538 Copied Contributing and reporting an issue sections out of the readme and into a CONTRIBUTING.md file 2016-04-04 12:32:51 -03:00
Ed Chipman 62a744d48f Merge pull request #91 from helpfulrobot/add-standard-scrutinizer-config
Added standard Scrutinizer config
2016-03-23 20:00:22 -03:00
helpfulrobot 69635d5070 Added standard Scrutinizer config 2016-03-24 11:56:30 +13:00
UndefinedOffset 07e24d52e6 Unit tests now pass on core releases with ss-2016-002 applied to them (fixes #88) 2016-03-03 09:47:28 -04:00
Ed Chipman 8f9201c696 Added unit tests for page sorting (fixes #85) 2016-02-13 15:09:25 -04:00
Ed Chipman f921aff29d Fixed single unit test failure on postgres 2016-02-13 13:51:01 -04:00
Ed Chipman 2f5cee4e36 Fixed query error causing crash on postgres (fixes #87) 2016-02-13 13:43:39 -04:00
Ed Chipman d37fdd2ea9 Added support for overriding the relationship name lookup (issue #86) 2016-02-09 13:32:43 -04:00
Ed Chipman 1eb946a8a5 Fixed travis config 2016-02-06 17:36:00 -04:00
Ed Chipman 9863a71380 Attempt to fix travis ignore of 3.1 and 3.2 PGSQL failures 2016-02-06 17:21:58 -04:00
Ed Chipman 7bd56c578d Allow PGSQL to fail for 3.1-3.2 2016-02-06 17:14:21 -04:00
Ed Chipman 10a52463f4 Added support for updating stages on versioned extensions managed through GridFieldSortableRows (ref #77) 2016-02-06 17:00:23 -04:00
UndefinedOffset c9ab0909f8 Adjusted readme to reflect that 3.x is now the version requirement 2016-01-18 09:59:54 -04:00
UndefinedOffset 925bc7c3f7 Removed top end version restriction, now requires 3.0.1+ (fixes #84) 2016-01-18 09:53:30 -04:00
Ed Chipman a5d88c1f3f Merge pull request #82 from helpfulrobot/add-standard-gitattributes-file
Added standard .gitattributes file
2016-01-15 14:30:41 -04:00
UndefinedOffset 01d03c61cf Translations sync 2016-01-15 14:29:43 -04:00
helpfulrobot 37fe9e4578 Added standard .gitattributes file 2016-01-15 16:51:59 +13:00
Ed Chipman 5e3802abe5 Merge pull request #80 from helpfulrobot/add-standard-editorconfig-file
Added standard .editorconfig file
2015-12-16 17:51:37 -04:00
helpfulrobot 0ca027931f Added standard .editorconfig file 2015-12-17 10:05:19 +13:00
Ed Chipman e1b94a3bf3 Merge branch 'spekulatius-fixing-must-be-an-Int-column-is-of-type-int' 2015-12-10 19:33:08 -04:00
Peter Thaleikis 1a55c0599c Fixing bug "must be an Int, column is of type int" 2015-12-10 19:32:09 -04:00
UndefinedOffset afffa5b09e Fixed regression introduced in fae85f04c1 where disableSelection() was no longer optional
Disable selection is now forced when the "allow drag and drop" is checked
2015-11-30 15:56:53 -04:00
UndefinedOffset b21e008b94 Updated Slovak translations 2015-11-16 10:54:24 -04:00
UndefinedOffset fa2731fea7 Updated German translation 2015-11-16 09:50:00 -04:00
UndefinedOffset 4105ebdcd0 Corrected moving to another page documentation 2015-10-13 12:20:27 -03:00
UndefinedOffset 51302bb0aa 3.2 is no longer allowed to fail 2015-10-13 12:08:42 -03:00
UndefinedOffset ebfaa26021 Fixed issue where if there are no pagination arrows from GridFieldPaginator the arrows for sortable gridfield would show when dragging 2015-10-09 10:20:23 -03:00
Ed Chipman c224c33173 Fixed column widths not maintaining in the helper element (fixes #74) 2015-09-18 16:03:06 -03:00
Ed Chipman df26fe04a6 Changed readme to prefer composer (fixes #75) 2015-09-18 15:30:26 -03:00
UndefinedOffset c7df1ab098 Increased opacity of the non-hovered page arrows to 0.4 from 0.3 2015-09-08 11:35:49 -03:00
UndefinedOffset 74780bb110 Fixed error when dropping onto the arrows 2015-09-08 11:33:54 -03:00
UndefinedOffset 3893e7b052 Redraw is now called when sorting starts 2015-09-08 09:56:21 -03:00
Ed Chipman 089d4e796d Re-enabled check for if the sort checkbox is enabled
Re-enabled removal code if the page sort does not have a previous and/or
next

Spacing corrections
2015-09-06 20:48:10 -03:00
Ed Chipman fae85f04c1 First pass of new page sort method issue #72 2015-09-06 20:45:00 -03:00
Ed Chipman 282ea74a76 Fixed issue where the even/odd highlight would differ from what the server would generate after sorting 2015-08-29 14:54:46 -03:00
Ed Chipman ddf5160891 disabled auto complete on the sort toggle checkbox so it is not still checked after a page reload 2015-08-29 14:19:59 -03:00
UndefinedOffset e21c0d5790 Updated readme to reflect 3.2 support 2015-08-05 14:00:38 -03:00
Daniel Hensby 32cb8d67be Move to new travis containerised infrastructure 2015-08-05 13:56:56 -03:00
Ed Chipman fe9b45bd3b Updated travis tests to check 3.2 but allow failures until 3.2 is stable #73 2015-07-22 19:49:23 -03:00
Ed Chipman 5af763d05d Relaxed composer requirements for the module, 3.2 seems to be working #73 2015-07-22 19:43:34 -03:00
UndefinedOffset e8ce22f9e9 Updated copyright year 2015-06-19 15:03:54 -03:00
UndefinedOffset 81e8fae97a Fixed issue where append to top would not correct the order properly in model admin fixes #71 2015-05-07 12:44:55 -03:00
Ed Chipman 9ce8bca316 Merge pull request #69 from danbroooks/master
Allow installation anywhere by adding yml config
2015-04-20 14:22:53 -03:00
Dan Brooks c6bd9e1878 Allow installation anywhere by adding yml config 2015-04-13 22:17:21 +01:00
Ed Chipman ff7a30825e Merge pull request #68 from webfox/master
Allow 'disableSelection' to be disabled
2015-04-07 09:26:53 -03:00
Matthew Hailwood 70512329a1 Allow 'disableSelection' to be disabled 2015-04-07 10:09:06 +12:00
Ed Chipman 3e9b1b6e2e Merge pull request #67 from AdenFraser/master
Resets the classes of .odd, .even, .first and .last for gridfields when a update (drop) event occurs.
2015-02-11 09:29:12 -04:00
Aden Fraser 68723005c8 Fix which reset the classes of .odd, .even, .first and .last when a draggable and drop event occurs, which ensures zebra striping remains correct 2015-02-11 12:29:26 +00:00
Ed Chipman 9d60a58343 Added note about many_many relationships and the gridfield needing to be named the same as the relationship (closes #66) 2015-01-17 12:46:20 -04:00
UndefinedOffset 946ca8dc9b Updated translations 2014-10-27 10:14:05 -03:00
UndefinedOffset b0d1ad1e9c Changed notice for many_many relationships if the extra field is not an int issue #62 2014-10-16 10:25:23 -03:00
Ed Chipman 2e9b7beffd Merge pull request #61 from ismooth/master
Initial sorting equals sortColumn
2014-10-03 11:23:10 -03:00
ismooth 3dfe104b53 Initial sorting equals sortColumn
After a user has changed sorting order of objects in the list, and when drag&drop is disabled, sorting is made by $default_sort from that DataObjects property or default set by SS. This results in different sorting when checkbox is enabled or disabled what confuses users. This change always sorts the list by $sortColumn, so that user always sees same sorting.
2014-10-03 16:01:27 +02:00
Ed Chipman 86d08f9757 Merge pull request #57 from purplespider/patch-1
Fixes fixSortColumn's Random Initial Sorting
2014-08-06 13:15:32 -03:00
James Cocker e493231556 Fixes fixSortColumn's Random Inital Sorting
When loading a GridField with several new items without a sort order (e.g. after uploading 10 images via GridFieldBulkEditingTools), it would previously apply a seemingly random order to them. This fix ensures that the order applied is the same order as the items were created (or uploaded) in.
2014-08-06 16:34:25 +01:00
Ed Chipman 81886ed81a Replaced use of DataList::where() with DataList::filter() 2014-08-02 13:55:52 -03:00
Ed Chipman 3f1f5dbdb4 Changed travis to not allow any failures 2014-05-03 17:07:22 -03:00
Ed Chipman 32a92e62f1 New translations 2014-03-02 15:11:12 -04:00
UndefinedOffset 97ca1f8f4d Corrected documentation example for appending to top 2014-02-24 16:03:50 -04:00
UndefinedOffset 1e24d1e888 Updated docs to correctly work for 3.1.x and future (mentioned in issue #52) 2014-01-27 17:04:32 -04:00
UndefinedOffset 3f07ee7450 Updated docs to correctly work for 3.1.x and future (mentioned in issue #52) 2014-01-27 14:45:11 -04:00
Ed c59ec68690 Updated requirements to restrict to 3.0.x and 3.1.x 2014-01-22 15:40:36 -04:00
UndefinedOffset 278b6d6844 New translations 2014-01-10 10:56:36 -04:00
Ed Chipman 8b185cae3b Merge pull request #49 from chillu/pulls/cn-mi-translations
Chinese/Arabic/Te Reo translations
2013-10-29 16:14:58 -07:00
Ingo Schommer e089a24856 Chinese/Arabic/Te Reo translations 2013-10-30 00:13:07 +01:00
UndefinedOffset 53efc6d9b1 Merge branch 'pulls/transifex' of git://github.com/chillu/SortableGridField 2013-10-25 09:24:52 -03:00
Ingo Schommer ddbd0e0e3f Transifex support 2013-10-24 23:14:40 +02:00
UndefinedOffset e3895cc5ef Fixed issue #48 where duplicate sort indexes would occur when appending to top instead of the bottom 2013-10-22 10:36:08 -03:00
Ed 15ead2ae94 Added note about appending to the top 2013-10-19 14:49:41 -03:00
Ed 9736c3a1a3 Added note about appending to the top 2013-10-19 14:43:26 -03:00
Ed eb1e736805 Made adjustments to make the changes more flexible
Fixed issue causing SortableGridField to not work on ModelAdmin lists
2013-10-19 14:34:45 -03:00
Ed 6ca5bf9a9d Merge branch 'append-to-top' of git://github.com/g4b0/SortableGridField into experimental 2013-10-19 12:23:47 -03:00
Ed Chipman 56b496509f Added note about installing with composer 2013-10-10 09:40:29 -03:00
Ed Chipman f0b711e1b5 Update LICENSE 2013-09-09 10:11:15 -03:00
Ed Chipman 73565ca916 Merge pull request #44 from silverstripesk/master
added slovak language
2013-09-05 05:12:17 -07:00
Pali Ondras 0a1dda4009 added slovak language 2013-09-05 08:37:17 +02:00
Tomas Bilek c77d7148a5 Added CS Language file 2013-09-04 20:22:37 -03:00
g4b0 aa222987af Append new items to the top by default 2013-09-04 15:05:28 +02:00
UndefinedOffset 479325a5bc Added license to composer.json 2013-09-03 12:10:16 -03:00
Ed Chipman 9240fa3193 Merge pull request #41 from simonwelsh/patch-1
Create LICENSE
2013-09-02 09:15:58 -07:00
Simon Welsh aa0b58f47a Create LICENSE 2013-09-02 21:12:00 +12:00
Ed 53eef90c97 Added ModelAdmin Example
Updated examples to say they are written with 3.0.x in mind and public statics should be private in 3.1.x
2013-08-23 21:19:35 -03:00
Ed 1e963bfef1 Added note about GridFieldRelationHandler resolves #40 2013-08-04 12:23:28 -03:00
Ed cd20fdf697 Added event methods that allow detection of the sort action being started or completed resolves #39 2013-08-04 12:15:37 -03:00
Ed f21e08f73e Added section about reporting an issue 2013-07-24 20:45:49 -03:00
Ed 44cae7f0f6 Fixed unit test 2013-07-18 21:01:41 -03:00
Ed 830115fa4f Removed extra language that was not needed
Renamed some of the internal actions
2013-07-18 20:54:33 -03:00
Ed 8b10f39dce Fixed issue with 3.1 caused by DataList being full immutable issue #33 2013-07-10 14:24:00 -03:00
Ed Chipman 7cfeb8dc49 Merge pull request #37 from mediaclinic/master
Finnish language file
2013-07-07 15:03:06 -07:00
Shrike ee7251f93e Finnish language 2013-07-07 23:51:03 +03:00
Ed Chipman e33d57cb28 Merge pull request #36 from ntd/pr1
Added italian language
2013-05-27 05:16:53 -07:00
Nicola Fontana f3d6adf3b2 Added italian language 2013-05-27 13:13:46 +02:00
Ed f75ebaea34 Improved how the last edit table is found using ClassInfo::baseDataClass() 2013-05-25 20:42:35 -03:00
Ed 60c8939f45 Display cms loading indicator when sorting rows on the same page fixes #34 2013-05-25 20:40:14 -03:00
Ed fc2d17ace5 Improved how the last edit table is found using ClassInfo::baseDataClass() 2013-05-25 20:28:46 -03:00
Ed a44c66dcb9 Fixed issue caused if the LastEdited column is not on the same table as the sort column 2013-05-25 13:35:55 -03:00
Ed 5af07ed75d LastEdited date is now changed when sorting rows Fixes #35 2013-05-24 19:36:26 -03:00
Ed 4de269e53f Resolved issue #33 when sorting to page the to page sort was called first then the on page sort was called which caused the indexes to become confused 2013-04-26 20:29:34 -03:00
Ed 4827b8e909 Fixed issue #33 were in newer browsers after dropping on the pagination arrows the edit screen would be displayed 2013-04-19 19:32:47 -03:00
UndefinedOffset 399dc0ab89 Allow PGSQL and SQLLITE to fail on 3.1 2013-04-09 13:44:47 -03:00
UndefinedOffset efdaed9899 Setup travis support 2013-04-09 12:09:06 -03:00
74 changed files with 3554 additions and 846 deletions

17
.editorconfig Normal file
View File

@ -0,0 +1,17 @@
# For more information about the properties used in this file,
# please see the EditorConfig documentation:
# http://editorconfig.org
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[{*.yml,package.json}]
indent_size = 2
# The indent size used in the package.json file cannot be changed:
# https://github.com/npm/npm/pull/3180#issuecomment-16336516

5
.gitattributes vendored Normal file
View File

@ -0,0 +1,5 @@
/tests export-ignore
/docs export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore
/.github export-ignore

14
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,14 @@
## Affected Version
Show version numbers by pasting the output of `composer info --direct`.
Alternatively, hover over the SilverStripe logo in the CMS to basic version information.
## Description
Describe expected and observed behaviour.
For visual issues, please include browser version and screenshots.
Please read https://docs.silverstripe.org/en/contributing/issues_and_bugs/
## Steps to Reproduce
Help us with step-by-step instructions.

5
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,5 @@
Thanks for contributing, you're awesome! :star:
Please describe expected and observed behaviour, and what you're fixing.
For visual fixes, please include tested browsers and screenshots.
Search for related existing issues and link to them if possible.
Please read https://github.com/UndefinedOffset/SortableGridField/blob/master/CONTRIBUTING.md

23
.github/matchers/phpcs.json vendored Normal file
View File

@ -0,0 +1,23 @@
{
"problemMatcher": [
{
"owner": "phpcs",
"severity": "error",
"pattern": [
{
"regexp": "^<file name=\"(?:\\/github\\/workspace\\/)?(.*)\">$",
"file": 1
},
{
"regexp": "<error line=\"(\\d*)\" column=\"(\\d*)\" severity=\"(error|warning)\" message=\"(.*)\" source=\"(.*)(\"\\/>+)$",
"line": 1,
"column": 2,
"severity": 3,
"message": 4,
"code": 5,
"loop": true
}
]
}
]
}

241
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,241 @@
name: CI
on:
push:
branches: [ '**' ]
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
branches: [ '**' ]
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
silverstripe_5_php81:
name: "Silverstripe 5.0 | PHP 8.1"
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
env:
php_version: 8.1
php_extensions: ctype, dom, fileinfo, hash, intl, mbstring, session, simplexml, tokenizer, xml, pdo, mysqli, gd, zip
services:
mysql:
image: mysql:5.7
env:
MYSQL_ALLOW_EMPTY_PASSWORD: false
MYSQL_ROOT_PASSWORD: testpassword
MYSQL_DATABASE: test_db
ports:
- 3306/tcp
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP Extension Cache
id: cache-env
uses: shivammathur/cache-extensions@v1
with:
php-version: ${{ env.php_version }}
extensions: ${{ env.php_extensions }}
key: php74-ext-cache-${{ hashFiles('.github/workflows/ci.yml') }}
- name: Cache PHP Extensions
uses: actions/cache@v3
with:
path: ${{ steps.cache-env.outputs.dir }}
key: ${{ steps.cache-env.outputs.key }}
restore-keys: ${{ steps.cache-env.outputs.key }}
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.php_version }}
extensions: ${{ env.php_extensions }}
ini-values: log_errors=On, error_log="${{github.workspace}}/artifacts/php_errors.log"
coverage: none
tools: composer:v2
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-ss5-php81-${{ hashFiles('**/composer.json') }}-${{ hashFiles('.github/workflows/ci.yml') }}
restore-keys: ${{ runner.os }}-composer-ss5-php81-
- name: Install Composer dependencies
env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
composer self-update --ansi || true
if [[ $GITHUB_ACCESS_TOKEN ]]; then composer config --ansi -g github-oauth.github.com $GITHUB_ACCESS_TOKEN; fi
composer config allow-plugins.composer/installers true
composer config allow-plugins.silverstripe/recipe-plugin true
composer config allow-plugins.silverstripe/vendor-plugin true
composer require silverstripe/admin 2.0.* --no-update
composer require silverstripe/versioned 2.0.*
composer install --verbose --no-interaction --no-progress --no-suggest --optimize-autoloader --ansi
- name: Configure Environment
run: |
cp tests/utils/actions.env.template .env
mkdir artifacts
- name: Perform PHPUnit Tests
env:
SS_DATABASE_PORT: ${{ job.services.mysql.ports['3306'] }}
run: vendor/bin/phpunit --colors=always --printer UndefinedOffset\\SortableGridField\\Tests\\PHPUnit\\Bootstrap\\GitHubActionsAnnotatorPrinter
silverstripe_411_php80:
name: "Silverstripe 4.11 | PHP 8.0"
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
env:
php_version: 8.0
php_extensions: ctype, dom, fileinfo, hash, intl, mbstring, session, simplexml, tokenizer, xml, pdo, mysqli, gd, zip
services:
mysql:
image: mysql:5.7
env:
MYSQL_ALLOW_EMPTY_PASSWORD: false
MYSQL_ROOT_PASSWORD: testpassword
MYSQL_DATABASE: test_db
ports:
- 3306/tcp
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP Extension Cache
id: cache-env
uses: shivammathur/cache-extensions@v1
with:
php-version: ${{ env.php_version }}
extensions: ${{ env.php_extensions }}
key: php80-ext-cache-${{ hashFiles('.github/workflows/ci.yml') }}
- name: Cache PHP Extensions
uses: actions/cache@v3
with:
path: ${{ steps.cache-env.outputs.dir }}
key: ${{ steps.cache-env.outputs.key }}
restore-keys: ${{ steps.cache-env.outputs.key }}
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.php_version }}
extensions: ${{ env.php_extensions }}
ini-values: log_errors=On, error_log="${{github.workspace}}/artifacts/php_errors.log"
coverage: none
tools: composer:v2
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-ss411-php80-${{ hashFiles('**/composer.json') }}-${{ hashFiles('.github/workflows/ci.yml') }}
restore-keys: ${{ runner.os }}-composer-ss411-php80-
- name: Install Composer dependencies
env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
composer self-update --ansi || true
if [[ $GITHUB_ACCESS_TOKEN ]]; then composer config --ansi -g github-oauth.github.com $GITHUB_ACCESS_TOKEN; fi
composer config allow-plugins.composer/installers true
composer config allow-plugins.silverstripe/recipe-plugin true
composer config allow-plugins.silverstripe/vendor-plugin true
composer require silverstripe/admin 1.11.* --no-update
composer require silverstripe/versioned 1.11.*
composer install --verbose --no-interaction --no-progress --no-suggest --optimize-autoloader --ansi
- name: Configure Environment
run: |
cp tests/utils/actions.env.template .env
mkdir artifacts
- name: Perform PHPUnit Tests
env:
SS_DATABASE_PORT: ${{ job.services.mysql.ports['3306'] }}
run: vendor/bin/phpunit --colors=always --printer UndefinedOffset\\SortableGridField\\Tests\\PHPUnit\\Bootstrap\\GitHubActionsAnnotatorPrinter
phpcs:
name: "PHP_CodeSniffer"
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
env:
php_version: 8.0
php_extensions: ctype, dom, fileinfo, hash, intl, mbstring, session, simplexml, tokenizer, xml, pdo, mysqli, gd, zip
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP Extension Cache
id: cache-env
uses: shivammathur/cache-extensions@v1
with:
php-version: ${{ env.php_version }}
extensions: ${{ env.php_extensions }}
key: php74-ext-cache-${{ hashFiles('.github/workflows/ci.yml') }}
- name: Cache PHP Extensions
uses: actions/cache@v3
with:
path: ${{ steps.cache-env.outputs.dir }}
key: ${{ steps.cache-env.outputs.key }}
restore-keys: ${{ steps.cache-env.outputs.key }}
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.php_version }}
extensions: ${{ env.php_extensions }}
ini-values: log_errors=On, error_log="${{github.workspace}}/artifacts/php_errors.log"
coverage: none
tools: composer:v2
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-phpcs-${{ hashFiles('.github/workflows/ci.yml') }}
restore-keys: ${{ runner.os }}-composer-phpcs-
- name: Install Composer dependencies
env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
rm composer.json
composer self-update --ansi || true
if [[ $GITHUB_ACCESS_TOKEN ]]; then composer config --ansi -g github-oauth.github.com $GITHUB_ACCESS_TOKEN; fi
composer require squizlabs/php_codesniffer ~3.4
composer install --verbose --no-interaction --no-progress --no-suggest --optimize-autoloader --ansi
mkdir artifacts
- name: Validate Code Style
run: vendor/bin/phpcs --colors --report=full --report-checkstyle=artifacts/phpcs.xml
- name: Annotate Code Style Issues
if: failure()
run: (test -f artifacts/phpcs.xml && echo "::add-matcher::.github/matchers/phpcs.json" && cat artifacts/phpcs.xml && echo "::remove-matcher owner=phpcs::")

3
.gitignore vendored
View File

@ -2,4 +2,5 @@
/.buildpath
/.project
/.settings
/.settings/*
/.settings/*
.phpunit.result.cache

69
.scrutinizer.yml Normal file
View File

@ -0,0 +1,69 @@
inherit: true
checks:
php:
verify_property_names: true
verify_argument_usable_as_reference: true
verify_access_scope_valid: true
useless_calls: true
use_statement_alias_conflict: true
variable_existence: true
unused_variables: true
unused_properties: true
unused_parameters: true
unused_methods: true
unreachable_code: true
too_many_arguments: true
sql_injection_vulnerabilities: true
simplify_boolean_return: true
side_effects_or_types: true
security_vulnerabilities: true
return_doc_comments: true
return_doc_comment_if_not_inferrable: true
require_scope_for_properties: true
require_scope_for_methods: true
require_php_tag_first: true
psr2_switch_declaration: true
psr2_class_declaration: true
property_assignments: true
prefer_while_loop_over_for_loop: true
precedence_mistakes: true
precedence_in_conditions: true
phpunit_assertions: true
php5_style_constructor: true
parse_doc_comments: true
parameter_non_unique: true
parameter_doc_comments: true
param_doc_comment_if_not_inferrable: true
optional_parameters_at_the_end: true
one_class_per_file: true
no_unnecessary_if: true
no_trailing_whitespace: true
no_property_on_interface: true
no_non_implemented_abstract_methods: true
no_error_suppression: true
no_duplicate_arguments: true
no_commented_out_code: true
newline_at_end_of_file: true
missing_arguments: true
method_calls_on_non_object: true
instanceof_class_exists: true
foreach_traversable: true
fix_line_ending: true
fix_doc_comments: true
duplication: true
deprecated_code_usage: true
deadlock_detection_in_loops: true
code_rating: true
closure_use_not_conflicting: true
catch_class_exists: true
blank_line_after_namespace_declaration: false
avoid_multiple_statements_on_same_line: true
avoid_duplicate_types: true
avoid_conflicting_incrementers: true
avoid_closing_tag: true
assignment_of_null_return: true
argument_type_checks: true
filter:
paths: [src/*, tests/*]

42
.travis.yml Normal file
View File

@ -0,0 +1,42 @@
language: php
dist: xenial
services:
- mysql
- postgresql
cache:
directories:
- $HOME/.composer/cache/files
matrix:
include:
- php: 5.6
env: DB=MYSQL CORE_RELEASE=4.4.x-dev PHPUNIT_TEST=1
- php: 7.1
env: DB=MYSQL CORE_RELEASE=4.5.x-dev PHPUNIT_TEST=1 PDO=1
- php: 7.2
env: DB=PGSQL CORE_RELEASE=4.6.x-dev PHPUNIT_TEST=1
- php: 7.3
env: DB=MYSQL CORE_RELEASE=4.6.x-dev PHPUNIT_TEST=1
- php: 7.4
env: DB=MYSQL CORE_RELEASE=4.x-dev PHPUNIT_TEST=1
before_script:
# Init PHP
- composer self-update || true
- phpenv rehash
- phpenv config-rm xdebug.ini
# Install composer dependencies
- composer validate
- composer require --no-update silverstripe/recipe-core:$CORE_RELEASE
- composer require --no-update phpunit/phpunit:^5.7
# Fix for running phpunit 5 on php 7.4+
- composer require --no-update sminnee/phpunit-mock-objects:^3
- if [[ $DB == PGSQL ]]; then composer require silverstripe/postgresql:^2 --prefer-dist --no-update; fi
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
script:
- vendor/bin/phpunit

8
.tx/config Normal file
View File

@ -0,0 +1,8 @@
[main]
host = https://www.transifex.com
[silverstripe-sortablegridfield.master]
file_filter = lang/<lang>.yml
source_file = lang/en.yml
source_lang = en
type = YML

370
CHANGELOG.md Normal file
View File

@ -0,0 +1,370 @@
# Change Log
## [2.2.0](https://github.com/UndefinedOffset/SortableGridField/tree/2.2.0) (2023-05-08)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.1.0...2.2.0)
## [2.1.0](https://github.com/UndefinedOffset/SortableGridField/tree/2.1.0) (2022-07-06)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.9...2.1.0)
## [2.0.9](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.9) (2021-07-19)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.8...2.0.9)
## [2.0.8](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.8) (2019-01-08)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.7...2.0.8)
## [2.0.7](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.7) (2018-12-13)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.6...2.0.7)
## [2.0.6](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.6) (2018-07-04)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.5...2.0.6)
## [2.0.5](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.5) (2018-06-07)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.4...2.0.5)
**Closed issues:**
- Int to DBInt [\#120](https://github.com/UndefinedOffset/SortableGridField/issues/120)
**Merged pull requests:**
- Fixes "undefined offset" error when moving a many\_many item to previous page [\#121](https://github.com/UndefinedOffset/SortableGridField/pull/121) ([ChrissiQ](https://github.com/ChrissiQ))
## [2.0.4](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.4) (2018-03-07)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.3...2.0.4)
## [2.0.3](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.3) (2018-03-01)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.2...2.0.3)
## [2.0.2](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.2) (2017-12-11)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.1...2.0.2)
**Closed issues:**
- many\_many ss 3.6 GridFieldAddNewMultiClass Column 'SortOrder' in field list is ambiguous [\#114](https://github.com/UndefinedOffset/SortableGridField/issues/114)
- mapTableNameAndReturn can't parse namespace+classname into mysql tablename [\#113](https://github.com/UndefinedOffset/SortableGridField/issues/113)
## [2.0.1](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.1) (2017-11-29)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.0...2.0.1)
**Closed issues:**
- Many Many relationships are broken due to renaming of many\_many and many\_many\_extraFields to manyMany and manyManyExtraFields [\#110](https://github.com/UndefinedOffset/SortableGridField/issues/110)
**Merged pull requests:**
- Merge 1.0.x into master [\#112](https://github.com/UndefinedOffset/SortableGridField/pull/112) ([dhensby](https://github.com/dhensby))
- DOCS Update docs examples to be 3.1+ relevant [\#111](https://github.com/UndefinedOffset/SortableGridField/pull/111) ([dhensby](https://github.com/dhensby))
## [2.0.0](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.0) (2017-11-24)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.0-beta2...2.0.0)
**Closed issues:**
- SilverStripe 4 Compatibility [\#103](https://github.com/UndefinedOffset/SortableGridField/issues/103)
## [2.0.0-beta2](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.0-beta2) (2017-11-01)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/1.0.0...2.0.0-beta2)
## [1.0.0](https://github.com/UndefinedOffset/SortableGridField/tree/1.0.0) (2017-10-27)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.10...1.0.0)
**Closed issues:**
- Remove tests against unsupported versions [\#97](https://github.com/UndefinedOffset/SortableGridField/issues/97)
## [0.6.10](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.10) (2017-08-14)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.0-beta1...0.6.10)
## [2.0.0-beta1](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.0-beta1) (2017-07-04)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.0-alpha1...2.0.0-beta1)
**Closed issues:**
- No search capabilities when using in ModelAdmin [\#108](https://github.com/UndefinedOffset/SortableGridField/issues/108)
**Merged pull requests:**
- Remove closing PHP tag [\#107](https://github.com/UndefinedOffset/SortableGridField/pull/107) ([robbieaverill](https://github.com/robbieaverill))
## [2.0.0-alpha1](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.0-alpha1) (2017-06-05)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.9...2.0.0-alpha1)
**Closed issues:**
- Tests required for tables with $table\_name declared [\#105](https://github.com/UndefinedOffset/SortableGridField/issues/105)
## [0.6.9](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.9) (2017-04-21)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.8...0.6.9)
**Closed issues:**
- Query errors when sorted field is on a relation and appending to top [\#101](https://github.com/UndefinedOffset/SortableGridField/issues/101)
## [0.6.8](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.8) (2017-04-18)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.7...0.6.8)
**Closed issues:**
- Typo error [\#100](https://github.com/UndefinedOffset/SortableGridField/issues/100)
## [0.6.7](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.7) (2017-04-13)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.6...0.6.7)
**Closed issues:**
- Wrong sort field type check [\#99](https://github.com/UndefinedOffset/SortableGridField/issues/99)
## [0.6.6](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.6) (2017-04-12)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.5...0.6.6)
**Closed issues:**
- Not working with SS3.5 fresh install [\#98](https://github.com/UndefinedOffset/SortableGridField/issues/98)
## [0.6.5](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.5) (2016-11-24)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.4...0.6.5)
## [0.6.4](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.4) (2016-11-22)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.3...0.6.4)
## [0.6.3](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.3) (2016-11-18)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.2...0.6.3)
**Merged pull requests:**
- Use Sort of GridField Header [\#96](https://github.com/UndefinedOffset/SortableGridField/pull/96) ([schellmax](https://github.com/schellmax))
## [0.6.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.2) (2016-06-09)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.1...0.6.2)
**Closed issues:**
- Drag and Drop Checkbox can't be selected [\#90](https://github.com/UndefinedOffset/SortableGridField/issues/90)
- Issue edit and Adding New Gallery Holder Page and Gallery Page [\#89](https://github.com/UndefinedOffset/SortableGridField/issues/89)
**Merged pull requests:**
- BUG Remove redundant leading underscore in strings [\#95](https://github.com/UndefinedOffset/SortableGridField/pull/95) ([tractorcow](https://github.com/tractorcow))
- Test against 3.3 core and PHP 5.6 [\#93](https://github.com/UndefinedOffset/SortableGridField/pull/93) ([chillu](https://github.com/chillu))
- Added standard Scrutinizer config [\#91](https://github.com/UndefinedOffset/SortableGridField/pull/91) ([helpfulrobot](https://github.com/helpfulrobot))
## [0.6.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.1) (2016-03-03)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.0...0.6.1)
**Closed issues:**
- Unit tests failing with framework 3.2.3 [\#88](https://github.com/UndefinedOffset/SortableGridField/issues/88)
## [0.6.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.0) (2016-02-13)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.4...0.6.0)
**Closed issues:**
- Can't set checkbox "Allow drag and drop re-ordering" [\#76](https://github.com/UndefinedOffset/SortableGridField/issues/76)
- Investigate build failures for PostgreSQL on 3.1 and 3.2 [\#87](https://github.com/UndefinedOffset/SortableGridField/issues/87)
- Multiple many\_many GridFields acting on the same relation not possible. [\#86](https://github.com/UndefinedOffset/SortableGridField/issues/86)
- Expand unit test to cover page sorting [\#85](https://github.com/UndefinedOffset/SortableGridField/issues/85)
## [0.5.4](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.4) (2016-01-18)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.3...0.5.4)
**Closed issues:**
- Support for 3.3 [\#84](https://github.com/UndefinedOffset/SortableGridField/issues/84)
- Lithuanian translation [\#83](https://github.com/UndefinedOffset/SortableGridField/issues/83)
**Merged pull requests:**
- Added standard .gitattributes file [\#82](https://github.com/UndefinedOffset/SortableGridField/pull/82) ([helpfulrobot](https://github.com/helpfulrobot))
- Added standard .editorconfig file [\#80](https://github.com/UndefinedOffset/SortableGridField/pull/80) ([helpfulrobot](https://github.com/helpfulrobot))
## [0.5.3](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.3) (2015-12-10)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.2...0.5.3)
## [0.5.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.2) (2015-11-30)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.1...0.5.2)
## [0.5.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.1) (2015-10-09)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.0...0.5.1)
## [0.5.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.0) (2015-09-18)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.5...0.5.0)
**Closed issues:**
- Change install instructions to prefer composer [\#75](https://github.com/UndefinedOffset/SortableGridField/issues/75)
- Helper Columns do not maintain the widths of the item being dragged [\#74](https://github.com/UndefinedOffset/SortableGridField/issues/74)
- Improved UI for moving to another page [\#72](https://github.com/UndefinedOffset/SortableGridField/issues/72)
## [0.4.5](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.5) (2015-08-29)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.4...0.4.5)
## [0.4.4](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.4) (2015-08-05)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.3...0.4.4)
**Closed issues:**
- 3.2 compat? [\#73](https://github.com/UndefinedOffset/SortableGridField/issues/73)
- Doesn't respect Dataobject's $default\_sort DESC [\#60](https://github.com/UndefinedOffset/SortableGridField/issues/60)
## [0.4.3](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.3) (2015-05-07)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.2...0.4.3)
**Closed issues:**
- setAppendToTop\(\) Does Not Work When the DataObjects Within the GridField are not Part of a RelationList [\#71](https://github.com/UndefinedOffset/SortableGridField/issues/71)
- \(Drag and drop\) re-ordering over multiple pages [\#70](https://github.com/UndefinedOffset/SortableGridField/issues/70)
**Merged pull requests:**
- Allow installation anywhere by adding yml config [\#69](https://github.com/UndefinedOffset/SortableGridField/pull/69) ([danbroooks](https://github.com/danbroooks))
## [0.4.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.2) (2015-04-07)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.1...0.4.2)
**Closed issues:**
- Add note for many\_many relations about the field name [\#66](https://github.com/UndefinedOffset/SortableGridField/issues/66)
- Doesnt work for Sitetree descendants because of versioned dataobjects managed by ModelAdmin. [\#64](https://github.com/UndefinedOffset/SortableGridField/issues/64)
- Error saving many to many relations [\#63](https://github.com/UndefinedOffset/SortableGridField/issues/63)
**Merged pull requests:**
- Allow 'disableSelection' to be disabled [\#68](https://github.com/UndefinedOffset/SortableGridField/pull/68) ([hailwood](https://github.com/hailwood))
- Resets the classes of .odd, .even, .first and .last for gridfields when a update \(drop\) event occurs. [\#67](https://github.com/UndefinedOffset/SortableGridField/pull/67) ([AdenFraser](https://github.com/AdenFraser))
## [0.4.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.1) (2014-10-27)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.0...0.4.1)
**Closed issues:**
- Sort column xxxxx must be an Int, column is of type [\#62](https://github.com/UndefinedOffset/SortableGridField/issues/62)
## [0.4.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.0) (2014-10-16)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.3.2...0.4.0)
**Closed issues:**
- Column 'SortOrder' in field list is ambiguous in a Many\_Many relationship Gridfield [\#59](https://github.com/UndefinedOffset/SortableGridField/issues/59)
- Sort column only updated when GridField is viewed [\#58](https://github.com/UndefinedOffset/SortableGridField/issues/58)
**Merged pull requests:**
- Initial sorting equals sortColumn [\#61](https://github.com/UndefinedOffset/SortableGridField/pull/61) ([ismooth](https://github.com/ismooth))
- Fixes fixSortColumn's Random Initial Sorting [\#57](https://github.com/UndefinedOffset/SortableGridField/pull/57) ([purplespider](https://github.com/purplespider))
## [0.3.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.3.2) (2014-08-02)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.3.1...0.3.2)
**Closed issues:**
- Gridfield sortable button [\#56](https://github.com/UndefinedOffset/SortableGridField/issues/56)
- Problem with $belongs\_many\_many [\#55](https://github.com/UndefinedOffset/SortableGridField/issues/55)
- setAppendToTop - Fatal error: Call to undefined method GridFieldSortableRows::create\(\) [\#54](https://github.com/UndefinedOffset/SortableGridField/issues/54)
- Frontend not working / Error in Examples [\#52](https://github.com/UndefinedOffset/SortableGridField/issues/52)
- Unable to toggle off, switched on by default [\#51](https://github.com/UndefinedOffset/SortableGridField/issues/51)
- Duplicate Column names [\#50](https://github.com/UndefinedOffset/SortableGridField/issues/50)
- Bug with pagination [\#38](https://github.com/UndefinedOffset/SortableGridField/issues/38)
## [0.3.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.3.1) (2013-10-29)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.3.0...0.3.1)
**Closed issues:**
- Duplicate sort id's when fixing the sort with append\_to\_top [\#47](https://github.com/UndefinedOffset/SortableGridField/issues/47)
**Merged pull requests:**
- Chinese/Arabic/Te Reo translations [\#49](https://github.com/UndefinedOffset/SortableGridField/pull/49) ([chillu](https://github.com/chillu))
- Transifex support [\#48](https://github.com/UndefinedOffset/SortableGridField/pull/48) ([chillu](https://github.com/chillu))
## [0.3.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.3.0) (2013-10-22)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.2.1...0.3.0)
**Closed issues:**
- is this available on composer/sivlerstripe addons? [\#45](https://github.com/UndefinedOffset/SortableGridField/issues/45)
- Add Docs On Migration from Data Object Manager [\#16](https://github.com/UndefinedOffset/SortableGridField/issues/16)
**Merged pull requests:**
- added slovak language [\#44](https://github.com/UndefinedOffset/SortableGridField/pull/44) ([silverstripesk](https://github.com/silverstripesk))
- Append new items to the top by default [\#42](https://github.com/UndefinedOffset/SortableGridField/pull/42) ([g4b0](https://github.com/g4b0))
## [0.2.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.2.1) (2013-09-03)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.2.0...0.2.1)
**Merged pull requests:**
- Create LICENSE [\#41](https://github.com/UndefinedOffset/SortableGridField/pull/41) ([simonwelsh](https://github.com/simonwelsh))
## [0.2.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.2.0) (2013-08-04)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.1.3...0.2.0)
**Closed issues:**
- Breaks when used with GridFieldManyRelationHandler [\#40](https://github.com/UndefinedOffset/SortableGridField/issues/40)
- \[REQUEST\] Event to indicate that there has been a change in sort order. [\#39](https://github.com/UndefinedOffset/SortableGridField/issues/39)
- Moving to next page doesn't work in FF 20.0.1 [\#33](https://github.com/UndefinedOffset/SortableGridField/issues/33)
## [0.1.3](https://github.com/UndefinedOffset/SortableGridField/tree/0.1.3) (2013-07-10)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.1.2...0.1.3)
**Closed issues:**
- Partical Cache and sortColumn update [\#35](https://github.com/UndefinedOffset/SortableGridField/issues/35)
- Add loading indicator to row sorting [\#34](https://github.com/UndefinedOffset/SortableGridField/issues/34)
**Merged pull requests:**
- Finnish language file [\#37](https://github.com/UndefinedOffset/SortableGridField/pull/37) ([mediaclinic](https://github.com/mediaclinic))
- Added italian language [\#36](https://github.com/UndefinedOffset/SortableGridField/pull/36) ([ntd](https://github.com/ntd))
## [0.1.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.1.2) (2013-04-26)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.1.1...0.1.2)
**Closed issues:**
- An error occured while fetching data from the server Please try again later. [\#31](https://github.com/UndefinedOffset/SortableGridField/issues/31)
## [0.1.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.1.1) (2013-04-19)
[Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.1.0...0.1.1)
**Closed issues:**
- Create a release tag [\#32](https://github.com/UndefinedOffset/SortableGridField/issues/32)
## [0.1.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.1.0) (2013-03-29)
**Closed issues:**
- SortOrder field on $has\_many relation [\#28](https://github.com/UndefinedOffset/SortableGridField/issues/28)
- nested gridfield in 3.1 [\#27](https://github.com/UndefinedOffset/SortableGridField/issues/27)
- Line 183 of the GridFieldSortableRows.php is producing problems [\#24](https://github.com/UndefinedOffset/SortableGridField/issues/24)
- GridFieldSortableRowsTest::testSortActionWithAdminPermission No items to sort [\#23](https://github.com/UndefinedOffset/SortableGridField/issues/23)
- Sorting on Extension classes [\#22](https://github.com/UndefinedOffset/SortableGridField/issues/22)
- Drag&Drop button broke GridField - SS 3.1 - Latest SS 3.1 [\#21](https://github.com/UndefinedOffset/SortableGridField/issues/21)
- Error "Sort column Sort Order must be an Int, column is of type Array" [\#20](https://github.com/UndefinedOffset/SortableGridField/issues/20)
- 3.1 SQLQuery::aggregate\(\) doesn't work with limit set on SQLQuery [\#19](https://github.com/UndefinedOffset/SortableGridField/issues/19)
- sortablegridfield not working with ss3.1 [\#18](https://github.com/UndefinedOffset/SortableGridField/issues/18)
- make upgrading from SortableDataObject painless [\#15](https://github.com/UndefinedOffset/SortableGridField/issues/15)
- IDE files should not be included in this repo [\#13](https://github.com/UndefinedOffset/SortableGridField/issues/13)
- Adding GridFieldSortableRows to the wrong column causes massive data loss [\#12](https://github.com/UndefinedOffset/SortableGridField/issues/12)
- Sort ModelAdmin GridFields [\#6](https://github.com/UndefinedOffset/SortableGridField/issues/6)
- fixSortColumn doesn't always run [\#5](https://github.com/UndefinedOffset/SortableGridField/issues/5)
- Optimize sorting in has\_many [\#4](https://github.com/UndefinedOffset/SortableGridField/issues/4)
- dragdropcheckbox triggers 'changed' on form [\#3](https://github.com/UndefinedOffset/SortableGridField/issues/3)
- sort order not being updated on delete [\#2](https://github.com/UndefinedOffset/SortableGridField/issues/2)
- installation [\#1](https://github.com/UndefinedOffset/SortableGridField/issues/1)
**Merged pull requests:**
- Fixing undefined variable $dataList [\#30](https://github.com/UndefinedOffset/SortableGridField/pull/30) ([halkyon](https://github.com/halkyon))
- FIX Do not use hardcoded IDs in tests, as records from fixture might not... [\#29](https://github.com/UndefinedOffset/SortableGridField/pull/29) ([patbolo](https://github.com/patbolo))
- BUGFIX: Use config layer to check for decorated $db fields [\#26](https://github.com/UndefinedOffset/SortableGridField/pull/26) ([unclecheese](https://github.com/unclecheese))
- Update code/forms/GridFieldSortableRows.php [\#25](https://github.com/UndefinedOffset/SortableGridField/pull/25) ([flxbot](https://github.com/flxbot))
- Dutch language! [\#14](https://github.com/UndefinedOffset/SortableGridField/pull/14) ([ARNHOE](https://github.com/ARNHOE))
- spanish translation. [\#11](https://github.com/UndefinedOffset/SortableGridField/pull/11) ([dospuntocero](https://github.com/dospuntocero))
- BUGFIX Make sure it's possible to rename the module's directory without ... [\#10](https://github.com/UndefinedOffset/SortableGridField/pull/10) ([drzax](https://github.com/drzax))
- German Translation [\#9](https://github.com/UndefinedOffset/SortableGridField/pull/9) ([PatrickElsing](https://github.com/PatrickElsing))
- Update lang/en.yml [\#8](https://github.com/UndefinedOffset/SortableGridField/pull/8) ([PatrickElsing](https://github.com/PatrickElsing))
- Renamed the Items array that is sent in post [\#7](https://github.com/UndefinedOffset/SortableGridField/pull/7) ([sheadawson](https://github.com/sheadawson))

11
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,11 @@
Contributing
=================
## Translations
Translations of the natural language strings are managed through a third party translation interface, transifex.com. Newly added strings will be periodically uploaded there for translation, and any new translations will be merged back to the project source code.
Please use [https://www.transifex.com/projects/p/silverstripe-sortablegridfield](https://www.transifex.com/projects/p/silverstripe-sortablegridfield) to contribute translations, rather than sending pull requests with YAML files.
## Reporting an issue
When you're reporting an issue please ensure you specify what version of SilverStripe you are using i.e. 3.0.5, 3.1beta3, 3.0-master etc. Also be sure to include any JavaScript or PHP errors you receive, for PHP errors please ensure you include the full stack trace. Also please include your implementation code (where your setting up your grid field) as well as how you produced the issue. You may also be asked to provide some of the classes to aid in re-producing the issue. Stick with the issue, remember that you seen the issue not the maintainer of the module so it may take allot of questions to arrive at a fix or answer.

27
LICENSE Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2024, Ed Chipman
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of Ed Chipman nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

138
README.md
View File

@ -1,19 +1,42 @@
SortableGridField
=================
Adds drag and drop functionality to SilverStripe 3's GridField
[![CI](https://github.com/UndefinedOffset/SortableGridField/actions/workflows/ci.yml/badge.svg)](https://github.com/UndefinedOffset/SortableGridField/actions/workflows/ci.yml)
[![Latest Stable Version](https://poser.pugx.org/undefinedoffset/sortablegridfield/version.svg)](http://www.silverstripe.org/stable-download/)
[![Latest Unstable Version](https://poser.pugx.org/undefinedoffset/sortablegridfield/v/unstable.svg)](https://packagist.org/packages/undefinedoffset/sortablegridfield)
[![Total Downloads](https://poser.pugx.org/undefinedoffset/sortablegridfield/downloads.svg)](https://packagist.org/packages/undefinedoffset/sortablegridfield)
[![License](https://poser.pugx.org/undefinedoffset/sortablegridfield/license.svg)](https://github.com/UndefinedOffset/SortableGridField/blob/master/LICENSE)
Adds drag and drop functionality to Silverstripe's GridField
## Requirements
* SilverStripe 3.x
* Silverstripe 4.11+ or 5.0+
## Installation
* Download the module from here https://github.com/UndefinedOffset/SortableGridField/archive/master.zip
* Extract the downloaded archive into your site root so that the destination folder is called SortableGridField, opening the extracted folder should contain _config.php in the root along with other files/folders
* Run dev/build?flush=all to regenerate the manifest
* Upon entering the cms and using GridFieldSortableRows component for the first time you make need to add ?flush=all to the end of the address to force the templates to regenerate
Installation is supported via composer only
```sh
composer require undefinedoffset/sortablegridfield
```
* Run `dev/build?flush=all` to regenerate the manifest
* Upon entering the cms and using `GridFieldSortableRows` component for the first time you make need to add `?flush=all`
to the end of the address to force the templates to regenerate
## Usage
To enable sorting on a has_many relationship set up an integer field on your data object. Also for has_many relationships make sure to set the $default_sort on the dataobject to this new integer field to ensure that the sort order is applied when the relationship is requested. For many_many relationships you must add a $many_many_extraFields static to the data object defining the relationship, see the SilverStripe documentation for more information on this. If you are using a many_many relationship you will need to do a custom getter to set the sort order of this relationship for use on the front end see bellow for an example. For new DataObjects you do not need to increment the Sort order yourself in your DataObject GridFieldSortableRows will automatically do this the next time the grid is displayed.
To enable sorting on a `has_many` relationship set up an integer field on your data object. Also for `has_many`
relationships make sure to set the `$default_sort` on the `DataObject` to this new integer field to ensure that the sort
order is applied when the relationship is requested. For `many_many` relationships you must add a
`$many_many_extraFields` static to the `DataObject` defining the relationship, see the
[SilverStripe documentation](https://docs.silverstripe.org/en/3/developer_guides/model/relations/#many-many)
for more information on this. If you are using a `many_many` relationship you will need to do a custom getter to set the
sort order of this relationship for use on the front end see below for an example. As well for `many_many` relationships
the name of the GridField *must* be the same as the relationship name other wise error's will occur. For new
`DataObject`s you do not need to increment the sort order yourself in your `DataObject`, `GridFieldSortableRows` will
automatically do this the next time the grid is displayed.
```php
public function getMyManyManyRelationship() {
@ -21,23 +44,104 @@ public function getMyManyManyRelationship() {
}
```
To enable drag and drop sorting on the grid field add the following to your grid field's config
*Grid Field Config*
To enable drag and drop sorting on the `GridField` add the following to your `GridField`'s config, also make sure you add
the namespace `UndefinedOffset\SortableGridField\Forms` to your file.
```php
//Namespace imports should be added to the top of your file
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
$myGridConfig->addComponent(new GridFieldSortableRows('{Column to store sort}'));
```
To move an item to another page drag the row over the respective page button and release.
To move an item to another page drag the row over the respective move to page button which appear on the left and right
of the GridField and release.
#### Full code Examples
* [has_many relationship] (https://github.com/UndefinedOffset/SortableGridField/blob/master/docs/HasManyExample.md)
* [many_many relationship] (https://github.com/UndefinedOffset/SortableGridField/blob/master/docs/ManyManyExample.md)
## Migrating from SilverStripe 2.4 and Data Object Manager's SortableDataObject
SortableGridField is not the same as SortableDataObject, since it is only a component of GridField it does not have the ability to catch the object when it is saved for the first time. So SortableGridField uses 1 as the first sort index because 0 is the default for an integer field/column in the database. For migrations from 2.4 with SortableDataObject you need to setup your DataObject based on the instructions above however you must name your sort column "SortOrder" to maintain your sort indexes defined by SortableDataObject. Then you need to run the following query on the table containing your sort field, for many_many relationships this will be something like {RelationshipClass}_{RelationshipName}. This query will maintain your sort order from SortableDataObject but increment the index by 1 giving it a starting number of 1.
* [has_many relationship](https://github.com/UndefinedOffset/SortableGridField/blob/master/docs/HasManyExample.md)
* [many_many relationship](https://github.com/UndefinedOffset/SortableGridField/blob/master/docs/ManyManyExample.md)
* [ModelAdmin implementation](https://github.com/UndefinedOffset/SortableGridField/blob/master/docs/ModelAdminExample.md)
```sql
UPDATE YourTable SET SortOrder=SortOrder+1;
#### Events
`GridFieldSortableRows` provides 4 "events" `onBeforeGridFieldRowSort()`, `onAfterGridFieldRowSort()`,
`onBeforeGridFieldPageSort()` and `onAfterGridFieldPageSort()`. These "events" are passed a clone of the `DataList`
used in `GridFieldSortableRows`, in the case of page sorting this list has a limit that shows you the current page
plus/minus one object. For `GridFieldSortableRows` that are on `ModelAdmin` descendants these events are called on the
`ModelAdmin` if they do not have an owner `DataObject`, if you are using `GridFieldSortableRows` on a `GridField` for a
`DataObject`'s relationship the events are called on that `DataObject`.
#### Appending to the top instead of the bottom
By default `GridFieldSortableRows` appends to the bottom of the list for performance on large data sets, however you can
set new records to append new records to the top by calling `setAppendToTop(true)` on your `GridFieldSortableRows`
instance.
```php
//Namespace imports should be added to the top of your file
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
$myGridConfig->addComponent($sortable = new GridFieldSortableRows('SortOrder'));
$sortable->setAppendToTop(true);
```
#### Working with versioned records
By default `GridFieldSortableRows` does not update any other stage for versioned than the base stage. However you can
enable this by calling `setUpdateVersionedStage()` and passing in the name of the stage you want to update along with
the base stage. For example passing in "Live" will also update the "Live" stage when any sort happens.
```php
//Namespace imports should be added to the top of your file
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
$myGridConfig->addComponent($sortable = new GridFieldSortableRows('SortOrder'));
$sortable->setUpdateVersionedStage('Live');
```
#### Overriding the default relationship name
By default the relationship name comes from the name of the `GridField`, however you can override this lookup by
calling `setCustomRelationName()` and passing in the name of the relationship. This allows for you to have multiple
`GridFields` on the same form interacting with the same `many_many` list maybe filtered slightly differently.
```php
//Namespace imports should be added to the top of your file
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
$myGridConfig->addComponent($sortable = new GridFieldSortableRows('SortOrder'));
$sortable->setCustomRelationName('MyRelationship');
```
## Reporting an issue
When you're reporting an issue please ensure you specify what version of SilverStripe you are using i.e. 3.0.5,
3.1beta3, 3.0-master etc. Also be sure to include any JavaScript or PHP errors you receive, for PHP errors please ensure
you include the full stack trace. Also please include your implementation code (where your setting up your grid field)
as well as how you produced the issue. You may also be asked to provide some of the classes to aid in re-producing the
issue. Stick with the issue, remember that you seen the issue not the maintainer of the module so it may take a lot of
questions to arrive at a fix or answer.
### Notes
* When using with GridFieldManyRelationHandler make sure that you add GridFieldSortableRows to your config before for
example `GridFieldManyRelationHandler`:
```php
//Namespace imports should be added to the top of your file
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
$config->addComponent(new GridFieldSortableRows('SortOrder'), 'GridFieldManyRelationHandler');
```
## Contributing
### Translations
Translations of the natural language strings are managed through a third party translation interface, transifex.com.
Newly added strings will be periodically uploaded there for translation, and any new translations will be merged back to
the project source code.
Please use [https://www.transifex.com/projects/p/silverstripe-sortablegridfield](https://www.transifex.com/projects/p/silverstripe-sortablegridfield)
to contribute translations, rather than sending pull requests with YAML files.

View File

@ -1,3 +1 @@
<?php
define('SORTABLE_GRIDFIELD_BASE', basename(dirname(__FILE__)));
?>

View File

@ -1,465 +0,0 @@
<?php
/**
* This component provides a checkbox which when checked enables drag-and-drop re-ordering of elements displayed in a {@link GridField}
*
* @package forms
*/
class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator {
protected $sortColumn;
/**
* @param String $sortColumn Column that should be used to update the sort information
*/
public function __construct($sortColumn) {
$this->sortColumn = $sortColumn;
}
/**
* Returns a map where the keys are fragment names and the values are pieces of HTML to add to these fragments.
* @param GridField $gridField Grid Field Reference
* @return Array Map where the keys are fragment names and the values are pieces of HTML to add to these fragments.
*/
public function getHTMLFragments($gridField) {
$dataList = $gridField->getList();
if(class_exists('UnsavedRelationList') && $dataList instanceof UnsavedRelationList) {
return array();
}
$state = $gridField->State->GridFieldSortableRows;
if(!is_bool($state->sortableToggle)) {
$state->sortableToggle = false;
}
//Ensure user can edit
if(!singleton($gridField->getModelClass())->canEdit()){
return array();
}
//Sort order toggle
$sortOrderToggle = Object::create(
'GridField_FormAction',
$gridField,
'sortablerows-toggle',
_t('GridFieldSortableRows.ALLOW_DRAG_DROP', '_Allow drag and drop re-ordering'),
'saveGridRowSort',
null
)->addExtraClass('sortablerows-toggle');
//Disable Pagenator
$disablePagenator = Object::create(
'GridField_FormAction',
$gridField,
'sortablerows-disablepagenator',
_t('GridFieldSortableRows.DISABLE_PAGINATOR', '_Disable Pagenator'),
'sortableRowsDisablePaginator',
null
)->addExtraClass('sortablerows-disablepagenator');
//Disable Pagenator
$sortToPage = Object::create(
'GridField_FormAction',
$gridField,
'sortablerows-sorttopage',
_t('GridFieldSortableRows.SORT_TO_PAGE', '_Sort To Page'),
'sortToPage',
null
)->addExtraClass('sortablerows-sorttopage');
$data = array('SortableToggle' => $sortOrderToggle,
'PagenatorToggle' => $disablePagenator,
'SortToPage' => $sortToPage,
'Checked' => ($state->sortableToggle == true ? ' checked = "checked"':''));
$forTemplate = new ArrayData($data);
//Inject Requirements
Requirements::css(SORTABLE_GRIDFIELD_BASE . '/css/GridFieldSortableRows.css');
Requirements::javascript(SORTABLE_GRIDFIELD_BASE . '/javascript/GridFieldSortableRows.js');
$args = array('Colspan' => count($gridField->getColumns()), 'ID' => $gridField->ID());
return array('header' => $forTemplate->renderWith('GridFieldSortableRows', $args));
}
/**
* Manipulate the datalist as needed by this grid modifier.
* @param GridField $gridField Grid Field Reference
* @param SS_List $dataList Data List to adjust
* @return DataList Modified Data List
*/
public function getManipulatedData(GridField $gridField, SS_List $dataList) {
//Detect and correct items with a sort column value of 0 (push to bottom)
$this->fixSortColumn($gridField, $dataList);
$headerState = $gridField->State->GridFieldSortableHeader;
$state = $gridField->State->GridFieldSortableRows;
if ((!is_bool($state->sortableToggle) || $state->sortableToggle==false) && $headerState && !empty($headerState->SortColumn)) {
return $dataList;
}
if ($state->sortableToggle == true) {
$gridField->getConfig()->removeComponentsByType('GridFieldFilterHeader');
$gridField->getConfig()->removeComponentsByType('GridFieldSortableHeader');
}
return $dataList->sort($this->sortColumn);
}
/**
* Detects and corrects items with a sort column value of 0, by appending them to the bottom of the list
* @param GridField $gridField Grid Field Reference
* @param SS_List $dataList Data List of items to be checked
*/
protected function fixSortColumn($gridField, SS_List $dataList) {
if(class_exists('UnsavedRelationList') && $dataList instanceof UnsavedRelationList) {
return;
}
$list=clone $dataList;
$list=$list->alterDataQuery(function($query, SS_List $tmplist) {
$query->limit(array());
return $query;
});
$many_many = ($list instanceof ManyManyList);
if (!$many_many) {
$sng=singleton($gridField->getModelClass());
$fieldType=$sng->db($this->sortColumn);
if(!$fieldType || !($fieldType=='Int' || is_subclass_of('Int', $fieldType))) {
if(is_array($fieldType)) {
user_error('Sort column '.$this->sortColumn.' could not be found in '.$gridField->getModelClass().'\'s ancestry', E_USER_ERROR);
}else {
user_error('Sort column '.$this->sortColumn.' must be an Int, column is of type '.$fieldType, E_USER_ERROR);
}
exit;
}
}
$max = $list->Max($this->sortColumn);
$list=$list->where('"'.$this->sortColumn.'"=0');
if($list->Count()>0) {
$owner = $gridField->Form->getRecord();
$sortColumn = $this->sortColumn;
$i = 1;
if ($many_many) {
list($parentClass, $componentClass, $parentField, $componentField, $table) = $owner->many_many($gridField->getName());
$extraFields=$owner->many_many_extraFields($gridField->getName());
if(!$extraFields || !array_key_exists($this->sortColumn, $extraFields) || !($extraFields[$this->sortColumn]=='Int' || is_subclass_of('Int', $extraFields[$this->sortColumn]))) {
user_error('Sort column '.$this->sortColumn.' must be an Int, column is of type '.$fieldType, E_USER_ERROR);
exit;
}
}else {
//Find table containing the sort column
$table=false;
$class=$gridField->getModelClass();
$db = Config::inst()->get($class, "db", CONFIG::UNINHERITED);
if(!empty($db) && array_key_exists($sortColumn, $db)) {
$table=$class;
}else {
$classes=ClassInfo::ancestry($class, true);
foreach($classes as $class) {
$db = Config::inst()->get($class, "db", CONFIG::UNINHERITED);
if(!empty($db) && array_key_exists($sortColumn, $db)) {
$table=$class;
break;
}
}
}
if($table===false) {
user_error('Sort column '.$this->sortColumn.' could not be found in '.$gridField->getModelClass().'\'s ancestry', E_USER_ERROR);
exit;
}
}
//Start transaction if supported
if(DB::getConn()->supportsTransactions()) {
DB::getConn()->transactionStart();
}
foreach($list as $obj) {
if($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn .'" = ' . ($max + $i)
. ' WHERE "' . $componentField . '" = ' . $obj->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
}else {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . ($max + $i)
. ' WHERE "ID" = '. $obj->ID);
}
$i++;
}
//End transaction if supported
if(DB::getConn()->supportsTransactions()) {
DB::getConn()->transactionEnd();
}
}
}
/**
* Return a list of the actions handled by this action provider.
* @param GridField $gridField Grid Field Reference
* @return Array Array with action identifier strings.
*/
public function getActions($gridField) {
return array('saveGridRowSort', 'sortableRowsDisablePaginator', 'sortToPage');
}
/**
* Handle an action on the given grid field.
* @param GridField $gridField Grid Field Reference
* @param String $actionName Action identifier, see {@link getActions()}.
* @param Array $arguments Arguments relevant for this
* @param Array $data All form data
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
$state = $gridField->State->GridFieldSortableRows;
if (!is_bool($state->sortableToggle)) {
$state->sortableToggle = false;
} else if ($state->sortableToggle == true) {
$gridField->getConfig()->removeComponentsByType('GridFieldFilterHeader');
$gridField->getConfig()->removeComponentsByType('GridFieldSortableHeader');
}
if ($actionName == 'savegridrowsort') {
return $this->saveGridRowSort($gridField, $data);
} else if ($actionName == 'sorttopage') {
return $this->sortToPage($gridField, $data);
}
}
/**
* Handles saving of the row sort order
* @param GridField $gridField Grid Field Reference
* @param Array $data Data submitted in the request
*/
protected function saveGridRowSort(GridField $gridField, $data) {
$dataList = $gridField->getList();
if(class_exists('UnsavedRelationList') && $dataList instanceof UnsavedRelationList) {
user_error('Cannot sort an UnsavedRelationList', E_USER_ERROR);
return;
}
if(!singleton($gridField->getModelClass())->canEdit()){
throw new ValidationException(_t('GridFieldSortableRows.EditPermissionsFailure', "No edit permissions"),0);
}
if (empty($data['ItemIDs'])) {
user_error('No items to sort', E_USER_ERROR);
}
$className = $gridField->getModelClass();
$owner = $gridField->Form->getRecord();
$items = clone $gridField->getList();
$many_many = ($items instanceof ManyManyList);
$sortColumn = $this->sortColumn;
$pageOffset = 0;
if ($paginator = $gridField->getConfig()->getComponentsByType('GridFieldPaginator')->First()) {
$pageState = $gridField->State->GridFieldPaginator;
if($pageState->currentPage && is_int($pageState->currentPage) && $pageState->currentPage>1) {
$pageOffset = $paginator->getItemsPerPage() * ($pageState->currentPage - 1);
}
}
if ($many_many) {
list($parentClass, $componentClass, $parentField, $componentField, $table) = $owner->many_many($gridField->getName());
}else {
//Find table containing the sort column
$table=false;
$class=$gridField->getModelClass();
$db = Config::inst()->get($class, "db", CONFIG::UNINHERITED);
if(!empty($db) && array_key_exists($sortColumn, $db)) {
$table=$class;
}else {
$classes=ClassInfo::ancestry($class, true);
foreach($classes as $class) {
$db = Config::inst()->get($class, "db", CONFIG::UNINHERITED);
if(!empty($db) && array_key_exists($sortColumn, $db)) {
$table=$class;
break;
}
}
}
if($table===false) {
user_error('Sort column '.$this->sortColumn.' could not be found in '.$gridField->getModelClass().'\'s ancestry', E_USER_ERROR);
exit;
}
}
//Start transaction if supported
if(DB::getConn()->supportsTransactions()) {
DB::getConn()->transactionStart();
}
$ids = explode(',', $data['ItemIDs']);
for($sort = 0;$sort<count($ids);$sort++) {
$id = intval($ids[$sort]);
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn.'" = ' . (($sort + 1) + $pageOffset)
. ' WHERE "' . $componentField . '" = ' . $id . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . (($sort + 1) + $pageOffset)
. ' WHERE "ID" = '. $id);
}
}
//End transaction if supported
if(DB::getConn()->supportsTransactions()) {
DB::getConn()->transactionEnd();
}
}
/**
* Handles sorting across pages
* @param GridField $gridField Grid Field Reference
* @param Array $data Data submitted in the request
*/
protected function sortToPage(GridField $gridField, $data) {
if (!$paginator = $gridField->getConfig()->getComponentsByType('GridFieldPaginator')->First()) {
user_error('Paginator not detected', E_USER_ERROR);
}
if (empty($data['ItemID'])) {
user_error('No item to sort', E_USER_ERROR);
}
if (empty($data['Target'])) {
user_error('No target page', E_USER_ERROR);
}
$className = $gridField->getModelClass();
$owner = $gridField->Form->getRecord();
$items = clone $gridField->getList();
$many_many = ($items instanceof ManyManyList);
$sortColumn = $this->sortColumn;
$targetItem = $items->byID(intval($data['ItemID']));
if (!$targetItem) {
user_error('Target item not found', E_USER_ERROR);
}
$currentPage = 1;
$pageState = $gridField->State->GridFieldPaginator;
if($pageState->currentPage && $pageState->currentPage>1) {
$currentPage = $pageState->currentPage;
}
if ($many_many) {
list($parentClass, $componentClass, $parentField, $componentField, $table) = $owner->many_many($gridField->getName());
}
if ($data['Target'] == 'previouspage') {
$sortPositions = $items->limit($paginator->getItemsPerPage() + 1, ($paginator->getItemsPerPage() * ($currentPage - 1)) - 1)->column($sortColumn);
} else if ($data['Target'] == 'nextpage') {
$sortPositions = $items->limit($paginator->getItemsPerPage() + 1, $paginator->getItemsPerPage() * ($currentPage - 1))->column($sortColumn);
} else {
user_error('Not implemented: '.$data['Target'], E_USER_ERROR);
}
//Start transaction if supported
if(DB::getConn()->supportsTransactions()) {
DB::getConn()->transactionStart();
}
if($data['Target']=='previouspage') {
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn.'" = ' . $sortPositions[0]
. ' WHERE "' . $componentField . '" = ' . $targetItem->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
$targetItem->$sortColumn = $sortPositions[0];
$targetItem->write();
}
$i = 1;
foreach ($items as $obj) {
if ($obj->ID == $targetItem->ID) {
continue;
}
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn.'" = ' . $sortPositions[$i]
. ' WHERE "' . $componentField . '" = ' . $obj->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
$obj->$sortColumn = $sortPositions[$i];
$obj->write();
}
$i++;
}
} else {
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn.'" = ' . $sortPositions[count($sortPositions) - 1]
. ' WHERE "' . $componentField . '" = ' . $targetItem->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
$targetItem->$sortColumn = $sortPositions[count($sortPositions) - 1];
$targetItem->write();
}
$i = 0;
foreach ($items as $obj) {
if ($obj->ID == $targetItem->ID) {
continue;
}
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn.'" = ' . $sortPositions[$i]
. ' WHERE "' . $componentField . '" = ' . $obj->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
$obj->$sortColumn = $sortPositions[$i];
$obj->write();
}
$i++;
}
}
//End transaction if supported
if(DB::getConn()->supportsTransactions()) {
DB::getConn()->transactionEnd();
}
}
}
?>

View File

@ -1,8 +1,9 @@
{
"name": "undefinedoffset/sortablegridfield",
"description": "Adds drag and drop functionality to SilverStripe 3.0's GridField",
"type": "silverstripe-module",
"keywords": ["silverstripe", "gridfield"],
"description": "Adds drag and drop functionality to Silverstripe's GridField",
"type": "silverstripe-vendormodule",
"keywords": ["silverstripe", "gridfield", "sortable", "sort"],
"license": "BSD-3-Clause",
"authors": [
{
"name": "Ed Chipman",
@ -10,15 +11,33 @@
"role": "Developer"
}
],
"require":
{
"silverstripe/framework": "3.*",
"composer/installers": "*"
"require": {
"silverstripe/framework": "~4.11 | ^5",
"silverstripe/versioned": "^1 | ^2"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "~3.4"
},
"autoload": {
"psr-4": {
"UndefinedOffset\\SortableGridField\\": "src/",
"UndefinedOffset\\SortableGridField\\Tests\\": "tests/"
}
},
"support": {
"issues": "https://github.com/undefinedoffset/SortableGridField/issues"
},
"extra": {
"installer-name": "sortablegridfield"
}
"installer-name": "sortablegridfield",
"expose": [
"css",
"javascript"
],
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"prefer-stable": true,
"minimum-stability": "dev"
}

View File

@ -1,23 +1,134 @@
.cms table.ss-gridfield-table thead tr th.sortablerowsheading {
form table.grid-field__table thead tr th.sortablerowsheading {
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.cms table.ss-gridfield-table thead tr th.sortablerowsheading .gridfield-sortablerows input[type=checkbox] {
form table.grid-field__table thead tr td.sortablerowsheading label {
margin-bottom: 0;
line-height: normal;
}
form table.grid-field__table thead tr td.sortablerowsheading .gridfield-sortablerows input[type=checkbox] {
height: auto;
}
.cms table.ss-gridfield-table thead tr th.sortablerowsheading .gridfield-sortablerows {
form table.grid-field__table thead tr td.sortablerowsheading .gridfield-sortablerows {
line-height: 28px;
}
.cms table.ss-gridfield-table thead tr th.sortablerowsheading .gridfield-sortablerows button {
form table.grid-field__table thead tr td.sortablerowsheading .gridfield-sortablerows button {
display: none;
}
.cms table.ss-gridfield-table.dragSorting tbody tr td {
form .ss-gridfield {
position: relative;
}
form .ss-gridfield .gridfield-sortablerows-movepage .sortablerows-psort-arrow {
background: #5589A7;
display: none;
font-weight: bold;
color: #FFFFFF;
position: absolute;
width: 40px;
opacity: 0.6;
text-align: center;
line-height: normal;
overflow: hidden;
z-index: 2000;
}
form .ss-gridfield .gridfield-sortablerows-movepage .sortablerows-psort-arrow i {
display: block;
position: relative;
font-style: normal;
position: absolute;
top: 50%;
white-space: nowrap;
}
form .ss-gridfield .gridfield-sortablerows-movepage .sortablerows-psort-arrow i:before {
display: block;
content: " ";
position: absolute;
top: -30px;
left: 50%;
height: 0;
width: 0;
border: solid transparent;
border-width: 14px;
border-bottom-color: #FFFFFF;
margin-left: -8px;
}
form .ss-gridfield .gridfield-sortablerows-movepage .sortablerows-psort-arrow:hover {
opacity: 0.8;
text-decoration: none;
}
form .ss-gridfield .gridfield-sortablerows-movepage .sortablerows-prev-page {
left: -20px;
}
form .ss-gridfield .gridfield-sortablerows-movepage .sortablerows-prev-page i {
left: 20px;
-o-transform: rotate(270deg) translateX(-50%);
-ms-transform: rotate(270deg) translateX(-50%);
-moz-transform: rotate(270deg) translateX(-50%);
-webkit-transform: rotate(270deg) translateX(-50%);
transform: rotate(270deg) translateX(-50%);
-o-transform-origin: top left;
-ms-transform-origin: top left;
-moz-transform-origin: top left;
-webkit-transform-origin: top left;
transform-origin: top left;
}
form .ss-gridfield .gridfield-sortablerows-movepage .sortablerows-next-page {
right: -20px;
}
form .ss-gridfield .gridfield-sortablerows-movepage .sortablerows-next-page i {
right: 36px;
-moz-transform: rotate(90deg) translateX(50%);
-o-transform: rotate(90deg) translateX(50%);
-ms-transform: rotate(90deg) translateX(50%);
-webkit-transform: rotate(90deg) translateX(50%);
transform: rotate(90deg) translateX(50%);
-o-transform-origin: bottom right;
-ms-transform-origin: bottom right;
-moz-transform-origin: bottom right;
-webkit-transform-origin: bottom right;
transform-origin: bottom right;
}
form table.grid-field__table.dragSorting tbody tr td {
cursor: move;
}
.cms table.ss-gridfield-table .datagrid-pagination button.sortablerows-droptarget {
background-image: url(./../images/pagination-arrows-drop.gif) !important;
form table.grid-field__table.dragSorting .ui-sortable-helper {
display: table;
}

View File

@ -1,32 +1,58 @@
has_many Example
=================
```php
/*** TestPage.php ***/
class TestPage extends Page {
public static $has_many=array(
'TestObjects'=>'TestObject'
);
public function getCMSFields() {
$fields=parent::getCMSFields();
$conf=GridFieldConfig_RelationEditor::create(10);
$conf->addComponent(new GridFieldSortableRows('SortOrder'));
$fields->addFieldToTab('Root.TestObjects', new GridField('TestObjects', 'TestObjects', $this->TestObjects(), $conf));
return $fields;
}
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
class TestPage extends Page
{
private static $has_many = [
'TestObjects' => 'TestObject',
];
public function getCMSFields()
{
$fields = parent::getCMSFields();
$conf = GridFieldConfig_RelationEditor::create(10);
$conf->addComponent(GridFieldSortableRows::create('SortOrder'));
$fields->addFieldToTab(
'Root.TestObjects',
GridField::create(
'TestObjects',
'TestObjects',
$this->TestObjects(),
$conf
)
);
return $fields;
}
}
/*** TestObject.php ***/
class TestObject extends DataObject {
public static $db=array(
'Title'=>'Text',
'SortOrder'=>'Int'
);
public static $default_sort='SortOrder';
use SilverStripe\ORM\DataObject;
class TestObject extends DataObject
{
private static $db = [
'Title' => 'Text',
'SortOrder' => 'Int',
];
private static $indexes = [
'SortOrder' => true,
];
private static $has_one = [
'Parent' => 'TestPage',
];
private static $default_sort = 'SortOrder';
}
```
```

View File

@ -1,44 +1,55 @@
many_many Example
=================
```php
/*** TestPage.php ***/
class TestPage extends Page {
public static $many_many=array(
'TestObjects'=>'TestObject'
);
public static $many_many_extraFields=array(
'TestObjects'=>array(
'SortOrder'=>'Int'
)
);
public function getCMSFields() {
$fields=parent::getCMSFields();
$conf=GridFieldConfig_RelationEditor::create(10);
$conf->addComponent(new GridFieldSortableRows('SortOrder'));
$fields->addFieldToTab('Root.TestObjects', new GridField('TestObjects', 'TestObjects', $this->TestObjects(), $conf));
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
class TestPage extends Page
{
private static $many_many = [
'TestObjects' => 'TestObject',
];
private static $many_many_extraFields = [
'TestObjects' => [
'SortOrder' => 'Int',
]
];
public function getCMSFields()
{
$fields = parent::getCMSFields();
$conf = GridFieldConfig_RelationEditor::create(10);
$conf->addComponent(GridFieldSortableRows::create('SortOrder'));
$fields->addFieldToTab('Root.TestObjects', GridField::create('TestObjects', 'TestObjects', $this->TestObjects(), $conf));
return $fields;
}
public function TestObjects() {
public function TestObjects()
{
return $this->getManyManyComponents('TestObjects')->sort('SortOrder');
}
}
/*** TestObject.php ***/
class TestObject extends DataObject {
public static $db=array(
'Title'=>'Text'
);
public static $belongs_many_many=array(
'TestPages'=>'TestPage'
);
use SilverStripe\ORM\DataObject;
class TestObject extends DataObject
{
private static $db = [
'Title' => 'Text',
];
private static $belongs_many_many = [
'TestPages' => 'TestPage',
];
}
```
```

54
docs/ModelAdminExample.md Normal file
View File

@ -0,0 +1,54 @@
ModelAdmin implementation Example
=================
```php
/**** MyModelAdmin.php ****/
use SilverStripe\Admin\ModelAdmin;
use SilverStripe\Forms\GridField\GridField;
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
class MyModelAdmin extends ModelAdmin
{
private static $menu_title = 'My Model Admin';
private static $url_segment = 'my-model-admin';
private static $managed_models = [
MATestObject::class,
];
public function getEditForm($id = null, $fields = null)
{
$form = parent::getEditForm($id, $fields);
// This check is simply to ensure you are on the managed model you want adjust accordingly
if ($this->modelClass === MATestObject::class) {
$gridField = $form->Fields()->dataFieldByName($this->sanitiseClassName($this->modelClass));
// This is just a precaution to ensure we got a GridField from dataFieldByName() which you should have
if ($gridField instanceof GridField) {
$gridField->getConfig()->addComponent(GridFieldSortableRows::create('SortOrder'));
}
}
return $form;
}
}
/**** MATestObject.php ****/
use SilverStripe\ORM\DataObject;
class MATestObject extends DataObject
{
private static $db = [
'Title' => 'Varchar',
'SortOrder' => 'Int',
];
private static $indexes = [
'SortOrder' => true,
];
private static $default_sort = 'SortOrder';
}
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,131 +1,206 @@
(function($) {
$.entwine('ss', function($) {
$('.ss-gridfield .gridfield-sortablerows input').entwine({
onmatch: function() {
var self=this;
var refCheckbox=$(this);
var gridField=this.getGridField();
if($(this).is(':checked')) {
gridField.find('table').addClass('dragSorting');
}else {
gridField.find('table').removeClass('dragSorting');
}
gridField.find('tbody').sortable({
opacity: 0.6,
disabled: ($(this).is(':checked')==false),
helper: function(e, ui) {
//Maintains width of the columns
ui.children().each(function() {
$(this).width($(this).width());
});
return ui;
},
update: function(event, ui) {
var dataRows=[];
var gridItems=gridField.getItems();
var button=refCheckbox.parent().find('.sortablerows-toggle');
for(var i=0;i<gridItems.length;i++) {
dataRows[i]=$(gridItems[i]).data('id');
}
self._makeRequest({data: [
{
name: button.attr('name'),
value: button.val()
},
{
name: 'ItemIDs',
value: dataRows
}
]});
}
}).disableSelection();
gridField.find('.datagrid-pagination .ss-gridfield-previouspage, .datagrid-pagination .ss-gridfield-nextpage').each(function() {
$(this).droppable({
disabled: $(this).is(':disabled'),
accept: 'tr.ss-gridfield-item',
activeClass: 'sortablerows-droptarget',
tolerance: 'pointer',
drop: function(event, ui) {
gridField.find('tbody').sortable('cancel');
var button=refCheckbox.parent().find('.sortablerows-sorttopage');
var itemID=$(ui.draggable).data('id');
var target='';
if($(this).hasClass('ss-gridfield-previouspage')) {
target='previouspage';
}else if($(this).hasClass('ss-gridfield-nextpage')) {
target='nextpage';
}
//Move and Reload the grid
gridField.reload({data: [
{
name: button.attr('name'),
value: button.val()
},
{
name: 'ItemID',
value: itemID
},
{
name: 'Target',
value: target
}
]});
event.stopPropagation();
event.stopImmediatePropagation();
}
});
});
},
onchange: function(e) {
var gridField=this.getGridField();
gridField.find('tbody').sortable('option', 'disabled', ($(this).is(':checked')==false));
gridField.setState('GridFieldSortableRows', {sortableToggle: $(this).is(':checked')});
var button=$(this).parent().find('.sortablerows-disablepagenator');
gridField.reload({data: [{name: button.attr('name'), value: button.val()}]});
},
_makeRequest: function(ajaxOpts, callback) {
var gridField=this.getGridField();
var form = gridField.closest('form'),
focusedElName = gridField.find(':input:focus').attr('name'); // Save focused element for restoring after refresh
ajaxOpts.data = ajaxOpts.data.concat(form.find(':input').serializeArray());
// 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);
}
$.ajax($.extend({}, {
headers: {"X-Pjax" : 'CurrentField'},
type: "POST",
url: gridField.data('url'),
dataType: 'html',
success: callback,
error: function(e) {
alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION'));
}
}, ajaxOpts));
}
});
});
})(jQuery);
(function ($) {
$.entwine('ss', function ($) {
$('.ss-gridfield .gridfield-sortablerows input').entwine({
PageSort: false,
onmatch: function () {
var self = this;
var refCheckbox = $(this);
var gridField = this.getGridField();
var form = gridField.closest('form');
var pageArrows = gridField.find('.gridfield-sortablerows-movepage .sortablerows-psort-arrow');
if ($(this).is(':checked')) {
gridField.find('table').addClass('dragSorting');
} else {
gridField.find('table').removeClass('dragSorting');
}
gridField.find('tbody').sortable({
opacity: 0.6,
disabled: ($(this).is(':checked') == false),
start: function (event, ui) {
pageArrows.show();
pageArrows.redraw();
pageArrows.startMoveTracking();
},
stop: function (event, ui) {
pageArrows.stopMoveTracking();
pageArrows.hide();
},
sort: function (event, ui) {
pageArrows.moveTracking(event, ui);
},
update: function (event, ui) {
if (self.getPageSort()) {
self.setPageSort(false);
return;
}
var gridItems = gridField.getItems();
gridItems.removeClass('first last odd even');
gridItems.first().addClass('first');
gridItems.last().addClass('last');
gridItems.filter(':even').addClass('odd');
gridItems.filter(':odd').addClass('even');
var dataRows = [];
var button = refCheckbox.parent().find('.sortablerows-savesort');
for (var i = 0; i < gridItems.length; i++) {
dataRows[i] = $(gridItems[i]).data('id');
}
self._makeRequest({
data: [
{
name: button.attr('name'),
value: button.val()
},
{
name: 'ItemIDs',
value: dataRows
}
]
}, function () {
form.removeClass('loading');
});
}
});
if (refCheckbox.hasClass('gridfield-sortablerows-noselection') || $(this).is(':checked')) {
gridField.find('tbody').disableSelection();
}
},
onchange: function (e) {
var gridField = this.getGridField();
gridField.find('tbody').sortable('option', 'disabled', ($(this).is(':checked') == false));
gridField.setState('GridFieldSortableRows', {sortableToggle: $(this).is(':checked')});
var button = $(this).parent().find('.sortablerows-toggle');
gridField.reload({data: [{name: button.attr('name'), value: button.val()}]});
},
_makeRequest: function (ajaxOpts, callback) {
var gridField = this.getGridField();
var form = gridField.closest('form');
form.addClass('loading');
ajaxOpts.data = ajaxOpts.data.concat(form.find(':input').serializeArray());
// 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);
}
$.ajax($.extend({}, {
headers: {"X-Pjax": 'CurrentField'},
type: "POST",
url: gridField.data('url'),
dataType: 'html',
success: callback,
error: function (e) {
alert(ss.i18n._t('Admin.ERRORINTRANSACTION', 'An error occured while fetching data from the server\n Please try again later.'));
}
}, ajaxOpts));
}
});
$('.ss-gridfield .gridfield-sortablerows-movepage .sortablerows-psort-arrow').entwine({
ArrowIcon: null,
onmatch: function () {
var gridField = this.getGridField();
var sortableCheckbox = gridField.find('.gridfield-sortablerows input');
var self = $(this);
if ($(this).hasClass('sortablerows-prev-page') && (gridField.find('.ss-gridfield-previouspage').length == 0 || gridField.find('.ss-gridfield-previouspage').is(':disabled'))) {
$(this).remove();
return;
} else if ($(this).hasClass('sortablerows-next-page') && (gridField.find('.ss-gridfield-nextpage').length == 0 || gridField.find('.ss-gridfield-nextpage').is(':disabled'))) {
$(this).remove();
return;
}
$(this).droppable({
disabled: $(this).is(':disabled'),
accept: 'tr.ss-gridfield-item',
activeClass: 'sortablerows-droptarget',
tolerance: 'pointer',
drop: function (event, ui) {
self.stopMoveTracking();
sortableCheckbox.setPageSort(true);
var button = gridField.find('.gridfield-sortablerows .sortablerows-sorttopage');
var itemID = $(ui.draggable).data('id');
var target = '';
if ($(this).hasClass('sortablerows-prev-page')) {
target = 'previouspage';
} else if ($(this).hasClass('sortablerows-next-page')) {
target = 'nextpage';
}
//Move and Reload the grid
gridField.reload({
data: [
{
name: button.attr('name'),
value: button.val()
},
{
name: 'ItemID',
value: itemID
},
{
name: 'Target',
value: target
}
]
});
}
});
this.redraw();
},
redraw: function () {
var gridField = this.getGridField();
var tbody = gridField.find('tbody');
var tbodyPos = tbody.position();
$(this).css('top', tbodyPos.top + 'px').height(tbody.height());
},
startMoveTracking: function () {
var self = $(this);
self.setArrowIcon(self.find('i'));
},
stopMoveTracking: function () {
$(this).setArrowIcon(null);
},
moveTracking: function (e, ui) {
var self = $(this);
var arrowIcon = self.getArrowIcon();
if (arrowIcon) {
var selfOffset = self.offset().top;
var arrowIconHeight = arrowIcon.width() + 10;
var railHeight = self.height() - arrowIconHeight;
var helperPos = ui.helper.offset().top;
if (helperPos > selfOffset + 10 && helperPos < selfOffset + railHeight) {
arrowIcon.css('top', ((helperPos - selfOffset) + arrowIconHeight / 2) + 'px');
}
}
}
});
});
})(jQuery);

4
lang/ar.yml Normal file
View File

@ -0,0 +1,4 @@
ar:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'السماح بإعادة الترتيب عن طريق السحب والإسقاط'
EditPermissionsFailure: 'التحرير غير مرخص له'

4
lang/cs.yml Normal file
View File

@ -0,0 +1,4 @@
cs:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Povolit přesuny metodou táhni a pusť'
EditPermissionsFailure: 'Nemáte práva pro úpravu'

6
lang/da.yml Normal file
View File

@ -0,0 +1,6 @@
da:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Tillad træk og slip sortering'
EditPermissionsFailure: 'Manglende tilladelse til at redigere'
NEXT: 'Flyt til næste side'
PREVIOUS: 'Flyt til forrige side'

View File

@ -1,6 +1,6 @@
de:
GridFieldSortableRows:
ALLOW_DRAG_DROP: "Drag & Drop neuordnen"
DISABLE_PAGINATOR: "Paginator deaktivieren"
SORT_TO_PAGE: "Seite zuordnen"
EditPermissionsFailure: "Keine Berechtigung zu Editieren"
ALLOW_DRAG_DROP: 'Drag & Drop neuordnen'
EditPermissionsFailure: 'Keine Berechtigung zu Editieren'
NEXT: 'Zur nächsten Seite wechseln'
PREVIOUS: 'Zur vorherigen Seite wechseln'

View File

@ -1,6 +1,6 @@
en:
GridFieldSortableRows:
ALLOW_DRAG_DROP: "Allow Drag and Drop"
DISABLE_PAGINATOR: "Disable Paginator"
SORT_TO_PAGE: "Sort To Page"
EditPermissionsFailure: "No edit permissions"
ALLOW_DRAG_DROP: 'Allow drag and drop re-ordering'
EditPermissionsFailure: 'No edit permissions'
NEXT: 'Move to Next Page'
PREVIOUS: 'Move to Previous Page'

6
lang/eo.yml Normal file
View File

@ -0,0 +1,6 @@
eo:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Enŝalti reordigadon ŝove kaj demete'
EditPermissionsFailure: 'Mankas enhavopermesoj'
NEXT: 'Movi al sekva paĝo'
PREVIOUS: 'Movi al antaŭa paĝo'

View File

@ -1,6 +1,4 @@
es:
GridFieldSortableRows:
ALLOW_DRAG_DROP: "Permitir arrastrar y soltar"
DISABLE_PAGINATOR: "Deshabilitar paginador"
SORT_TO_PAGE: "Ordenar a la página"
EditPermissionsFailure: "No tienes permisos de edición"
ALLOW_DRAG_DROP: 'Permitir arrastrar y soltar'
EditPermissionsFailure: 'No tienes permisos de edición'

4
lang/fa_IR.yml Normal file
View File

@ -0,0 +1,4 @@
fa_IR:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'اجازه مرتب سازی مجدد با کشیدن و رها کردن'
EditPermissionsFailure: 'بدون مجوز ویرایش'

6
lang/fi.yml Normal file
View File

@ -0,0 +1,6 @@
fi:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Salli raahaus ja pudotus'
EditPermissionsFailure: 'Ei oikeuksia muokata'
NEXT: 'Siirrä seuraavalle sivulle'
PREVIOUS: 'Siirrä edelliselle sivulle'

6
lang/fr.yml Normal file
View File

@ -0,0 +1,6 @@
fr:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Activer le classement par glisser-déposer'
EditPermissionsFailure: 'Vous n''avez pas les permissions pour éditer'
NEXT: 'Aller à la page suivante'
PREVIOUS: 'Aller à la page précédente'

6
lang/hr.yml Normal file
View File

@ -0,0 +1,6 @@
hr:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Omogući raspoređivanje drag and drop metodom'
EditPermissionsFailure: 'Nema dozvola uređivanja'
NEXT: 'Prelazak na sljedeću stranicu'
PREVIOUS: 'Prelazak na prethodnu stranicu'

6
lang/it.yml Normal file
View File

@ -0,0 +1,6 @@
it:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Consenti il trascinamento'
EditPermissionsFailure: 'Non hai i permessi per modificare'
NEXT: 'Vai alla pagina successiva'
PREVIOUS: 'Vai alla pagina precedente'

4
lang/lt.yml Normal file
View File

@ -0,0 +1,4 @@
lt:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Įjungti tempimo ir įmetimo rėžimą rikiavimui'
EditPermissionsFailure: 'Nėra leidimų redagavimui'

4
lang/mi.yml Normal file
View File

@ -0,0 +1,4 @@
mi:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Tukuna te raupapa anō mā te tō me te taka'
EditPermissionsFailure: 'Kāore he whakaaetanga whakatika'

4
lang/nb.yml Normal file
View File

@ -0,0 +1,4 @@
nb:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Tillat dra og slipp-sortering'
EditPermissionsFailure: 'Ingen redigeringstilgang'

4
lang/nb_NO.yml Normal file
View File

@ -0,0 +1,4 @@
nb_NO:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Tillat dra og slipp-sortering'
EditPermissionsFailure: 'Ingen redigeringstilgang'

View File

@ -1,6 +1,6 @@
nl:
GridFieldSortableRows:
ALLOW_DRAG_DROP: "Rangschikken met drag & drop toestaan"
DISABLE_PAGINATOR: "Rangschikken uitzetten"
SORT_TO_PAGE: "Sorteer op Pagina"
EditPermissionsFailure: "Geen toegang"
nl:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Rangschikken met drag & drop toestaan'
EditPermissionsFailure: 'Geen toegang'
NEXT: 'Verplaats naar Volgende Pagina'
PREVIOUS: 'Verplaats naar Vorige Pagina'

6
lang/pl.yml Normal file
View File

@ -0,0 +1,6 @@
pl:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Pozwól na reorganizację przez drag and drop'
EditPermissionsFailure: 'Brak możliwości edycji'
NEXT: 'Przenieś do następnej strony'
PREVIOUS: 'Przenieś do poprzedniej strony'

6
lang/ru.yml Normal file
View File

@ -0,0 +1,6 @@
ru:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Разрешить перетаскивание для сортировки'
EditPermissionsFailure: 'Нет прав на изменение'
NEXT: 'Переместить на следующую страницу'
PREVIOUS: 'Переместить на предыдущую страницу'

6
lang/sk.yml Normal file
View File

@ -0,0 +1,6 @@
sk:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Pracovať v režime Drag & Drop (Ťahaj a pusť)'
EditPermissionsFailure: 'Nemáte oprávnenie upravovať'
NEXT: 'Prejsť na nasledujúcu stránku'
PREVIOUS: 'Prejsť na predchádzajúcu stránku'

6
lang/sl.yml Normal file
View File

@ -0,0 +1,6 @@
sl:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Omogoči razvrščanje s "povleci in spusti" '
EditPermissionsFailure: 'Nimate pravic za urejanje'
NEXT: 'Prestavi na naslednjo stran'
PREVIOUS: 'Prestavi na prejšnjo stran'

6
lang/sv.yml Normal file
View File

@ -0,0 +1,6 @@
sv:
GridFieldSortableRows:
ALLOW_DRAG_DROP: 'Aktivera dra och släpp sortering'
EditPermissionsFailure: 'Rättighet för att redigera saknas'
NEXT: 'Gå till nästa sida'
PREVIOUS: 'Gå till föregående sida'

4
lang/zh.yml Normal file
View File

@ -0,0 +1,4 @@
zh:
GridFieldSortableRows:
ALLOW_DRAG_DROP: '允许拖放重新排序'
EditPermissionsFailure: '没有编辑权限'

49
phpcs.xml.dist Normal file
View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="SilverStripe">
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
<!-- base rules are PSR-2 -->
<rule ref="PSR2" >
<!-- Current exclusions -->
<exclude name="PSR1.Methods.CamelCapsMethodName" />
<exclude name="PSR1.Files.SideEffects.FoundWithSymbols" />
<exclude name="PSR2.Classes.PropertyDeclaration" />
<exclude name="PSR2.Methods.MethodDeclaration.Underscore" />
<exclude name="Squiz.Scope.MethodScope" />
<exclude name="Squiz.Classes.ValidClassName.NotCamelCaps" />
<exclude name="Generic.Files.LineLength.TooLong" />
<exclude name="PEAR.Functions.ValidDefaultValue.NotAtEnd" />
<exclude name="PSR2.ControlStructures.ElseIfDeclaration" />
</rule>
<rule ref="Squiz.Strings.ConcatenationSpacing">
<properties>
<property name="spacing" value="1" />
<property name="ignoreNewlines" value="true"/>
</properties>
</rule>
<rule ref="PEAR.ControlStructures.MultiLineCondition" />
<rule ref="Generic.Formatting.SpaceAfterCast" />
<rule ref="Generic.Arrays.DisallowLongArraySyntax.Found">
<type>warning</type>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.NoComma" />
<rule ref="Squiz.WhiteSpace.OperatorSpacing">
<properties>
<property name="ignoreNewlines" value="true" />
</properties>
</rule>
<rule ref="PSR12.Functions.ReturnTypeDeclaration" />
<!-- include php files only -->
<arg name="extensions" value="php,lib,inc,php5"/>
<file>./src</file>
<file>./tests</file>
<file>./_config.php</file>
</ruleset>

27
phpunit.xml.dist Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<testsuites>
<testsuite name="Default">
<directory suffix=".php">tests/PHPUnit</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>sanitychecks</group>
</exclude>
</groups>
<coverage includeUncoveredFiles="true">
<include>
<directory suffix=".php">src/</directory>
</include>
<exclude>
<directory suffix=".php">tests/</directory>
</exclude>
</coverage>
<php>
<get name="flush" value="1"/>
</php>
</phpunit>

View File

@ -0,0 +1,808 @@
<?php
namespace UndefinedOffset\SortableGridField\Forms;
use SilverStripe\Admin\ModelAdmin;
use SilverStripe\Control\Controller;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\GridField\AbstractGridFieldComponent;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridField_ActionProvider;
use SilverStripe\Forms\GridField\GridField_DataManipulator;
use SilverStripe\Forms\GridField\GridField_FormAction;
use SilverStripe\Forms\GridField\GridField_HTMLProvider;
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
use SilverStripe\Forms\GridField\GridFieldPaginator;
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectSchema;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\ManyManyList;
use SilverStripe\ORM\ManyManyThroughList;
use SilverStripe\ORM\RelationList;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\UnsavedRelationList;
use SilverStripe\ORM\ValidationException;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements;
/**
* This component provides a checkbox which when checked enables drag-and-drop re-ordering of elements displayed in a {@link GridField}
*/
class GridFieldSortableRows extends AbstractGridFieldComponent implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator
{
/** @var string */
protected $sortColumn;
/** @var bool */
protected $disable_selection = true;
/** @var bool */
protected $append_to_top = false;
/** @var null|string */
protected $update_versioned_stage = null;
/** @var null|string */
protected $custom_relation_name = null;
/**
* @param string $sortColumn Column that should be used to update the sort information
* @param bool $disableSelection Disable selection on the GridField when dragging
* @param string $updateVersionStage Name of the versioned stage to update this disabled by default unless this is set
* @param string $customRelationName Name of the relationship to use, if left null the name is determined from the GridField's name
*/
public function __construct($sortColumn, $disableSelection = true, $updateVersionStage = null, $customRelationName = null)
{
$this->sortColumn = $sortColumn;
$this->disable_selection = $disableSelection;
$this->update_versioned_stage = $updateVersionStage;
$this->custom_relation_name = $customRelationName;
}
/**
* Returns a map where the keys are fragment names and the values are pieces of HTML to add to these fragments.
* @param GridField $gridField Grid Field Reference
* @return array Map where the keys are fragment names and the values are pieces of HTML to add to these fragments.
*/
public function getHTMLFragments($gridField)
{
$dataList = $gridField->getList();
if (class_exists('UnsavedRelationList') && $dataList instanceof UnsavedRelationList) {
return [];
}
$state = $gridField->State->GridFieldSortableRows;
if (!is_bool($state->sortableToggle)) {
$state->sortableToggle = false;
}
//Ensure user can edit
if (!singleton($gridField->getModelClass())->canEdit()) {
return [];
}
//Sort order toggle
$sortOrderToggle = GridField_FormAction::create(
$gridField,
'sortablerows-toggle',
'sorttoggle',
'sortableRowsToggle',
null
)->addExtraClass('sortablerows-toggle');
$sortOrderSave = GridField_FormAction::create(
$gridField,
'sortablerows-savesort',
'savesort',
'saveGridRowSort',
null
)->addExtraClass('sortablerows-savesort');
//Sort to Page Action
$sortToPage = GridField_FormAction::create(
$gridField,
'sortablerows-sorttopage',
'sorttopage',
'sortToPage',
null
)->addExtraClass('sortablerows-sorttopage');
$data = [
'SortableToggle' => $sortOrderToggle,
'SortOrderSave' => $sortOrderSave,
'SortToPage' => $sortToPage,
'Checked' => ($state->sortableToggle == true ? ' checked = "checked"' : ''),
'List' => $dataList,
];
$forTemplate = new ArrayData($data);
Requirements::css('undefinedoffset/sortablegridfield:css/GridFieldSortableRows.css');
Requirements::javascript('undefinedoffset/sortablegridfield:javascript/GridFieldSortableRows.js');
$args = ['Colspan' => count($gridField->getColumns()), 'ID' => $gridField->ID(), 'DisableSelection' => $this->disable_selection];
$fragments = ['header' => $forTemplate->renderWith('SortableGridField\Forms\Includes\GridFieldSortableRows', $args)];
if ($gridField->getConfig()->getComponentByType(GridFieldPaginator::class)) {
$fragments['after'] = $forTemplate->renderWith('SortableGridField\Forms\Includes\GridFieldSortableRows_paginator');
}
return $fragments;
}
/**
* Manipulate the datalist as needed by this grid modifier.
* @param GridField $gridField Grid Field Reference
* @param SS_List|\SilverStripe\ORM\DataList $dataList Data List to adjust
* @return \SilverStripe\ORM\DataList Modified Data List
*/
public function getManipulatedData(GridField $gridField, SS_List $dataList)
{
//Detect and correct items with a sort column value of 0 (push to bottom)
$this->fixSortColumn($gridField, $dataList);
$headerState = $gridField->State->GridFieldSortableHeader;
$state = $gridField->State->GridFieldSortableRows;
if ((!is_bool($state->sortableToggle) || $state->sortableToggle === false) && $headerState && is_string($headerState->SortColumn) && is_string($headerState->SortDirection)) {
return $dataList->sort($headerState->SortColumn, $headerState->SortDirection);
}
if ($state->sortableToggle === true) {
$gridField->getConfig()->removeComponentsByType(GridFieldFilterHeader::class);
$gridField->getConfig()->removeComponentsByType(GridFieldSortableHeader::class);
}
return $dataList->sort($this->sortColumn);
}
/**
* Sets if new records should be appended to the top or the bottom of the list
* @param bool $value Boolean true to append to the top false to append to the bottom
* @return GridFieldSortableRows Returns the current instance
*/
public function setAppendToTop($value)
{
$this->append_to_top = $value;
return $this;
}
/**
* @param bool $value Boolean true to disable selection of table contents false to enable selection
* @return GridFieldSortableRows Returns the current instance
*/
public function setDisableSelection($value)
{
$this->disable_selection = $value;
return $this;
}
/**
* Sets the suffix of the versioned stage that should be updated along side the default stage
* @param string $value Versioned Stage to update this is disabled by default unless this is set
* @return GridFieldSortableRows Returns the current instance
*/
public function setUpdateVersionedStage($value)
{
$this->update_versioned_stage = $value;
return $this;
}
/**
* Sets the name of the relationship to use, by default the name is determined from the GridField's name
* @param string $value Name of the relationship to use, by default the name is determined from the GridField's name
* @return GridFieldSortableRows Returns the current instance
*/
public function setCustomRelationName($value)
{
$this->custom_relation_name = $value;
return $this;
}
/**
* Detects and corrects items with a sort column value of 0, by appending them to the bottom of the list
* @param GridField $gridField Grid Field Reference
* @param SS_List|\SilverStripe\ORM\DataList $dataList Data List of items to be checked
*/
protected function fixSortColumn($gridField, SS_List $dataList)
{
if ($dataList instanceof UnsavedRelationList) {
return;
}
/** @var SS_List|\SilverStripe\ORM\DataList $list */
$list = clone $dataList;
$list = $list->alterDataQuery(function ($query, SS_List $tmplist) {
/** @var \SilverStripe\ORM\DataQuery $query */
$query->limit(null);
return $query;
});
$many_many = ($list instanceof ManyManyList || $list instanceof ManyManyThroughList);
if (!$many_many) {
$sng = singleton($gridField->getModelClass());
$fieldType = $sng->config()->db[$this->sortColumn];
if (!$fieldType || !($fieldType == 'Int' || $fieldType == 'SilverStripe\\ORM\\FieldType\\DBInt' || is_subclass_of($fieldType, 'SilverStripe\\ORM\\FieldType\\DBInt'))) {
if (is_array($fieldType)) {
user_error('Sort column ' . $this->sortColumn . ' could not be found in ' . $gridField->getModelClass() . '\'s ancestry', E_USER_ERROR);
} else {
user_error('Sort column ' . $this->sortColumn . ' must be an instance of SilverStripe\\ORM\\FieldType\\DBInt, column is of type ' . $fieldType, E_USER_ERROR);
}
exit;
}
}
$max = $list->Max($this->sortColumn);
$list = $list->filter($this->sortColumn, 0)->sort("Created,ID");
if ($list->Count() > 0) {
$owner = $gridField->getForm()->getRecord();
$sortColumn = $this->sortColumn;
$i = 1;
if ($many_many) {
$schema = Injector::inst()->get(DataObjectSchema::class);
$componentDetails = $schema->manyManyComponent(get_class($owner), (!empty($this->custom_relation_name) ? $this->custom_relation_name : $gridField->getName()));
if (empty($componentDetails)) {
user_error('Could not find the relationship "' . (!empty($this->custom_relation_name) ? $this->custom_relation_name : $gridField->getName()) . '" on "' . get_class($owner) . '"', E_USER_ERROR);
}
$parentField = $componentDetails['parentField'];
$componentField = $componentDetails['childField'];
$table = $componentDetails['join'];
//For ManyManyThroughLists get the right join table
if ($list instanceof ManyManyThroughList && class_exists($table)) {
$table = $schema->tableName($table);
}
$extraFields = $list->getExtraFields();
if (!$extraFields || !array_key_exists($this->sortColumn, $extraFields) || !($extraFields[$this->sortColumn] == 'Int' || $extraFields[$this->sortColumn] == 'SilverStripe\\ORM\\FieldType\\DBInt' || is_subclass_of('SilverStripe\\ORM\\FieldType\\DBInt', $extraFields[$this->sortColumn]))) {
user_error('Sort column ' . $this->sortColumn . ' must be an SilverStripe\\ORM\\FieldType\\DBInt, column is of type ' . $extraFields[$this->sortColumn], E_USER_ERROR);
exit;
}
} else {
//Find table containing the sort column
$table = false;
$class = $gridField->getModelClass();
$db = Config::inst()->get($class, "db", CONFIG::UNINHERITED);
if (!empty($db) && array_key_exists($sortColumn, $db)) {
$table = DataObject::getSchema()->tableName($class);
} else {
$classes = ClassInfo::ancestry($class, true);
foreach ($classes as $class) {
$db = Config::inst()->get($class, "db", CONFIG::UNINHERITED);
if (!empty($db) && array_key_exists($sortColumn, $db)) {
$table = DataObject::getSchema()->tableName($class);
break;
}
}
}
if ($table === false) {
user_error('Sort column ' . $this->sortColumn . ' could not be found in ' . $gridField->getModelClass() . '\'s ancestry', E_USER_ERROR);
exit;
}
$baseDataClass = DataObject::getSchema()->baseDataClass($gridField->getModelClass());
$baseDataTable = DataObject::getSchema()->tableName($baseDataClass);
}
//Start transaction if supported
if (DB::get_conn()->supportsTransactions()) {
DB::get_conn()->transactionStart();
}
$idCondition = null;
if ($this->append_to_top && !($list instanceof RelationList)) {
$idCondition = '"ID" IN(\'' . implode("','", $dataList->getIDList()) . '\')';
}
if ($this->append_to_top) {
$topIncremented = [];
}
$modelClass = $gridField->getModelClass();
$hasVersioned = $this->hasVersionedExtension($modelClass);
foreach ($list as $obj) {
if ($many_many) {
if ($this->append_to_top) {
//Upgrade all the records (including the last inserted from 0 to 1)
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = "' . $sortColumn . '"+1'
. ' WHERE "' . $parentField . '" = ' . $owner->ID . (!empty($topIncremented) ? ' AND "' . $componentField . '" NOT IN(\'' . implode('\',\'', $topIncremented) . '\')' : ''));
$topIncremented[] = $obj->ID;
} else {
//Append the last record to the bottom
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . ($max + $i)
. ' WHERE "' . $componentField . '" = ' . $obj->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
}
} else if ($this->append_to_top) {
if ($hasVersioned) {
// For versioned objects, modify them with the ORM so that the *_versions table is updated
$itemsToUpdate = $modelClass::get()->where(($list instanceof RelationList ? '"' . $list->foreignKey . '" = ' . $owner->ID : $idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\'' . implode('\',\'', $topIncremented) . '\')' : ''));
if ($itemsToUpdate->exists()) {
foreach ($itemsToUpdate as $item) {
$item->$sortColumn = $item->$sortColumn + 1;
$item->write();
}
}
} else {
//Upgrade all the records (including the last inserted from 0 to 1)
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = "' . $sortColumn . '"+1'
. ' WHERE ' . ($list instanceof RelationList ? '"' . $list->foreignKey . '" = ' . $owner->ID : $idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\'' . implode('\',\'', $topIncremented) . '\')' : ''));
}
if ($this->update_versioned_stage && $this->hasVersionedExtension($gridField->getModelClass())) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = "' . $sortColumn . '"+1'
. ' WHERE ' . ($list instanceof RelationList ? '"' . $list->foreignKey . '" = ' . $owner->ID : $idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\'' . implode('\',\'', $topIncremented) . '\')' : ''));
}
$topIncremented[] = $obj->ID;
} else {
if ($hasVersioned) {
// For versioned objects, modify them with the ORM so that the *_versions table is updated
$obj->$sortColumn = ($max + $i);
$obj->write();
} else {
//Append the last record to the bottom
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . ($max + $i)
. ' WHERE "ID" = ' . $obj->ID);
//LastEdited
DB::query('UPDATE "' . $baseDataTable
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" = ' . $obj->ID);
}
if ($this->update_versioned_stage && $this->hasVersionedExtension($gridField->getModelClass())) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = ' . ($max + $i)
. ' WHERE "ID" = ' . $obj->ID);
if ($this->hasVersionedExtension($baseDataClass)) {
DB::query('UPDATE "' . $baseDataTable . '_' . $this->update_versioned_stage
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" = ' . $obj->ID);
}
}
}
$i++;
}
//Update LastEdited for affected records when using append to top on a many_many relationship
if (!$many_many && $this->append_to_top && count($topIncremented) > 0) {
DB::query('UPDATE "' . $baseDataTable
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" IN(\'' . implode('\',\'', $topIncremented) . '\')');
if ($this->update_versioned_stage && $this->hasVersionedExtension($gridField->getModelClass()) && $this->hasVersionedExtension($baseDataClass)) {
DB::query('UPDATE "' . $baseDataTable . '_' . $this->update_versioned_stage
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" IN(\'' . implode('\',\'', $topIncremented) . '\')');
}
}
//End transaction if supported
if (DB::get_conn()->supportsTransactions()) {
DB::get_conn()->transactionEnd();
}
}
}
/**
* Return a list of the actions handled by this action provider.
* @param GridField $gridField Grid Field Reference
* @return array array with action identifier strings.
*/
public function getActions($gridField)
{
return ['saveGridRowSort', 'sortableRowsToggle', 'sortToPage'];
}
/**
* Handle an action on the given grid field.
* @param GridField $gridField Grid Field Reference
* @param String $actionName Action identifier, see {@link getActions()}.
* @param array $arguments Arguments relevant for this
* @param array $data All form data
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
{
$state = $gridField->State->GridFieldSortableRows;
if (!is_bool($state->sortableToggle)) {
$state->sortableToggle = false;
} else if ($state->sortableToggle == true) {
$gridField->getConfig()->removeComponentsByType(GridFieldFilterHeader::class);
$gridField->getConfig()->removeComponentsByType(GridFieldSortableHeader::class);
}
if ($actionName == 'savegridrowsort') {
return $this->saveGridRowSort($gridField, $data);
} else if ($actionName == 'sorttopage') {
return $this->sortToPage($gridField, $data);
}
}
/**
* Handles saving of the row sort order
* @param GridField $gridField Grid Field Reference
* @param array $data Data submitted in the request
* @throws ValidationException If user has no edit permissions
*/
protected function saveGridRowSort(GridField $gridField, $data)
{
$dataList = $gridField->getList();
if ($dataList instanceof UnsavedRelationList) {
user_error('Cannot sort an UnsavedRelationList', E_USER_ERROR);
return;
}
if (!singleton($gridField->getModelClass())->canEdit()) {
throw new ValidationException(_t('GridFieldSortableRows.EditPermissionsFailure', "No edit permissions"), 0);
}
if (empty($data['ItemIDs'])) {
user_error('No items to sort', E_USER_ERROR);
}
$className = $gridField->getModelClass();
$owner = $gridField->Form->getRecord();
$items = clone $gridField->getList();
$many_many = ($items instanceof ManyManyList || $items instanceof ManyManyThroughList);
$sortColumn = $this->sortColumn;
$pageOffset = 0;
if ($paginator = $gridField->getConfig()->getComponentsByType(GridFieldPaginator::class)->First()) {
$pageState = $gridField->State->GridFieldPaginator;
if ($pageState->currentPage && is_int($pageState->currentPage) && $pageState->currentPage > 1) {
$pageOffset = $paginator->getItemsPerPage() * ($pageState->currentPage - 1);
}
}
if ($many_many) {
$schema = Injector::inst()->get(DataObjectSchema::class);
$componentDetails = $schema->manyManyComponent(get_class($owner), (!empty($this->custom_relation_name) ? $this->custom_relation_name : $gridField->getName()));
if (empty($componentDetails)) {
user_error('Could not find the relationship "' . (!empty($this->custom_relation_name) ? $this->custom_relation_name : $gridField->getName()) . '" on "' . get_class($owner) . '"', E_USER_ERROR);
}
$parentField = $componentDetails['parentField'];
$componentField = $componentDetails['childField'];
$table = $componentDetails['join'];
//For ManyManyThroughLists get the right join table
if ($items instanceof ManyManyThroughList && class_exists($table)) {
$table = $schema->tableName($table);
}
} else {
//Find table containing the sort column
$table = false;
$class = $gridField->getModelClass();
$db = Config::inst()->get($class, "db", CONFIG::UNINHERITED);
if (!empty($db) && array_key_exists($sortColumn, $db)) {
$table = DataObject::getSchema()->tableName($class);
} else {
$classes = ClassInfo::ancestry($class, true);
foreach ($classes as $class) {
$db = Config::inst()->get($class, "db", CONFIG::UNINHERITED);
if (!empty($db) && array_key_exists($sortColumn, $db)) {
$table = DataObject::getSchema()->tableName($class);
break;
}
}
}
if ($table === false) {
user_error('Sort column ' . $this->sortColumn . ' could not be found in ' . $gridField->getModelClass() . '\'s ancestry', E_USER_ERROR);
exit;
}
$baseDataClass = DataObject::getSchema()->baseDataClass($gridField->getModelClass());
$baseDataTable = DataObject::getSchema()->tableName($baseDataClass);
}
//Event to notify the Controller or owner DataObject before list sort
if ($owner && $owner instanceof DataObject && method_exists($owner, 'onBeforeGridFieldRowSort')) {
$owner->onBeforeGridFieldRowSort(clone $items);
} else if (Controller::has_curr() && Controller::curr() instanceof ModelAdmin && method_exists(Controller::curr(), 'onBeforeGridFieldRowSort')) {
Controller::curr()->onBeforeGridFieldRowSort(clone $items);
}
//Start transaction if supported
if (DB::get_conn()->supportsTransactions()) {
DB::get_conn()->transactionStart();
}
//Perform sorting
$ids = explode(',', $data['ItemIDs']);
$modelClass = $gridField->getModelClass();
$hasVersioned = $this->hasVersionedExtension($modelClass);
for ($sort = 0; $sort < count($ids); $sort++) {
$id = intval($ids[$sort]);
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . (($sort + 1) + $pageOffset)
. ' WHERE "' . $componentField . '" = ' . $id . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
if ($hasVersioned) {
// For versioned objects, modify them with the ORM so that the *_versions table is updated
$obj = $modelClass::get()->byID(intval($id));
if (!empty($obj) && $obj !== false && $obj->exists()) {
$obj->$sortColumn = (($sort + 1) + $pageOffset);
$obj->write();
}
} else {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . (($sort + 1) + $pageOffset)
. ' WHERE "ID" = ' . $id);
DB::query('UPDATE "' . $baseDataTable
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" = ' . $id);
}
if ($this->update_versioned_stage && $hasVersioned) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = ' . (($sort + 1) + $pageOffset)
. ' WHERE "ID" = ' . $id);
if ($this->hasVersionedExtension($baseDataClass)) {
DB::query('UPDATE "' . $baseDataTable . '_' . $this->update_versioned_stage
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" = ' . $id);
}
}
}
}
//End transaction if supported
if (DB::get_conn()->supportsTransactions()) {
DB::get_conn()->transactionEnd();
}
//Event to notify the Controller or owner DataObject after list sort
if ($owner && $owner instanceof DataObject && method_exists($owner, 'onAfterGridFieldRowSort')) {
$owner->onAfterGridFieldRowSort(clone $items);
} else if (Controller::has_curr() && Controller::curr() instanceof ModelAdmin && method_exists(Controller::curr(), 'onAfterGridFieldRowSort')) {
Controller::curr()->onAfterGridFieldRowSort(clone $items);
}
}
/**
* Handles sorting across pages
* @param GridField $gridField Grid Field Reference
* @param array $data Data submitted in the request
*/
protected function sortToPage(GridField $gridField, $data)
{
if (!$paginator = $gridField->getConfig()->getComponentsByType(GridFieldPaginator::class)->First()) {
user_error('Paginator not detected', E_USER_ERROR);
}
if (empty($data['ItemID'])) {
user_error('No item to sort', E_USER_ERROR);
}
if (empty($data['Target'])) {
user_error('No target page', E_USER_ERROR);
}
/** @var \SilverStripe\Core\Extensible $className */
$className = $gridField->getModelClass();
$owner = $gridField->Form->getRecord();
/** @var DataList $items */
$items = clone $gridField->getList();
$many_many = ($items instanceof ManyManyList || $items instanceof ManyManyThroughList);
$sortColumn = $this->sortColumn;
$targetItem = $items->byID(intval($data['ItemID']));
if (!$targetItem) {
user_error('Target item not found', E_USER_ERROR);
}
$currentPage = 1;
$pageState = $gridField->State->GridFieldPaginator;
if ($pageState->currentPage && $pageState->currentPage > 1) {
$currentPage = $pageState->currentPage;
}
if ($many_many) {
$schema = Injector::inst()->get(DataObjectSchema::class);
$componentDetails = $schema->manyManyComponent(get_class($owner), (!empty($this->custom_relation_name) ? $this->custom_relation_name : $gridField->getName()));
$parentField = $componentDetails['parentField'];
$componentField = $componentDetails['childField'];
$table = $componentDetails['join'];
//For ManyManyThroughLists get the right join table
if ($items instanceof ManyManyThroughList && class_exists($table)) {
$table = $schema->tableName($table);
}
}
if ($data['Target'] == 'previouspage') {
$items = $items->limit($paginator->getItemsPerPage() + 1, ($paginator->getItemsPerPage() * ($currentPage - 1)) - 1);
} else if ($data['Target'] == 'nextpage') {
$items = $items->limit($paginator->getItemsPerPage() + 1, $paginator->getItemsPerPage() * ($currentPage - 1));
} else {
user_error('Not implemented: ' . $data['Target'], E_USER_ERROR);
}
$sortPositions = $items->column($sortColumn);
//Event to notify the Controller or owner DataObject before list sort
if ($owner && $owner instanceof DataObject && method_exists($owner, 'onBeforeGridFieldPageSort')) {
$owner->onBeforeGridFieldPageSort(clone $items);
} else if (Controller::has_curr() && Controller::curr() instanceof ModelAdmin && method_exists(Controller::curr(), 'onBeforeGridFieldPageSort')) {
Controller::curr()->onBeforeGridFieldPageSort(clone $items);
}
//Find the sort column
if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) {
$table = false;
$classes = ClassInfo::ancestry($className, true);
foreach ($classes as $class) {
$db = Config::inst()->get($class, "db", CONFIG::UNINHERITED);
if (!empty($db) && array_key_exists($sortColumn, $db)) {
$table = DataObject::getSchema()->tableName($class);
break;
}
}
if ($table === false) {
user_error('Sort column ' . $this->sortColumn . ' could not be found in ' . $gridField->getModelClass() . '\'s ancestry', E_USER_ERROR);
exit;
}
}
//Start transaction if supported
if (DB::get_conn()->supportsTransactions()) {
DB::get_conn()->transactionStart();
}
if ($data['Target'] == 'previouspage') {
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . $sortPositions[0]
. ' WHERE "' . $componentField . '" = ' . $targetItem->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
$targetItem->$sortColumn = $sortPositions[0];
$targetItem->write();
if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = ' . $sortPositions[0]
. ' WHERE "ID" = ' . $targetItem->ID);
}
}
$i = 1;
foreach ($items as $obj) {
if ($obj->ID == $targetItem->ID || $i >= count($sortPositions)) {
continue;
}
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . $sortPositions[$i]
. ' WHERE "' . $componentField . '" = ' . $obj->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
$obj->$sortColumn = $sortPositions[$i];
$obj->write();
if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = ' . $sortPositions[$i]
. ' WHERE "ID" = ' . $obj->ID);
}
}
$i++;
}
} else {
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . $sortPositions[count($sortPositions) - 1]
. ' WHERE "' . $componentField . '" = ' . $targetItem->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
$targetItem->$sortColumn = $sortPositions[count($sortPositions) - 1];
$targetItem->write();
if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = ' . $sortPositions[count($sortPositions) - 1]
. ' WHERE "ID" = ' . $targetItem->ID);
}
}
$i = 0;
foreach ($items as $obj) {
if ($obj->ID == $targetItem->ID) {
continue;
}
if ($many_many) {
DB::query('UPDATE "' . $table
. '" SET "' . $sortColumn . '" = ' . $sortPositions[$i]
. ' WHERE "' . $componentField . '" = ' . $obj->ID . ' AND "' . $parentField . '" = ' . $owner->ID);
} else {
$obj->$sortColumn = $sortPositions[$i];
$obj->write();
if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = ' . $sortPositions[$i]
. ' WHERE "ID" = ' . $obj->ID);
}
}
$i++;
}
}
//End transaction if supported
if (DB::get_conn()->supportsTransactions()) {
DB::get_conn()->transactionEnd();
}
//Event to notify the Controller or owner DataObject after list sort
if ($owner && $owner instanceof DataObject && method_exists($owner, 'onAfterGridFieldPageSort')) {
$owner->onAfterGridFieldPageSort(clone $items);
} else if (Controller::has_curr() && Controller::curr() instanceof ModelAdmin && method_exists(Controller::curr(), 'onAfterGridFieldPageSort')) {
Controller::curr()->onAfterGridFieldPageSort(clone $items);
}
}
/**
* Check to see if the given class name has the Versioned extension
* @param \SilverStripe\Core\Extensible|string $className
* @return bool
*/
public function hasVersionedExtension($className)
{
return $className::has_extension(Versioned::class);
}
/**
* Checks to see if $table_name is declared on the DataObject, if not returns string as given
* @param $className
* @return string
* @deprecated Use DataObject::getSchema()->tableName() instead
*/
public function mapTableNameAndReturn($className)
{
return DataObject::getSchema()->tableName($className);
}
}

View File

@ -1,10 +0,0 @@
<tr>
<th class="extra sortablerowsheading" colspan="$Colspan">
<div class="gridfield-sortablerows">
<input type="checkbox" id="{$ID}_AllowDragDropCheck" value="1" class="no-change-track"$Checked/> <label for="{$ID}_AllowDragDropCheck"><%t GridFieldSortableRows.ALLOW_DRAG_DROP "_Allow drag and drop re-ordering" %></label>
$SortableToggle
$PagenatorToggle
$SortToPage
</div>
</th>
</tr>

View File

@ -0,0 +1,11 @@
<tr>
<td class="sortablerowsheading" colspan="$Colspan">
<div class="gridfield-sortablerows">
<input type="checkbox" id="{$ID}_AllowDragDropCheck" value="1" autocomplete="off" class="no-change-track<% if $DisableSelection %> gridfield-sortablerows-noselection<% end_if %>"$Checked/>
<label for="{$ID}_AllowDragDropCheck"><%t GridFieldSortableRows.ALLOW_DRAG_DROP "Allow drag and drop re-ordering" %></label>
$SortableToggle
$SortOrderSave
$SortToPage
</div>
</td>
</tr>

View File

@ -0,0 +1,7 @@
<% if $Checked %>
<div class="gridfield-sortablerows-movepage" colspan="$Colspan">
<a href="" class="sortablerows-psort-arrow sortablerows-prev-page"><i><%t GridFieldSortableRows.PREVIOUS 'Move to Previous Page' %></i></a>
<a href="" class="sortablerows-psort-arrow sortablerows-next-page"><i><%t GridFieldSortableRows.NEXT 'Move to Next Page' %></i></a>
</div>
<% end_if %>

View File

@ -0,0 +1,112 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\PHPUnit\Bootstrap;
use PHPUnit\Framework\TestFailure;
use PHPUnit\TextUI\DefaultResultPrinter;
class GitHubActionsAnnotatorPrinter extends DefaultResultPrinter
{
protected $currentType = null;
/**
* Handles printing of the defects
* @param array $defects Array of Test Failures
* @param string $type Type of the failure
*/
protected function printDefects(array $defects, string $type): void
{
$this->currentType = $type;
parent::printDefects($defects, $type);
}
/**
* Handles printing of a single defect
* @param TestFailure $defect Test Failure Object
* @param int $count Current position
*/
protected function printDefect(TestFailure $defect, int $count): void
{
parent::printDefect($defect, $count);
$this->printGitHubAnnotation($defect);
}
/**
* Prints a GitHub Annotation Command
* @param TestFailure $defect Defect to print
*/
protected function printGitHubAnnotation(TestFailure $defect)
{
$e = $defect->thrownException();
$errorLines = array_filter(
explode("\n", (string) $e),
function ($l) {
return $l;
}
);
$error = end($errorLines);
$lineIndex = strrpos($error, ":");
$path = substr($error, 0, $lineIndex);
$line = substr($error, $lineIndex + 1);
if (!$path) {
list($path, $line) = $this->getReflectionFromTest(
$defect->getTestName()
);
}
$message = explode("\n", trim($defect->getTestName() . "\n\n" . (string) $e));
$message = implode('%0A', $message);
$type = $this->getCurrentType();
$file = "file={$this->relativePath($path)}";
$line = "line={$line}";
$this->write("::{$type} $file,$line::{$message}\n");
}
/**
* Gets the current type of the defect
* @return string
*/
protected function getCurrentType()
{
if (in_array($this->currentType, ['error', 'failure'])) {
return 'error';
}
return 'warning';
}
/**
* Gets the relative path to the file
* @param string $path Path to make relative
* @return string
*/
protected function relativePath($path)
{
$relative = str_replace(getcwd() . DIRECTORY_SEPARATOR, '', $path);
//Normalize
$relative = str_replace('\\', '/', $relative);
return $relative;
}
/**
* Gets the file name and start line for the test
* @param string $name Name of the test
* @return array
*/
protected function getReflectionFromTest($name)
{
list($klass, $method) = explode('::', $name);
$c = new \ReflectionClass($klass);
$m = $c->getMethod($method);
return [$m->getFileName(), $m->getStartLine()];
}
}

View File

@ -0,0 +1,264 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig;
use SilverStripe\ORM\DB;
use SilverStripe\Security\Security;
use SilverStripe\Versioned\Versioned;
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
use UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\BaseObject;
use UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\ChildObject;
use UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\DummyController;
use UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\Player;
use UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\TestParent;
use UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\VPlayer;
class AutoSortTest extends SapphireTest
{
/** @var string */
public static $fixture_file = 'AutoSortTest.yml';
/** @var array */
protected static $extra_dataobjects = [
Player::class,
VPlayer::class,
TestParent::class,
BaseObject::class,
ChildObject::class
];
public function testAutoSort()
{
if (Security::getCurrentUser()) {
$this->logOut();
}
$list = Player::get();
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder'));
$gridField = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new DummyController(), 'mockform', new FieldList([$gridField]), new FieldList());
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', [], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $form->getSecurityToken()->getName() => $form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($form->getSecurityToken()->getName(), $form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'sortableRowsToggle', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true]]]);
$request->setSession($session);
$gridField->gridFieldAlterAction(['StateID' => $stateID], $form, $request);
//Insure sort ran
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run');
//Check for duplicates (there shouldn't be any)
$count = $list->Count();
$indexes = count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count - $indexes, 'Duplicate indexes detected');
}
public function testAppendToTopAutoSort()
{
if (Security::getCurrentUser()) {
$this->logOut();
}
$list = Player::get();
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder'));
$gridField = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new DummyController(), 'mockform', new FieldList([$gridField]), new FieldList());
/** @var GridFieldSortableRows $sortableRows */
$sortableRows = $gridField->getConfig()->getComponentByType(GridFieldSortableRows::class);
$sortableRows->setAppendToTop(true);
$this->assertEquals(0, $list->last()->SortOrder, 'Auto sort should not have run');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', [], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $form->getSecurityToken()->getName() => $form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($form->getSecurityToken()->getName(), $form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'sortableRowsToggle', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true]]]);
$request->setSession($session);
$gridField->gridFieldAlterAction(['StateID' => $stateID], $form, $request);
//Insure sort ran
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run');
//Check for duplicates (there shouldn't be any)
$count = $list->Count();
$indexes = count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count - $indexes, 'Duplicate indexes detected');
}
public function testAutoSortVersioned()
{
if (Security::getCurrentUser()) {
$this->logOut();
}
//Force versioned to reset
Versioned::reset();
$list = VPlayer::get();
//Publish all records
foreach ($list as $item) {
$item->publishSingle();
}
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder', true, 'Live'));
$gridField = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new DummyController(), 'mockform', new FieldList([$gridField]), new FieldList());
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', [], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $form->getSecurityToken()->getName() => $form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($form->getSecurityToken()->getName(), $form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'sortableRowsToggle', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true]]]);
$request->setSession($session);
$gridField->gridFieldAlterAction(['StateID' => $stateID], $form, $request);
//Insure sort ran
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run on Versioned stage "Stage"');
//Check for duplicates (there shouldn't be any)
$count = $list->Count();
$indexes = count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count - $indexes, 'Duplicate indexes detected on Versioned stage "Stage"');
//Force versioned over to Live stage
Versioned::set_reading_mode('Live');
//Get live instance
$obj = Versioned::get_one_by_stage(VPlayer::class, 'Live', '"ID"=' . $list->last()->ID);
//Insure sort ran
$this->assertEquals(3, $obj->SortOrder, 'Auto sort should have run on Versioned stage "Live"');
//Check for duplicates (there shouldn't be any)
$list = Versioned::get_by_stage(VPlayer::class, 'Live');
$count = $list->Count();
$indexes = count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count - $indexes, 'Duplicate indexes detected on Versioned stage "Live"');
}
public function testAppendToTopAutoSortVersioned()
{
if (Security::getCurrentUser()) {
$this->logOut();
}
//Force versioned to reset
Versioned::reset();
$list = VPlayer::get();
//Publish all records
foreach ($list as $item) {
$item->publishSingle();
}
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder', true, 'Live'));
$gridField = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new DummyController(), 'mockform', new FieldList([$gridField]), new FieldList());
/** @var GridFieldSortableRows $sortableRows */
$sortableRows = $gridField->getConfig()->getComponentByType(GridFieldSortableRows::class);
$sortableRows->setAppendToTop(true);
$this->assertEquals(0, $list->last()->SortOrder, 'Auto sort should not have run on Versioned stage "Stage"');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', [], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $form->getSecurityToken()->getName() => $form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($form->getSecurityToken()->getName(), $form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'sortableRowsToggle', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true]]]);
$request->setSession($session);
$gridField->gridFieldAlterAction(['StateID' => $stateID], $form, $request);
//Insure sort ran
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run on Versioned stage "Stage"');
//Check for duplicates (there shouldn't be any)
$count = $list->Count();
$indexes = count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count - $indexes, 'Duplicate indexes detected on Versioned stage "Stage"');
//Force versioned over to Live stage
Versioned::set_reading_mode('Live');
//Insure sort ran
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run on Versioned stage "Live"');
//Check for duplicates (there shouldn't be any)
$count = $list->Count();
$indexes = count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count - $indexes, 'Duplicate indexes detected on Versioned stage "Live"');
}
public function testAppendToTopAutoSortChild()
{
if (Security::getCurrentUser()) {
$this->logOut();
}
//Push the edit date into the past, we're checking this later
DB::query('UPDATE "GridFieldAction_SortOrder_BaseObject" SET "LastEdited"=\'' . date('Y-m-d 00:00:00', strtotime('yesterday')) . '\'');
/** @var TestParent $parent */
$parent = TestParent::get()->first();
/** @var DataList $list */
$list = $parent->TestRelation();
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder'));
$gridField = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new DummyController(), 'mockform', new FieldList([$gridField]), new FieldList());
$form->loadDataFrom($parent);
/** @var GridFieldSortableRows $sortableRows */
$sortableRows = $gridField->getConfig()->getComponentByType(GridFieldSortableRows::class);
$sortableRows->setAppendToTop(true);
$this->assertEquals(0, $list->last()->SortOrder, 'Auto sort should not have run');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', [], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $form->getSecurityToken()->getName() => $form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($form->getSecurityToken()->getName(), $form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'sortableRowsToggle', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true]]]);
$request->setSession($session);
$gridField->gridFieldAlterAction(['StateID' => $stateID], $form, $request);
//Insure sort ran
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run');
//Check for duplicates (there shouldn't be any)
$count = $list->Count();
$indexes = count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count - $indexes, 'Duplicate indexes detected');
//Make sure the last edited is today for all records
$this->assertEquals(3, $list->filter('LastEdited:GreaterThan', date('Y-m-d 00:00:00'))->count());
}
}

View File

@ -0,0 +1,39 @@
UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\Player:
player1:
Name: Player 1
SortOrder: 0
player2:
Name: Player 2
SortOrder: 0
player3:
Name: Player 3
SortOrder: 0
UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\VPlayer:
player1:
Name: Player 1
SortOrder: 0
player2:
Name: Player 2
SortOrder: 0
player3:
Name: Player 3
SortOrder: 0
UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\TestParent:
testparent:
Name: Test
UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\ChildObject:
testitem1:
Name: Test 1
SortOrder: 0
Parent: =>UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\TestParent.testparent
testitem2:
Name: Test 2
SortOrder: 0
Parent: =>UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\TestParent.testparent
testitem3:
Name: Test 3
SortOrder: 0
Parent: =>UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\TestParent.testparent

View File

@ -0,0 +1,19 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\BaseObject
*
* @property string Name
*/
class BaseObject extends DataObject implements TestOnly
{
private static $table_name = 'GridFieldAction_SortOrder_BaseObject';
private static $db = [
'Name' => 'Varchar',
];
}

View File

@ -0,0 +1,27 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\FieldType\DBInt;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\ChildObject
*
* @package SortableGridField\Tests
* @property int SortOrder
* @method TestParent Parent
*/
class ChildObject extends BaseObject implements TestOnly
{
private static $table_name = 'GridFieldAction_SortOrder_ChildObject';
private static $db = [
'SortOrder' => DBInt::class,
];
private static $has_one = [
'Parent' => TestParent::class,
];
private static $default_sort = 'SortOrder';
}

View File

@ -0,0 +1,12 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest;
use SilverStripe\Control\Controller;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\DummyController
*/
class DummyController extends Controller
{
private static $url_segment = 'sortable-grid-field';
}

View File

@ -0,0 +1,23 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\Player
*
* @property string Name
* @property int SortOrder
*/
class Player extends DataObject implements TestOnly
{
private static $table_name = 'GridFieldAction_SortOrder_Player';
private static $db = [
'Name' => 'Varchar',
'SortOrder' => 'Int',
];
private static $default_sort = 'SortOrder';
}

View File

@ -0,0 +1,25 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\TestParent
*
* @package SortableGridField\Tests
* @property string Name
* @method ChildObject TestRelation
*/
class TestParent extends DataObject implements TestOnly
{
private static $table_name = 'GridFieldAction_SortOrder_TestParent';
private static $db = [
'Name' => 'Varchar',
];
private static $has_many = [
'TestRelation' => ChildObject::class,
];
}

View File

@ -0,0 +1,28 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\VPlayer
*
* @property string Name
* @property int SortOrder
*/
class VPlayer extends DataObject implements TestOnly
{
private static $table_name = 'GridFieldAction_SortOrder_VPlayer';
private static $db = [
'Name' => 'Varchar',
'SortOrder' => 'Int',
];
private static $default_sort = 'SortOrder';
private static $extensions = [
Versioned::class,
];
}

View File

@ -0,0 +1,123 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig;
use SilverStripe\ORM\ValidationException;
use SilverStripe\Security\IdentityStore;
use SilverStripe\Security\Security;
use SilverStripe\Versioned\Versioned;
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
use UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\DummyController;
use UndefinedOffset\SortableGridField\Tests\Forms\OrderingTest\Team;
use UndefinedOffset\SortableGridField\Tests\Forms\OrderingTest\VTeam;
class OrderingTest extends SapphireTest
{
/** @var ArrayList */
protected $list;
/** @var GridField */
protected $gridField;
/** @var Form */
protected $form;
/** @var string */
public static $fixture_file = 'OrderingTest.yml';
/** @var array */
protected static $extra_dataobjects = [
Team::class,
VTeam::class
];
protected function setUp(): void
{
parent::setUp();
$this->list = Team::get();
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder'));
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
$this->form = new Form(new DummyController(), 'mockform', new FieldList([$this->gridField]), new FieldList());
}
public function testSortActionWithoutCorrectPermission()
{
if (Security::getCurrentUser()) {
Injector::inst()->get(IdentityStore::class)->logOut(Controller::curr()->getRequest());
}
$this->expectException(ValidationException::class);
$team1 = $this->objFromFixture(Team::class, 'team1');
$team2 = $this->objFromFixture(Team::class, 'team2');
$team3 = $this->objFromFixture(Team::class, 'team3');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', ['ItemIDs' => "$team1->ID, $team3->ID, $team2->ID"], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $this->form->getSecurityToken()->getName() => $this->form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($this->form->getSecurityToken()->getName(), $this->form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'saveGridRowSort', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true]]]);
$request->setSession($session);
$this->gridField->gridFieldAlterAction(['StateID' => $stateID], $this->form, $request);
$this->assertEquals($team3->ID, $this->list->last()->ID, 'User should\'t be able to sort records without correct permissions.');
}
public function testSortActionWithAdminPermission()
{
$team1 = $this->objFromFixture(Team::class, 'team1');
$team2 = $this->objFromFixture(Team::class, 'team2');
$team3 = $this->objFromFixture(Team::class, 'team3');
$this->logInWithPermission('ADMIN');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', ['ItemIDs' => "$team1->ID, $team3->ID, $team2->ID"], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $this->form->getSecurityToken()->getName() => $this->form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($this->form->getSecurityToken()->getName(), $this->form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'saveGridRowSort', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true]]]);
$request->setSession($session);
$this->gridField->gridFieldAlterAction(['StateID' => $stateID], $this->form, $request);
$this->assertEquals($team2->ID, $this->list->last()->ID, 'User should be able to sort records with ADMIN permission.');
}
public function testSortActionVersioned()
{
//Force versioned to reset
Versioned::reset();
$list = VTeam::get();
$this->gridField->setList($list);
/** @var GridFieldSortableRows $sortableGrid */
$sortableGrid = $this->gridField->getConfig()->getComponentByType(GridFieldSortableRows::class);
$sortableGrid->setUpdateVersionedStage('Live');
//Publish all records
foreach ($list as $item) {
$item->publishSingle();
}
$team1 = $this->objFromFixture(VTeam::class, 'team1');
$team2 = $this->objFromFixture(VTeam::class, 'team2');
$team3 = $this->objFromFixture(VTeam::class, 'team3');
$this->logInWithPermission('ADMIN');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', ['ItemIDs' => "$team1->ID, $team3->ID, $team2->ID"], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $this->form->getSecurityToken()->getName() => $this->form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($this->form->getSecurityToken()->getName(), $this->form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'saveGridRowSort', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true]]]);
$request->setSession($session);
$this->gridField->gridFieldAlterAction(['StateID' => $stateID], $this->form, $request);
$this->assertEquals($team2->ID, $list->last()->ID, 'Sort should have happened on Versioned stage "Stage"');
$list = Versioned::get_by_stage(VTeam::class, 'Live');
$this->assertEquals($team2->ID, $list->last()->ID, 'Sort should have happened on Versioned stage "Live"');
}
}

View File

@ -0,0 +1,27 @@
UndefinedOffset\SortableGridField\Tests\Forms\OrderingTest\Team:
team1:
Name: Team 1
City: Cologne
SortOrder: 1
team2:
Name: Team 2
City: Wellington
SortOrder: 2
team3:
Name: Team 3
City: Auckland
SortOrder: 3
UndefinedOffset\SortableGridField\Tests\Forms\OrderingTest\VTeam:
team1:
Name: Team 1
City: Cologne
SortOrder: 1
team2:
Name: Team 2
City: Wellington
SortOrder: 2
team3:
Name: Team 3
City: Auckland
SortOrder: 3

View File

@ -0,0 +1,25 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\OrderingTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\OrderingTest\Team
*
* @property string Name
* @property string City
* @property int SortOrder
*/
class Team extends DataObject implements TestOnly
{
private static $table_name = 'GridFieldAction_SortOrder_Team';
private static $db = [
'Name' => 'Varchar',
'City' => 'Varchar',
'SortOrder' => 'Int',
];
private static $default_sort = 'SortOrder';
}

View File

@ -0,0 +1,29 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\OrderingTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\OrderingTest\VTeam
*
* @property string Name
* @property string City
* @property int SortOrder
*/
class VTeam extends DataObject implements TestOnly
{
private static $table_name = 'GridFieldAction_SortOrder_VTeam';
private static $db = [
'Name' => 'Varchar',
'City' => 'Varchar',
'SortOrder' => 'Int',
];
private static $default_sort = 'SortOrder';
private static $extensions = [
Versioned::class,
];
}

View File

@ -0,0 +1,188 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_Base;
use SilverStripe\Versioned\Versioned;
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
use UndefinedOffset\SortableGridField\Tests\Forms\AutoSortTest\DummyController;
use UndefinedOffset\SortableGridField\Tests\Forms\PageSortingTest\Team;
use UndefinedOffset\SortableGridField\Tests\Forms\PageSortingTest\VTeam;
/**
* Class \UndefinedOffset\SortableGridField\Tests\PageSortingTest
*/
class PageSortingTest extends SapphireTest
{
/** @var ArrayList */
protected $list;
/** @var GridField */
protected $gridField;
/** @var Form */
protected $form;
/** @var string */
public static $fixture_file = 'PageSortingTest.yml';
/** @var array */
protected static $extra_dataobjects = [
Team::class,
VTeam::class
];
protected function setUp(): void
{
parent::setUp();
$this->list = Team::get();
$config = GridFieldConfig_Base::create(5)->addComponent(new GridFieldSortableRows('SortOrder'));
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
$this->form = new Form(new DummyController(), 'mockform', FieldList::create([$this->gridField]), FieldList::create());
}
public function testSortToNextPage()
{
$this->gridField->State->GridFieldPaginator->currentPage = 1;
$team3 = $this->objFromFixture(Team::class, 'team3');
$this->logInWithPermission('ADMIN');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', ['ItemID' => $team3->ID, 'Target' => 'nextpage'], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $this->form->getSecurityToken()->getName() => $this->form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($this->form->getSecurityToken()->getName(), $this->form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'sortToPage', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true], 'GridFieldPaginator' => ['currentPage' => 1]]]);
$request->setSession($session);
$this->gridField->gridFieldAlterAction(['StateID' => $stateID], $this->form, $request);
$team6 = $this->objFromFixture(Team::class, 'team6');
$this->assertEquals(5, $team6->SortOrder, 'Team 6 Should have moved to the bottom of the first page');
$team3 = $this->objFromFixture(Team::class, 'team3');
$this->assertEquals(6, $team3->SortOrder, 'Team 3 Should have moved to the top of the second page');
}
public function testSortToPrevPage()
{
$this->gridField->State->GridFieldPaginator->currentPage = 2;
$team7 = $this->objFromFixture(Team::class, 'team7');
$this->logInWithPermission('ADMIN');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', ['ItemID' => $team7->ID, 'Target' => 'previouspage'], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $this->form->getSecurityToken()->getName() => $this->form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($this->form->getSecurityToken()->getName(), $this->form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'sortToPage', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true], 'GridFieldPaginator' => ['currentPage' => 1]]]);
$request->setSession($session);
$this->gridField->gridFieldAlterAction(['StateID' => $stateID], $this->form, $request);
$team7 = $this->objFromFixture(Team::class, 'team7');
$this->assertEquals(5, $team7->SortOrder, 'Team 7 Should have moved to the bottom of the first page');
$team5 = $this->objFromFixture(Team::class, 'team5');
$this->assertEquals(6, $team5->SortOrder, 'Team 5 Should have moved to the top of the second page');
}
public function testSortToNextPageVersioned()
{
//Force versioned to reset
Versioned::reset();
$list = VTeam::get();
$this->gridField->setList($list);
/** @var GridFieldSortableRows $sortableGrid */
$sortableGrid = $this->gridField->getConfig()->getComponentByType(GridFieldSortableRows::class);
$sortableGrid->setUpdateVersionedStage('Live');
$this->gridField->State->GridFieldPaginator->currentPage = 1;
//Publish all records
foreach ($list as $item) {
$item->publishSingle();
}
$team3 = $this->objFromFixture(VTeam::class, 'team3');
$this->logInWithPermission('ADMIN');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', ['ItemID' => $team3->ID, 'Target' => 'nextpage'], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $this->form->getSecurityToken()->getName() => $this->form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($this->form->getSecurityToken()->getName(), $this->form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'sortToPage', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true], 'GridFieldPaginator' => ['currentPage' => 1]]]);
$request->setSession($session);
$this->gridField->gridFieldAlterAction(['StateID' => $stateID], $this->form, $request);
$team6 = $this->objFromFixture(VTeam::class, 'team6');
$this->assertEquals(5, $team6->SortOrder, 'Team 6 Should have moved to the bottom of the first page on Versioned stage "Stage"');
$team3 = $this->objFromFixture(VTeam::class, 'team3');
$this->assertEquals(6, $team3->SortOrder, 'Team 3 Should have moved to the top of the second page on Versioned stage "Stage"');
$list = Versioned::get_by_stage(VTeam::class, 'Live');
$team6 = $list->byID($team6->ID);
$this->assertEquals(5, $team6->SortOrder, 'Team 6 Should have moved to the bottom of the first page on Versioned stage "Live"');
$team3 = $list->byID($team3->ID);
$this->assertEquals(6, $team3->SortOrder, 'Team 3 Should have moved to the top of the second page on Versioned stage "Live"');
}
public function testSortToPrevPageVersioned()
{
//Force versioned to reset
Versioned::reset();
$list = VTeam::get();
$this->gridField->setList($list);
/** @var GridFieldSortableRows $sortableGrid */
$sortableGrid = $this->gridField->getConfig()->getComponentByType(GridFieldSortableRows::class);
$sortableGrid->setUpdateVersionedStage('Live');
$this->gridField->State->GridFieldPaginator->currentPage = 2;
//Publish all records
foreach ($list as $item) {
$item->publishSingle();
}
$team7 = $this->objFromFixture(VTeam::class, 'team7');
$this->logInWithPermission('ADMIN');
$stateID = 'testGridStateActionField';
$request = new HTTPRequest('POST', 'url', ['ItemID' => $team7->ID, 'Target' => 'previouspage'], ['action_gridFieldAlterAction?StateID=' . $stateID => true, $this->form->getSecurityToken()->getName() => $this->form->getSecurityToken()->getValue()]);
$session = Controller::curr()->getRequest()->getSession();
$session->set($this->form->getSecurityToken()->getName(), $this->form->getSecurityToken()->getValue());
$session->set($stateID, ['grid' => '', 'actionName' => 'sortToPage', 'args' => ['GridFieldSortableRows' => ['sortableToggle' => true], 'GridFieldPaginator' => ['currentPage' => 1]]]);
$request->setSession($session);
$this->gridField->gridFieldAlterAction(['StateID' => $stateID], $this->form, $request);
$team7 = $this->objFromFixture(VTeam::class, 'team7');
$this->assertEquals(5, $team7->SortOrder, 'Team 7 Should have moved to the bottom of the first page on Versioned stage "Stage"');
$team5 = $this->objFromFixture(VTeam::class, 'team5');
$this->assertEquals(6, $team5->SortOrder, 'Team 5 Should have moved to the top of the second page on Versioned stage "Stage"');
$list = Versioned::get_by_stage(VTeam::class, 'Live');
$team7 = $list->byID($team7->ID);
$this->assertEquals(5, $team7->SortOrder, 'Team 7 Should have moved to the bottom of the first page on Versioned stage "Live"');
$team5 = $list->byID($team5->ID);
$this->assertEquals(6, $team5->SortOrder, 'Team 5 Should have moved to the top of the second page on Versioned stage "Live"');
}
}

View File

@ -0,0 +1,75 @@
UndefinedOffset\SortableGridField\Tests\Forms\PageSortingTest\Team:
team1:
Name: Team 1
City: Cologne
SortOrder: 1
team2:
Name: Team 2
City: Wellington
SortOrder: 2
team3:
Name: Team 3
City: Auckland
SortOrder: 3
team4:
Name: Team 4
City: Cologne
SortOrder: 4
team5:
Name: Team 5
City: Wellington
SortOrder: 5
team6:
Name: Team 6
City: Auckland
SortOrder: 6
team7:
Name: Team 7
City: Cologne
SortOrder: 7
team8:
Name: Team 8
City: Wellington
SortOrder: 8
team9:
Name: Team 9
City: Auckland
SortOrder: 9
UndefinedOffset\SortableGridField\Tests\Forms\PageSortingTest\VTeam:
team1:
Name: Team 1
City: Cologne
SortOrder: 1
team2:
Name: Team 2
City: Wellington
SortOrder: 2
team3:
Name: Team 3
City: Auckland
SortOrder: 3
team4:
Name: Team 4
City: Cologne
SortOrder: 4
team5:
Name: Team 5
City: Wellington
SortOrder: 5
team6:
Name: Team 6
City: Auckland
SortOrder: 6
team7:
Name: Team 7
City: Cologne
SortOrder: 7
team8:
Name: Team 8
City: Wellington
SortOrder: 8
team9:
Name: Team 9
City: Auckland
SortOrder: 9

View File

@ -0,0 +1,25 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\PageSortingTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\PageSortingTest\Team
*
* @property string Name
* @property string City
* @property int SortOrder
*/
class Team extends DataObject implements TestOnly
{
private static $table_name = 'GridFieldAction_PageSortOrder_Team';
private static $db = [
'Name' => 'Varchar',
'City' => 'Varchar',
'SortOrder' => 'Int',
];
private static $default_sort = 'SortOrder';
}

View File

@ -0,0 +1,30 @@
<?php
namespace UndefinedOffset\SortableGridField\Tests\Forms\PageSortingTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
/**
* Class \UndefinedOffset\SortableGridField\Tests\Forms\PageSortingTest\VTeam
*
* @property string Name
* @property string City
* @property int SortOrder
*/
class VTeam extends DataObject implements TestOnly
{
private static $table_name = 'GridFieldAction_PageSortOrder_VTeam';
private static $db = [
'Name' => 'Varchar',
'City' => 'Varchar',
'SortOrder' => 'Int',
];
private static $default_sort = 'SortOrder';
private static $extensions = [
Versioned::class,
];
}

View File

@ -1,46 +0,0 @@
<?php
class GridFieldSortableRowsAutoSortTest extends SapphireTest {
/** @var ArrayList */
protected $list;
/** @var GridField */
protected $gridField;
/** @var Form */
protected $form;
/** @var string */
public static $fixture_file = 'GridFieldSortableRowsAutoSortTest.yml';
/** @var array */
protected $extraDataObjects = array('GridFieldAction_SortOrder_Player');
public function setUp() {
parent::setUp();
$this->list = GridFieldAction_SortOrder_Player::get();
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder'));
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
$this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
}
public function testAutoSort() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'sortableRowsDisablePaginator', 'args'=>array('GridFieldSortableRows'=>array('sortableToggle'=>true))));
$request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals(3, $this->list->last()->SortOrder, 'Auto sort should have run');
}
}
class GridFieldAction_SortOrder_Player extends DataObject implements TestOnly {
static $db = array(
'Name' => 'Varchar',
'SortOrder' => 'Int'
);
static $default_sort='SortOrder';
}
?>

View File

@ -1,10 +0,0 @@
GridFieldAction_SortOrder_Player:
player1:
Name: Player 1
SortOrder: 0
player2:
Name: Player 2
SortOrder: 0
player3:
Name: Player 3
SortOrder: 0

View File

@ -1,63 +0,0 @@
<?php
class GridFieldSortableRowsTest extends SapphireTest {
/** @var ArrayList */
protected $list;
/** @var GridField */
protected $gridField;
/** @var Form */
protected $form;
/** @var string */
public static $fixture_file = 'GridFieldSortableRowsTest.yml';
/** @var array */
protected $extraDataObjects = array('GridFieldAction_SortOrder_Team');
public function setUp() {
parent::setUp();
$this->list = GridFieldAction_SortOrder_Team::get();
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder'));
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
$this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
}
public function testSortActionWithoutCorrectPermission() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$this->setExpectedException('ValidationException');
$team1 = $this->objFromFixture('GridFieldAction_SortOrder_Team', 'team1');
$team2 = $this->objFromFixture('GridFieldAction_SortOrder_Team', 'team2');
$team3 = $this->objFromFixture('GridFieldAction_SortOrder_Team', 'team3');
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'saveGridRowSort', 'args'=>array('GridFieldSortableRows'=>array('sortableToggle'=>true))));
$request = new SS_HTTPRequest('POST', 'url', array('ItemIDs'=>"$team1->ID, $team3->ID, $team2->ID"), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals($team3->ID, $this->list->last()->ID, 'User should\'t be able to sort records without correct permissions.');
}
public function testSortActionWithAdminPermission() {
$team1 = $this->objFromFixture('GridFieldAction_SortOrder_Team', 'team1');
$team2 = $this->objFromFixture('GridFieldAction_SortOrder_Team', 'team2');
$team3 = $this->objFromFixture('GridFieldAction_SortOrder_Team', 'team3');
$this->logInWithPermission('ADMIN');
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'saveGridRowSort', 'args'=>array('GridFieldSortableRows'=>array('sortableToggle'=>true))));
$request = new SS_HTTPRequest('POST', 'url', array('ItemIDs'=>"$team1->ID, $team3->ID, $team2->ID"), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals($team2->ID, $this->list->last()->ID, 'User should be able to sort records with ADMIN permission.');
}
}
class GridFieldAction_SortOrder_Team extends DataObject implements TestOnly {
static $db = array(
'Name' => 'Varchar',
'City' => 'Varchar',
'SortOrder' => 'Int'
);
static $default_sort='SortOrder';
}
?>

View File

@ -1,13 +0,0 @@
GridFieldAction_SortOrder_Team:
team1:
Name: Team 1
City: Cologne
SortOrder: 1
team2:
Name: Team 2
City: Wellington
SortOrder: 2
team3:
Name: Team 3
City: Auckland
SortOrder: 3

View File

@ -0,0 +1,15 @@
SS_ENVIRONMENT_TYPE="dev"
SS_TRUSTED_PROXY_IPS="*"
SS_DATABASE_CLASS="MySQLDatabase"
SS_DATABASE_SERVER="127.0.0.1"
SS_DATABASE_USERNAME="root"
SS_DATABASE_PASSWORD="testpassword"
SS_DATABASE_NAME="test_db"
SS_DEFAULT_ADMIN_USERNAME="username"
SS_DEFAULT_ADMIN_PASSWORD="password"
SS_ERROR_LOG="artifacts/silverstripe-errors.log"
TRAVIS_TEST_SESSION="1"