From 11b2e8db187c6abd98f80a82d17bcfd4c87e1531 Mon Sep 17 00:00:00 2001 From: garvinhicking <garvinhicking> Date: Thu, 1 Jun 2006 11:18:53 +0000 Subject: [PATCH] Drag and drop based plugin layout manager. Hooray for DHTML. ;) --- bundled-libs/dragdrop.js | 676 +++++++++++++++++++ docs/NEWS | 4 + include/admin/plugins.inc.php | 44 +- include/functions_plugins_admin.inc.php | 85 ++- include/plugin_api.inc.php | 18 +- serendipity_admin.php | 1 + templates/default/admin/img/grablet.gif | Bin 0 -> 102 bytes templates/default/admin/img/grablet_over.gif | Bin 0 -> 102 bytes templates/default/admin/pluginmanager.css | 54 ++ 9 files changed, 836 insertions(+), 46 deletions(-) create mode 100644 bundled-libs/dragdrop.js create mode 100644 templates/default/admin/img/grablet.gif create mode 100644 templates/default/admin/img/grablet_over.gif create mode 100644 templates/default/admin/pluginmanager.css diff --git a/bundled-libs/dragdrop.js b/bundled-libs/dragdrop.js new file mode 100644 index 0000000..ffc4e73 --- /dev/null +++ b/bundled-libs/dragdrop.js @@ -0,0 +1,676 @@ +/********************************************************** + Very minorly modified from the example by Tim Taylor + http://tool-man.org/examples/sorting.html + + Added Coordinate.prototype.inside( northwest, southeast ); + + Copyright (c) 2005 Tim Taylor Consulting <http://tool-man.org/> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + **********************************************************/ + +var Coordinates = { + ORIGIN : new Coordinate(0, 0), + + northwestPosition : function(element) { + var x = parseInt(element.style.left); + var y = parseInt(element.style.top); + + return new Coordinate(isNaN(x) ? 0 : x, isNaN(y) ? 0 : y); + }, + + southeastPosition : function(element) { + return Coordinates.northwestPosition(element).plus( + new Coordinate(element.offsetWidth, element.offsetHeight)); + }, + + northwestOffset : function(element, isRecursive) { + var offset = new Coordinate(element.offsetLeft, element.offsetTop); + + if (!isRecursive) return offset; + + var parent = element.offsetParent; + while (parent) { + offset = offset.plus( + new Coordinate(parent.offsetLeft, parent.offsetTop)); + parent = parent.offsetParent; + } + return offset; + }, + + southeastOffset : function(element, isRecursive) { + return Coordinates.northwestOffset(element, isRecursive).plus( + new Coordinate(element.offsetWidth, element.offsetHeight)); + }, + + fixEvent : function(event) { + event.windowCoordinate = new Coordinate(event.clientX, event.clientY); + } +}; + +function Coordinate(x, y) { + this.x = x; + this.y = y; +} + +Coordinate.prototype.toString = function() { + return "(" + this.x + "," + this.y + ")"; +} + +Coordinate.prototype.plus = function(that) { + return new Coordinate(this.x + that.x, this.y + that.y); +} + +Coordinate.prototype.minus = function(that) { + return new Coordinate(this.x - that.x, this.y - that.y); +} + +Coordinate.prototype.distance = function(that) { + var deltaX = this.x - that.x; + var deltaY = this.y - that.y; + + return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); +} + +Coordinate.prototype.max = function(that) { + var x = Math.max(this.x, that.x); + var y = Math.max(this.y, that.y); + return new Coordinate(x, y); +} + +Coordinate.prototype.constrain = function(min, max) { + if (min.x > max.x || min.y > max.y) return this; + + var x = this.x; + var y = this.y; + + if (min.x != null) x = Math.max(x, min.x); + if (max.x != null) x = Math.min(x, max.x); + if (min.y != null) y = Math.max(y, min.y); + if (max.y != null) y = Math.min(y, max.y); + + return new Coordinate(x, y); +} + +Coordinate.prototype.reposition = function(element) { + element.style["top"] = this.y + "px"; + element.style["left"] = this.x + "px"; +} + +Coordinate.prototype.equals = function(that) { + if (this == that) return true; + if (!that || that == null) return false; + + return this.x == that.x && this.y == that.y; +} + +// returns true of this point is inside specified box +Coordinate.prototype.inside = function(northwest, southeast) { + if ((this.x >= northwest.x) && (this.x <= southeast.x) && + (this.y >= northwest.y) && (this.y <= southeast.y)) { + + return true; + } + return false; +} + +/* + * drag.js - click & drag DOM elements + * + * originally based on Youngpup's dom-drag.js, www.youngpup.net + */ + +/********************************************************** + Further modified from the example by Tim Taylor + http://tool-man.org/examples/sorting.html + + Changed onMouseMove where it calls group.onDrag and then + adjusts the offset for changes to the DOM. If the item + being moved changed parents it would be off so changed to + get the absolute offset (recursive northwestOffset). + + **********************************************************/ + +var Drag = { + BIG_Z_INDEX : 10000, + group : null, + isDragging : false, + + makeDraggable : function(group) { + group.handle = group; + group.handle.group = group; + + group.minX = null; + group.minY = null; + group.maxX = null; + group.maxY = null; + group.threshold = 0; + group.thresholdY = 0; + group.thresholdX = 0; + + group.onDragStart = new Function(); + group.onDragEnd = new Function(); + group.onDrag = new Function(); + + // TODO: use element.prototype.myFunc + group.setDragHandle = Drag.setDragHandle; + group.setDragThreshold = Drag.setDragThreshold; + group.setDragThresholdX = Drag.setDragThresholdX; + group.setDragThresholdY = Drag.setDragThresholdY; + group.constrain = Drag.constrain; + group.constrainVertical = Drag.constrainVertical; + group.constrainHorizontal = Drag.constrainHorizontal; + + group.onmousedown = Drag.onMouseDown; + }, + + constrainVertical : function() { + var nwOffset = Coordinates.northwestOffset(this, true); + this.minX = nwOffset.x; + this.maxX = nwOffset.x; + }, + + constrainHorizontal : function() { + var nwOffset = Coordinates.northwestOffset(this, true); + this.minY = nwOffset.y; + this.maxY = nwOffset.y; + }, + + constrain : function(nwPosition, sePosition) { + this.minX = nwPosition.x; + this.minY = nwPosition.y; + this.maxX = sePosition.x; + this.maxY = sePosition.y; + }, + + setDragHandle : function(handle) { + if (handle && handle != null) + this.handle = handle; + else + this.handle = this; + + this.handle.group = this; + this.onmousedown = null; + this.handle.onmousedown = Drag.onMouseDown; + }, + + setDragThreshold : function(threshold) { + if (isNaN(parseInt(threshold))) return; + + this.threshold = threshold; + }, + + setDragThresholdX : function(threshold) { + if (isNaN(parseInt(threshold))) return; + + this.thresholdX = threshold; + }, + + setDragThresholdY : function(threshold) { + if (isNaN(parseInt(threshold))) return; + + this.thresholdY = threshold; + }, + + onMouseDown : function(event) { + event = Drag.fixEvent(event); + Drag.group = this.group; + + var group = this.group; + var mouse = event.windowCoordinate; + var nwOffset = Coordinates.northwestOffset(group, true); + var nwPosition = Coordinates.northwestPosition(group); + var sePosition = Coordinates.southeastPosition(group); + var seOffset = Coordinates.southeastOffset(group, true); + + group.originalOpacity = group.style.opacity; + group.originalZIndex = group.style.zIndex; + group.initialWindowCoordinate = mouse; + // TODO: need a better name, but don't yet understand how it + // participates in the magic while dragging + group.dragCoordinate = mouse; + + Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset); + + group.onDragStart(nwPosition, sePosition, nwOffset, seOffset); + + // TODO: need better constraint API + if (group.minX != null) + group.minMouseX = mouse.x - nwPosition.x + + group.minX - nwOffset.x; + if (group.maxX != null) + group.maxMouseX = group.minMouseX + group.maxX - group.minX; + + if (group.minY != null) + group.minMouseY = mouse.y - nwPosition.y + + group.minY - nwOffset.y; + if (group.maxY != null) + group.maxMouseY = group.minMouseY + group.maxY - group.minY; + + group.mouseMin = new Coordinate(group.minMouseX, group.minMouseY); + group.mouseMax = new Coordinate(group.maxMouseX, group.maxMouseY); + + document.onmousemove = Drag.onMouseMove; + document.onmouseup = Drag.onMouseUp; + + return false; + }, + + showStatus : function(mouse, nwPosition, sePosition, nwOffset, seOffset) { + /*window.status = + "mouse: " + mouse.toString() + " " + + "NW pos: " + nwPosition.toString() + " " + + "SE pos: " + sePosition.toString() + " " + + "NW offset: " + nwOffset.toString() + " " + + "SE offset: " + seOffset.toString();*/ + }, + + onMouseMove : function(event) { + event = Drag.fixEvent(event); + var group = Drag.group; + var mouse = event.windowCoordinate; + var nwOffset = Coordinates.northwestOffset(group, true); + var nwPosition = Coordinates.northwestPosition(group); + var sePosition = Coordinates.southeastPosition(group); + var seOffset = Coordinates.southeastOffset(group, true); + + Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset); + + if (!Drag.isDragging) { + if (group.threshold > 0) { + var distance = group.initialWindowCoordinate.distance( + mouse); + if (distance < group.threshold) return true; + } else if (group.thresholdY > 0) { + var deltaY = Math.abs(group.initialWindowCoordinate.y - mouse.y); + if (deltaY < group.thresholdY) return true; + } else if (group.thresholdX > 0) { + var deltaX = Math.abs(group.initialWindowCoordinate.x - mouse.x); + if (deltaX < group.thresholdX) return true; + } + + Drag.isDragging = true; + group.style["zIndex"] = Drag.BIG_Z_INDEX; + group.style["opacity"] = 0.75; + } + + // TODO: need better constraint API + var adjusted = mouse.constrain(group.mouseMin, group.mouseMax); + nwPosition = nwPosition.plus(adjusted.minus(group.dragCoordinate)); + nwPosition.reposition(group); + group.dragCoordinate = adjusted; + + // once dragging has started, the position of the group + // relative to the mouse should stay fixed. They can get out + // of sync if the DOM is manipulated while dragging, so we + // correct the error here + // + // TODO: what we really want to do is find the offset from + // our corner to the mouse coordinate and adjust to keep it + // the same + + // changed to be recursive/use absolute offset for corrections + var offsetBefore = Coordinates.northwestOffset(group, true); + group.onDrag(nwPosition, sePosition, nwOffset, seOffset); + var offsetAfter = Coordinates.northwestOffset(group, true); + + if (!offsetBefore.equals(offsetAfter)) { + var errorDelta = offsetBefore.minus(offsetAfter); + nwPosition = Coordinates.northwestPosition(group).plus(errorDelta); + nwPosition.reposition(group); + } + + return false; + }, + + onMouseUp : function(event) { + event = Drag.fixEvent(event); + var group = Drag.group; + + var mouse = event.windowCoordinate; + var nwOffset = Coordinates.northwestOffset(group, true); + var nwPosition = Coordinates.northwestPosition(group); + var sePosition = Coordinates.southeastPosition(group); + var seOffset = Coordinates.southeastOffset(group, true); + + document.onmousemove = null; + document.onmouseup = null; + group.onDragEnd(nwPosition, sePosition, nwOffset, seOffset); + + if (Drag.isDragging) { + // restoring zIndex before opacity avoids visual flicker in Firefox + group.style["zIndex"] = group.originalZIndex; + group.style["opacity"] = group.originalOpacity; + } + + Drag.group = null; + Drag.isDragging = false; + + return false; + }, + + fixEvent : function(event) { + if (typeof event == 'undefined') event = window.event; + Coordinates.fixEvent(event); + + return event; + } +}; + +/********************************************************** + Adapted from the sortable lists example by Tim Taylor + http://tool-man.org/examples/sorting.html + Modified by Tom Westcott : http://www.cyberdummy.co.uk + **********************************************************/ + +var DragDrop = { + firstContainer : null, + lastContainer : null, + parent_id : null, + parent_group : null, + makeListContainer : function(list, group) { + // each container becomes a linked list node + if (this.firstContainer == null) { + this.firstContainer = this.lastContainer = list; + list.previousContainer = null; + list.nextContainer = null; + } else { + list.previousContainer = this.lastContainer; + list.nextContainer = null; + this.lastContainer.nextContainer = list; + this.lastContainer = list; + } + + // these functions are called when an item is draged over + // a container or out of a container bounds. onDragOut + // is also called when the drag ends with an item having + // been added to the container + list.onDragOver = new Function(); + list.onDragOut = new Function(); + list.onDragDrop = new Function(); + list.group = group; + + var items = list.getElementsByTagName( "li" ); + + for (var i = 0; i < items.length; i++) { + DragDrop.makeItemDragable(items[i]); + } + }, + + serData : function ( group, theid ) { + var container = DragDrop.firstContainer; + var j = 0; + var string = ""; + + while (container != null) { + if(theid != null && container.id != theid) { + container = container.nextContainer; + continue; + } + + if(group != null && container.group != group) { + container = container.nextContainer; + continue; + } + + j ++; + if (j > 1) { + string += ":"; + } + string += container.id; + + var items = container.getElementsByTagName( "li" ); + string += "("; + for (var i = 0; i < items.length; i++) { + if(i > 0) { + string += ","; + } + + string += items[i].id; + } + string += ")"; + + container = container.nextContainer; + } + + return string; + }, + + makeItemDragable : function(item) { + Drag.makeDraggable(item); + item.setDragThreshold(5); + + // tracks if the item is currently outside all containers + item.isOutside = false; + + item.onDragStart = DragDrop.onDragStart; + item.onDrag = DragDrop.onDrag; + item.onDragEnd = DragDrop.onDragEnd; + }, + + onDragStart : function(nwPosition, sePosition, nwOffset, seOffset) { + // update all container bounds, since they may have changed + // on a previous drag + // + // could be more smart about when to do this + var container = DragDrop.firstContainer; + while (container != null) { + container.northwest = Coordinates.northwestOffset( container, true ); + container.southeast = Coordinates.southeastOffset( container, true ); + container = container.nextContainer; + } + + // item starts out over current parent + this.parentNode.onDragOver(); + parent_id = this.parentNode.id; + parent_group = this.parentNode.group; + }, + + onDrag : function(nwPosition, sePosition, nwOffset, seOffset) { + // check if we were nowhere + if (this.isOutside) { + // check each container to see if in its bounds + var container = DragDrop.firstContainer; + while (container != null) { + + if ((nwOffset.inside( container.northwest, container.southeast ) || + seOffset.inside( container.northwest, container.southeast )) && container.group == parent_group) { + // we're inside this one + container.onDragOver(); + this.isOutside = false; + + // since isOutside was true, the current parent is a + // temporary clone of some previous container node and + // it needs to be removed from the document + var tempParent = this.parentNode; + tempParent.removeChild( this ); + container.appendChild( this ); + tempParent.parentNode.removeChild( tempParent ); + break; + } + container = container.nextContainer; + } + // we're still not inside the bounds of any container + if (this.isOutside) + return; + + // check if we're outside our parent's bounds + } else if (!(nwOffset.inside( this.parentNode.northwest, this.parentNode.southeast ) || + seOffset.inside( this.parentNode.northwest, this.parentNode.southeast ))) { + + this.parentNode.onDragOut(); + this.isOutside = true; + + // check if we're inside a new container's bounds + var container = DragDrop.firstContainer; + while (container != null) { + if ((nwOffset.inside( container.northwest, container.southeast ) || + seOffset.inside( container.northwest, container.southeast )) && container.group == parent_group) { + // we're inside this one + container.onDragOver(); + this.isOutside = false; + this.parentNode.removeChild( this ); + container.appendChild( this ); + break; + } + container = container.nextContainer; + } + // if we're not in any container now, make a temporary clone of + // the previous container node and add it to the document + if (this.isOutside) { + var tempParent = this.parentNode.cloneNode( false ); + this.parentNode.removeChild( this ); + tempParent.appendChild( this ); + // body puts a border or item at bottom of page if do not have this + tempParent.style.border = 0; + document.getElementsByTagName( "body" ).item(0).appendChild( tempParent ); + return; + } + } + + // if we get here, we're inside some container bounds, so we do + // everything the original dragsort script did to swap us into the + // correct position + + var parent = this.parentNode; + + var item = this; + var next = DragUtils.nextItem(item); + while (next != null && this.offsetTop >= next.offsetTop - 2) { + var item = next; + var next = DragUtils.nextItem(item); + } + if (this != item) { + DragUtils.swap(this, next); + return; + } + + var item = this; + var previous = DragUtils.previousItem(item); + while (previous != null && this.offsetTop <= previous.offsetTop + 2) { + var item = previous; + var previous = DragUtils.previousItem(item); + } + if (this != item) { + DragUtils.swap(this, item); + return; + } + }, + + onDragEnd : function(nwPosition, sePosition, nwOffset, seOffset) { + // if the drag ends and we're still outside all containers + // it's time to remove ourselves from the document or add + // to the trash bin + if (this.isOutside) { + var container = DragDrop.firstContainer; + while (container != null) { + if (container.id == parent_id) { + break; + } + container = container.nextContainer; + } + this.isOutside = false; + this.parentNode.removeChild( this ); + container.appendChild( this ); + this.style["top"] = "0px"; + this.style["left"] = "0px"; + //var container = DragDrop.firstContainer; + //container.appendChild( this ); + return; + } + this.parentNode.onDragOut(); + this.parentNode.onDragDrop(); + this.style["top"] = "0px"; + this.style["left"] = "0px"; + } +}; + +var DragUtils = { + swap : function(item1, item2) { + var parent = item1.parentNode; + parent.removeChild(item1); + parent.insertBefore(item1, item2); + + item1.style["top"] = "0px"; + item1.style["left"] = "0px"; + }, + + nextItem : function(item) { + var sibling = item.nextSibling; + while (sibling != null) { + if (sibling.nodeName == item.nodeName) return sibling; + sibling = sibling.nextSibling; + } + return null; + }, + + previousItem : function(item) { + var sibling = item.previousSibling; + while (sibling != null) { + if (sibling.nodeName == item.nodeName) return sibling; + sibling = sibling.previousSibling; + } + return null; + } +}; + +/************************* + * Custom Serendipity code + *************************/ + +function pluginMoverInit() { + var list = document.getElementById("left_col"); + DragDrop.makeListContainer(list, 'g1'); + list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; }; + list.onDragOut = function() { this.style["border"] = "none"; }; + + list = document.getElementById("hide_col"); + DragDrop.makeListContainer(list, 'g1'); + list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; }; + list.onDragOut = function() {this.style["border"] = "none"; }; + + list = document.getElementById("right_col"); + DragDrop.makeListContainer(list, 'g1'); + list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; }; + list.onDragOut = function() {this.style["border"] = "none"; }; +} + +function pluginMoverInitEvent() { + var list = document.getElementById("event_col"); + DragDrop.makeListContainer(list, 'g1'); + list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; }; + list.onDragOut = function() { this.style["border"] = "none"; }; + + list = document.getElementById("eventh_col"); + DragDrop.makeListContainer(list, 'g1'); + list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; }; + list.onDragOut = function() {this.style["border"] = "none"; }; +} + +function pluginMovergetSort() { + order = document.getElementById("order"); + order.value = DragDrop.serData('g1', null); + return order.value; +} + +function pluginMovergetSortEvent() { + order = document.getElementById("eventorder"); + order.value = DragDrop.serData('g1', null); + return order.value; +} \ No newline at end of file diff --git a/docs/NEWS b/docs/NEWS index 3358446..bf52a6e 100644 --- a/docs/NEWS +++ b/docs/NEWS @@ -3,6 +3,10 @@ Version 1.1-alpha5() ------------------------------------------------------------------------ + * Implemented Drag and Drop based plugin configuration panel for + re-ordering plugin layout. Uses JavaScript - works like old + manager when having JS disabled! (garvinhicking) + * Changed "Auto-Login" via Cookie behaviour to only issue single- time valid cookies to users and no longer put username/pw into serialized cookie data. Many thanks to Yasuo Ohgaki for giving diff --git a/include/admin/plugins.inc.php b/include/admin/plugins.inc.php index e0f1630..0325575 100644 --- a/include/admin/plugins.inc.php +++ b/include/admin/plugins.inc.php @@ -111,7 +111,7 @@ if (isset($_GET['serendipity']['plugin_to_conf'])) { echo "</ul>\n"; ?> </div> -<?php } elseif ( isset($_POST['SAVECONF']) ) { ?> +<?php } elseif ( isset($_POST['SAVECONF'])) { ?> <div class="serendipityAdminMsgSuccess"><?php echo DONE .': '. sprintf(SETTINGS_SAVED_AT, serendipity_strftime('%H:%M:%S')); ?></div> <?php } ?> @@ -344,6 +344,41 @@ if (isset($_GET['serendipity']['plugin_to_conf'])) { } else { /* show general plugin list */ + /* preparse Javascript-generated input */ + if (isset($_POST['SAVE']) && !empty($_POST['serendipity']['pluginorder'])) { + $parts = explode(':', $_POST['serendipity']['pluginorder']); + $col_assoc = array( + 'left_col' => 'left', + 'right_col' => 'right', + 'hide_col' => 'hide', + 'event_col' => 'event', + 'eventh_col' => 'eventh' + ); + foreach($parts AS $sidepart) { + preg_match('@^(.+)\((.*)\)$@imsU', $sidepart, $matches); + if (!isset($col_assoc[$matches[1]])) { + continue; + } + $pluginsidelist = explode(',', $matches[2]); + foreach($pluginsidelist AS $pluginname) { + $pluginname = trim(urldecode($pluginname)); + + if (empty($pluginname)) { + continue; + } + $serendipity['POST']['placement'][$pluginname] = $col_assoc[$matches[1]]; + $new_order[] = $pluginname; + + } + } + + if (is_array($new_order)) { + foreach($new_order AS $new_order_pos => $order_plugin) { + serendipity_db_query("UPDATE {$serendipity['dbPrefix']}plugins SET sort_order = ". (int)$new_order_pos . " WHERE name='" . serendipity_db_escape_string($order_plugin) . "'"); + } + } + } + if (isset($_POST['SAVE']) && isset($_POST['serendipity']['placement']) && serendipity_checkFormToken()) { foreach ($_POST['serendipity']['placement'] as $plugin_name => $placement) { serendipity_plugin_api::update_plugin_placement( @@ -405,7 +440,14 @@ if (isset($_GET['serendipity']['plugin_to_conf'])) { } } ?> + +<?php if (isset($_POST['SAVE'])) { ?> + <div class="serendipityAdminMsgSuccess"><?php echo DONE .': '. sprintf(SETTINGS_SAVED_AT, serendipity_strftime('%H:%M:%S')); ?></div> +<?php } ?> + <?php echo BELOW_IS_A_LIST_OF_INSTALLED_PLUGINS ?> + <?php echo '<script src="bundled-libs/dragdrop.js" type="text/javascript"></script>'; ?> + <br /> <br /> diff --git a/include/functions_plugins_admin.inc.php b/include/functions_plugins_admin.inc.php index 225f3b9..dd4b548 100644 --- a/include/functions_plugins_admin.inc.php +++ b/include/functions_plugins_admin.inc.php @@ -2,6 +2,7 @@ # Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team) # All rights reserved. See LICENSE file for licensing details + if (defined('S9Y_FRAMEWORK_PLUGINS_ADMIN')) { return; } @@ -50,17 +51,30 @@ function serendipity_pluginListSort($x, $y) { */ function show_plugins($event_only = false) { + static $opts = array( + 'left' => LEFT, + 'right' => RIGHT, + 'hide' => HIDDEN, + 'event' => PLUGIN_ACTIVE, + 'eventh' => PLUGIN_INACTIVE + ); + global $serendipity; + + if (!$event_only) { + echo '<script type="text/javascript">addLoadEvent(pluginMoverInit);</script>'; + echo ' <form action="?serendipity[adminModule]=plugins" method="post" onsubmit="pluginMovergetSort(); return true">'; + echo ' <input type="hidden" name="serendipity[pluginorder]" id="order" value="" />'; + + } else { + echo '<script type="text/javascript">addLoadEvent(pluginMoverInitEvent);</script>'; + echo ' <form action="?serendipity[adminModule]=plugins" method="post" onsubmit="pluginMovergetSortEvent(); return true">'; + echo ' <input type="hidden" name="serendipity[pluginorder]" id="eventorder" value="" />'; + } + + echo serendipity_setFormToken(); ?> - <form action="?serendipity[adminModule]=plugins" method="post"> - <?php echo serendipity_setFormToken(); ?> - <table border="0" cellpadding="5" cellspacing="0" width="100%"> - <tr> - <td colspan="2"> </td> - <td><strong><?php echo TITLE; ?></strong></td> - <td><strong><?php echo PERMISSIONS; ?></strong></td> - <td colspan="3" align="center"><strong><?php echo PLACEMENT; ?></strong></td> - </tr> + <table class="pluginmanager" border="0" cellpadding="5" cellspacing="3" width="100%"> <?php $errors = array(); @@ -68,11 +82,14 @@ function show_plugins($event_only = false) if ($event_only) { $plugin_placements = array('event', 'eventh'); } else { - $plugin_placements = array('left', 'right', 'hide'); + $plugin_placements = array('left', 'hide', 'right'); } $total = 0; foreach ($plugin_placements as $plugin_placement) { + echo '<td class="pluginmanager_side">'; + echo '<div class="heading">' . $opts[$plugin_placement] . '</div>'; + echo '<ol id="' . $plugin_placement . '_col" class="pluginmanager_container">'; $plugins = serendipity_plugin_api::enum_plugins($plugin_placement); if (!is_array($plugins)) { @@ -131,48 +148,44 @@ function show_plugins($event_only = false) $movedown = ($moveup != '' ? ' ' : '') . '<a href="?' . serendipity_setFormToken('url') . '&serendipity[adminModule]=plugins&submit=move+down&serendipity[plugin_to_move]=' . $key . $event_only_uri . '" style="border: 0"><img src="' . serendipity_getTemplateFile('admin/img/downarrow.png') . '" height="16" width="16" alt="'. DOWN .'" border="0" /></a>'; } ?> - <tr> - <td style="border-bottom: 1px solid #000000"> - <div> - <?php if ($is_plugin_editable) { ?> - <input type="checkbox" name="serendipity[plugin_to_remove][]" value="<?php echo $plugin_data['name']; ?>" /> - <?php } else { ?> - - <?php } ?> - </div> - </td> - <td style="border-bottom: 1px solid #000000" width="16"> + <li class="pluginmanager_item_<?php echo ($sort_idx % 2 ? 'even' : 'uneven'); ?>" id="<?php echo $key; ?>"> + <a href="#grab<?php echo $key; ?>" id="grab<?php echo $key; ?>" class="pluginmanager_grablet"></a> + <?php if ($is_plugin_editable) { ?> + <input type="checkbox" name="serendipity[plugin_to_remove][]" value="<?php echo $plugin_data['name']; ?>" /> + <?php } ?> + <?php if ( $can_configure ) { ?> - <a href="?serendipity[adminModule]=plugins&serendipity[plugin_to_conf]=<?php echo $key ?>"><img src="<?php echo serendipity_getTemplateFile('admin/img/configure.png') ?>" style="border: 0; vertical-align: bottom;"></a> - <?php } else { ?> - + <a class="pluginmanager_configure" href="?serendipity[adminModule]=plugins&serendipity[plugin_to_conf]=<?php echo $key ?>"><img src="<?php echo serendipity_getTemplateFile('admin/img/configure.png') ?>" style="border: 0; vertical-align: bottom;"></a> <?php } ?> - </td> - <td style="border-bottom: 1px solid #000000"><strong> + + <span class="pluginmanager_title"> <?php if ( $can_configure ) { ?> <a href="?serendipity[adminModule]=plugins&serendipity[plugin_to_conf]=<?php echo $key ?>"><?php echo $title; ?></a> <?php } else { ?> <?php echo $title; ?> - <?php } ?></strong><br /> - <div style="font-size: 8pt"><?php echo $desc; ?></div> - </td> - <td style="border-bottom: 1px solid #000000" nowrap="nowrap"><?php ownership($plugin_data['authorid'], $plugin_data['name'], $is_plugin_owner); ?></td> - <td style="border-bottom: 1px solid #000000" nowrap="nowrap"><?php echo $place ?></td> - <td style="border-bottom: 1px solid #000000"><?php echo $moveup ?></td> - <td style="border-bottom: 1px solid #000000"><?php echo $movedown ?></td> - </tr> + <?php } ?></span><br /> + <div class="pluginmanager_description" style="font-size: 8pt"><?php echo $desc; ?></div> + <div class="pluginmanager_ownership"><?php ownership($plugin_data['authorid'], $plugin_data['name'], $is_plugin_owner); ?></div> + <noscript> + <div class="pluginmanager_place"><?php echo $place; ?></div> + <div class="pluginmanager_move"><?php echo $moveup ?> <?php echo $movedown ?></div> + </noscript> + </li> <?php $sort_idx++; } + + echo '</ol></td>'; } + ?> <tr> - <td colspan="6" align="right"><?php printf(PLUGIN_AVAILABLE_COUNT, $total); ?></td> + <td colspan="3" align="right"><?php printf(PLUGIN_AVAILABLE_COUNT, $total); ?></td> </tr> </table> <br /> <div> - <input type="submit" name="REMOVE" title="<?php echo REMOVE_TICKED_PLUGINS; ?>" value="<?php echo DELETE; ?>" class="serendipityPrettyButton" /> + <input type="submit" name="REMOVE" title="<?php echo DELETE; ?>" value="<?php echo REMOVE_TICKED_PLUGINS; ?>" class="serendipityPrettyButton" /> <input type="submit" name="SAVE" title="<?php echo SAVE_CHANGES_TO_LAYOUT; ?>" value="<?php echo SAVE; ?>" class="serendipityPrettyButton" /> </div> </form> diff --git a/include/plugin_api.inc.php b/include/plugin_api.inc.php index 393560b..0dc1a5b 100644 --- a/include/plugin_api.inc.php +++ b/include/plugin_api.inc.php @@ -95,7 +95,7 @@ class serendipity_plugin_api { * @access public * @param string classname of the plugin to insert (see description above for details) * @param boolean (reserved) variable to indicate a copy of an existing instance - * @param string The type of the plugin to insert (event/left/right/hidden) + * @param string The type of the plugin to insert (event/left/right/hide/eventh) * @param int The authorid of the plugin owner * @param string The source path of the plugin file * @return string ID of the new plugin @@ -322,7 +322,7 @@ class serendipity_plugin_api { * Returns a list of currently installed plugins * * @access public - * @param string The filter for plugins (left|right|hidden|event) + * @param string The filter for plugins (left|right|hide|event|eventh) * @return array The list of plugins */ function get_installed_plugins($filter = '*') { @@ -342,7 +342,7 @@ class serendipity_plugin_api { * Searches for installed plugins based on specific conditions * * @access public - * @param string The filter for plugins (left|right|hidden|event) + * @param string The filter for plugins (left|right|hide|event|eventh) * @param boolean If true, the filtering logic will be reversed an all plugins that are NOT part of the filter will be returned * @param string Filter by a specific classname (like 'serendipity_plugin_archives'). Can take SQL wildcards. * @param string Filter by a specific plugin instance id @@ -384,7 +384,7 @@ class serendipity_plugin_api { * Count the number of plugins to which the filter criteria matches * * @access public - * @param string The filter for plugins (left|right|hidden|event) + * @param string The filter for plugins (left|right|hide|event|eventh) * @param boolean If true, the filtering logic will be reversed an all plugins that are NOT part of the filter will be evaluated * @return int Number of plugins that were found. */ @@ -726,7 +726,7 @@ class serendipity_plugin_api { * * @access public * @param string The instance ID of a plugin - * @param string The new placement of a plugin (left|right|hidden) + * @param string The new placement of a plugin (left|right|hide|event|eventh) * @param string A new sort order for the plugin * @return */ @@ -735,8 +735,8 @@ class serendipity_plugin_api { global $serendipity; $admin = ''; - if (!serendipity_checkPermission('adminPlugins') && $placement == 'hidden') { - // Only administrators can set plugins to 'hidden' if they are not the owners. + if (!serendipity_checkPermission('adminPlugins') && $placement == 'hide') { + // Only administrators can set plugins to 'hide' if they are not the owners. $admin = " AND (authorid = 0 OR authorid = {$serendipity['authorid']})"; } @@ -747,7 +747,7 @@ class serendipity_plugin_api { } $sql .= "WHERE name='$name' $admin"; - + return serendipity_db_query($sql); } @@ -781,7 +781,7 @@ class serendipity_plugin_api { * Get a list of Sidebar plugins and pass them to Smarty * * @access public - * @param string The side of plugins to show (left/right/hidden) + * @param string The side of plugins to show (left/right/hide/event/eventh) * @param string deprecated: Indicated which wrapping HTML element to use for plugins * @param boolean Indicates whether only all plugins should be shown that are not in the $side list * @param string Only show plugins of this plugin class diff --git a/serendipity_admin.php b/serendipity_admin.php index 6c4ce54..9389fff 100644 --- a/serendipity_admin.php +++ b/serendipity_admin.php @@ -42,6 +42,7 @@ if (serendipity_is_iframe()) { <title><?php echo SERENDIPITY_ADMIN_SUITE; ?></title> <meta http-equiv="Content-Type" content="text/html; charset=<?php echo LANG_CHARSET; ?>" /> <link rel="stylesheet" type="text/css" href="<?php echo $css_file; ?>" /> + <link rel="stylesheet" type="text/css" href="<?php echo serendipity_getTemplateFile('admin/pluginmanager.css'); ?>" /> <script type="text/javascript"> function spawn() { if (self.Spawnextended) { diff --git a/templates/default/admin/img/grablet.gif b/templates/default/admin/img/grablet.gif new file mode 100644 index 0000000000000000000000000000000000000000..0c42cd557bc659c2812e25839e26fe5a6e8dddda GIT binary patch literal 102 zcmZ?wbhEHb6k*_GSj5Wk_U+R*FK-m)r>t2q^Y_mW|NsAI00YIJEQ|~ctPDCJ9!M<% zvv$O;JG#z}lBc(nvILoYzbjV6>NtxlYTlB^`wDsjzx329t$6-u)+W}5t_chb)&R6p BCqe)K literal 0 HcmV?d00001 diff --git a/templates/default/admin/img/grablet_over.gif b/templates/default/admin/img/grablet_over.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b47a985e8e5139c13cf0ab53abbf7fa8d410ae3 GIT binary patch literal 102 zcmZ?wbhEHb6k*_GSj5V3`0$Q{dzVQ|a%HCmUcG$$|Ns9CV4(Pug^_`Ql|cu@1F2<T z){fY9N7va=^7NKcmLQYwcg2cW9cOVx&0F$#UqMgcm!4Xs70(~d+Qiz>HGzS_8UUo~ BBk=$L literal 0 HcmV?d00001 diff --git a/templates/default/admin/pluginmanager.css b/templates/default/admin/pluginmanager.css new file mode 100644 index 0000000..a9973f8 --- /dev/null +++ b/templates/default/admin/pluginmanager.css @@ -0,0 +1,54 @@ +.pluginmanager_side { + vertical-align: top; + width: 33%; +} + +.pluginmanager_container { + margin: 0px; + padding: 2px; + list-style-type: none; + background-color: #fdfdff; + height: 98%; +} + +.pluginmanager_container li { + margin: 0px 0px 15px 0px; + padding: 3px; + position: relative; +} + +.pluginmanager_side .heading { + margin: 0px 0px 15px 0px; + padding: 3px; + font-weight: bold; + font-size: 12pt; + padding: 20px; + text-align: center; + border-bottom: 1px dotted #FAFAFA; +} + +.pluginmanager_item_even, .pluginmanager_item_uneven { + background-color: #FAFAFA; + border: 1px solid #DADADA; +} + +.pluginmanager_place, +.pluginmanager_ownership { + text-align: right; + margin: 2px; +} + +.pluginmanager_grablet { + display: block; + height: 10px; + width: 10%; + margin: 0px 0px 5px 0px; + padding: 0px; + background-image: url(img/grablet.gif); + background-repeat: repeat-x; + cursor:move; +} + +.grablet:hover { + background-image: url(img/grablet_over.gif); +} \ No newline at end of file -- 2.39.5