diff options
| author | Jeremy Kerr <jk@ozlabs.org> | 2009-02-08 21:40:17 +1100 | 
|---|---|---|
| committer | Jeremy Kerr <jk@ozlabs.org> | 2009-02-08 21:44:25 +1100 | 
| commit | 6ce62d26739ebf0dd81ecff5284adf3fbe2aed23 (patch) | |
| tree | 1bd8034844b48ef8c91f232de7000ff12450a06d | |
| parent | 6cf8d6e128b9117f10431eb9b534e9c8b1024cdf (diff) | |
| download | patchwork-6ce62d26739ebf0dd81ecff5284adf3fbe2aed23.tar.bz2 patchwork-6ce62d26739ebf0dd81ecff5284adf3fbe2aed23.tar.xz | |
Bundle reordering support
Bundles can now be reordered and saved.
Add dependency on jquery in INSTALL.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
| -rw-r--r-- | apps/patchwork/models.py | 22 | ||||
| -rw-r--r-- | apps/patchwork/utils.py | 32 | ||||
| -rw-r--r-- | apps/patchwork/views/bundle.py | 23 | ||||
| -rw-r--r-- | apps/patchwork/views/patch.py | 12 | ||||
| -rw-r--r-- | docs/INSTALL | 12 | ||||
| -rw-r--r-- | htdocs/css/style.css | 27 | ||||
| -rw-r--r-- | htdocs/js/bundle.js | 99 | ||||
| l--------- | htdocs/js/jquery-1.2.6.js | 1 | ||||
| -rw-r--r-- | templates/patchwork/bundle.html | 2 | ||||
| -rw-r--r-- | templates/patchwork/patch-list.html | 33 | 
10 files changed, 203 insertions, 60 deletions
| 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('<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); +} 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 %}    <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"> diff --git a/templates/patchwork/patch-list.html b/templates/patchwork/patch-list.html index 5518805..d4dd325 100644 --- a/templates/patchwork/patch-list.html +++ b/templates/patchwork/patch-list.html @@ -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> | 
