diff --git a/code/forms/GridFieldSortableRows.php b/code/forms/GridFieldSortableRows.php index 786f3bd..abe8f6f 100644 --- a/code/forms/GridFieldSortableRows.php +++ b/code/forms/GridFieldSortableRows.php @@ -53,8 +53,20 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP )->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); @@ -91,7 +103,7 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP * @return Array Array with action identifier strings. */ public function getActions($gridField) { - return array('saveGridRowSort', 'sortableRowsDisablePaginator'); + return array('saveGridRowSort', 'sortableRowsDisablePaginator', 'sortToPage'); } /** @@ -113,7 +125,9 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP if ($actionName == 'savegridrowsort') { return $this->saveGridRowSort($gridField, $data); - } + } else if ($actionName == 'sorttopage') { + return $this->sortToPage($gridField, $data); + } } /** @@ -121,7 +135,7 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP * @param GridField $gridField Grid Field Reference * @param Array $data Data submitted in the request */ - private function saveGridRowSort(GridField $gridField, $data) { + protected function saveGridRowSort(GridField $gridField, $data) { if(!singleton($gridField->getModelClass())->canEdit()){ throw new ValidationException(_t('GridFieldSortableRows.EditPermissionsFailure', "No edit permissions"),0); } @@ -137,10 +151,10 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP $sortColumn = $this->sortColumn; $pageOffset = 0; - if ($paginator=$gridField->getConfig()->getComponentsByType('GridFieldPaginator')->First()) { - $pageState=$gridField->State->GridFieldPaginator; + if ($paginator = $gridField->getConfig()->getComponentsByType('GridFieldPaginator')->First()) { + $pageState = $gridField->State->GridFieldPaginator; if($pageState->currentPage && $pageState->currentPage>1) { - $pageOffset=$paginator->getItemsPerPage()*($pageState->currentPage-1); + $pageOffset = $paginator->getItemsPerPage() * ($pageState->currentPage - 1); } } @@ -155,16 +169,18 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP DB::getConn()->transactionStart(); } + + //@TODO Need to optimize this to eliminate some of the resource load could use raw queries to be more efficient $data['Items'] = explode(',', $data['Items']); for($sort = 0;$sort$sortColumn = ($sort+1)+$pageOffset; + $obj->$sortColumn = ($sort + 1) + $pageOffset; $obj->write(); } } @@ -174,5 +190,100 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP 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 = $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); + } + + $sortPosition = $targetItem->$sortColumn; + $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'] == 'firstpage') { + $sortPosition = $paginator->getItemsPerPage(); + } else if ($data['Target'] == 'previouspage') { + $sortPosition = $paginator->getItemsPerPage() * ($currentPage - 1); + } else if ($data['Target'] == 'nextpage') { + $sortPosition = ($paginator->getItemsPerPage() * $currentPage) + 1; + } else if ($data['Target'] == 'lastpage') { + $sortPosition = ($paginator->getItemsPerPage() * (ceil($items->count() / $paginator->getItemsPerPage()) - 1)) + 1; + } else { + user_error('Not implemented: '.$data['Target'], E_USER_ERROR); + } + + + if($targetItem->$sortColumn != $sortPosition) { + //Start transaction if supported + if(DB::getConn()->supportsTransactions()) { + DB::getConn()->transactionStart(); + } + + + //Swap with the item around the target position + $swapItem = $items->where('"'.$sortColumn.'" >= '.$sortPosition)->First(); + if ($many_many) { + DB::query('UPDATE "' . $table + . '" SET "' . $sortColumn.'" = ' . $targetItem->$sortColumn + . ' WHERE "' . $componentField . '" = ' . $targetItem->ID . ' AND "' . $parentField . '" = ' . $owner->ID); + } else { + $swapItem->$sortColumn = $targetItem->$sortColumn; + $swapItem->write(); + } + + + //Update target item position + if ($many_many) { + DB::query('UPDATE "' . $table + . '" SET "' . $sortColumn.'" = ' . $sortPosition + . ' WHERE "' . $componentField . '" = ' . $targetItem->ID . ' AND "' . $parentField . '" = ' . $owner->ID); + } else { + $targetItem->$sortColumn = $sortPosition; + $targetItem->write(); + } + + + //End transaction if supported + if(DB::getConn()->supportsTransactions()) { + DB::getConn()->transactionEnd(); + } + } + } } ?> \ No newline at end of file diff --git a/javascript/GridFieldSortableRows.js b/javascript/GridFieldSortableRows.js index cb2995d..fda45ce 100644 --- a/javascript/GridFieldSortableRows.js +++ b/javascript/GridFieldSortableRows.js @@ -1,6 +1,7 @@ (function($) { $('.ss-gridfield .gridfield-sortablerows input').entwine({ onmatch: function() { + var self=this; var refCheckbox=$(this); var gridField=$(this).getGridField(); @@ -12,6 +13,7 @@ } gridField.find('tbody').sortable({ + opacity: 0.6, disabled: ($(this).is(':checked')==false), helper: function(e, ui) { //Maintains width of the columns @@ -32,37 +34,64 @@ } - var form = gridField.closest('form'), - focusedElName = gridField.find(':input:focus').attr('name'); // Save focused element for restoring after refresh - var ajaxOpts = {data: [ - { - name: button.attr('name'), - value: button.val()}, - { - name: 'Items', - value: dataRows - } - ]}; - - 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', - error: function(e) { - alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION')); - } - }, ajaxOpts)); + self._makeRequest({data: [ + { + name: button.attr('name'), + value: button.val() + }, + { + name: 'Items', + value: dataRows + } + ]}); } }).disableSelection(); + + gridField.find('.datagrid-pagination button').each(function() { + $(this).droppable({ + disabled: $(this).is(':disabled'), + accept: 'tr.ss-gridfield-item', + 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-firstpage')) { + target='firstpage'; + }else if($(this).hasClass('ss-gridfield-previouspage')) { + target='previouspage'; + }else if($(this).hasClass('ss-gridfield-nextpage')) { + target='nextpage'; + }else if($(this).hasClass('ss-gridfield-lastpage')) { + target='lastpage'; + } + + + //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) { @@ -73,6 +102,31 @@ 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); \ No newline at end of file diff --git a/lang/en.yml b/lang/en.yml index 728e0be..2a9dfed 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -2,4 +2,5 @@ en: GridFieldSortableRows: ALLOW_DRAG_DROP: "Allow Drag and Drop" DISABLE_PAGINATOR: "Disable Pagenator" + SORT_TO_PAGE: "Sort To Page" EditPermissionsFailure: "No edit permissions" \ No newline at end of file diff --git a/templates/Includes/GridFieldSortableRows.ss b/templates/Includes/GridFieldSortableRows.ss index 1872df8..68cd8b9 100644 --- a/templates/Includes/GridFieldSortableRows.ss +++ b/templates/Includes/GridFieldSortableRows.ss @@ -4,6 +4,7 @@ <%t GridFieldSortableRows.ALLOW_DRAG_DROP "_Allow drag and drop re-ordering" %> $SortableToggle $PagenatorToggle + $SortToPage \ No newline at end of file