From: avl Date: Tue, 16 Dec 2008 16:28:39 +0000 (+0200) Subject: admin grid orderable X-Git-Tag: 0.5~438 X-Git-Url: https://git.andy128k.dev/?a=commitdiff_plain;h=de7d4f82aa090ba4022d8ab5b088e20d684317e2;p=ipf.git admin grid orderable --- diff --git a/ipf/admin/app.php b/ipf/admin/app.php index 94c75a8..7b8da86 100644 --- a/ipf/admin/app.php +++ b/ipf/admin/app.php @@ -10,6 +10,7 @@ class IPF_Admin_App extends IPF_Application{ return array( array('regex'=>'$#', 'func'=>'IPF_Admin_Views_Index'), array('regex'=>'([\w\_\-]+)/([\w\_\-]+)/$#i', 'func'=>'IPF_Admin_Views_ListItems'), + array('regex'=>'([\w\_\-]+)/([\w\_\-]+)/reorder/$#i', 'func'=>'IPF_Admin_Views_Reorder'), array('regex'=>'([\w\_\-]+)/([\w\_\-]+)/add/$#i', 'func'=>'IPF_Admin_Views_AddItem'), array('regex'=>'([\w\_\-]+)/([\w\_\-]+)/([\w\_\-]+)/$#i', 'func'=>'IPF_Admin_Views_EditItem'), array('regex'=>'([\w\_\-]+)/([\w\_\-]+)/([\w\_\-]+)/delete/$#i', 'func'=>'IPF_Admin_Views_DeleteItem'), diff --git a/ipf/admin/media/css/changelists.css b/ipf/admin/media/css/changelists.css index 7470635..af231e9 100644 --- a/ipf/admin/media/css/changelists.css +++ b/ipf/admin/media/css/changelists.css @@ -48,3 +48,5 @@ .paginator .end { border-width:2px !important; margin-right:6px; } .paginator .this-page { border:solid 1px #ccc; padding:2px 6px; font-weight:bold; vertical-align:top; } .paginator a:hover { color:white; background:#5b80b2; border-color:#036; } + +.ItemsDragClass{background-color:#E3F3FE;} diff --git a/ipf/admin/media/js/jquery.tablednd.js b/ipf/admin/media/js/jquery.tablednd.js new file mode 100644 index 0000000..324ef10 --- /dev/null +++ b/ipf/admin/media/js/jquery.tablednd.js @@ -0,0 +1,303 @@ +jQuery.tableDnD = { + /** Keep hold of the current table being dragged */ + currentTable : null, + /** Keep hold of the current drag object if any */ + dragObject: null, + /** The current mouse offset */ + mouseOffset: null, + /** Remember the old value of Y so that we don't do too much processing */ + oldY: 0, + + /** Actually build the structure */ + build: function(options) { + // Set up the defaults if any + + this.each(function() { + // This is bound to each matching table, set up the defaults and override with user options + this.tableDnDConfig = jQuery.extend({ + onDragStyle: null, + onDropStyle: null, + // Add in the default class for whileDragging + onDragClass: "tDnD_whileDrag", + onDrop: null, + onDragStart: null, + scrollAmount: 5, + serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs + serializeParamName: null, // If you want to specify another parameter name instead of the table ID + dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable + }, options || {}); + // Now make the rows draggable + jQuery.tableDnD.makeDraggable(this); + }); + + // Now we need to capture the mouse up and mouse move event + // We can use bind so that we don't interfere with other event handlers + jQuery(document) + .bind('mousemove', jQuery.tableDnD.mousemove) + .bind('mouseup', jQuery.tableDnD.mouseup); + + // Don't break the chain + return this; + }, + + /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ + makeDraggable: function(table) { + var config = table.tableDnDConfig; + if (table.tableDnDConfig.dragHandle) { + // We only need to add the event to the specified cells + var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table); + cells.each(function() { + // The cell is bound to "this" + jQuery(this).mousedown(function(ev) { + jQuery.tableDnD.dragObject = this.parentNode; + jQuery.tableDnD.currentTable = table; + jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev); + if (config.onDragStart) { + // Call the onDrop method if there is one + config.onDragStart(table, this); + } + return false; + }); + }) + } else { + // For backwards compatibility, we add the event to the whole row + var rows = jQuery("tr", table); // get all the rows as a wrapped set + rows.each(function() { + // Iterate through each row, the row is bound to "this" + var row = jQuery(this); + if (! row.hasClass("nodrag")) { + row.mousedown(function(ev) { + if (ev.target.tagName == "TD") { + jQuery.tableDnD.dragObject = this; + jQuery.tableDnD.currentTable = table; + jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev); + if (config.onDragStart) { + // Call the onDrop method if there is one + config.onDragStart(table, this); + } + return false; + } + }).css("cursor", "move"); // Store the tableDnD object + } + }); + } + }, + + updateTables: function() { + this.each(function() { + // this is now bound to each matching table + if (this.tableDnDConfig) { + jQuery.tableDnD.makeDraggable(this); + } + }) + }, + + /** Get the mouse coordinates from the event (allowing for browser differences) */ + mouseCoords: function(ev){ + if(ev.pageX || ev.pageY){ + return {x:ev.pageX, y:ev.pageY}; + } + return { + x:ev.clientX + document.body.scrollLeft - document.body.clientLeft, + y:ev.clientY + document.body.scrollTop - document.body.clientTop + }; + }, + + /** Given a target element and a mouse event, get the mouse offset from that element. + To do this we need the element's position and the mouse position */ + getMouseOffset: function(target, ev) { + ev = ev || window.event; + + var docPos = this.getPosition(target); + var mousePos = this.mouseCoords(ev); + return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y}; + }, + + /** Get the position of an element by going up the DOM tree and adding up all the offsets */ + getPosition: function(e){ + var left = 0; + var top = 0; + /** Safari fix -- thanks to Luis Chato for this! */ + if (e.offsetHeight == 0) { + /** Safari 2 doesn't correctly grab the offsetTop of a table row + this is detailed here: + http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/ + the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild. + note that firefox will return a text node as a first child, so designing a more thorough + solution may need to take that into account, for now this seems to work in firefox, safari, ie */ + e = e.firstChild; // a table cell + } + + while (e.offsetParent){ + left += e.offsetLeft; + top += e.offsetTop; + e = e.offsetParent; + } + + left += e.offsetLeft; + top += e.offsetTop; + + return {x:left, y:top}; + }, + + mousemove: function(ev) { + if (jQuery.tableDnD.dragObject == null) { + return; + } + + var dragObj = jQuery(jQuery.tableDnD.dragObject); + var config = jQuery.tableDnD.currentTable.tableDnDConfig; + var mousePos = jQuery.tableDnD.mouseCoords(ev); + var y = mousePos.y - jQuery.tableDnD.mouseOffset.y; + //auto scroll the window + var yOffset = window.pageYOffset; + if (document.all) { + // Windows version + //yOffset=document.body.scrollTop; + if (typeof document.compatMode != 'undefined' && + document.compatMode != 'BackCompat') { + yOffset = document.documentElement.scrollTop; + } + else if (typeof document.body != 'undefined') { + yOffset=document.body.scrollTop; + } + + } + + if (mousePos.y-yOffset < config.scrollAmount) { + window.scrollBy(0, -config.scrollAmount); + } else { + var windowHeight = window.innerHeight ? window.innerHeight + : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight; + if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) { + window.scrollBy(0, config.scrollAmount); + } + } + + + if (y != jQuery.tableDnD.oldY) { + // work out if we're going up or down... + var movingDown = y > jQuery.tableDnD.oldY; + // update the old value + jQuery.tableDnD.oldY = y; + // update the style to show we're dragging + if (config.onDragClass) { + dragObj.addClass(config.onDragClass); + } else { + dragObj.css(config.onDragStyle); + } + // If we're over a row then move the dragged row to there so that the user sees the + // effect dynamically + var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y); + if (currentRow) { + // TODO worry about what happens when there are multiple TBODIES + if (movingDown && jQuery.tableDnD.dragObject != currentRow) { + jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling); + } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) { + jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow); + } + } + } + + return false; + }, + + /** We're only worried about the y position really, because we can only move rows up and down */ + findDropTargetRow: function(draggedRow, y) { + var rows = jQuery.tableDnD.currentTable.rows; + for (var i=0; i rowY - rowHeight) && (y < (rowY + rowHeight))) { + // that's the row we're over + // If it's the same as the current row, ignore it + if (row == draggedRow) {return null;} + var config = jQuery.tableDnD.currentTable.tableDnDConfig; + if (config.onAllowDrop) { + if (config.onAllowDrop(draggedRow, row)) { + return row; + } else { + return null; + } + } else { + // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) + var nodrop = jQuery(row).hasClass("nodrop"); + if (! nodrop) { + return row; + } else { + return null; + } + } + return row; + } + } + return null; + }, + + mouseup: function(e) { + if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) { + var droppedRow = jQuery.tableDnD.dragObject; + var config = jQuery.tableDnD.currentTable.tableDnDConfig; + // If we have a dragObject, then we need to release it, + // The row will already have been moved to the right place so we just reset stuff + if (config.onDragClass) { + jQuery(droppedRow).removeClass(config.onDragClass); + } else { + jQuery(droppedRow).css(config.onDropStyle); + } + jQuery.tableDnD.dragObject = null; + if (config.onDrop) { + // Call the onDrop method if there is one + config.onDrop(jQuery.tableDnD.currentTable, droppedRow); + } + jQuery.tableDnD.currentTable = null; // let go of the table too + } + }, + + serialize: function() { + if (jQuery.tableDnD.currentTable) { + return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable); + } else { + return "Error: No Table id set, you need to set an id on your table and every row"; + } + }, + + serializeTable: function(table) { + var result = ""; + var tableId = table.id; + var rows = table.rows; + for (var i=0; i 0) result += ","; + result += rowId; + } + return result; + }, + + serializeTables: function() { + var result = ""; + this.each(function() { + // this is now bound to each matching table + result += jQuery.tableDnD.serializeTable(this); + }); + return result; + } + +} + +jQuery.fn.extend( + { + tableDnD : jQuery.tableDnD.build, + tableDnDUpdate : jQuery.tableDnD.updateTables, + tableDnDSerialize: jQuery.tableDnD.serializeTables + } +); \ No newline at end of file diff --git a/ipf/admin/model.php b/ipf/admin/model.php index 4ad4f14..0b8f7bc 100644 --- a/ipf/admin/model.php +++ b/ipf/admin/model.php @@ -102,6 +102,9 @@ class IPF_Admin_Model{ } public function ListItemsHeader(){ + + + $this->header = array(); if (method_exists($this,'list_display')) $this->names = $this->list_display(); @@ -192,8 +195,6 @@ class IPF_Admin_Model{ return 'admin/change.html'; } - - protected function _beforeEdit($o){ $this->_beforeChange($o); } @@ -394,9 +395,9 @@ class IPF_Admin_Model{ $objects = $pager->getPager()->execute(); $context = array( + 'orderable'=>method_exists($this, 'list_order'), 'page_title'=>$this->modelName.' List', 'header'=>$this->header, - 'classname'=>$this->modelName, 'objects'=>$objects, 'pager'=>$pager, 'classname'=>$this->modelName, diff --git a/ipf/admin/templates/admin/items.html b/ipf/admin/templates/admin/items.html index 3b167e6..b24fa46 100644 --- a/ipf/admin/templates/admin/items.html +++ b/ipf/admin/templates/admin/items.html @@ -7,6 +7,24 @@ {block content} +{if $orderable} + + +{/if} +

