Commit 6ce62d26 authored by Jeremy Kerr's avatar Jeremy Kerr
Browse files

Bundle reordering support



Bundles can now be reordered and saved.

Add dependency on jquery in INSTALL.
Signed-off-by: default avatarJeremy Kerr <jk@ozlabs.org>
parent 6cf8d6e1
......@@ -295,12 +295,25 @@ class Bundle(models.Model):
def n_patches(self):
return self.patches.all().count()
def ordered_patches(self):
return self.patches.order_by('bundlepatch__order');
def append_patch(self, patch):
# todo: use the aggregate queries in django 1.1
orders = BundlePatch.objects.filter(bundle = self).values('order')
max_order = max([ v for (k, v) in orders])
orders = BundlePatch.objects.filter(bundle = self).order_by('-order') \
.values('order')
if len(orders) > 0:
max_order = orders[0]['order']
else:
max_order = 0
# see if the patch is already in this bundle
if BundlePatch.objects.filter(bundle = self, patch = patch).count():
raise Exception("patch is already in bundle")
bp = BundlePatch.objects.create(bundle = self, patch = patch, order = max_order + 1)
bp = BundlePatch.objects.create(bundle = self, patch = patch,
order = max_order + 1)
bp.save()
class Meta:
......@@ -327,7 +340,8 @@ class BundlePatch(models.Model):
order = models.IntegerField()
class Meta:
unique_together = [('bundle', 'patch'), ('bundle', 'order')]
unique_together = [('bundle', 'patch')]
ordering = ['order']
class UserPersonConfirmation(models.Model):
user = models.ForeignKey(User)
......
......@@ -19,7 +19,7 @@
from patchwork.forms import MultiplePatchForm
from patchwork.models import Bundle, Project, State, UserProfile
from patchwork.models import Bundle, Project, BundlePatch, State, UserProfile
from django.conf import settings
from django.shortcuts import render_to_response, get_object_or_404
......@@ -100,35 +100,35 @@ def set_bundle(user, project, action, data, patches, context):
bundle = Bundle(owner = user, project = project,
name = data['bundle_name'])
bundle.save()
str = 'added to new bundle "%s"' % bundle.name
auth_required = False
context.add_message("Bundle %s created" % bundle.name)
elif action =='add':
bundle = get_object_or_404(Bundle, id = data['bundle_id'])
str = 'added to bundle "%s"' % bundle.name
auth_required = False
elif action =='remove':
bundle = get_object_or_404(Bundle, id = data['removed_bundle_id'])
str = 'removed from bundle "%s"' % bundle.name
auth_required = False
if not bundle:
return ['no such bundle']
for patch in patches:
if action == 'create' or action == 'add':
bundle.append_patch(patch)
try:
bundle.append_patch(patch)
context.add_message("Patch '%s' added to bundle %s" % \
(patch.name, bundle.name))
except Exception, ex:
context.add_message("Couldn't add patch '%s' to bundle: %s" % \
(patch.name, ex.message))
elif action == 'remove':
bundle.patches.remove(patch)
if len(patches) > 0:
if len(patches) == 1:
str = 'patch ' + str
else:
str = 'patches ' + str
context.add_message(str)
try:
bp = BundlePatch.objects.get(bundle = bundle, patch = patch)
bp.delete()
context.add_message("Patch '%s' removed from bundle %s\n" % \
(patch.name, bundle.name))
except Exception:
pass
bundle.save()
......
......@@ -23,7 +23,7 @@ from django.shortcuts import render_to_response, get_object_or_404
from patchwork.requestcontext import PatchworkRequestContext
from django.http import HttpResponse, HttpResponseRedirect
import django.core.urlresolvers
from patchwork.models import Patch, Bundle, Project
from patchwork.models import Patch, Bundle, BundlePatch, Project
from patchwork.utils import get_patch_ids
from patchwork.forms import BundleForm, DeleteBundleForm
from patchwork.views import generic_list
......@@ -49,7 +49,10 @@ def setbundle(request):
patch_id = request.POST.get('patch_id', None)
if patch_id:
patch = get_object_or_404(Patch, id = patch_id)
bundle.patches.add(patch)
try:
bundle.append_patch(patch)
except Exception:
pass
bundle.save()
elif action == 'add':
bundle = get_object_or_404(Bundle,
......@@ -65,7 +68,7 @@ def setbundle(request):
for id in patch_ids:
try:
patch = Patch.objects.get(id = id)
bundle.patches.add(patch)
bundle.append_patch(patch)
except ex:
pass
......@@ -143,11 +146,23 @@ def bundle(request, bundle_id):
else:
form = BundleForm(instance = bundle)
if request.method == 'POST' and request.POST.get('form') == 'reorderform':
order = get_object_or_404(BundlePatch, bundle = bundle,
patch__id = request.POST.get('order_start')).order
for patch_id in request.POST.getlist('neworder'):
bundlepatch = get_object_or_404(BundlePatch,
bundle = bundle, patch__id = patch_id)
bundlepatch.order = order
bundlepatch.save()
order += 1
context = generic_list(request, bundle.project,
'patchwork.views.bundle.bundle',
view_args = {'bundle_id': bundle_id},
filter_settings = filter_settings,
patches = bundle.patches.all())
patches = bundle.ordered_patches(),
editable_order = True)
context['bundle'] = bundle
context['bundleform'] = form
......
......@@ -59,7 +59,7 @@ def patch(request, patch_id):
data = request.POST)
if createbundleform.is_valid():
createbundleform.save()
bundle.patches.add(patch)
bundle.append_patch(patch)
bundle.save()
createbundleform = CreateBundleForm()
context.add_message('Bundle %s created' % bundle.name)
......@@ -67,9 +67,13 @@ def patch(request, patch_id):
elif action == 'addtobundle':
bundle = get_object_or_404(Bundle, id = \
request.POST.get('bundle_id'))
bundle.patches.add(patch)
bundle.save()
context.add_message('Patch added to bundle "%s"' % bundle.name)
try:
bundle.append_patch(patch)
bundle.save()
context.add_message('Patch added to bundle "%s"' % bundle.name)
except Exception, ex:
context.add_message("Couldn't add patch '%s' to bundle %s: %s" \
% (patch.name, bundle.name, ex.message))
# all other actions require edit privs
elif not editable:
......
......@@ -70,6 +70,18 @@ in brackets):
cd ../../apps
ln -s ../lib/packages/django-registration ./registration
We also use some Javascript libraries:
cd lib/packages
mkdir jquery
cd jquery
wget http://jqueryjs.googlecode.com/files/jquery-1.3.min.js
wget http://www.isocra.com/articles/jquery.tablednd_0_5.js.zip
unzip jquery.tablednd_0_5.js.zip jquery.tablednd_0_5.js
cd ../../../htdocs/js/
ln -s ../../lib/packages/jquery/jquery-1.3.min.js ./
ln -s ../../lib/packages/jquery/jquery.tablednd_0_5.js ./
The settings.py file contains default settings for patchwork, you'll
need to configure settings for your own setup.
......
......@@ -152,7 +152,13 @@ table.patchlist td.patchlistfilters {
border-top: thin solid gray;
border-bottom: thin solid black;
font-size: smaller;
}
table.patchlist td.patchlistreorder {
background: #c0c0ff;
border-top: thin solid gray;
border-bottom: thin solid black;
font-size: smaller;
text-align: right;
}
table.patchlist tr.odd {
background: #ffffff;
......@@ -178,6 +184,25 @@ div.patchforms {
margin-top: 2em;
}
/* list order manipulation */
table.patchlist tr.draghover {
background: #e8e8e8 !important;
}
.dragging {
border: thin solid black;
background: #e8e8e8 !important;
}
input#reorder-cancel {
display: none;
color: #505050;
}
input#reorder-change {
}
/* list pagination */
.paginator { padding-bottom: 1em; padding-top: 1em; font-size: 80%; }
......
function parse_patch_id(id_str)
var editing_order = false;
var dragging = false;
function order_button_click(node)
{
var i;
var rows, form;
i = id_str.indexOf(':');
if (i == -1)
return null;
form = $("#reorderform");
rows = $("#patchlist").get(0).tBodies[0].rows;
return id_str.substring(i + 1);
}
if (rows.length < 1)
return;
function bundle_handle_drop(table, row)
{
var relative, relation, current;
var relative_id, current_id;
if (editing_order) {
current = $(row);
relative = $(current).prev();
relation = 'after';
/* disable the save button */
node.disabled = true;
/* if we have no previous row, position ourselves before the next
* row instead */
if (!relative.length) {
relative = current.next();
relation = 'before';
/* add input elements as the sequence of patches */
for (var i = 0; i < rows.length; i++) {
form.append('<input type="hidden" name="neworder" value="' +
row_to_patch_id(rows[i]) + '"/>');
}
if (!relative)
return;
form.get(0).submit();
} else {
/* store the first order value */
start_order = row_to_patch_id(rows[0]);
$("input[name='order_start']").attr("value", start_order);
/* update buttons */
node.setAttribute("value", "Save order");
$("#reorder\\-cancel").css("display", "inline");
/* show help text */
$("#reorderhelp").text('Drag & drop rows to reorder');
/* enable drag & drop on the patches list */
$("#patchlist").tableDnD({
onDragClass: 'dragging',
onDragStart: function() { dragging = true; },
onDrop: function() { dragging = false; }
});
/* replace zebra striping with hover */
$("#patchlist tbody tr").css("background", "inherit");
$("#patchlist tbody tr").hover(drag_hover_in, drag_hover_out);
}
current_id = parse_patch_id(current.attr('id'));
relative_id = parse_patch_id(relative.attr('id'));
editing_order = !editing_order;
}
alert("put patch " + current_id + " " + relation + " " + relative_id);
function order_cancel_click(node)
{
node.form.submit();
}
$(document).ready(function() {
$("#patchlist").tableDnD({
onDrop: bundle_handle_drop
});
});
/* dragging helper functions */
function drag_hover_in()
{
if (!dragging)
$(this).addClass("draghover");
}
function drag_hover_out()
{
$(this).removeClass("draghover");
}
function row_to_patch_id(node)
{
var id_str, i;
id_str = node.getAttribute("id");
i = id_str.indexOf(':');
if (i == -1)
return null;
return id_str.substring(i + 1);
}
../../lib/packages/jquery/jquery-1.2.6.js
\ No newline at end of file
......@@ -4,7 +4,7 @@
{% block headers %}
<script language="JavaScript" type="text/javascript"
src="/js/jquery-1.2.6.js">
src="/js/jquery-1.3.min.js">
</script>
<script language="JavaScript" type="text/javascript"
src="/js/jquery.tablednd_0_5.js">
......
......@@ -9,6 +9,19 @@
<td class="patchlistfilters">
{% include "patchwork/filters.html" %}
</td>
{% if order.editable %}
<td class="patchlistreorder">
<form method="post" id="reorderform">
<input type="hidden" name="form" value="reorderform"/>
<input type="hidden" name="order_start" value="0"/>
<span id="reorderhelp"></span>
<input id="reorder-cancel" type="button" value="Cancel"
onClick="order_cancel_click(this)"/>
<input id="reorder-change" type="button" value="Change order"
onClick="order_button_click(this)"/>
</form>
</td>
{% endif %}
</tr>
</table>
......@@ -40,7 +53,11 @@
></a> <a class="colactive"
href="{% listurl order=order.reversed_name %}">Patch</a>
{% else %}
{% if not order.editable %}
<a class="colinactive" href="{% listurl order="name" %}">Patch</a>
{% else %}
<span class="colinactive">Patch</span>
{% endif %}
{% endifequal %}
</th>
......@@ -53,7 +70,11 @@
></a> <a class="colactive"
href="{% listurl order=order.reversed_name %}">Date</a>
{% else %}
{% if not order.editable %}
<a class="colinactive" href="{% listurl order="date" %}">Date</a>
{% else %}
<span class="colinactive">Date</span>
{% endif %}
{% endifequal %}
</th>
......@@ -66,7 +87,11 @@
></a> <a class="colactive"
href="{% listurl order=order.reversed_name %}">Submitter</a>
{% else %}
{% if not order.editable %}
<a class="colinactive" href="{% listurl order="submitter" %}">Submitter</a>
{% else %}
<span class="colinactive">Submitter</span>
{% endif %}
{% endifequal %}
</th>
......@@ -79,7 +104,11 @@
></a> <a class="colactive"
href="{% listurl order=order.reversed_name %}">Delegate</a>
{% else %}
{% if not order.editable %}
<a class="colinactive" href="{% listurl order="delegate" %}">Delegate</a>
{% else %}
<span class="colinactive">Delegate</span>
{% endif %}
{% endifequal %}
</th>
......@@ -92,7 +121,11 @@
></a> <a class="colactive"
href="{% listurl order=order.reversed_name %}">State</a>
{% else %}
{% if not order.editable %}
<a class="colinactive" href="{% listurl order="state" %}">State</a>
{% else %}
<span class="colinactive">State</span>
{% endif %}
{% endifequal %}
</th>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment