summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/patchwork/models.py22
-rw-r--r--apps/patchwork/utils.py32
-rw-r--r--apps/patchwork/views/bundle.py23
-rw-r--r--apps/patchwork/views/patch.py12
-rw-r--r--docs/INSTALL12
-rw-r--r--htdocs/css/style.css27
-rw-r--r--htdocs/js/bundle.js99
l---------htdocs/js/jquery-1.2.6.js1
-rw-r--r--templates/patchwork/bundle.html2
-rw-r--r--templates/patchwork/patch-list.html33
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>