From: Jeremy Kerr Date: Sun, 8 Feb 2009 10:40:17 +0000 (+1100) Subject: Bundle reordering support X-Git-Url: https://git.ozlabs.org/?p=patchwork;a=commitdiff_plain;h=6ce62d26739ebf0dd81ecff5284adf3fbe2aed23 Bundle reordering support Bundles can now be reordered and saved. Add dependency on jquery in INSTALL. Signed-off-by: Jeremy Kerr --- diff --git a/apps/patchwork/models.py b/apps/patchwork/models.py index d0c2a6e..a672f9a 100644 --- a/apps/patchwork/models.py +++ b/apps/patchwork/models.py @@ -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) diff --git a/apps/patchwork/utils.py b/apps/patchwork/utils.py index 63daa85..5bd6925 100644 --- a/apps/patchwork/utils.py +++ b/apps/patchwork/utils.py @@ -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() diff --git a/apps/patchwork/views/bundle.py b/apps/patchwork/views/bundle.py index d8e4e2f..9995fc6 100644 --- a/apps/patchwork/views/bundle.py +++ b/apps/patchwork/views/bundle.py @@ -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 diff --git a/apps/patchwork/views/patch.py b/apps/patchwork/views/patch.py index 72472ca..49843eb 100644 --- a/apps/patchwork/views/patch.py +++ b/apps/patchwork/views/patch.py @@ -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: diff --git a/docs/INSTALL b/docs/INSTALL index 42822e3..5075f9e 100644 --- a/docs/INSTALL +++ b/docs/INSTALL @@ -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. diff --git a/htdocs/css/style.css b/htdocs/css/style.css index 4d1e440..1813c20 100644 --- a/htdocs/css/style.css +++ b/htdocs/css/style.css @@ -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%; } diff --git a/htdocs/js/bundle.js b/htdocs/js/bundle.js index dc4fb9c..0bdf41a 100644 --- a/htdocs/js/bundle.js +++ b/htdocs/js/bundle.js @@ -1,41 +1,82 @@ -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(''); + } - 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); +} diff --git a/htdocs/js/jquery-1.2.6.js b/htdocs/js/jquery-1.2.6.js deleted file mode 120000 index cb24de6..0000000 --- a/htdocs/js/jquery-1.2.6.js +++ /dev/null @@ -1 +0,0 @@ -../../lib/packages/jquery/jquery-1.2.6.js \ No newline at end of file diff --git a/templates/patchwork/bundle.html b/templates/patchwork/bundle.html index d9a2785..616a62e 100644 --- a/templates/patchwork/bundle.html +++ b/templates/patchwork/bundle.html @@ -4,7 +4,7 @@ {% block headers %}