├── .editorconfig ├── .gitattributes ├── .scrutinizer.yml ├── .tx └── config ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── _config.php ├── composer.json ├── css └── GridFieldSortableRows.css ├── javascript └── GridFieldSortableRows.js ├── lang ├── ar.yml ├── cs.yml ├── da.yml ├── de.yml ├── en.yml ├── eo.yml ├── es.yml ├── fa_IR.yml ├── fi.yml ├── fr.yml ├── hr.yml ├── it.yml ├── lt.yml ├── mi.yml ├── nb.yml ├── nb_NO.yml ├── nl.yml ├── pl.yml ├── ru.yml ├── sk.yml ├── sl.yml ├── sv.yml └── zh.yml ├── phpcs.xml.dist ├── phpunit.xml.dist ├── src └── Forms │ └── GridFieldSortableRows.php └── templates └── SortableGridField └── Forms └── Includes ├── GridFieldSortableRows.ss └── GridFieldSortableRows_paginator.ss /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in this file, 2 | # please see the EditorConfig documentation: 3 | # http://editorconfig.org 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [{*.yml,package.json}] 14 | indent_size = 2 15 | 16 | # The indent size used in the package.json file cannot be changed: 17 | # https://github.com/npm/npm/pull/3180#issuecomment-16336516 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /tests export-ignore 2 | /docs export-ignore 3 | /.gitignore export-ignore 4 | /.travis.yml export-ignore 5 | /.github export-ignore 6 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | inherit: true 2 | 3 | checks: 4 | php: 5 | verify_property_names: true 6 | verify_argument_usable_as_reference: true 7 | verify_access_scope_valid: true 8 | useless_calls: true 9 | use_statement_alias_conflict: true 10 | variable_existence: true 11 | unused_variables: true 12 | unused_properties: true 13 | unused_parameters: true 14 | unused_methods: true 15 | unreachable_code: true 16 | too_many_arguments: true 17 | sql_injection_vulnerabilities: true 18 | simplify_boolean_return: true 19 | side_effects_or_types: true 20 | security_vulnerabilities: true 21 | return_doc_comments: true 22 | return_doc_comment_if_not_inferrable: true 23 | require_scope_for_properties: true 24 | require_scope_for_methods: true 25 | require_php_tag_first: true 26 | psr2_switch_declaration: true 27 | psr2_class_declaration: true 28 | property_assignments: true 29 | prefer_while_loop_over_for_loop: true 30 | precedence_mistakes: true 31 | precedence_in_conditions: true 32 | phpunit_assertions: true 33 | php5_style_constructor: true 34 | parse_doc_comments: true 35 | parameter_non_unique: true 36 | parameter_doc_comments: true 37 | param_doc_comment_if_not_inferrable: true 38 | optional_parameters_at_the_end: true 39 | one_class_per_file: true 40 | no_unnecessary_if: true 41 | no_trailing_whitespace: true 42 | no_property_on_interface: true 43 | no_non_implemented_abstract_methods: true 44 | no_error_suppression: true 45 | no_duplicate_arguments: true 46 | no_commented_out_code: true 47 | newline_at_end_of_file: true 48 | missing_arguments: true 49 | method_calls_on_non_object: true 50 | instanceof_class_exists: true 51 | foreach_traversable: true 52 | fix_line_ending: true 53 | fix_doc_comments: true 54 | duplication: true 55 | deprecated_code_usage: true 56 | deadlock_detection_in_loops: true 57 | code_rating: true 58 | closure_use_not_conflicting: true 59 | catch_class_exists: true 60 | blank_line_after_namespace_declaration: false 61 | avoid_multiple_statements_on_same_line: true 62 | avoid_duplicate_types: true 63 | avoid_conflicting_incrementers: true 64 | avoid_closing_tag: true 65 | assignment_of_null_return: true 66 | argument_type_checks: true 67 | 68 | filter: 69 | paths: [src/*, tests/*] 70 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [silverstripe-sortablegridfield.master] 5 | file_filter = lang/.yml 6 | source_file = lang/en.yml 7 | source_lang = en 8 | type = YML -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [2.2.0](https://github.com/UndefinedOffset/SortableGridField/tree/2.2.0) (2023-05-08) 4 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.1.0...2.2.0) 5 | 6 | ## [2.1.0](https://github.com/UndefinedOffset/SortableGridField/tree/2.1.0) (2022-07-06) 7 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.9...2.1.0) 8 | 9 | ## [2.0.9](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.9) (2021-07-19) 10 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.8...2.0.9) 11 | 12 | ## [2.0.8](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.8) (2019-01-08) 13 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.7...2.0.8) 14 | 15 | ## [2.0.7](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.7) (2018-12-13) 16 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.6...2.0.7) 17 | 18 | ## [2.0.6](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.6) (2018-07-04) 19 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.5...2.0.6) 20 | 21 | ## [2.0.5](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.5) (2018-06-07) 22 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.4...2.0.5) 23 | 24 | **Closed issues:** 25 | 26 | - Int to DBInt [\#120](https://github.com/UndefinedOffset/SortableGridField/issues/120) 27 | 28 | **Merged pull requests:** 29 | 30 | - 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)) 31 | 32 | ## [2.0.4](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.4) (2018-03-07) 33 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.3...2.0.4) 34 | 35 | ## [2.0.3](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.3) (2018-03-01) 36 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.2...2.0.3) 37 | 38 | ## [2.0.2](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.2) (2017-12-11) 39 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.1...2.0.2) 40 | 41 | **Closed issues:** 42 | 43 | - many\_many ss 3.6 GridFieldAddNewMultiClass Column 'SortOrder' in field list is ambiguous [\#114](https://github.com/UndefinedOffset/SortableGridField/issues/114) 44 | - mapTableNameAndReturn can't parse namespace+classname into mysql tablename [\#113](https://github.com/UndefinedOffset/SortableGridField/issues/113) 45 | 46 | ## [2.0.1](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.1) (2017-11-29) 47 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.0...2.0.1) 48 | 49 | **Closed issues:** 50 | 51 | - 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) 52 | 53 | **Merged pull requests:** 54 | 55 | - Merge 1.0.x into master [\#112](https://github.com/UndefinedOffset/SortableGridField/pull/112) ([dhensby](https://github.com/dhensby)) 56 | - DOCS Update docs examples to be 3.1+ relevant [\#111](https://github.com/UndefinedOffset/SortableGridField/pull/111) ([dhensby](https://github.com/dhensby)) 57 | 58 | ## [2.0.0](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.0) (2017-11-24) 59 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.0-beta2...2.0.0) 60 | 61 | **Closed issues:** 62 | 63 | - SilverStripe 4 Compatibility [\#103](https://github.com/UndefinedOffset/SortableGridField/issues/103) 64 | 65 | ## [2.0.0-beta2](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.0-beta2) (2017-11-01) 66 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/1.0.0...2.0.0-beta2) 67 | 68 | ## [1.0.0](https://github.com/UndefinedOffset/SortableGridField/tree/1.0.0) (2017-10-27) 69 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.10...1.0.0) 70 | 71 | **Closed issues:** 72 | 73 | - Remove tests against unsupported versions [\#97](https://github.com/UndefinedOffset/SortableGridField/issues/97) 74 | 75 | ## [0.6.10](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.10) (2017-08-14) 76 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.0-beta1...0.6.10) 77 | 78 | ## [2.0.0-beta1](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.0-beta1) (2017-07-04) 79 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/2.0.0-alpha1...2.0.0-beta1) 80 | 81 | **Closed issues:** 82 | 83 | - No search capabilities when using in ModelAdmin [\#108](https://github.com/UndefinedOffset/SortableGridField/issues/108) 84 | 85 | **Merged pull requests:** 86 | 87 | - Remove closing PHP tag [\#107](https://github.com/UndefinedOffset/SortableGridField/pull/107) ([robbieaverill](https://github.com/robbieaverill)) 88 | 89 | ## [2.0.0-alpha1](https://github.com/UndefinedOffset/SortableGridField/tree/2.0.0-alpha1) (2017-06-05) 90 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.9...2.0.0-alpha1) 91 | 92 | **Closed issues:** 93 | 94 | - Tests required for tables with $table\_name declared [\#105](https://github.com/UndefinedOffset/SortableGridField/issues/105) 95 | 96 | ## [0.6.9](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.9) (2017-04-21) 97 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.8...0.6.9) 98 | 99 | **Closed issues:** 100 | 101 | - Query errors when sorted field is on a relation and appending to top [\#101](https://github.com/UndefinedOffset/SortableGridField/issues/101) 102 | 103 | ## [0.6.8](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.8) (2017-04-18) 104 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.7...0.6.8) 105 | 106 | **Closed issues:** 107 | 108 | - Typo error [\#100](https://github.com/UndefinedOffset/SortableGridField/issues/100) 109 | 110 | ## [0.6.7](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.7) (2017-04-13) 111 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.6...0.6.7) 112 | 113 | **Closed issues:** 114 | 115 | - Wrong sort field type check [\#99](https://github.com/UndefinedOffset/SortableGridField/issues/99) 116 | 117 | ## [0.6.6](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.6) (2017-04-12) 118 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.5...0.6.6) 119 | 120 | **Closed issues:** 121 | 122 | - Not working with SS3.5 fresh install [\#98](https://github.com/UndefinedOffset/SortableGridField/issues/98) 123 | 124 | ## [0.6.5](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.5) (2016-11-24) 125 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.4...0.6.5) 126 | 127 | ## [0.6.4](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.4) (2016-11-22) 128 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.3...0.6.4) 129 | 130 | ## [0.6.3](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.3) (2016-11-18) 131 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.2...0.6.3) 132 | 133 | **Merged pull requests:** 134 | 135 | - Use Sort of GridField Header [\#96](https://github.com/UndefinedOffset/SortableGridField/pull/96) ([schellmax](https://github.com/schellmax)) 136 | 137 | ## [0.6.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.2) (2016-06-09) 138 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.1...0.6.2) 139 | 140 | **Closed issues:** 141 | 142 | - Drag and Drop Checkbox can't be selected [\#90](https://github.com/UndefinedOffset/SortableGridField/issues/90) 143 | - Issue edit and Adding New Gallery Holder Page and Gallery Page [\#89](https://github.com/UndefinedOffset/SortableGridField/issues/89) 144 | 145 | **Merged pull requests:** 146 | 147 | - BUG Remove redundant leading underscore in strings [\#95](https://github.com/UndefinedOffset/SortableGridField/pull/95) ([tractorcow](https://github.com/tractorcow)) 148 | - Test against 3.3 core and PHP 5.6 [\#93](https://github.com/UndefinedOffset/SortableGridField/pull/93) ([chillu](https://github.com/chillu)) 149 | - Added standard Scrutinizer config [\#91](https://github.com/UndefinedOffset/SortableGridField/pull/91) ([helpfulrobot](https://github.com/helpfulrobot)) 150 | 151 | ## [0.6.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.1) (2016-03-03) 152 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.6.0...0.6.1) 153 | 154 | **Closed issues:** 155 | 156 | - Unit tests failing with framework 3.2.3 [\#88](https://github.com/UndefinedOffset/SortableGridField/issues/88) 157 | 158 | ## [0.6.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.6.0) (2016-02-13) 159 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.4...0.6.0) 160 | 161 | **Closed issues:** 162 | 163 | - Can't set checkbox "Allow drag and drop re-ordering" [\#76](https://github.com/UndefinedOffset/SortableGridField/issues/76) 164 | - Investigate build failures for PostgreSQL on 3.1 and 3.2 [\#87](https://github.com/UndefinedOffset/SortableGridField/issues/87) 165 | - Multiple many\_many GridFields acting on the same relation not possible. [\#86](https://github.com/UndefinedOffset/SortableGridField/issues/86) 166 | - Expand unit test to cover page sorting [\#85](https://github.com/UndefinedOffset/SortableGridField/issues/85) 167 | 168 | ## [0.5.4](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.4) (2016-01-18) 169 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.3...0.5.4) 170 | 171 | **Closed issues:** 172 | 173 | - Support for 3.3 [\#84](https://github.com/UndefinedOffset/SortableGridField/issues/84) 174 | - Lithuanian translation [\#83](https://github.com/UndefinedOffset/SortableGridField/issues/83) 175 | 176 | **Merged pull requests:** 177 | 178 | - Added standard .gitattributes file [\#82](https://github.com/UndefinedOffset/SortableGridField/pull/82) ([helpfulrobot](https://github.com/helpfulrobot)) 179 | - Added standard .editorconfig file [\#80](https://github.com/UndefinedOffset/SortableGridField/pull/80) ([helpfulrobot](https://github.com/helpfulrobot)) 180 | 181 | ## [0.5.3](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.3) (2015-12-10) 182 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.2...0.5.3) 183 | 184 | ## [0.5.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.2) (2015-11-30) 185 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.1...0.5.2) 186 | 187 | ## [0.5.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.1) (2015-10-09) 188 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.5.0...0.5.1) 189 | 190 | ## [0.5.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.5.0) (2015-09-18) 191 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.5...0.5.0) 192 | 193 | **Closed issues:** 194 | 195 | - Change install instructions to prefer composer [\#75](https://github.com/UndefinedOffset/SortableGridField/issues/75) 196 | - Helper Columns do not maintain the widths of the item being dragged [\#74](https://github.com/UndefinedOffset/SortableGridField/issues/74) 197 | - Improved UI for moving to another page [\#72](https://github.com/UndefinedOffset/SortableGridField/issues/72) 198 | 199 | ## [0.4.5](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.5) (2015-08-29) 200 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.4...0.4.5) 201 | 202 | ## [0.4.4](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.4) (2015-08-05) 203 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.3...0.4.4) 204 | 205 | **Closed issues:** 206 | 207 | - 3.2 compat? [\#73](https://github.com/UndefinedOffset/SortableGridField/issues/73) 208 | - Doesn't respect Dataobject's $default\_sort DESC [\#60](https://github.com/UndefinedOffset/SortableGridField/issues/60) 209 | 210 | ## [0.4.3](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.3) (2015-05-07) 211 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.2...0.4.3) 212 | 213 | **Closed issues:** 214 | 215 | - setAppendToTop\(\) Does Not Work When the DataObjects Within the GridField are not Part of a RelationList [\#71](https://github.com/UndefinedOffset/SortableGridField/issues/71) 216 | - \(Drag and drop\) re-ordering over multiple pages [\#70](https://github.com/UndefinedOffset/SortableGridField/issues/70) 217 | 218 | **Merged pull requests:** 219 | 220 | - Allow installation anywhere by adding yml config [\#69](https://github.com/UndefinedOffset/SortableGridField/pull/69) ([danbroooks](https://github.com/danbroooks)) 221 | 222 | ## [0.4.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.2) (2015-04-07) 223 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.1...0.4.2) 224 | 225 | **Closed issues:** 226 | 227 | - Add note for many\_many relations about the field name [\#66](https://github.com/UndefinedOffset/SortableGridField/issues/66) 228 | - Doesnt work for Sitetree descendants because of versioned dataobjects managed by ModelAdmin. [\#64](https://github.com/UndefinedOffset/SortableGridField/issues/64) 229 | - Error saving many to many relations [\#63](https://github.com/UndefinedOffset/SortableGridField/issues/63) 230 | 231 | **Merged pull requests:** 232 | 233 | - Allow 'disableSelection' to be disabled [\#68](https://github.com/UndefinedOffset/SortableGridField/pull/68) ([hailwood](https://github.com/hailwood)) 234 | - 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)) 235 | 236 | ## [0.4.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.1) (2014-10-27) 237 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.4.0...0.4.1) 238 | 239 | **Closed issues:** 240 | 241 | - Sort column xxxxx must be an Int, column is of type [\#62](https://github.com/UndefinedOffset/SortableGridField/issues/62) 242 | 243 | ## [0.4.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.4.0) (2014-10-16) 244 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.3.2...0.4.0) 245 | 246 | **Closed issues:** 247 | 248 | - Column 'SortOrder' in field list is ambiguous in a Many\_Many relationship Gridfield [\#59](https://github.com/UndefinedOffset/SortableGridField/issues/59) 249 | - Sort column only updated when GridField is viewed [\#58](https://github.com/UndefinedOffset/SortableGridField/issues/58) 250 | 251 | **Merged pull requests:** 252 | 253 | - Initial sorting equals sortColumn [\#61](https://github.com/UndefinedOffset/SortableGridField/pull/61) ([ismooth](https://github.com/ismooth)) 254 | - Fixes fixSortColumn's Random Initial Sorting [\#57](https://github.com/UndefinedOffset/SortableGridField/pull/57) ([purplespider](https://github.com/purplespider)) 255 | 256 | ## [0.3.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.3.2) (2014-08-02) 257 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.3.1...0.3.2) 258 | 259 | **Closed issues:** 260 | 261 | - Gridfield sortable button [\#56](https://github.com/UndefinedOffset/SortableGridField/issues/56) 262 | - Problem with $belongs\_many\_many [\#55](https://github.com/UndefinedOffset/SortableGridField/issues/55) 263 | - setAppendToTop - Fatal error: Call to undefined method GridFieldSortableRows::create\(\) [\#54](https://github.com/UndefinedOffset/SortableGridField/issues/54) 264 | - Frontend not working / Error in Examples [\#52](https://github.com/UndefinedOffset/SortableGridField/issues/52) 265 | - Unable to toggle off, switched on by default [\#51](https://github.com/UndefinedOffset/SortableGridField/issues/51) 266 | - Duplicate Column names [\#50](https://github.com/UndefinedOffset/SortableGridField/issues/50) 267 | - Bug with pagination [\#38](https://github.com/UndefinedOffset/SortableGridField/issues/38) 268 | 269 | ## [0.3.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.3.1) (2013-10-29) 270 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.3.0...0.3.1) 271 | 272 | **Closed issues:** 273 | 274 | - Duplicate sort id's when fixing the sort with append\_to\_top [\#47](https://github.com/UndefinedOffset/SortableGridField/issues/47) 275 | 276 | **Merged pull requests:** 277 | 278 | - Chinese/Arabic/Te Reo translations [\#49](https://github.com/UndefinedOffset/SortableGridField/pull/49) ([chillu](https://github.com/chillu)) 279 | - Transifex support [\#48](https://github.com/UndefinedOffset/SortableGridField/pull/48) ([chillu](https://github.com/chillu)) 280 | 281 | ## [0.3.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.3.0) (2013-10-22) 282 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.2.1...0.3.0) 283 | 284 | **Closed issues:** 285 | 286 | - is this available on composer/sivlerstripe addons? [\#45](https://github.com/UndefinedOffset/SortableGridField/issues/45) 287 | - Add Docs On Migration from Data Object Manager [\#16](https://github.com/UndefinedOffset/SortableGridField/issues/16) 288 | 289 | **Merged pull requests:** 290 | 291 | - added slovak language [\#44](https://github.com/UndefinedOffset/SortableGridField/pull/44) ([silverstripesk](https://github.com/silverstripesk)) 292 | - Append new items to the top by default [\#42](https://github.com/UndefinedOffset/SortableGridField/pull/42) ([g4b0](https://github.com/g4b0)) 293 | 294 | ## [0.2.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.2.1) (2013-09-03) 295 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.2.0...0.2.1) 296 | 297 | **Merged pull requests:** 298 | 299 | - Create LICENSE [\#41](https://github.com/UndefinedOffset/SortableGridField/pull/41) ([simonwelsh](https://github.com/simonwelsh)) 300 | 301 | ## [0.2.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.2.0) (2013-08-04) 302 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.1.3...0.2.0) 303 | 304 | **Closed issues:** 305 | 306 | - Breaks when used with GridFieldManyRelationHandler [\#40](https://github.com/UndefinedOffset/SortableGridField/issues/40) 307 | - \[REQUEST\] Event to indicate that there has been a change in sort order. [\#39](https://github.com/UndefinedOffset/SortableGridField/issues/39) 308 | - Moving to next page doesn't work in FF 20.0.1 [\#33](https://github.com/UndefinedOffset/SortableGridField/issues/33) 309 | 310 | ## [0.1.3](https://github.com/UndefinedOffset/SortableGridField/tree/0.1.3) (2013-07-10) 311 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.1.2...0.1.3) 312 | 313 | **Closed issues:** 314 | 315 | - Partical Cache and sortColumn update [\#35](https://github.com/UndefinedOffset/SortableGridField/issues/35) 316 | - Add loading indicator to row sorting [\#34](https://github.com/UndefinedOffset/SortableGridField/issues/34) 317 | 318 | **Merged pull requests:** 319 | 320 | - Finnish language file [\#37](https://github.com/UndefinedOffset/SortableGridField/pull/37) ([mediaclinic](https://github.com/mediaclinic)) 321 | - Added italian language [\#36](https://github.com/UndefinedOffset/SortableGridField/pull/36) ([ntd](https://github.com/ntd)) 322 | 323 | ## [0.1.2](https://github.com/UndefinedOffset/SortableGridField/tree/0.1.2) (2013-04-26) 324 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.1.1...0.1.2) 325 | 326 | **Closed issues:** 327 | 328 | - An error occured while fetching data from the server Please try again later. [\#31](https://github.com/UndefinedOffset/SortableGridField/issues/31) 329 | 330 | ## [0.1.1](https://github.com/UndefinedOffset/SortableGridField/tree/0.1.1) (2013-04-19) 331 | [Full Changelog](https://github.com/UndefinedOffset/SortableGridField/compare/0.1.0...0.1.1) 332 | 333 | **Closed issues:** 334 | 335 | - Create a release tag [\#32](https://github.com/UndefinedOffset/SortableGridField/issues/32) 336 | 337 | ## [0.1.0](https://github.com/UndefinedOffset/SortableGridField/tree/0.1.0) (2013-03-29) 338 | **Closed issues:** 339 | 340 | - SortOrder field on $has\_many relation [\#28](https://github.com/UndefinedOffset/SortableGridField/issues/28) 341 | - nested gridfield in 3.1 [\#27](https://github.com/UndefinedOffset/SortableGridField/issues/27) 342 | - Line 183 of the GridFieldSortableRows.php is producing problems [\#24](https://github.com/UndefinedOffset/SortableGridField/issues/24) 343 | - GridFieldSortableRowsTest::testSortActionWithAdminPermission No items to sort [\#23](https://github.com/UndefinedOffset/SortableGridField/issues/23) 344 | - Sorting on Extension classes [\#22](https://github.com/UndefinedOffset/SortableGridField/issues/22) 345 | - Drag&Drop button broke GridField - SS 3.1 - Latest SS 3.1 [\#21](https://github.com/UndefinedOffset/SortableGridField/issues/21) 346 | - Error "Sort column Sort Order must be an Int, column is of type Array" [\#20](https://github.com/UndefinedOffset/SortableGridField/issues/20) 347 | - 3.1 SQLQuery::aggregate\(\) doesn't work with limit set on SQLQuery [\#19](https://github.com/UndefinedOffset/SortableGridField/issues/19) 348 | - sortablegridfield not working with ss3.1 [\#18](https://github.com/UndefinedOffset/SortableGridField/issues/18) 349 | - make upgrading from SortableDataObject painless [\#15](https://github.com/UndefinedOffset/SortableGridField/issues/15) 350 | - IDE files should not be included in this repo [\#13](https://github.com/UndefinedOffset/SortableGridField/issues/13) 351 | - Adding GridFieldSortableRows to the wrong column causes massive data loss [\#12](https://github.com/UndefinedOffset/SortableGridField/issues/12) 352 | - Sort ModelAdmin GridFields [\#6](https://github.com/UndefinedOffset/SortableGridField/issues/6) 353 | - fixSortColumn doesn't always run [\#5](https://github.com/UndefinedOffset/SortableGridField/issues/5) 354 | - Optimize sorting in has\_many [\#4](https://github.com/UndefinedOffset/SortableGridField/issues/4) 355 | - dragdropcheckbox triggers 'changed' on form [\#3](https://github.com/UndefinedOffset/SortableGridField/issues/3) 356 | - sort order not being updated on delete [\#2](https://github.com/UndefinedOffset/SortableGridField/issues/2) 357 | - installation [\#1](https://github.com/UndefinedOffset/SortableGridField/issues/1) 358 | 359 | **Merged pull requests:** 360 | 361 | - Fixing undefined variable $dataList [\#30](https://github.com/UndefinedOffset/SortableGridField/pull/30) ([halkyon](https://github.com/halkyon)) 362 | - 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)) 363 | - BUGFIX: Use config layer to check for decorated $db fields [\#26](https://github.com/UndefinedOffset/SortableGridField/pull/26) ([unclecheese](https://github.com/unclecheese)) 364 | - Update code/forms/GridFieldSortableRows.php [\#25](https://github.com/UndefinedOffset/SortableGridField/pull/25) ([flxbot](https://github.com/flxbot)) 365 | - Dutch language! [\#14](https://github.com/UndefinedOffset/SortableGridField/pull/14) ([ARNHOE](https://github.com/ARNHOE)) 366 | - spanish translation. [\#11](https://github.com/UndefinedOffset/SortableGridField/pull/11) ([dospuntocero](https://github.com/dospuntocero)) 367 | - 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)) 368 | - German Translation [\#9](https://github.com/UndefinedOffset/SortableGridField/pull/9) ([PatrickElsing](https://github.com/PatrickElsing)) 369 | - Update lang/en.yml [\#8](https://github.com/UndefinedOffset/SortableGridField/pull/8) ([PatrickElsing](https://github.com/PatrickElsing)) 370 | - Renamed the Items array that is sent in post [\#7](https://github.com/UndefinedOffset/SortableGridField/pull/7) ([sheadawson](https://github.com/sheadawson)) 371 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ================= 3 | ## Translations 4 | 5 | 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. 6 | 7 | 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. 8 | 9 | 10 | ## Reporting an issue 11 | 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. 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024, Ed Chipman 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | Neither the name of Ed Chipman nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SortableGridField 2 | ================= 3 | 4 | [![CI](https://github.com/UndefinedOffset/SortableGridField/actions/workflows/ci.yml/badge.svg)](https://github.com/UndefinedOffset/SortableGridField/actions/workflows/ci.yml) 5 | [![Latest Stable Version](https://poser.pugx.org/undefinedoffset/sortablegridfield/version.svg)](http://www.silverstripe.org/stable-download/) 6 | [![Latest Unstable Version](https://poser.pugx.org/undefinedoffset/sortablegridfield/v/unstable.svg)](https://packagist.org/packages/undefinedoffset/sortablegridfield) 7 | [![Total Downloads](https://poser.pugx.org/undefinedoffset/sortablegridfield/downloads.svg)](https://packagist.org/packages/undefinedoffset/sortablegridfield) 8 | [![License](https://poser.pugx.org/undefinedoffset/sortablegridfield/license.svg)](https://github.com/UndefinedOffset/SortableGridField/blob/master/LICENSE) 9 | 10 | Adds drag and drop functionality to Silverstripe's GridField 11 | 12 | ## Requirements 13 | 14 | * Silverstripe 4.11+ or 5.0+ 15 | 16 | ## Installation 17 | 18 | Installation is supported via composer only 19 | 20 | ```sh 21 | composer require undefinedoffset/sortablegridfield 22 | ``` 23 | 24 | * Run `dev/build?flush=all` to regenerate the manifest 25 | * Upon entering the cms and using `GridFieldSortableRows` component for the first time you make need to add `?flush=all` 26 | to the end of the address to force the templates to regenerate 27 | 28 | ## Usage 29 | 30 | To enable sorting on a `has_many` relationship set up an integer field on your data object. Also for `has_many` 31 | relationships make sure to set the `$default_sort` on the `DataObject` to this new integer field to ensure that the sort 32 | order is applied when the relationship is requested. For `many_many` relationships you must add a 33 | `$many_many_extraFields` static to the `DataObject` defining the relationship, see the 34 | [SilverStripe documentation](https://docs.silverstripe.org/en/3/developer_guides/model/relations/#many-many) 35 | for more information on this. If you are using a `many_many` relationship you will need to do a custom getter to set the 36 | sort order of this relationship for use on the front end see below for an example. As well for `many_many` relationships 37 | the name of the GridField *must* be the same as the relationship name other wise error's will occur. For new 38 | `DataObject`s you do not need to increment the sort order yourself in your `DataObject`, `GridFieldSortableRows` will 39 | automatically do this the next time the grid is displayed. 40 | 41 | ```php 42 | public function getMyManyManyRelationship() { 43 | return $this->getManyManyComponents('MyManyManyRelationship')->sort('SortColumn'); 44 | } 45 | ``` 46 | 47 | To enable drag and drop sorting on the `GridField` add the following to your `GridField`'s config, also make sure you add 48 | the namespace `UndefinedOffset\SortableGridField\Forms` to your file. 49 | 50 | ```php 51 | //Namespace imports should be added to the top of your file 52 | use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows; 53 | 54 | $myGridConfig->addComponent(new GridFieldSortableRows('{Column to store sort}')); 55 | ``` 56 | 57 | To move an item to another page drag the row over the respective move to page button which appear on the left and right 58 | of the GridField and release. 59 | 60 | #### Full code Examples 61 | 62 | * [has_many relationship](https://github.com/UndefinedOffset/SortableGridField/blob/master/docs/HasManyExample.md) 63 | * [many_many relationship](https://github.com/UndefinedOffset/SortableGridField/blob/master/docs/ManyManyExample.md) 64 | * [ModelAdmin implementation](https://github.com/UndefinedOffset/SortableGridField/blob/master/docs/ModelAdminExample.md) 65 | 66 | #### Events 67 | 68 | `GridFieldSortableRows` provides 4 "events" `onBeforeGridFieldRowSort()`, `onAfterGridFieldRowSort()`, 69 | `onBeforeGridFieldPageSort()` and `onAfterGridFieldPageSort()`. These "events" are passed a clone of the `DataList` 70 | used in `GridFieldSortableRows`, in the case of page sorting this list has a limit that shows you the current page 71 | plus/minus one object. For `GridFieldSortableRows` that are on `ModelAdmin` descendants these events are called on the 72 | `ModelAdmin` if they do not have an owner `DataObject`, if you are using `GridFieldSortableRows` on a `GridField` for a 73 | `DataObject`'s relationship the events are called on that `DataObject`. 74 | 75 | #### Appending to the top instead of the bottom 76 | 77 | By default `GridFieldSortableRows` appends to the bottom of the list for performance on large data sets, however you can 78 | set new records to append new records to the top by calling `setAppendToTop(true)` on your `GridFieldSortableRows` 79 | instance. 80 | 81 | ```php 82 | //Namespace imports should be added to the top of your file 83 | use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows; 84 | 85 | $myGridConfig->addComponent($sortable = new GridFieldSortableRows('SortOrder')); 86 | $sortable->setAppendToTop(true); 87 | ``` 88 | 89 | #### Working with versioned records 90 | 91 | By default `GridFieldSortableRows` does not update any other stage for versioned than the base stage. However you can 92 | enable this by calling `setUpdateVersionedStage()` and passing in the name of the stage you want to update along with 93 | the base stage. For example passing in "Live" will also update the "Live" stage when any sort happens. 94 | 95 | ```php 96 | //Namespace imports should be added to the top of your file 97 | use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows; 98 | 99 | $myGridConfig->addComponent($sortable = new GridFieldSortableRows('SortOrder')); 100 | $sortable->setUpdateVersionedStage('Live'); 101 | ``` 102 | 103 | #### Overriding the default relationship name 104 | 105 | By default the relationship name comes from the name of the `GridField`, however you can override this lookup by 106 | calling `setCustomRelationName()` and passing in the name of the relationship. This allows for you to have multiple 107 | `GridFields` on the same form interacting with the same `many_many` list maybe filtered slightly differently. 108 | 109 | ```php 110 | //Namespace imports should be added to the top of your file 111 | use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows; 112 | 113 | $myGridConfig->addComponent($sortable = new GridFieldSortableRows('SortOrder')); 114 | $sortable->setCustomRelationName('MyRelationship'); 115 | ``` 116 | 117 | ## Reporting an issue 118 | 119 | When you're reporting an issue please ensure you specify what version of SilverStripe you are using i.e. 3.0.5, 120 | 3.1beta3, 3.0-master etc. Also be sure to include any JavaScript or PHP errors you receive, for PHP errors please ensure 121 | you include the full stack trace. Also please include your implementation code (where your setting up your grid field) 122 | 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 123 | issue. Stick with the issue, remember that you seen the issue not the maintainer of the module so it may take a lot of 124 | questions to arrive at a fix or answer. 125 | 126 | ### Notes 127 | 128 | * When using with GridFieldManyRelationHandler make sure that you add GridFieldSortableRows to your config before for 129 | example `GridFieldManyRelationHandler`: 130 | 131 | ```php 132 | //Namespace imports should be added to the top of your file 133 | use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows; 134 | 135 | $config->addComponent(new GridFieldSortableRows('SortOrder'), 'GridFieldManyRelationHandler'); 136 | ``` 137 | 138 | ## Contributing 139 | 140 | ### Translations 141 | 142 | Translations of the natural language strings are managed through a third party translation interface, transifex.com. 143 | Newly added strings will be periodically uploaded there for translation, and any new translations will be merged back to 144 | the project source code. 145 | 146 | Please use [https://www.transifex.com/projects/p/silverstripe-sortablegridfield](https://www.transifex.com/projects/p/silverstripe-sortablegridfield) 147 | to contribute translations, rather than sending pull requests with YAML files. 148 | -------------------------------------------------------------------------------- /_config.php: -------------------------------------------------------------------------------- 1 | selfOffset + 10 && helperPos < selfOffset + railHeight) { 199 | arrowIcon.css('top', ((helperPos - selfOffset) + arrowIconHeight / 2) + 'px'); 200 | } 201 | } 202 | } 203 | }); 204 | }); 205 | 206 | })(jQuery); 207 | -------------------------------------------------------------------------------- /lang/ar.yml: -------------------------------------------------------------------------------- 1 | ar: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'السماح بإعادة الترتيب عن طريق السحب والإسقاط' 4 | EditPermissionsFailure: 'التحرير غير مرخص له' 5 | -------------------------------------------------------------------------------- /lang/cs.yml: -------------------------------------------------------------------------------- 1 | cs: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Povolit přesuny metodou táhni a pusť' 4 | EditPermissionsFailure: 'Nemáte práva pro úpravu' 5 | -------------------------------------------------------------------------------- /lang/da.yml: -------------------------------------------------------------------------------- 1 | da: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Tillad træk og slip sortering' 4 | EditPermissionsFailure: 'Manglende tilladelse til at redigere' 5 | NEXT: 'Flyt til næste side' 6 | PREVIOUS: 'Flyt til forrige side' 7 | -------------------------------------------------------------------------------- /lang/de.yml: -------------------------------------------------------------------------------- 1 | de: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Drag & Drop neuordnen' 4 | EditPermissionsFailure: 'Keine Berechtigung zu Editieren' 5 | NEXT: 'Zur nächsten Seite wechseln' 6 | PREVIOUS: 'Zur vorherigen Seite wechseln' 7 | -------------------------------------------------------------------------------- /lang/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Allow drag and drop re-ordering' 4 | EditPermissionsFailure: 'No edit permissions' 5 | NEXT: 'Move to Next Page' 6 | PREVIOUS: 'Move to Previous Page' 7 | -------------------------------------------------------------------------------- /lang/eo.yml: -------------------------------------------------------------------------------- 1 | eo: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Enŝalti reordigadon ŝove kaj demete' 4 | EditPermissionsFailure: 'Mankas enhavopermesoj' 5 | NEXT: 'Movi al sekva paĝo' 6 | PREVIOUS: 'Movi al antaŭa paĝo' 7 | -------------------------------------------------------------------------------- /lang/es.yml: -------------------------------------------------------------------------------- 1 | es: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Permitir arrastrar y soltar' 4 | EditPermissionsFailure: 'No tienes permisos de edición' 5 | -------------------------------------------------------------------------------- /lang/fa_IR.yml: -------------------------------------------------------------------------------- 1 | fa_IR: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'اجازه مرتب سازی مجدد با کشیدن و رها کردن' 4 | EditPermissionsFailure: 'بدون مجوز ویرایش' 5 | -------------------------------------------------------------------------------- /lang/fi.yml: -------------------------------------------------------------------------------- 1 | fi: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Salli raahaus ja pudotus' 4 | EditPermissionsFailure: 'Ei oikeuksia muokata' 5 | NEXT: 'Siirrä seuraavalle sivulle' 6 | PREVIOUS: 'Siirrä edelliselle sivulle' 7 | -------------------------------------------------------------------------------- /lang/fr.yml: -------------------------------------------------------------------------------- 1 | fr: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Activer le classement par glisser-déposer' 4 | EditPermissionsFailure: 'Vous n''avez pas les permissions pour éditer' 5 | NEXT: 'Aller à la page suivante' 6 | PREVIOUS: 'Aller à la page précédente' 7 | -------------------------------------------------------------------------------- /lang/hr.yml: -------------------------------------------------------------------------------- 1 | hr: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Omogući raspoređivanje drag and drop metodom' 4 | EditPermissionsFailure: 'Nema dozvola uređivanja' 5 | NEXT: 'Prelazak na sljedeću stranicu' 6 | PREVIOUS: 'Prelazak na prethodnu stranicu' 7 | -------------------------------------------------------------------------------- /lang/it.yml: -------------------------------------------------------------------------------- 1 | it: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Consenti il trascinamento' 4 | EditPermissionsFailure: 'Non hai i permessi per modificare' 5 | NEXT: 'Vai alla pagina successiva' 6 | PREVIOUS: 'Vai alla pagina precedente' 7 | -------------------------------------------------------------------------------- /lang/lt.yml: -------------------------------------------------------------------------------- 1 | lt: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Įjungti tempimo ir įmetimo rėžimą rikiavimui' 4 | EditPermissionsFailure: 'Nėra leidimų redagavimui' 5 | -------------------------------------------------------------------------------- /lang/mi.yml: -------------------------------------------------------------------------------- 1 | mi: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Tukuna te raupapa anō mā te tō me te taka' 4 | EditPermissionsFailure: 'Kāore he whakaaetanga whakatika' 5 | -------------------------------------------------------------------------------- /lang/nb.yml: -------------------------------------------------------------------------------- 1 | nb: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Tillat dra og slipp-sortering' 4 | EditPermissionsFailure: 'Ingen redigeringstilgang' 5 | -------------------------------------------------------------------------------- /lang/nb_NO.yml: -------------------------------------------------------------------------------- 1 | nb_NO: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Tillat dra og slipp-sortering' 4 | EditPermissionsFailure: 'Ingen redigeringstilgang' 5 | -------------------------------------------------------------------------------- /lang/nl.yml: -------------------------------------------------------------------------------- 1 | nl: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Rangschikken met drag & drop toestaan' 4 | EditPermissionsFailure: 'Geen toegang' 5 | NEXT: 'Verplaats naar Volgende Pagina' 6 | PREVIOUS: 'Verplaats naar Vorige Pagina' 7 | -------------------------------------------------------------------------------- /lang/pl.yml: -------------------------------------------------------------------------------- 1 | pl: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Pozwól na reorganizację przez drag and drop' 4 | EditPermissionsFailure: 'Brak możliwości edycji' 5 | NEXT: 'Przenieś do następnej strony' 6 | PREVIOUS: 'Przenieś do poprzedniej strony' 7 | -------------------------------------------------------------------------------- /lang/ru.yml: -------------------------------------------------------------------------------- 1 | ru: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Разрешить перетаскивание для сортировки' 4 | EditPermissionsFailure: 'Нет прав на изменение' 5 | NEXT: 'Переместить на следующую страницу' 6 | PREVIOUS: 'Переместить на предыдущую страницу' 7 | -------------------------------------------------------------------------------- /lang/sk.yml: -------------------------------------------------------------------------------- 1 | sk: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Pracovať v režime Drag & Drop (Ťahaj a pusť)' 4 | EditPermissionsFailure: 'Nemáte oprávnenie upravovať' 5 | NEXT: 'Prejsť na nasledujúcu stránku' 6 | PREVIOUS: 'Prejsť na predchádzajúcu stránku' 7 | -------------------------------------------------------------------------------- /lang/sl.yml: -------------------------------------------------------------------------------- 1 | sl: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Omogoči razvrščanje s "povleci in spusti" ' 4 | EditPermissionsFailure: 'Nimate pravic za urejanje' 5 | NEXT: 'Prestavi na naslednjo stran' 6 | PREVIOUS: 'Prestavi na prejšnjo stran' 7 | -------------------------------------------------------------------------------- /lang/sv.yml: -------------------------------------------------------------------------------- 1 | sv: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: 'Aktivera dra och släpp sortering' 4 | EditPermissionsFailure: 'Rättighet för att redigera saknas' 5 | NEXT: 'Gå till nästa sida' 6 | PREVIOUS: 'Gå till föregående sida' 7 | -------------------------------------------------------------------------------- /lang/zh.yml: -------------------------------------------------------------------------------- 1 | zh: 2 | GridFieldSortableRows: 3 | ALLOW_DRAG_DROP: '允许拖放重新排序' 4 | EditPermissionsFailure: '没有编辑权限' 5 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodeSniffer ruleset for SilverStripe coding conventions. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | warning 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ./src 46 | ./tests 47 | ./_config.php 48 | 49 | 50 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests/PHPUnit 6 | 7 | 8 | 9 | 10 | 11 | sanitychecks 12 | 13 | 14 | 15 | 16 | 17 | src/ 18 | 19 | 20 | tests/ 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Forms/GridFieldSortableRows.php: -------------------------------------------------------------------------------- 1 | sortColumn = $sortColumn; 60 | $this->disable_selection = $disableSelection; 61 | $this->update_versioned_stage = $updateVersionStage; 62 | $this->custom_relation_name = $customRelationName; 63 | } 64 | 65 | /** 66 | * Returns a map where the keys are fragment names and the values are pieces of HTML to add to these fragments. 67 | * @param GridField $gridField Grid Field Reference 68 | * @return array Map where the keys are fragment names and the values are pieces of HTML to add to these fragments. 69 | */ 70 | public function getHTMLFragments($gridField) 71 | { 72 | $dataList = $gridField->getList(); 73 | 74 | if (class_exists('UnsavedRelationList') && $dataList instanceof UnsavedRelationList) { 75 | return []; 76 | } 77 | 78 | $state = $gridField->State->GridFieldSortableRows; 79 | if (!is_bool($state->sortableToggle)) { 80 | $state->sortableToggle = false; 81 | } 82 | 83 | //Ensure user can edit 84 | if (!singleton($gridField->getModelClass())->canEdit()) { 85 | return []; 86 | } 87 | 88 | 89 | //Sort order toggle 90 | $sortOrderToggle = GridField_FormAction::create( 91 | $gridField, 92 | 'sortablerows-toggle', 93 | 'sorttoggle', 94 | 'sortableRowsToggle', 95 | null 96 | )->addExtraClass('sortablerows-toggle'); 97 | 98 | 99 | $sortOrderSave = GridField_FormAction::create( 100 | $gridField, 101 | 'sortablerows-savesort', 102 | 'savesort', 103 | 'saveGridRowSort', 104 | null 105 | )->addExtraClass('sortablerows-savesort'); 106 | 107 | 108 | //Sort to Page Action 109 | $sortToPage = GridField_FormAction::create( 110 | $gridField, 111 | 'sortablerows-sorttopage', 112 | 'sorttopage', 113 | 'sortToPage', 114 | null 115 | )->addExtraClass('sortablerows-sorttopage'); 116 | 117 | 118 | $data = [ 119 | 'SortableToggle' => $sortOrderToggle, 120 | 'SortOrderSave' => $sortOrderSave, 121 | 'SortToPage' => $sortToPage, 122 | 'Checked' => ($state->sortableToggle == true ? ' checked = "checked"' : ''), 123 | 'List' => $dataList, 124 | ]; 125 | 126 | $forTemplate = new ArrayData($data); 127 | 128 | Requirements::css('undefinedoffset/sortablegridfield:css/GridFieldSortableRows.css'); 129 | Requirements::javascript('undefinedoffset/sortablegridfield:javascript/GridFieldSortableRows.js'); 130 | 131 | $args = ['Colspan' => count($gridField->getColumns()), 'ID' => $gridField->ID(), 'DisableSelection' => $this->disable_selection]; 132 | 133 | $fragments = ['header' => $forTemplate->renderWith('SortableGridField\Forms\Includes\GridFieldSortableRows', $args)]; 134 | 135 | if ($gridField->getConfig()->getComponentByType(GridFieldPaginator::class)) { 136 | $fragments['after'] = $forTemplate->renderWith('SortableGridField\Forms\Includes\GridFieldSortableRows_paginator'); 137 | } 138 | 139 | return $fragments; 140 | } 141 | 142 | /** 143 | * Manipulate the datalist as needed by this grid modifier. 144 | * @param GridField $gridField Grid Field Reference 145 | * @param SS_List|\SilverStripe\ORM\DataList $dataList Data List to adjust 146 | * @return \SilverStripe\ORM\DataList Modified Data List 147 | */ 148 | public function getManipulatedData(GridField $gridField, SS_List $dataList) 149 | { 150 | //Detect and correct items with a sort column value of 0 (push to bottom) 151 | $this->fixSortColumn($gridField, $dataList); 152 | 153 | 154 | $headerState = $gridField->State->GridFieldSortableHeader; 155 | $state = $gridField->State->GridFieldSortableRows; 156 | if ((!is_bool($state->sortableToggle) || $state->sortableToggle === false) && $headerState && is_string($headerState->SortColumn) && is_string($headerState->SortDirection)) { 157 | return $dataList->sort($headerState->SortColumn, $headerState->SortDirection); 158 | } 159 | 160 | if ($state->sortableToggle === true) { 161 | $gridField->getConfig()->removeComponentsByType(GridFieldFilterHeader::class); 162 | $gridField->getConfig()->removeComponentsByType(GridFieldSortableHeader::class); 163 | } 164 | 165 | return $dataList->sort($this->sortColumn); 166 | } 167 | 168 | /** 169 | * Sets if new records should be appended to the top or the bottom of the list 170 | * @param bool $value Boolean true to append to the top false to append to the bottom 171 | * @return GridFieldSortableRows Returns the current instance 172 | */ 173 | public function setAppendToTop($value) 174 | { 175 | $this->append_to_top = $value; 176 | return $this; 177 | } 178 | 179 | /** 180 | * @param bool $value Boolean true to disable selection of table contents false to enable selection 181 | * @return GridFieldSortableRows Returns the current instance 182 | */ 183 | public function setDisableSelection($value) 184 | { 185 | $this->disable_selection = $value; 186 | return $this; 187 | } 188 | 189 | /** 190 | * Sets the suffix of the versioned stage that should be updated along side the default stage 191 | * @param string $value Versioned Stage to update this is disabled by default unless this is set 192 | * @return GridFieldSortableRows Returns the current instance 193 | */ 194 | public function setUpdateVersionedStage($value) 195 | { 196 | $this->update_versioned_stage = $value; 197 | return $this; 198 | } 199 | 200 | /** 201 | * Sets the name of the relationship to use, by default the name is determined from the GridField's name 202 | * @param string $value Name of the relationship to use, by default the name is determined from the GridField's name 203 | * @return GridFieldSortableRows Returns the current instance 204 | */ 205 | public function setCustomRelationName($value) 206 | { 207 | $this->custom_relation_name = $value; 208 | return $this; 209 | } 210 | 211 | /** 212 | * Detects and corrects items with a sort column value of 0, by appending them to the bottom of the list 213 | * @param GridField $gridField Grid Field Reference 214 | * @param SS_List|\SilverStripe\ORM\DataList $dataList Data List of items to be checked 215 | */ 216 | protected function fixSortColumn($gridField, SS_List $dataList) 217 | { 218 | if ($dataList instanceof UnsavedRelationList) { 219 | return; 220 | } 221 | 222 | /** @var SS_List|\SilverStripe\ORM\DataList $list */ 223 | $list = clone $dataList; 224 | $list = $list->alterDataQuery(function ($query, SS_List $tmplist) { 225 | /** @var \SilverStripe\ORM\DataQuery $query */ 226 | $query->limit(null); 227 | return $query; 228 | }); 229 | 230 | $many_many = ($list instanceof ManyManyList || $list instanceof ManyManyThroughList); 231 | if (!$many_many) { 232 | $sng = singleton($gridField->getModelClass()); 233 | $fieldType = $sng->config()->db[$this->sortColumn]; 234 | 235 | if (!$fieldType || !($fieldType == 'Int' || $fieldType == 'SilverStripe\\ORM\\FieldType\\DBInt' || is_subclass_of($fieldType, 'SilverStripe\\ORM\\FieldType\\DBInt'))) { 236 | if (is_array($fieldType)) { 237 | user_error('Sort column ' . $this->sortColumn . ' could not be found in ' . $gridField->getModelClass() . '\'s ancestry', E_USER_ERROR); 238 | } else { 239 | user_error('Sort column ' . $this->sortColumn . ' must be an instance of SilverStripe\\ORM\\FieldType\\DBInt, column is of type ' . $fieldType, E_USER_ERROR); 240 | } 241 | 242 | exit; 243 | } 244 | } 245 | 246 | $max = $list->Max($this->sortColumn); 247 | $list = $list->filter($this->sortColumn, 0)->sort("Created,ID"); 248 | if ($list->Count() > 0) { 249 | $owner = $gridField->getForm()->getRecord(); 250 | $sortColumn = $this->sortColumn; 251 | $i = 1; 252 | 253 | if ($many_many) { 254 | $schema = Injector::inst()->get(DataObjectSchema::class); 255 | $componentDetails = $schema->manyManyComponent(get_class($owner), (!empty($this->custom_relation_name) ? $this->custom_relation_name : $gridField->getName())); 256 | if (empty($componentDetails)) { 257 | 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); 258 | } 259 | 260 | $parentField = $componentDetails['parentField']; 261 | $componentField = $componentDetails['childField']; 262 | $table = $componentDetails['join']; 263 | 264 | //For ManyManyThroughLists get the right join table 265 | if ($list instanceof ManyManyThroughList && class_exists($table)) { 266 | $table = $schema->tableName($table); 267 | } 268 | 269 | $extraFields = $list->getExtraFields(); 270 | 271 | 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]))) { 272 | user_error('Sort column ' . $this->sortColumn . ' must be an SilverStripe\\ORM\\FieldType\\DBInt, column is of type ' . $extraFields[$this->sortColumn], E_USER_ERROR); 273 | exit; 274 | } 275 | } else { 276 | //Find table containing the sort column 277 | $table = false; 278 | $class = $gridField->getModelClass(); 279 | 280 | $db = Config::inst()->get($class, "db", CONFIG::UNINHERITED); 281 | if (!empty($db) && array_key_exists($sortColumn, $db)) { 282 | $table = DataObject::getSchema()->tableName($class); 283 | } else { 284 | $classes = ClassInfo::ancestry($class, true); 285 | foreach ($classes as $class) { 286 | $db = Config::inst()->get($class, "db", CONFIG::UNINHERITED); 287 | if (!empty($db) && array_key_exists($sortColumn, $db)) { 288 | $table = DataObject::getSchema()->tableName($class); 289 | break; 290 | } 291 | } 292 | } 293 | 294 | if ($table === false) { 295 | user_error('Sort column ' . $this->sortColumn . ' could not be found in ' . $gridField->getModelClass() . '\'s ancestry', E_USER_ERROR); 296 | exit; 297 | } 298 | 299 | $baseDataClass = DataObject::getSchema()->baseDataClass($gridField->getModelClass()); 300 | $baseDataTable = DataObject::getSchema()->tableName($baseDataClass); 301 | } 302 | 303 | 304 | //Start transaction if supported 305 | if (DB::get_conn()->supportsTransactions()) { 306 | DB::get_conn()->transactionStart(); 307 | } 308 | 309 | $idCondition = null; 310 | if ($this->append_to_top && !($list instanceof RelationList)) { 311 | $idCondition = '"ID" IN(\'' . implode("','", $dataList->getIDList()) . '\')'; 312 | } 313 | 314 | if ($this->append_to_top) { 315 | $topIncremented = []; 316 | } 317 | 318 | $modelClass = $gridField->getModelClass(); 319 | $hasVersioned = $this->hasVersionedExtension($modelClass); 320 | foreach ($list as $obj) { 321 | if ($many_many) { 322 | if ($this->append_to_top) { 323 | //Upgrade all the records (including the last inserted from 0 to 1) 324 | DB::query('UPDATE "' . $table 325 | . '" SET "' . $sortColumn . '" = "' . $sortColumn . '"+1' 326 | . ' WHERE "' . $parentField . '" = ' . $owner->ID . (!empty($topIncremented) ? ' AND "' . $componentField . '" NOT IN(\'' . implode('\',\'', $topIncremented) . '\')' : '')); 327 | 328 | $topIncremented[] = $obj->ID; 329 | } else { 330 | //Append the last record to the bottom 331 | DB::query('UPDATE "' . $table 332 | . '" SET "' . $sortColumn . '" = ' . ($max + $i) 333 | . ' WHERE "' . $componentField . '" = ' . $obj->ID . ' AND "' . $parentField . '" = ' . $owner->ID); 334 | } 335 | } else if ($this->append_to_top) { 336 | if ($hasVersioned) { 337 | // For versioned objects, modify them with the ORM so that the *_versions table is updated 338 | $itemsToUpdate = $modelClass::get()->where(($list instanceof RelationList ? '"' . $list->foreignKey . '" = ' . $owner->ID : $idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\'' . implode('\',\'', $topIncremented) . '\')' : '')); 339 | if ($itemsToUpdate->exists()) { 340 | foreach ($itemsToUpdate as $item) { 341 | $item->$sortColumn = $item->$sortColumn + 1; 342 | $item->write(); 343 | } 344 | } 345 | } else { 346 | //Upgrade all the records (including the last inserted from 0 to 1) 347 | DB::query('UPDATE "' . $table 348 | . '" SET "' . $sortColumn . '" = "' . $sortColumn . '"+1' 349 | . ' WHERE ' . ($list instanceof RelationList ? '"' . $list->foreignKey . '" = ' . $owner->ID : $idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\'' . implode('\',\'', $topIncremented) . '\')' : '')); 350 | } 351 | 352 | if ($this->update_versioned_stage && $this->hasVersionedExtension($gridField->getModelClass())) { 353 | DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage 354 | . '" SET "' . $sortColumn . '" = "' . $sortColumn . '"+1' 355 | . ' WHERE ' . ($list instanceof RelationList ? '"' . $list->foreignKey . '" = ' . $owner->ID : $idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\'' . implode('\',\'', $topIncremented) . '\')' : '')); 356 | } 357 | 358 | $topIncremented[] = $obj->ID; 359 | } else { 360 | if ($hasVersioned) { 361 | // For versioned objects, modify them with the ORM so that the *_versions table is updated 362 | $obj->$sortColumn = ($max + $i); 363 | $obj->write(); 364 | } else { 365 | //Append the last record to the bottom 366 | DB::query('UPDATE "' . $table 367 | . '" SET "' . $sortColumn . '" = ' . ($max + $i) 368 | . ' WHERE "ID" = ' . $obj->ID); 369 | 370 | //LastEdited 371 | DB::query('UPDATE "' . $baseDataTable 372 | . '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\'' 373 | . ' WHERE "ID" = ' . $obj->ID); 374 | } 375 | 376 | if ($this->update_versioned_stage && $this->hasVersionedExtension($gridField->getModelClass())) { 377 | DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage 378 | . '" SET "' . $sortColumn . '" = ' . ($max + $i) 379 | . ' WHERE "ID" = ' . $obj->ID); 380 | 381 | if ($this->hasVersionedExtension($baseDataClass)) { 382 | DB::query('UPDATE "' . $baseDataTable . '_' . $this->update_versioned_stage 383 | . '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\'' 384 | . ' WHERE "ID" = ' . $obj->ID); 385 | } 386 | } 387 | } 388 | 389 | $i++; 390 | } 391 | 392 | //Update LastEdited for affected records when using append to top on a many_many relationship 393 | if (!$many_many && $this->append_to_top && count($topIncremented) > 0) { 394 | DB::query('UPDATE "' . $baseDataTable 395 | . '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\'' 396 | . ' WHERE "ID" IN(\'' . implode('\',\'', $topIncremented) . '\')'); 397 | 398 | if ($this->update_versioned_stage && $this->hasVersionedExtension($gridField->getModelClass()) && $this->hasVersionedExtension($baseDataClass)) { 399 | DB::query('UPDATE "' . $baseDataTable . '_' . $this->update_versioned_stage 400 | . '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\'' 401 | . ' WHERE "ID" IN(\'' . implode('\',\'', $topIncremented) . '\')'); 402 | } 403 | } 404 | 405 | 406 | //End transaction if supported 407 | if (DB::get_conn()->supportsTransactions()) { 408 | DB::get_conn()->transactionEnd(); 409 | } 410 | } 411 | } 412 | 413 | /** 414 | * Return a list of the actions handled by this action provider. 415 | * @param GridField $gridField Grid Field Reference 416 | * @return array array with action identifier strings. 417 | */ 418 | public function getActions($gridField) 419 | { 420 | return ['saveGridRowSort', 'sortableRowsToggle', 'sortToPage']; 421 | } 422 | 423 | /** 424 | * Handle an action on the given grid field. 425 | * @param GridField $gridField Grid Field Reference 426 | * @param String $actionName Action identifier, see {@link getActions()}. 427 | * @param array $arguments Arguments relevant for this 428 | * @param array $data All form data 429 | */ 430 | public function handleAction(GridField $gridField, $actionName, $arguments, $data) 431 | { 432 | $state = $gridField->State->GridFieldSortableRows; 433 | if (!is_bool($state->sortableToggle)) { 434 | $state->sortableToggle = false; 435 | } else if ($state->sortableToggle == true) { 436 | $gridField->getConfig()->removeComponentsByType(GridFieldFilterHeader::class); 437 | $gridField->getConfig()->removeComponentsByType(GridFieldSortableHeader::class); 438 | } 439 | 440 | 441 | if ($actionName == 'savegridrowsort') { 442 | return $this->saveGridRowSort($gridField, $data); 443 | } else if ($actionName == 'sorttopage') { 444 | return $this->sortToPage($gridField, $data); 445 | } 446 | } 447 | 448 | /** 449 | * Handles saving of the row sort order 450 | * @param GridField $gridField Grid Field Reference 451 | * @param array $data Data submitted in the request 452 | * @throws ValidationException If user has no edit permissions 453 | */ 454 | protected function saveGridRowSort(GridField $gridField, $data) 455 | { 456 | $dataList = $gridField->getList(); 457 | 458 | if ($dataList instanceof UnsavedRelationList) { 459 | user_error('Cannot sort an UnsavedRelationList', E_USER_ERROR); 460 | return; 461 | } 462 | 463 | if (!singleton($gridField->getModelClass())->canEdit()) { 464 | throw new ValidationException(_t('GridFieldSortableRows.EditPermissionsFailure', "No edit permissions"), 0); 465 | } 466 | 467 | if (empty($data['ItemIDs'])) { 468 | user_error('No items to sort', E_USER_ERROR); 469 | } 470 | 471 | $className = $gridField->getModelClass(); 472 | $owner = $gridField->Form->getRecord(); 473 | $items = clone $gridField->getList(); 474 | $many_many = ($items instanceof ManyManyList || $items instanceof ManyManyThroughList); 475 | $sortColumn = $this->sortColumn; 476 | $pageOffset = 0; 477 | 478 | if ($paginator = $gridField->getConfig()->getComponentsByType(GridFieldPaginator::class)->First()) { 479 | $pageState = $gridField->State->GridFieldPaginator; 480 | 481 | if ($pageState->currentPage && is_int($pageState->currentPage) && $pageState->currentPage > 1) { 482 | $pageOffset = $paginator->getItemsPerPage() * ($pageState->currentPage - 1); 483 | } 484 | } 485 | 486 | 487 | if ($many_many) { 488 | $schema = Injector::inst()->get(DataObjectSchema::class); 489 | $componentDetails = $schema->manyManyComponent(get_class($owner), (!empty($this->custom_relation_name) ? $this->custom_relation_name : $gridField->getName())); 490 | if (empty($componentDetails)) { 491 | 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); 492 | } 493 | 494 | $parentField = $componentDetails['parentField']; 495 | $componentField = $componentDetails['childField']; 496 | $table = $componentDetails['join']; 497 | 498 | //For ManyManyThroughLists get the right join table 499 | if ($items instanceof ManyManyThroughList && class_exists($table)) { 500 | $table = $schema->tableName($table); 501 | } 502 | } else { 503 | //Find table containing the sort column 504 | $table = false; 505 | $class = $gridField->getModelClass(); 506 | $db = Config::inst()->get($class, "db", CONFIG::UNINHERITED); 507 | if (!empty($db) && array_key_exists($sortColumn, $db)) { 508 | $table = DataObject::getSchema()->tableName($class); 509 | } else { 510 | $classes = ClassInfo::ancestry($class, true); 511 | foreach ($classes as $class) { 512 | $db = Config::inst()->get($class, "db", CONFIG::UNINHERITED); 513 | if (!empty($db) && array_key_exists($sortColumn, $db)) { 514 | $table = DataObject::getSchema()->tableName($class); 515 | break; 516 | } 517 | } 518 | } 519 | 520 | if ($table === false) { 521 | user_error('Sort column ' . $this->sortColumn . ' could not be found in ' . $gridField->getModelClass() . '\'s ancestry', E_USER_ERROR); 522 | exit; 523 | } 524 | 525 | $baseDataClass = DataObject::getSchema()->baseDataClass($gridField->getModelClass()); 526 | $baseDataTable = DataObject::getSchema()->tableName($baseDataClass); 527 | } 528 | 529 | 530 | //Event to notify the Controller or owner DataObject before list sort 531 | if ($owner && $owner instanceof DataObject && method_exists($owner, 'onBeforeGridFieldRowSort')) { 532 | $owner->onBeforeGridFieldRowSort(clone $items); 533 | } else if (Controller::has_curr() && Controller::curr() instanceof ModelAdmin && method_exists(Controller::curr(), 'onBeforeGridFieldRowSort')) { 534 | Controller::curr()->onBeforeGridFieldRowSort(clone $items); 535 | } 536 | 537 | //Start transaction if supported 538 | if (DB::get_conn()->supportsTransactions()) { 539 | DB::get_conn()->transactionStart(); 540 | } 541 | 542 | //Perform sorting 543 | $ids = explode(',', $data['ItemIDs']); 544 | $modelClass = $gridField->getModelClass(); 545 | $hasVersioned = $this->hasVersionedExtension($modelClass); 546 | for ($sort = 0; $sort < count($ids); $sort++) { 547 | $id = intval($ids[$sort]); 548 | if ($many_many) { 549 | DB::query('UPDATE "' . $table 550 | . '" SET "' . $sortColumn . '" = ' . (($sort + 1) + $pageOffset) 551 | . ' WHERE "' . $componentField . '" = ' . $id . ' AND "' . $parentField . '" = ' . $owner->ID); 552 | } else { 553 | if ($hasVersioned) { 554 | // For versioned objects, modify them with the ORM so that the *_versions table is updated 555 | $obj = $modelClass::get()->byID(intval($id)); 556 | if (!empty($obj) && $obj !== false && $obj->exists()) { 557 | $obj->$sortColumn = (($sort + 1) + $pageOffset); 558 | $obj->write(); 559 | } 560 | } else { 561 | DB::query('UPDATE "' . $table 562 | . '" SET "' . $sortColumn . '" = ' . (($sort + 1) + $pageOffset) 563 | . ' WHERE "ID" = ' . $id); 564 | 565 | DB::query('UPDATE "' . $baseDataTable 566 | . '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\'' 567 | . ' WHERE "ID" = ' . $id); 568 | } 569 | 570 | if ($this->update_versioned_stage && $hasVersioned) { 571 | DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage 572 | . '" SET "' . $sortColumn . '" = ' . (($sort + 1) + $pageOffset) 573 | . ' WHERE "ID" = ' . $id); 574 | 575 | if ($this->hasVersionedExtension($baseDataClass)) { 576 | DB::query('UPDATE "' . $baseDataTable . '_' . $this->update_versioned_stage 577 | . '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\'' 578 | . ' WHERE "ID" = ' . $id); 579 | } 580 | } 581 | } 582 | } 583 | 584 | 585 | //End transaction if supported 586 | if (DB::get_conn()->supportsTransactions()) { 587 | DB::get_conn()->transactionEnd(); 588 | } 589 | 590 | 591 | //Event to notify the Controller or owner DataObject after list sort 592 | if ($owner && $owner instanceof DataObject && method_exists($owner, 'onAfterGridFieldRowSort')) { 593 | $owner->onAfterGridFieldRowSort(clone $items); 594 | } else if (Controller::has_curr() && Controller::curr() instanceof ModelAdmin && method_exists(Controller::curr(), 'onAfterGridFieldRowSort')) { 595 | Controller::curr()->onAfterGridFieldRowSort(clone $items); 596 | } 597 | } 598 | 599 | /** 600 | * Handles sorting across pages 601 | * @param GridField $gridField Grid Field Reference 602 | * @param array $data Data submitted in the request 603 | */ 604 | protected function sortToPage(GridField $gridField, $data) 605 | { 606 | if (!$paginator = $gridField->getConfig()->getComponentsByType(GridFieldPaginator::class)->First()) { 607 | user_error('Paginator not detected', E_USER_ERROR); 608 | } 609 | 610 | if (empty($data['ItemID'])) { 611 | user_error('No item to sort', E_USER_ERROR); 612 | } 613 | 614 | if (empty($data['Target'])) { 615 | user_error('No target page', E_USER_ERROR); 616 | } 617 | 618 | /** @var \SilverStripe\Core\Extensible $className */ 619 | $className = $gridField->getModelClass(); 620 | $owner = $gridField->Form->getRecord(); 621 | 622 | /** @var DataList $items */ 623 | $items = clone $gridField->getList(); 624 | 625 | $many_many = ($items instanceof ManyManyList || $items instanceof ManyManyThroughList); 626 | $sortColumn = $this->sortColumn; 627 | $targetItem = $items->byID(intval($data['ItemID'])); 628 | 629 | if (!$targetItem) { 630 | user_error('Target item not found', E_USER_ERROR); 631 | } 632 | 633 | $currentPage = 1; 634 | 635 | $pageState = $gridField->State->GridFieldPaginator; 636 | if ($pageState->currentPage && $pageState->currentPage > 1) { 637 | $currentPage = $pageState->currentPage; 638 | } 639 | 640 | if ($many_many) { 641 | $schema = Injector::inst()->get(DataObjectSchema::class); 642 | $componentDetails = $schema->manyManyComponent(get_class($owner), (!empty($this->custom_relation_name) ? $this->custom_relation_name : $gridField->getName())); 643 | $parentField = $componentDetails['parentField']; 644 | $componentField = $componentDetails['childField']; 645 | $table = $componentDetails['join']; 646 | 647 | //For ManyManyThroughLists get the right join table 648 | if ($items instanceof ManyManyThroughList && class_exists($table)) { 649 | $table = $schema->tableName($table); 650 | } 651 | } 652 | 653 | if ($data['Target'] == 'previouspage') { 654 | $items = $items->limit($paginator->getItemsPerPage() + 1, ($paginator->getItemsPerPage() * ($currentPage - 1)) - 1); 655 | } else if ($data['Target'] == 'nextpage') { 656 | $items = $items->limit($paginator->getItemsPerPage() + 1, $paginator->getItemsPerPage() * ($currentPage - 1)); 657 | } else { 658 | user_error('Not implemented: ' . $data['Target'], E_USER_ERROR); 659 | } 660 | 661 | $sortPositions = $items->column($sortColumn); 662 | 663 | //Event to notify the Controller or owner DataObject before list sort 664 | if ($owner && $owner instanceof DataObject && method_exists($owner, 'onBeforeGridFieldPageSort')) { 665 | $owner->onBeforeGridFieldPageSort(clone $items); 666 | } else if (Controller::has_curr() && Controller::curr() instanceof ModelAdmin && method_exists(Controller::curr(), 'onBeforeGridFieldPageSort')) { 667 | Controller::curr()->onBeforeGridFieldPageSort(clone $items); 668 | } 669 | 670 | //Find the sort column 671 | if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) { 672 | $table = false; 673 | $classes = ClassInfo::ancestry($className, true); 674 | foreach ($classes as $class) { 675 | $db = Config::inst()->get($class, "db", CONFIG::UNINHERITED); 676 | if (!empty($db) && array_key_exists($sortColumn, $db)) { 677 | $table = DataObject::getSchema()->tableName($class); 678 | break; 679 | } 680 | } 681 | 682 | if ($table === false) { 683 | user_error('Sort column ' . $this->sortColumn . ' could not be found in ' . $gridField->getModelClass() . '\'s ancestry', E_USER_ERROR); 684 | exit; 685 | } 686 | } 687 | 688 | //Start transaction if supported 689 | if (DB::get_conn()->supportsTransactions()) { 690 | DB::get_conn()->transactionStart(); 691 | } 692 | 693 | if ($data['Target'] == 'previouspage') { 694 | if ($many_many) { 695 | DB::query('UPDATE "' . $table 696 | . '" SET "' . $sortColumn . '" = ' . $sortPositions[0] 697 | . ' WHERE "' . $componentField . '" = ' . $targetItem->ID . ' AND "' . $parentField . '" = ' . $owner->ID); 698 | } else { 699 | $targetItem->$sortColumn = $sortPositions[0]; 700 | $targetItem->write(); 701 | 702 | if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) { 703 | DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage 704 | . '" SET "' . $sortColumn . '" = ' . $sortPositions[0] 705 | . ' WHERE "ID" = ' . $targetItem->ID); 706 | } 707 | } 708 | 709 | $i = 1; 710 | foreach ($items as $obj) { 711 | if ($obj->ID == $targetItem->ID || $i >= count($sortPositions)) { 712 | continue; 713 | } 714 | 715 | 716 | if ($many_many) { 717 | DB::query('UPDATE "' . $table 718 | . '" SET "' . $sortColumn . '" = ' . $sortPositions[$i] 719 | . ' WHERE "' . $componentField . '" = ' . $obj->ID . ' AND "' . $parentField . '" = ' . $owner->ID); 720 | } else { 721 | $obj->$sortColumn = $sortPositions[$i]; 722 | $obj->write(); 723 | 724 | if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) { 725 | DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage 726 | . '" SET "' . $sortColumn . '" = ' . $sortPositions[$i] 727 | . ' WHERE "ID" = ' . $obj->ID); 728 | } 729 | } 730 | 731 | $i++; 732 | } 733 | } else { 734 | if ($many_many) { 735 | DB::query('UPDATE "' . $table 736 | . '" SET "' . $sortColumn . '" = ' . $sortPositions[count($sortPositions) - 1] 737 | . ' WHERE "' . $componentField . '" = ' . $targetItem->ID . ' AND "' . $parentField . '" = ' . $owner->ID); 738 | } else { 739 | $targetItem->$sortColumn = $sortPositions[count($sortPositions) - 1]; 740 | $targetItem->write(); 741 | 742 | if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) { 743 | DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage 744 | . '" SET "' . $sortColumn . '" = ' . $sortPositions[count($sortPositions) - 1] 745 | . ' WHERE "ID" = ' . $targetItem->ID); 746 | } 747 | } 748 | 749 | $i = 0; 750 | foreach ($items as $obj) { 751 | if ($obj->ID == $targetItem->ID) { 752 | continue; 753 | } 754 | 755 | 756 | if ($many_many) { 757 | DB::query('UPDATE "' . $table 758 | . '" SET "' . $sortColumn . '" = ' . $sortPositions[$i] 759 | . ' WHERE "' . $componentField . '" = ' . $obj->ID . ' AND "' . $parentField . '" = ' . $owner->ID); 760 | } else { 761 | $obj->$sortColumn = $sortPositions[$i]; 762 | $obj->write(); 763 | 764 | if ($this->update_versioned_stage && $this->hasVersionedExtension($className)) { 765 | DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage 766 | . '" SET "' . $sortColumn . '" = ' . $sortPositions[$i] 767 | . ' WHERE "ID" = ' . $obj->ID); 768 | } 769 | } 770 | 771 | $i++; 772 | } 773 | } 774 | 775 | //End transaction if supported 776 | if (DB::get_conn()->supportsTransactions()) { 777 | DB::get_conn()->transactionEnd(); 778 | } 779 | 780 | //Event to notify the Controller or owner DataObject after list sort 781 | if ($owner && $owner instanceof DataObject && method_exists($owner, 'onAfterGridFieldPageSort')) { 782 | $owner->onAfterGridFieldPageSort(clone $items); 783 | } else if (Controller::has_curr() && Controller::curr() instanceof ModelAdmin && method_exists(Controller::curr(), 'onAfterGridFieldPageSort')) { 784 | Controller::curr()->onAfterGridFieldPageSort(clone $items); 785 | } 786 | } 787 | 788 | /** 789 | * Check to see if the given class name has the Versioned extension 790 | * @param \SilverStripe\Core\Extensible|string $className 791 | * @return bool 792 | */ 793 | public function hasVersionedExtension($className) 794 | { 795 | return $className::has_extension(Versioned::class); 796 | } 797 | 798 | /** 799 | * Checks to see if $table_name is declared on the DataObject, if not returns string as given 800 | * @param $className 801 | * @return string 802 | * @deprecated Use DataObject::getSchema()->tableName() instead 803 | */ 804 | public function mapTableNameAndReturn($className) 805 | { 806 | return DataObject::getSchema()->tableName($className); 807 | } 808 | } 809 | -------------------------------------------------------------------------------- /templates/SortableGridField/Forms/Includes/GridFieldSortableRows.ss: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | $SortableToggle 7 | $SortOrderSave 8 | $SortToPage 9 |
10 | 11 | -------------------------------------------------------------------------------- /templates/SortableGridField/Forms/Includes/GridFieldSortableRows_paginator.ss: -------------------------------------------------------------------------------- 1 | <% if $Checked %> 2 |
3 | <%t GridFieldSortableRows.PREVIOUS 'Move to Previous Page' %> 4 | 5 | <%t GridFieldSortableRows.NEXT 'Move to Next Page' %> 6 |
7 | <% end_if %> 8 | --------------------------------------------------------------------------------