Introduction
This tutorial serves as a basis for drag drop functionality, with a push in the direction of portal website development. I kept the code short and simple so it can be implemented in any coding environment.
NOTE!!!: This is not a fully functional portal CMS, just a small working example of drag drop Widget Zone functionality with a push in the right direction towards developing your own portal website.
Background
I worked with many tools for drag drop functionality (mainly not understanding their inner workings) including Telerik RadDock and Dropthings drag drop framework and my aim was to create similar functionality that was not dependant on a server side framework (the above mentioned being dependant on the Microsoft Ajax extensions framework).
Recently, I found a very good and lightweight example here and found that it was a very good basis to run language independent drag drop interface with a minor change needed to be done in the drag drop script to return me the important values needed, namely (what widget move, to where it moved and into which position it moved). Calling the following function:
<script language="javascript" type="text/javascript">
function moveWidget(elementid, parentid, index) {
window.alert("element: " + elementid + " ,parent: " +
parentid + " ,index: " + index);
}
</script>
Using the Code
The script imports in the header are very important to assign drag drop functionality to the columns and the docklets, but primarily you'll be focused on the moveWidget
function that will return you the values that are important. In .NET you can use Ajax, web method or a service to parse these values to the server side:
<!--Drag drop Functionality Scripts -->
<script language="javascript" type="text/javascript">
function moveWidget(elementid, parentid, index) {
window.alert("element: " + elementid + " ,parent: " +
parentid + " ,index: " + index);
}
</script>
<script type="text/javascript" src="Scripts/prototype.js"></script>
<script type="text/javascript"
src="Scripts/scriptaculous.js?load=effects,dragdrop"></script>
<script type="text/javascript" src="Scripts/portal.js"></script>
<script type="text/javascript">
var settings = {};
var portal;
function init() {
portal = new Portal();
portal.applySettings(settings);
}
try {
Event.observe(window, 'load', init, false);
} catch (exception) { }
</script>
To define a column/drag drop zone, use...
class="portal-column" id="portal-column-0"
... naming each consecutive column with a different integer.
To define a Widget/Docking Container, use...
<div class="block " id="block-archive-0">
<div class="handle">
Dock 1</div>
<div class="content">
<div>
Dock 1 content
<br />
<br />
<br />
<br />
</div>
</div>
</div>
... naming each block archive id with a different id.
Here is an example:
<td style="vertical-align:top;">
<div class="portal-column" id="portal-column-0">
<h2>
Column 0</h2>
<div class="block " id="block-archive-0">
<div class="handle">
Dock 1</div>
<div class="content">
<div>
Dock 1 content
<br />
<br />
<br />
<br />
</div>
</div>
</div>
<div class="block " id="block-archive-1">
<div class="handle">
Dock2</div>
<div class="content">
<div>
Dock 2 content
<br />
<br />
<br />
<br />
</div>
</div>
</div>
<div class="block " id="block-archive-2">
<div class="handle">
Dock 3</div>
<div class="content">
<div>
Dock 3 content
<br />
<br />
<br />
<br />
</div>
</div>
</div>
</div>
</td>
<td style="vertical-align:top;">
<div class="portal-column" id="portal-column-1">
<h2>
Column 1</h2>
</div>
</td>
<td style="vertical-align:top;">
<div class="portal-column" id="portal-column-2">
<h2>
Column 2</h2>
</div>
</td>
Moving Towards Server Side Driven Data
Below is a LINQ to SQL diagram of entity relations. It is still work in progress, but I wanted you to see how the entities should be mapped so widgets can be bound to a specific Zone, Page and Template, as well as the sequence property on the PortalWidget
will be of main focus here.
When a drag and drop occurs, you can use Ajax, web methods or web services to update the database. (Please secure these methods because a hacker can bomb your server with requests.)
Here's the snippet I use to update the database with the latest position data:
public static void PerformDragDrop(Guid widgetid,
Guid templateid, Guid pageid, string zoneID, int seq)
{
LINQ.DatabaseDataContext db =
new WebCMS.LINQ.DatabaseDataContext(LINQ.Connection.GetDBConnectionString());
LINQ.PortalWidget movedWidget =
db.PortalWidgets.SingleOrDefault(p => p.WidgetID.Equals(widgetid)
&&p.TemplateID.Equals(templateid)&&p.PageID.Equals(pageid));
if (movedWidget != null)
{
var previousZoneWidgets = from p in movedWidget.PortalZone.PortalWidgets
orderby p.Sequence ascending
select p;
int counter = 0;
foreach (LINQ.PortalWidget widget in previousZoneWidgets)
{
if (!widget.Equals(movedWidget))
{
widget.Sequence = counter;
counter++;
}
}
counter = 0;
var newZoneWidgets = from p in db.PortalWidgets
where p.ZoneID.Equals(zoneID)
orderby p.Sequence ascending
select p;
movedWidget.Sequence = seq;
movedWidget.ZoneID = zoneID;
foreach (LINQ.PortalWidget widget in newZoneWidgets)
{
if (widget.Sequence >= seq)
{
if (widget.Sequence == seq)
{
counter = seq + 1;
widget.Sequence = counter;
}
else
{
widget.Sequence = counter;
counter++;
}
}
}
db.SubmitChanges();
}
}
This is the LINQ statement that works with the above mentioned diagram.
First I reorder the previous Zone the widget was in excluding the widget that was moved, then I loop through the widgets in the zone my widget moved to and insert the moved widget to the correct sequence.
Known Issues and Limitations
When dragging and dropping in the example, the widget might unexpectedly jump to the middle column. This is only because of the alert and when clicked, it suddenly jumps to the middle column because that marks as the last mouse position. This will go away once you don't alert in the moveWidget
JavaScript function in the header.
History
- 5th June, 2009: Initial post