New motion version diff view. Improved version history table in motion detail template.

This commit is contained in:
Emanuel Schuetze 2013-03-14 21:28:03 +01:00
parent 196ad217b0
commit a1a11ff53c
6 changed files with 207 additions and 78 deletions

View File

@ -15,6 +15,8 @@
from datetime import datetime from datetime import datetime
import difflib
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.db.models import Max from django.db.models import Max
@ -496,6 +498,12 @@ class MotionVersion(models.Model):
"""Return True, if the version is the active version of a motion. Else: False.""" """Return True, if the version is the active version of a motion. Else: False."""
return self.active_version.exists() return self.active_version.exists()
def make_htmldiff(self, rev1, rev2):
"""Return string of html diff between two strings (rev1 and rev2)"""
diff = difflib.HtmlDiff(wrapcolumn=60)
return diff.make_table(rev1.splitlines(), rev2.splitlines())
class MotionSubmitter(models.Model): class MotionSubmitter(models.Model):
"""Save the submitter of a Motion.""" """Save the submitter of a Motion."""

View File

@ -0,0 +1,41 @@
/**
* OpenSlides motion style
*
* :copyright: 2013 by OpenSlides team, see AUTHORS.
* :license: GNU GPL, see LICENSE for more details.
*/
/* motion version diff table */
table.diff, .diff_row {
font-size: 12px;
width: 100%;
border: 0px;
}
table.diff[rules][rules="groups"] > colgroup, table[rules][rules="groups"] > tfoot, table[rules][rules="groups"] > thead, table[rules][rules="groups"] > tbody {
border: none;
}
table.diff td {
padding: 0 !important;
border: none;
}
.diff_header {
background-color:#e0e0e0;
}
td.diff_header {
text-align:right;
display: none;
}
.diff_next {
background-color:#c0c0c0;
display: none;
}
.diff_add {
background-color:#aaffaa;
}
.diff_chg {
background-color:#ffff77;
}
.diff_sub {
background-color:#ffaaaa;
}

View File