{$page_title}

@@ -26,9 +44,7 @@ {/foreach}
{/if} - - - +
{foreach $header as $h} @@ -38,7 +54,7 @@ {foreach $objects as $o} - + {foreach $o.ModelAdmin().ListRow($o) as $v} {/foreach} diff --git a/ipf/admin/views.php b/ipf/admin/views.php index 7026209..ee214f6 100644 --- a/ipf/admin/views.php +++ b/ipf/admin/views.php @@ -56,6 +56,8 @@ function IPF_Admin_Views_Index($request, $match){ return IPF_Shortcuts::RenderToResponse('admin/index.html', $context, $request); } + + function IPF_Admin_Views_ListItems($request, $match){ $ca = IPF_Admin_App::checkAdminAuth($request); if ($ca!==true) return $ca; @@ -73,6 +75,47 @@ function IPF_Admin_Views_ListItems($request, $match){ } } +function IPF_Admin_Views_Reorder($request, $match){ + $ca = IPF_Admin_App::checkAdminAuth($request); + if ($ca!==true) return $ca; + + if ($request->method != 'POST') + return new IPF_HTTP_Response_NotFound(); + + if (!isset($request->POST['ids'])) + return new IPF_HTTP_Response_NotFound(); + + $lapp = $match[1]; + $lmodel = $match[2]; + + foreach (IPF_Project::getInstance()->appList() as $app){ + foreach($app->modelList() as $m){ + if (strtolower($m)==$lmodel){ + $ma = IPF_Admin_Model::getModelAdmin($m); + if ($ma===null) + return new IPF_HTTP_Response_NotFound(); + + if (method_exists($ma, 'list_order')) + $ord_field = $ma->list_order(); + else + return new IPF_HTTP_Response_NotFound(); + + $o = new $m(); + $ids = split(',',(string)$request->POST['ids']); + $ord = 1; + foreach($ids as $id){ + $item = $o->getTable()->find($id); + $item[$ord_field] = $ord; + $item->save(); + $ord++; + } + return new IPF_HTTP_Response_Json("Ok"); + } + } + } + return new IPF_HTTP_Response_Json("Cannot find model"); +} + function IPF_Admin_Views_EditItem($request, $match){ $ca = IPF_Admin_App::checkAdminAuth($request); if ($ca!==true) return $ca;
{$v|safe}