@ -56,14 +56,14 @@
<div class="row-fluid"> <div class="row-fluid">
<div class="span8"> <div class="span8">
{% if motion.active_version.id != motion.version.id %}
<span class="label label-warning"><i class="icon-warning-sign icon-white"></i> <span class="label label-warning"><i class="icon-warning-sign icon-white"></i>
{% if motion.version.version_number < motion.last_version.version_number %} {% if motion.version.version_number < motion.last_version.version_number %}
{% trans "This is not the newest version." %}</span> <a href="{% model_url motion.last_version %}">{% trans "Go to version" %} {{ motion.last_version.version_number }}.</a> {% trans "This is not the newest version." %}</span> <a href="{% model_url motion.last_version %}">{% trans "Go to version" %} {{ motion.last_version.version_number }}.</a>
{% else %} {% else %}
{% trans "This is not the authorized version." %}</span> <a href="{% model_url motion.active_version %}">{% trans "Go to version" %} {{ motion.active_version.version_number }}.</a> {% trans "This is not the authorized version." %}</span> <a href="{% model_url motion.active_version %}">{% trans "Go to version" %} {{ motion.active_version.version_number }}.</a>
{% endif %} {% endif %}
{% endif %}
<!-- Text --> <!-- Text -->
<h4>{% trans "Motion text" %}:</h4> <h4>{% trans "Motion text" %}:</h4>
@ -80,84 +80,60 @@
<br> <br>
<!-- Version history --> <!-- Version history -->
{% for version in motion.versions.all %} {% with versions=motion.versions.all %}
{% if forloop.first %} {% if versions|length > 1 %}
<h4>{% trans "Version history" %}:</h4> {% for version in versions %}
<table class="table table-striped table-bordered"> {% if forloop.first %}
<tr> <h4>{% trans "Version history" %}:</h4>
<th></th> <form action="{% url 'motion_version_diff' motion.pk %}" method="get">
<th>{% trans "Version" %}</th> <table class="table table-striped table-bordered">
<th>{% trans "Time" %}</th> <tr>
<th>{% trans "Title" %}</th> <th></th>
<th>{% trans "Text" %}</th> <th>#</th>
<th>{% trans "Reason" %}</th> <th>{% trans "Time" %}</th>
</tr> <th><button class="btn btn-small" type="submit">{% trans 'Difference' %}</button></th>
{% endif %} <th>{% trans "Actions" %}</th>
<tr> </tr>
<td class="nobr">
{% if version == motion.active_version %}
<span class="badge badge-success" title="{% trans 'This version is authorized' %}"><i class="icon-ok icon-white"></i></span>
{% else %}
{% if perms.motion.can_manage_motion %}
<a class="btn btn-mini" href="{% url 'motion_version_permit' motion.id version.version_number %}" title="{% trans 'Permit this version' %}"><i class="icon-ok"></i></a>
{% endif %}
{% if not version.rejected and version.id > motion.active_version.id and perms.motion.can_manage_motion %}
<a class="btn btn-mini" href="{% url 'motion_version_reject' motion.id version.version_number %}" title="{% trans 'Reject this version' %}"><i class="icon-ban-circle"></i></a>
{% endif %}
{% endif %} {% endif %}
{% if version.rejected %} <tr {% if version == motion.version %}class="offline"{%endif %}>
<span class="badge badge-important" title="{% trans 'This version is rejected' %}"><i class="icon-ban-circle icon-white"></i></span> <td class="nobr">
{% endif %} {% if version == motion.active_version %}
</td> <span class="badge badge-success" title="{% trans 'This version is authorized' %}"><i class="icon-ok icon-white"></i></span>
<td><a href="{% model_url version %}">{{ version.version_number }}</a></td> {% else %}
<td><i>{{ version.creation_time }}</i></td> {% if perms.motion.can_manage_motion %}
<td> <a class="btn btn-mini" href="{% url 'motion_version_permit' motion.id version.version_number %}" title="{% trans 'Permit this version' %}"><i class="icon-ok"></i></a>
{% ifchanged %} {% endif %}
<b>{{ version.title }}</b> {% if not version.rejected and version.id > motion.active_version.id and perms.motion.can_manage_motion %}
{% else %} <a class="btn btn-mini" href="{% url 'motion_version_reject' motion.id version.version_number %}" title="{% trans 'Reject this version' %}"><i class="icon-ban-circle"></i></a>
<i>[{% trans "unchanged" %}]</i> {% endif %}
{% endifchanged %} {% endif %}
</td> {% if version.rejected %}
<td> <span class="badge badge-important" title="{% trans 'This version is rejected' %}"><i class="icon-ban-circle icon-white"></i></span>
{% ifchanged %} {% endif %}
{{ version.text|linebreaks }} </td>
{% else %} <td>{{ version.version_number }}</td>
<i>[{% trans "unchanged" %}]</i> <td><i>{{ version.creation_time }}</i></td>
{% endifchanged %} <td>
</td> <input type="radio" value="{{ version.version_number }}" name="rev1">
<td> <input type="radio" value="{{ version.version_number }}" name="rev2">
{% ifchanged %} </td>
{{ version.reason|linebreaks }} <td>
{% else %} <a href="{% model_url version %}" title="{% trans 'Show version number' %} {{ version.version_number }}" class="btn btn-mini">
<i>[{% trans "unchanged" %}]</i> <i class="icon-search"></i>
{% endifchanged %} </a>
</td> {# TODO: add delete version function #}
</tr> <a href="{% model_url version 'delete' %}" title="{% trans 'Delete version number' %} {{ version.version_number }}" class="btn btn-mini">
{% if forloop.last %} <i class="icon-remove"></i>
</table> </a>
</td>
</tr>
{% if forloop.last %}
</table>
</form>
{% endif %}
{% endfor %}
{% endif %} {% endif %}
{% endfor %} {% endwith %}
<!-- TODO: For testing -->
<ol>
{% for motion_version in motion.versions.all %}
<li>
{% if motion_version.id == motion.version.id %}
<strong><a href="{% model_url motion_version %}">{{ motion_version }}</a></strong>
{% else %}
<a href="{% model_url motion_version %}">{{ motion_version }}</a>
{% endif %}
{% if motion_version.active %}
(active)
{% endif %}
{% if motion_version.rejected %}
(rejected)
{% endif %}
</li>
{% endfor %}
</ol>
<!-- End TODO -->
<!-- Log --> <!-- Log -->
{% if perms.motion.can_manage_motion %} {% if perms.motion.can_manage_motion %}

View File

@ -0,0 +1,67 @@
{% extends "base.html" %}
{% load tags %}
{% load i18n %}
{% load staticfiles %}
{% block title %}{{ block.super }} {% trans "Motion" %} {{ motion.number }}{% endblock %}
{% block header %}
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/motion.css' %}" />
{% endblock %}
{% block content %}
<h1>
{{ motion.title }}
<br>
<small>
{% if motion.number != None %}
{% trans "Motion" %} {{ motion.number }},
{% else %}
<i>[{% trans "no number" %}]</i>,
{% endif %}
{% trans "Diff view" %}
</small>
<small class="pull-right">
<div class="btn-toolbar">
<a href="{% url 'motion_detail' motion.id %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to motion" %}</a>
</div>
</small>
</h1>
{% if version_rev1 and version_rev2 %}
<table class="table table-striped table-bordered">
<tr>
<td><b>{% trans "Version" %} {{ version_rev1.version_number }}</b><br>
{% trans "created: " %} {{ version_rev1.creation_time }}<br>
<h4>{{ version_rev1.title }}</h4>
</td>
<td><b>{% trans "Version" %} {{ version_rev2.version_number }}</b><br>
{% trans "created: " %} {{ version_rev1.creation_time }}<br>
<h4>{{ version_rev2.title }}</h4>
</td>
</tr>
<!-- Text -->
<tr class="diff_row">
{% if not version_rev1.text == version_rev2.text %}
<td colspan="2">{{ diff_text|safe }}</td>
{% else %}
<td>{{ version_rev1.text }}</td>
<td>{{ version_rev2.text }}</td>
{% endif %}
</tr>
<!-- Reason -->
<tr><td colspan="2"><h4>{% trans "Reason" %}:</h4></td></tr>
<tr class="diff_row">
{% if not version_rev1.reason == version_rev2.reason %}
<td colspan="2">{{ diff_reason|safe }}</td>
{% else %}
<td>{{ version_rev1.reason }}</td>
<td>{{ version_rev2.reason }}</td>
{% endif %}
</tr>
</table>
{% endif %}
{% endblock %}

View File

@ -55,6 +55,11 @@ urlpatterns = patterns('openslides.motion.views',
name='motion_version_reject', name='motion_version_reject',
), ),
url(r'^(?P<pk>\d+)/diff/$',
'version_diff',
name='motion_version_diff',
),
url(r'^(?P<pk>\d+)/support/$', url(r'^(?P<pk>\d+)/support/$',
'motion_support', 'motion_support',
name='motion_support', name='motion_support',

View File

@ -230,6 +230,38 @@ class VersionRejectView(GetVersionMixin, SingleObjectMixin, QuestionMixin, Redir
version_reject = VersionRejectView.as_view() version_reject = VersionRejectView.as_view()
class VersionDiffView(GetVersionMixin, DetailView):
"""Show diff between two versions of a motion."""
permission_required = 'motion.can_see_motion'
model = Motion
template_name = 'motion/motion_diff.html'
def get_context_data(self, **kwargs):
"""Return the template context with versions and html diff strings."""
try:
rev1 = int(self.request.GET['rev1'])
rev2 = int(self.request.GET['rev2'])
version_rev1 = self.object.version.motion.versions.get(version_number=self.request.GET['rev1'])
version_rev2 = self.object.version.motion.versions.get(version_number=self.request.GET['rev2'])
diff_text = self.object.version.make_htmldiff(version_rev1.text, version_rev2.text)
diff_reason = self.object.version.make_htmldiff(version_rev1.reason, version_rev2.reason)
except (KeyError, ValueError, MotionVersion.DoesNotExist):
messages.error(self.request, _('At least one version number was not valid.'))
version_rev1 = None
version_rev2 = None
diff_text = None
diff_reason = None
context = super(VersionDiffView, self).get_context_data(**kwargs)
context.update({
'version_rev1': version_rev1,
'version_rev2': version_rev2,
'diff_text': diff_text,
'diff_reason': diff_reason,
})
return context
version_diff = VersionDiffView.as_view()
class SupportView(SingleObjectMixin, RedirectView): class SupportView(SingleObjectMixin, RedirectView):
"""View to support or unsupport a motion. """View to support or unsupport a motion.