From ee0e599a6c270aa70d0679d22f00ee7de1a5eeaf Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Sat, 9 Mar 2013 14:29:40 +0100 Subject: [PATCH 01/15] Update motion templates to new template design. Added some missing model and view functions. --- openslides/motion/models.py | 30 +- .../templates/motion/motion_detail.html | 325 ++++++++++++++---- .../motion/templates/motion/motion_form.html | 38 +- .../motion/templates/motion/motion_list.html | 99 +++++- .../motion/templates/motion/poll_form.html | 112 +++--- openslides/motion/views.py | 21 +- openslides/static/styles/base.css | 4 + 7 files changed, 492 insertions(+), 137 deletions(-) diff --git a/openslides/motion/models.py b/openslides/motion/models.py index 88f6c665e..b5b6a1303 100644 --- a/openslides/motion/models.py +++ b/openslides/motion/models.py @@ -19,6 +19,7 @@ from django.core.urlresolvers import reverse from django.db import models from django.db.models import Max from django.dispatch import receiver +from django.utils import formats from django.utils.translation import pgettext from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _ @@ -101,7 +102,7 @@ class Motion(SlideMixin, models.Model): and the config 'motion_create_new_version' is set to - 'ALLWASY_CREATE_NEW_VERSION'. + 'ALWAYS_CREATE_NEW_VERSION'. """ if not self.state: self.reset_state() @@ -296,10 +297,20 @@ class Motion(SlideMixin, models.Model): except IndexError: return self.new_version + @property + def submitters(self): + return sorted([object.person for object in self.submitter.all()], + key=lambda person: person.sort_name) + def is_submitter(self, person): """Return True, if person is a submitter of this motion. Else: False.""" self.submitter.filter(person=person).exists() + @property + def supporters(self): + return sorted([object.person for object in self.supporter.all()], + key=lambda person: person.sort_name) + def is_supporter(self, person): """Return True, if person is a supporter of this motion. Else: False.""" return self.supporter.filter(person=person).exists() @@ -379,13 +390,18 @@ class Motion(SlideMixin, models.Model): 'support': (self.state.allow_support and config['motion_min_supporters'] > 0 and - not self.is_submitter(person)), + not self.is_submitter(person) and + not self.is_supporter(person)), + + 'unsupport': (self.state.allow_support and + config['motion_min_supporters'] > 0 and + not self.is_submitter(person) and + self.is_supporter(person)), 'change_state': person.has_perm('motion.can_manage_motion'), } actions['delete'] = actions['edit'] # TODO: Only if the motion has no number - actions['unsupport'] = actions['support'] actions['reset_state'] = actions['change_state'] return actions @@ -394,7 +410,7 @@ class Motion(SlideMixin, models.Model): Message should be in english and translatable. - e.G: motion.write_log(ugettext_noob('Message Text')) + e.g.: motion.write_log(ugettext_noob('Message Text')) """ MotionLog.objects.create(motion=self, message=message, person=person) @@ -546,11 +562,11 @@ class MotionLog(models.Model): def __unicode__(self): """Return a string, representing the log message.""" - # TODO: write time in the local time format. + time = formats.date_format(self.time, 'DATETIME_FORMAT') if self.person is None: - return "%s %s" % (self.time, _(self.message)) + return "%s %s" % (time, _(self.message)) else: - return "%s %s by %s" % (self.time, _(self.message), self.person) + return "%s %s by %s" % (time, _(self.message), self.person) class MotionVote(BaseVote): diff --git a/openslides/motion/templates/motion/motion_detail.html b/openslides/motion/templates/motion/motion_detail.html index ccf616aab..a82796a36 100644 --- a/openslides/motion/templates/motion/motion_detail.html +++ b/openslides/motion/templates/motion/motion_detail.html @@ -4,26 +4,141 @@ {% load i18n %} {% load staticfiles %} -{% block title %}{{ block.super }} – {% trans "Motion" %} "{{ motion.title }}"{% endblock %} +{% block title %}{{ block.super }} – {% trans "Motion" %} {{ motion.number }}{% endblock %} {% block content %} -

Titel: {{ motion.title }}

-

Text: {{ motion.text }}

-

Reason: {{ motion.reason }}

-

Submitter: {% for submitter in motion.submitter.all %}{{ submitter.person }} {% endfor %}

-

Supporter: {% for supporter in motion.supporter.all %}{{ supporter.person }} {% endfor %}

-

Active Version: {{ motion.active_version }}

-

State: {{ motion.state }}

-

possible stats:

- +

+ {{ motion.title }} +
+ + {% if motion.number != None %} + {% trans "Motion" %} {{ motion.number }}, + {% else %} + [{% trans "no number" %}], + {% endif %} + {% trans "Version" %} {{ motion.version.version_number }} + + +
+ {% trans "Back to overview" %} + PDF + + {% if perms.projector.can_manage_projector %} + + + + {% endif %} +
+ + {% trans 'More actions' %} + + + +
+
+
+

+
+
+ {% if motion.active_version.id != motion.version.id %} + + {% if motion.version == motion.public_version %} + {% trans "This is not the newest version." %} {% trans "Go to version" %} {{ motion.last_version.version_number }}. + {% else %} + {% trans "This is not the authorized version." %} {% trans "Go to version" %} {{ motion.public_version.aid }}. + {% endif %} + {% endif %} -

Versions

+ +

{% trans "Motion text" %}:

+ {{ motion.version.text|linebreaks }} +
+ + +

{% trans "Reason" %}:

+ {% if motion.version.reason %} + {{ motion.version.reason|linebreaks }} + {% else %} + – + {% endif %} +
+ + + {% for version in motion.versions.all %} + {% if forloop.first %} +

{% trans "Version history" %}:

+ + + + + + + + + + {% endif %} + + + + + + + + + {% if forloop.last %} +
{% trans "Version" %}{% trans "Time" %}{% trans "Title" %}{% trans "Text" %}{% trans "Reason" %}
+ {% if version == motion.active_version %} + + {% else %} + {% if perms.motion.can_manage_motion %} + + {% endif %} + {% if not version.rejected and version.id > motion.active_version.id and perms.motion.can_manage_motion %} + + {% endif %} + {% endif %} + {% if version.rejected %} + + {% endif %} + {{ version.version_number }}{{ version.creation_time }} + {% ifchanged %} + {{ version.title }} + {% else %} + [{% trans "unchanged" %}] + {% endifchanged %} + + {% ifchanged %} + {{ version.text|linebreaks }} + {% else %} + [{% trans "unchanged" %}] + {% endifchanged %} + + {% ifchanged %} + {{ version.reason|linebreaks }} + {% else %} + [{% trans "unchanged" %}] + {% endifchanged %} +
+ {% endif %} + {% endfor %} + +
    {% for motion_version in motion.versions.all %}
  1. @@ -41,60 +156,152 @@
  2. {% endfor %}
+ -

{% trans "Vote results" %}:

- {% with motion.polls.all as polls %} -
    + + {% if perms.motion.can_manage_motion %} + +
    +
      + {% for message in motion.log_messages.all %} +
    • {{ message }}
    • + {% endfor %} +
    +
    + {% endif %} +
+ + +
+
+ +
{% trans "Submitter" %}:
+ {% for submitter in motion.submitter.all %} + {{ submitter.person }}{% if not forloop.last %}, {% endif %} + {% endfor %} + + + {% if min_supporters > 0 %} +
{% trans "Supporters" %}: *
+ {% if not motion.supporters %} + - + {% else %} +
    + {% for supporter in motion.supporter.all %} +
  1. {{ supporter }}
  2. + {% endfor %} +
+ {% endif %} + {% endif %} + + +
{% trans "Status" %}:
+ {% if motion.state_id != "pub" %} + {# TODO: trans motion.state #} + {{ motion.state }} +
+ {% endif %} + {% for note in motion.notes %} + {{ note }} + {% if not forloop.last %}
{% endif %} + {% endfor %} + + +
{% trans "Vote results" %}:
+ {% with motion.polls.all as polls %} +
    {% for poll in polls %} {% if perms.motion.can_manage_motion or poll.has_votes %} -
  1. - {% if perms.motion.can_manage_motion %} - {{ forloop.counter }}. {% trans "Vote" %} - - - - - - - {% elif poll.has_votes %} - {{ forloop.counter }}. {% trans "Vote" %}: - {% endif %} -
    - {% if poll.has_votes %} - {% with poll.get_options.0 as option %} - {{ option.Yes }}
    - {{ option.No }}
    - {{ option.Abstain }}
    - {{ poll.print_votesinvalid }}
    -
    - {{ poll.print_votescast }} -
    - {% endwith %} - {% else %} +
  2. {% trans "Vote" %} {% if perms.motion.can_manage_motion %} - - {% trans 'Enter result' %} - - {% endif %} - {% endif %} -
  3. + + + {% endif %} +
    + {% if poll.has_votes %} + {% with poll.get_options.0 as option %} + {{ option.Yes }}
    + {{ option.No }}
    + {{ option.Abstain }}
    + {{ poll.print_votesinvalid }}
    +
    + {{ poll.print_votescast }} +
    + {% endwith %} + {% else %} + {% if perms.motion.can_manage_motion %} + {% trans 'No results' %} + {% endif %} + {% endif %} + {% endif %} {% endfor %} - +
{% if allowed_actions.create_poll %} - - {% trans 'New vote' %} + + {% trans 'New vote' %} + + {% endif %} + {% endwith %} + + +
{% trans "Creation Time" %}:
+ {# TODO: use creation time of _first_ version #} + {{ motion.version.creation_time }} + + + {% if allowed_actions.wit and user in motion.submitters %} +

+ + {% trans 'Withdraw motion' %} {% endif %} - {% endwith %} + + {% if perms.motion.can_support_motion and min_supporters > 0 %} + {% if allowed_actions.unsupport %} +

+ + {% trans 'Unsupport' %} + + {% endif %} + {% if allowed_actions.support %} +

+ + {% trans 'Support' %} + + {% endif %} + {% endif %} + + + {% if min_supporters > 0 %} +

+ * {% trans "minimum required supporters" %}: {{ min_supporters }} + {% endif %} +
+ + {% if perms.motion.can_manage_motion %} + +
+

{% trans "Manage motion" %}

+
+ {% for state in motion.state.next_states.all %} + {{ state }} + {% endfor %} +
+

+
+
{% trans "For administration only:" %}
+ + {% trans 'Reset state' %} + +
+ {% endif %} +
+
-

log

- {% endblock %} diff --git a/openslides/motion/templates/motion/motion_form.html b/openslides/motion/templates/motion/motion_form.html index ddffb8254..42dd96c27 100644 --- a/openslides/motion/templates/motion/motion_form.html +++ b/openslides/motion/templates/motion/motion_form.html @@ -2,25 +2,35 @@ {% load tags %} {% load i18n %} -{% load staticfiles %} -{% block title %}{{ block.super }} – {% trans "Motion Form" %}{% endblock %} +{% block title %} + {{ block.super }} – + {% if motion %} + {% trans "Edit motion" %} + {% else %} + {% trans "New motion" %} + {% endif %} +{% endblock %} {% block content %} -

{% trans "Motions Forms" %}

+

+ {% if motion %} + {% trans "Edit motion" %} + {% else %} + {% trans "New motion" %} + {% endif %} + + {% trans "Back to overview" %} + +

{% csrf_token %} - {{ form.as_p }} - - - - + {% include "form.html" %} +

+ {% include "formbuttons_saveapply.html" %} + + {% trans 'Cancel' %} +

* {% trans "required" %}
{% endblock %} diff --git a/openslides/motion/templates/motion/motion_list.html b/openslides/motion/templates/motion/motion_list.html index 9cb2e5506..3bdf74e87 100644 --- a/openslides/motion/templates/motion/motion_list.html +++ b/openslides/motion/templates/motion/motion_list.html @@ -2,15 +2,104 @@ {% load tags %} {% load i18n %} -{% load staticfiles %} {% block title %}{{ block.super }} – {% trans "Motions" %}{% endblock %} {% block content %} -

{% trans "Motions" %}

-
    +

    + {% trans "Motions" %} + + {% if perms.motion.can_create_motion %} + {% trans "New" %} + {% endif %} + {% if perms.motion.can_manage_motion %} + {# {% trans 'Import' %}#} + {% endif %} + PDF + +

    + +
    + {% if min_supporters > 0 %} + + {% endif %} + + + +
    + {{ motion_list|length }} + {% blocktrans count counter=motion_list|length context "number of motions"%}motion{% plural %}motions{% endblocktrans %} + + + + + + {% if min_supporters > 0 %} + + {% endif %} + + + + + {% for motion in motion_list %} -
  1. {{ motion }}
  2. + + + + {% if min_supporters > 0 %} + + {% endif %} + + + {# TODO: user creation_time of _first_ version #} + + + + {% empty %} + + + {% endfor %} - +
    {% trans "#" %}{% trans "Motion title" %}{% trans "Number of supporters" %}{% trans "Status" %}{% trans "Submitter" %}{% trans "Creation Time" %}{% trans "Actions" %}
    {{ motion.number }}{{ motion.title }}{# motion.count_supporters #}{{ motion.state }} + {% for submitter in motion.submitter.all %} + {{ submitter.person }}{% if not forloop.last %}, {% endif %} + {% endfor %} + {{ motion.version.creation_time }} + + {% if perms.projector.can_manage_projector %} + + + + {% endif %} + {% if perms.motion.can_manage_motion %} + + + + {% if "delete" in useractions %} + + + + {% endif %} + {% endif %} + + PDF + + +
    {% trans "No motions available." %}
    {% endblock %} diff --git a/openslides/motion/templates/motion/poll_form.html b/openslides/motion/templates/motion/poll_form.html index f7cd3635a..f3bc4aa68 100644 --- a/openslides/motion/templates/motion/poll_form.html +++ b/openslides/motion/templates/motion/poll_form.html @@ -1,48 +1,78 @@ {% extends 'base.html' %} {% load i18n %} -{% load staticfiles %} -{% load tags %} + +{% block title %} + {{ block.super }} - {% trans "Motion" %} {{ motion.number }}, {{ ballot }}. {% trans "Vote" %} +{% endblock %} {% block content %} -

    {{ motion }}

    - {% trans "Special values" %}: -1 = {% trans 'majority' %}; -2 = {% trans 'undocumented' %} -
    {% csrf_token %} - {{ pre_form }} - - - - - - - {% for value in forms.0 %} - - - - - {% endfor %} - - - - - - - - -
    {% trans "Option" %}{% trans "Votes" %}
    {{ value.label }}{{ value.errors }}{{ value }}
    {% trans "Invalid votes" %}{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}
    {% trans "Votes cast" %}{{ pollform.votescast.errors }}{{ pollform.votescast }}
    - - {{ post_form }} - -

    - - - - {% trans 'Cancel' %} +

    + {{ motion }} +
    + + {% trans "Motion" %} {{ motion.number }}, {{ ballot }}. {% trans "Vote" %} + + +
    + {% trans "Back to motion" %} + + {% if perms.projector.can_manage_projector %} + + -

    - + {% endif %} +
    +
    +

    + +

    +{% trans "Special values" %}: -1 = {% trans 'majority' %} | -2 = {% trans 'undocumented' %} +

    +
    {% csrf_token %} + {{ pre_form }} + + + + + + + {% for value in forms.0 %} + + + + + {% endfor %} + + + + + + + + +
    {% trans "Option" %}{% trans "Votes" %}
    {{ value.label }}{{ value.errors }}{{ value }}
    {% trans "Invalid votes" %}{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}
    {% trans "Votes cast" %}{{ pollform.votescast.errors }}{{ pollform.votescast }}
    + + {{ post_form }} + + +

    +{# TODO: create ballot paper instead of motion_detail_pdf ! #} + + {% trans 'Ballot paper as PDF' %} + +

    + +
    + + + + {% trans 'Cancel' %} + +
    +
    {% endblock %} diff --git a/openslides/motion/views.py b/openslides/motion/views.py index e3cbfe499..7ac10ba4d 100644 --- a/openslides/motion/views.py +++ b/openslides/motion/views.py @@ -74,6 +74,8 @@ class MotionDetailView(GetVersionMixin, DetailView): """ context = super(MotionDetailView, self).get_context_data(**kwargs) context['allowed_actions'] = self.object.get_allowed_actions(self.request.user) + context['min_supporters'] = int(config['motion_min_supporters']) + context['user'] = self.request.user return context motion_detail = MotionDetailView.as_view() @@ -227,7 +229,7 @@ class VersionRejectView(GetVersionMixin, SingleObjectMixin, QuestionMixin, Redir version_reject = VersionRejectView.as_view() -class SupportView(SingleObjectMixin, QuestionMixin, RedirectView): +class SupportView(SingleObjectMixin, RedirectView): """View to support or unsupport a motion. If self.support is True, the view will append a request.user to the supporter list. @@ -246,7 +248,6 @@ class SupportView(SingleObjectMixin, QuestionMixin, RedirectView): def check_permission(self, request): """Return True if the user can support or unsupport the motion. Else: False.""" - allowed_actions = self.object.get_allowed_actions(request.user) if self.support and not allowed_actions['support']: messages.error(request, _('You can not support this motion.')) @@ -257,15 +258,8 @@ class SupportView(SingleObjectMixin, QuestionMixin, RedirectView): else: return True - def get_question(self): - """Return the question string.""" - if self.support: - return _('Do you really want to support this motion?') - else: - return _('Do you really want to unsupport this motion?') - - def case_yes(self): - """Append or remove the request.user from the motion. + def pre_redirect(self, request, *args, **kwargs): + """Append or remove the request.user from the motion and return success message. First the method checks the permissions, and writes a log message after appending or removing the user. @@ -278,6 +272,7 @@ class SupportView(SingleObjectMixin, QuestionMixin, RedirectView): else: self.object.unsupport(person=user) self.object.write_log(ugettext_noop("Supporter: -%s") % user, user) + messages.success(request, self.get_success_message()) def get_success_message(self): """Return the success message.""" @@ -373,6 +368,10 @@ class PollDeleteView(PollMixin, DeleteView): super(PollDeleteView, self).case_yes() self.object.write_log(ugettext_noop('Poll deleted'), self.request.user) + def get_redirect_url(self, **kwargs): + """Return the URL to the DetailView of the motion.""" + return reverse('motion_detail', args=[self.object.motion.pk]) + poll_delete = PollDeleteView.as_view() diff --git a/openslides/static/styles/base.css b/openslides/static/styles/base.css index 7075a4103..05d137c98 100644 --- a/openslides/static/styles/base.css +++ b/openslides/static/styles/base.css @@ -80,6 +80,10 @@ a:hover { padding: 8px 5px 4px 5px; } +/* Log */ +#log { + padding-left: 14px; +} /** Utils **/ tr.offline td, li.offline { From 5c70b4a044e27771883b89e30c2139e414452b11 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Mon, 11 Mar 2013 16:56:56 +0100 Subject: [PATCH 02/15] Update after comments: Removed user context var. Removed condition in unupport state check. --- openslides/motion/models.py | 1 - openslides/motion/templates/motion/motion_detail.html | 2 +- openslides/motion/views.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/openslides/motion/models.py b/openslides/motion/models.py index b5b6a1303..f6479c22e 100644 --- a/openslides/motion/models.py +++ b/openslides/motion/models.py @@ -394,7 +394,6 @@ class Motion(SlideMixin, models.Model): not self.is_supporter(person)), 'unsupport': (self.state.allow_support and - config['motion_min_supporters'] > 0 and not self.is_submitter(person) and self.is_supporter(person)), diff --git a/openslides/motion/templates/motion/motion_detail.html b/openslides/motion/templates/motion/motion_detail.html index a82796a36..a9de961b6 100644 --- a/openslides/motion/templates/motion/motion_detail.html +++ b/openslides/motion/templates/motion/motion_detail.html @@ -253,7 +253,7 @@ {{ motion.version.creation_time }} - {% if allowed_actions.wit and user in motion.submitters %} + {% if allowed_actions.wit and user in motion.submitters %}

    {% trans 'Withdraw motion' %} diff --git a/openslides/motion/views.py b/openslides/motion/views.py index 7ac10ba4d..808e91a7e 100644 --- a/openslides/motion/views.py +++ b/openslides/motion/views.py @@ -75,7 +75,6 @@ class MotionDetailView(GetVersionMixin, DetailView): context = super(MotionDetailView, self).get_context_data(**kwargs) context['allowed_actions'] = self.object.get_allowed_actions(self.request.user) context['min_supporters'] = int(config['motion_min_supporters']) - context['user'] = self.request.user return context motion_detail = MotionDetailView.as_view() From d9fbaf9aa787ccb8e75166cea309fb41ccb1b5fa Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Mon, 11 Mar 2013 21:29:56 +0100 Subject: [PATCH 03/15] Updated motion category template. --- openslides/motion/models.py | 4 ++- .../templates/motion/category_form.html | 6 ++-- .../templates/motion/category_list.html | 32 +++++++++++++++++-- .../motion/templates/motion/motion_list.html | 3 +- openslides/motion/views.py | 2 ++ 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/openslides/motion/models.py b/openslides/motion/models.py index 2446747c6..47646094e 100644 --- a/openslides/motion/models.py +++ b/openslides/motion/models.py @@ -527,7 +527,7 @@ class MotionSupporter(models.Model): class Category(models.Model): name = models.CharField(max_length=255, verbose_name=ugettext_lazy("Category name")) - prefix = models.CharField(max_length=32, verbose_name=ugettext_lazy("Category prefix")) + prefix = models.CharField(max_length=32, verbose_name=ugettext_lazy("Prefix")) def __unicode__(self): return self.name @@ -536,6 +536,8 @@ class Category(models.Model): if link == 'update' or link == 'edit': return reverse('motion_category_update', args=[str(self.id)]) + class Meta: + ordering = ['prefix'] ## class Comment(models.Model): ## motion_version = models.ForeignKey(MotionVersion) diff --git a/openslides/motion/templates/motion/category_form.html b/openslides/motion/templates/motion/category_form.html index 5114c9a0a..421b3a019 100644 --- a/openslides/motion/templates/motion/category_form.html +++ b/openslides/motion/templates/motion/category_form.html @@ -5,7 +5,7 @@ {% block title %} {{ block.super }} – - {% if motion %} + {% if category %} {% trans "Edit category" %} {% else %} {% trans "New category" %} @@ -14,7 +14,7 @@ {% block content %}

    - {% if motion %} + {% if category %} {% trans "Edit category" %} {% else %} {% trans "New category" %} @@ -24,7 +24,7 @@ {% include "form.html" %}

    {% include "formbuttons_saveapply.html" %} - + {% trans 'Cancel' %}

    diff --git a/openslides/motion/templates/motion/category_list.html b/openslides/motion/templates/motion/category_list.html index 175cf2854..8c0869474 100644 --- a/openslides/motion/templates/motion/category_list.html +++ b/openslides/motion/templates/motion/category_list.html @@ -6,9 +6,37 @@ {% block title %}{{ block.super }} – {% trans "Motions" %}{% endblock %} {% block content %} -

    {% trans "Categories" %}

    +

    + {% trans "Categories" %} + + {% if perms.motion.can_manage_motion %} + {% trans 'New' %} + {% endif %} + {% trans "Back to overview" %} + +

    + + + + + + + {% for category in category_list %} -

    {{ category }}

    + + + + + {% empty %}

    No Categories

    {% endfor %} diff --git a/openslides/motion/templates/motion/motion_list.html b/openslides/motion/templates/motion/motion_list.html index 3bdf74e87..09b8c8a34 100644 --- a/openslides/motion/templates/motion/motion_list.html +++ b/openslides/motion/templates/motion/motion_list.html @@ -10,9 +10,10 @@ {% trans "Motions" %} {% if perms.motion.can_create_motion %} - {% trans "New" %} + {% trans 'New' %} {% endif %} {% if perms.motion.can_manage_motion %} + {% trans 'Category' %} {# {% trans 'Import' %}#} {% endif %} PDF diff --git a/openslides/motion/views.py b/openslides/motion/views.py index d28e76d4e..126c1eeab 100644 --- a/openslides/motion/views.py +++ b/openslides/motion/views.py @@ -479,6 +479,7 @@ category_list = CategoryListView.as_view() class CategoryCreateView(CreateView): permission_required = 'motion.can_manage_motion' model = Category + success_url_name = 'motion_category_list' category_create = CategoryCreateView.as_view() @@ -486,6 +487,7 @@ category_create = CategoryCreateView.as_view() class CategoryUpdateView(UpdateView): permission_required = 'motion.can_manage_motion' model = Category + success_url_name = 'motion_category_list' category_update = CategoryUpdateView.as_view() From d41ce146e201170d750f1f382a5798fcd560976c Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Mon, 11 Mar 2013 21:50:16 +0100 Subject: [PATCH 04/15] Added category_delete function to template. --- openslides/motion/templates/motion/category_list.html | 3 +-- openslides/motion/views.py | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openslides/motion/templates/motion/category_list.html b/openslides/motion/templates/motion/category_list.html index 8c0869474..e885ee738 100644 --- a/openslides/motion/templates/motion/category_list.html +++ b/openslides/motion/templates/motion/category_list.html @@ -30,8 +30,7 @@ - {# TODO: motion_category_delete #} - + diff --git a/openslides/motion/views.py b/openslides/motion/views.py index a9aae4b0a..c871b443a 100644 --- a/openslides/motion/views.py +++ b/openslides/motion/views.py @@ -495,6 +495,7 @@ category_update = CategoryUpdateView.as_view() class CategoryDeleteView(DeleteView): permission_required = 'motion.can_manage_motion' model = Category + question_url_name = 'motion_category_list' success_url_name = 'motion_category_list' category_delete = CategoryDeleteView.as_view() From 196ad217b04f0ecf0049f4d3e5d4324cdafc69db Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Mon, 11 Mar 2013 23:58:45 +0100 Subject: [PATCH 05/15] Fixed version history. --- .../motion/templates/motion/motion_detail.html | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openslides/motion/templates/motion/motion_detail.html b/openslides/motion/templates/motion/motion_detail.html index a9de961b6..231854e97 100644 --- a/openslides/motion/templates/motion/motion_detail.html +++ b/openslides/motion/templates/motion/motion_detail.html @@ -58,10 +58,10 @@
    {% if motion.active_version.id != motion.version.id %} - {% if motion.version == motion.public_version %} - {% trans "This is not the newest version." %} {% trans "Go to version" %} {{ motion.last_version.version_number }}. + {% if motion.version.version_number < motion.last_version.version_number %} + {% trans "This is not the newest version." %} {% trans "Go to version" %} {{ motion.last_version.version_number }}. {% else %} - {% trans "This is not the authorized version." %} {% trans "Go to version" %} {{ motion.public_version.aid }}. + {% trans "This is not the authorized version." %} {% trans "Go to version" %} {{ motion.active_version.version_number }}. {% endif %} {% endif %} @@ -99,10 +99,10 @@ {% else %} {% if perms.motion.can_manage_motion %} - + {% endif %} {% if not version.rejected and version.id > motion.active_version.id and perms.motion.can_manage_motion %} - + {% endif %} {% endif %} {% if version.rejected %} @@ -247,6 +247,10 @@ {% endif %} {% endwith %} + +
    {% trans "Category" %}:
    + {{ motion.category }} +
    {% trans "Creation Time" %}:
    {# TODO: use creation time of _first_ version #} @@ -287,7 +291,7 @@

    {% trans "Manage motion" %}

    -
    +
    {% for state in motion.state.next_states.all %} {{ state }} {% endfor %} From a1a11ff53c260cf903505555f3632d844bd2bf82 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Thu, 14 Mar 2013 21:28:03 +0100 Subject: [PATCH 06/15] New motion version diff view. Improved version history table in motion detail template. --- openslides/motion/models.py | 8 ++ openslides/motion/static/styles/motion.css | 41 ++++++ .../templates/motion/motion_detail.html | 132 +++++++----------- .../motion/templates/motion/motion_diff.html | 67 +++++++++ openslides/motion/urls.py | 5 + openslides/motion/views.py | 32 +++++ 6 files changed, 207 insertions(+), 78 deletions(-) create mode 100644 openslides/motion/static/styles/motion.css create mode 100644 openslides/motion/templates/motion/motion_diff.html diff --git a/openslides/motion/models.py b/openslides/motion/models.py index 25260f71d..ac9a41854 100644 --- a/openslides/motion/models.py +++ b/openslides/motion/models.py @@ -15,6 +15,8 @@ from datetime import datetime +import difflib + from django.core.urlresolvers import reverse from django.db import models 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 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): """Save the submitter of a Motion.""" diff --git a/openslides/motion/static/styles/motion.css b/openslides/motion/static/styles/motion.css new file mode 100644 index 000000000..951f94309 --- /dev/null +++ b/openslides/motion/static/styles/motion.css @@ -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; +} + diff --git a/openslides/motion/templates/motion/motion_detail.html b/openslides/motion/templates/motion/motion_detail.html index 231854e97..3548beba8 100644 --- a/openslides/motion/templates/motion/motion_detail.html +++ b/openslides/motion/templates/motion/motion_detail.html @@ -56,14 +56,14 @@
    - {% if motion.active_version.id != motion.version.id %} + {% if motion.version.version_number < motion.last_version.version_number %} {% trans "This is not the newest version." %} {% trans "Go to version" %} {{ motion.last_version.version_number }}. {% else %} {% trans "This is not the authorized version." %} {% trans "Go to version" %} {{ motion.active_version.version_number }}. {% endif %} - {% endif %} +

    {% trans "Motion text" %}:

    @@ -80,84 +80,60 @@
    - {% for version in motion.versions.all %} - {% if forloop.first %} -

    {% trans "Version history" %}:

    -
    {% trans "Prefix" %}{% trans "Category name" %}{% trans "Actions" %}
    {{ category.prefix }}{{ category }} + + + + {# TODO: motion_category_delete #} + + + + +
    - - - - - - - - - {% endif %} - - + + + + + + + {% if forloop.last %} +
    {% trans "Version" %}{% trans "Time" %}{% trans "Title" %}{% trans "Text" %}{% trans "Reason" %}
    - {% if version == motion.active_version %} - - {% else %} - {% if perms.motion.can_manage_motion %} - - {% endif %} - {% if not version.rejected and version.id > motion.active_version.id and perms.motion.can_manage_motion %} - - {% endif %} + {% with versions=motion.versions.all %} + {% if versions|length > 1 %} + {% for version in versions %} + {% if forloop.first %} +

    {% trans "Version history" %}:

    +
    + + + + + + + + {% endif %} - {% if version.rejected %} - - {% endif %} - - - - - - - - {% if forloop.last %} -
    #{% trans "Time" %}{% trans "Actions" %}
    {{ version.version_number }}{{ version.creation_time }} - {% ifchanged %} - {{ version.title }} - {% else %} - [{% trans "unchanged" %}] - {% endifchanged %} - - {% ifchanged %} - {{ version.text|linebreaks }} - {% else %} - [{% trans "unchanged" %}] - {% endifchanged %} - - {% ifchanged %} - {{ version.reason|linebreaks }} - {% else %} - [{% trans "unchanged" %}] - {% endifchanged %} -
    +
    + {% if version == motion.active_version %} + + {% else %} + {% if perms.motion.can_manage_motion %} + + {% endif %} + {% if not version.rejected and version.id > motion.active_version.id and perms.motion.can_manage_motion %} + + {% endif %} + {% endif %} + {% if version.rejected %} + + {% endif %} + {{ version.version_number }}{{ version.creation_time }} + + + + + + + {# TODO: add delete version function #} + + + +
    + + {% endif %} + {% endfor %} {% endif %} - {% endfor %} - - -
      -{% for motion_version in motion.versions.all %} -
    1. - {% if motion_version.id == motion.version.id %} - {{ motion_version }} - {% else %} - {{ motion_version }} - {% endif %} - {% if motion_version.active %} - (active) - {% endif %} - {% if motion_version.rejected %} - (rejected) - {% endif %} -
    2. -{% endfor %} -
    - - + {% endwith %} {% if perms.motion.can_manage_motion %} diff --git a/openslides/motion/templates/motion/motion_diff.html b/openslides/motion/templates/motion/motion_diff.html new file mode 100644 index 000000000..65ceec68f --- /dev/null +++ b/openslides/motion/templates/motion/motion_diff.html @@ -0,0 +1,67 @@ +{% extends "base.html" %} + +{% load tags %} +{% load i18n %} +{% load staticfiles %} + +{% block title %}{{ block.super }} – {% trans "Motion" %} {{ motion.number }}{% endblock %} + +{% block header %} + +{% endblock %} + + +{% block content %} +

    + {{ motion.title }} +
    + + {% if motion.number != None %} + {% trans "Motion" %} {{ motion.number }}, + {% else %} + [{% trans "no number" %}], + {% endif %} + {% trans "Diff view" %} + + + + +

    + +{% if version_rev1 and version_rev2 %} + + + + + + + + {% if not version_rev1.text == version_rev2.text %} + + {% else %} + + + {% endif %} + + + + + {% if not version_rev1.reason == version_rev2.reason %} + + {% else %} + + + {% endif %} + +
    {% trans "Version" %} {{ version_rev1.version_number }}
    + {% trans "created: " %} {{ version_rev1.creation_time }}
    +

    {{ version_rev1.title }}

    +
    {% trans "Version" %} {{ version_rev2.version_number }}
    + {% trans "created: " %} {{ version_rev1.creation_time }}
    +

    {{ version_rev2.title }}

    +
    {{ diff_text|safe }}{{ version_rev1.text }}{{ version_rev2.text }}

    {% trans "Reason" %}:

    {{ diff_reason|safe }}{{ version_rev1.reason }}{{ version_rev2.reason }}
    +{% endif %} + +{% endblock %} diff --git a/openslides/motion/urls.py b/openslides/motion/urls.py index 1fd507ad1..946aab95b 100644 --- a/openslides/motion/urls.py +++ b/openslides/motion/urls.py @@ -55,6 +55,11 @@ urlpatterns = patterns('openslides.motion.views', name='motion_version_reject', ), + url(r'^(?P\d+)/diff/$', + 'version_diff', + name='motion_version_diff', + ), + url(r'^(?P\d+)/support/$', 'motion_support', name='motion_support', diff --git a/openslides/motion/views.py b/openslides/motion/views.py index c871b443a..27df47795 100644 --- a/openslides/motion/views.py +++ b/openslides/motion/views.py @@ -230,6 +230,38 @@ class VersionRejectView(GetVersionMixin, SingleObjectMixin, QuestionMixin, Redir 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): """View to support or unsupport a motion. From fa098f6bd3f3076e05ffda269f4e0dee97542f30 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Thu, 14 Mar 2013 22:54:00 +0100 Subject: [PATCH 07/15] Typo. --- openslides/motion/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openslides/motion/models.py b/openslides/motion/models.py index 7ff707a7a..c9a6cdfd3 100644 --- a/openslides/motion/models.py +++ b/openslides/motion/models.py @@ -52,9 +52,9 @@ class Motion(SlideMixin, models.Model): related_name="active_version") """Points to a specific version. - Used be the permitted-version-system to deside witch version is the active - Version. Could also be used to only choose a specific version as a default - version. Like the Sighted versions on Wikipedia. + Used be the permitted-version-system to deside which version is the active + version. Could also be used to only choose a specific version as a default + version. Like the sighted versions on Wikipedia. """ state = models.ForeignKey('State', null=True) # TODO: Check whether null=True is necessary. From 73430e45d856032d0fed50eee61c4b9737b387e9 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Thu, 14 Mar 2013 22:54:33 +0100 Subject: [PATCH 08/15] Changed style for empty category table. --- openslides/motion/templates/motion/category_list.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openslides/motion/templates/motion/category_list.html b/openslides/motion/templates/motion/category_list.html index e885ee738..41e9f821d 100644 --- a/openslides/motion/templates/motion/category_list.html +++ b/openslides/motion/templates/motion/category_list.html @@ -37,7 +37,9 @@ {% empty %} -

    No Categories

    + + {% trans "No categories available." %} + {% endfor %} {% endblock %} From 88a32dc5463ee5ed5f8825fbd03963391b786580 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Thu, 14 Mar 2013 22:56:25 +0100 Subject: [PATCH 09/15] Updated motion version warnings (not latest/authorized version) --- .../motion/templates/motion/motion_detail.html | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/openslides/motion/templates/motion/motion_detail.html b/openslides/motion/templates/motion/motion_detail.html index d392cb9bf..1c56f8568 100644 --- a/openslides/motion/templates/motion/motion_detail.html +++ b/openslides/motion/templates/motion/motion_detail.html @@ -16,6 +16,7 @@ {% else %} [{% trans "no number" %}], {% endif %} + {# TODO: show only for complex workflow #} {% trans "Version" %} {{ motion.version.version_number }}
    @@ -56,11 +57,20 @@
    - + {# TODO: show only for complex workflow #} {% if motion.version.version_number < motion.last_version.version_number %} - {% trans "This is not the newest version." %} {% trans "Go to version" %} {{ motion.last_version.version_number }}. - {% else %} - {% trans "This is not the authorized version." %} {% trans "Go to version" %} {{ motion.active_version.version_number }}. + + {% trans "This is not the newest version." %} + + {% trans "Go to last version" %} + (#{{ motion.last_version.version_number }}) + {% endif %} + {% if motion.version.version_number > motion.active_version.version_number %} + + {% trans "This version is not yet authorized." %} + + {% trans "Go to last authorized version" %} + (#{{ motion.active_version.version_number }}) {% endif %} From b2dc3820bd0034481980035a9763571cf708eb80 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Thu, 14 Mar 2013 23:04:32 +0100 Subject: [PATCH 10/15] Changed motion.number to motion.identifier. --- openslides/motion/templates/motion/motion_detail.html | 4 ++-- openslides/motion/templates/motion/motion_diff.html | 6 +++--- openslides/motion/templates/motion/motion_list.html | 2 +- openslides/motion/templates/motion/poll_form.html | 4 ++-- openslides/motion/templates/motion/widget.html | 4 ++-- openslides/motion/templates/projector/Motion.html | 6 +++--- .../templates/participant/personal_info_widget.html | 8 ++++---- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/openslides/motion/templates/motion/motion_detail.html b/openslides/motion/templates/motion/motion_detail.html index 1c56f8568..cda0ad03b 100644 --- a/openslides/motion/templates/motion/motion_detail.html +++ b/openslides/motion/templates/motion/motion_detail.html @@ -4,14 +4,14 @@ {% load i18n %} {% load staticfiles %} -{% block title %}{{ block.super }} – {% trans "Motion" %} {{ motion.number }}{% endblock %} +{% block title %}{{ block.super }} – {% trans "Motion" %} {{ motion.identifier }}{% endblock %} {% block content %}

    {{ motion.title }} {{ motion.category }}
    - {% if motion.identifier != None %} + {% if motion.identifier %} {% trans "Motion" %} {{ motion.identifier }}, {% else %} [{% trans "no number" %}], diff --git a/openslides/motion/templates/motion/motion_diff.html b/openslides/motion/templates/motion/motion_diff.html index 65ceec68f..f03d54b85 100644 --- a/openslides/motion/templates/motion/motion_diff.html +++ b/openslides/motion/templates/motion/motion_diff.html @@ -4,7 +4,7 @@ {% load i18n %} {% load staticfiles %} -{% block title %}{{ block.super }} – {% trans "Motion" %} {{ motion.number }}{% endblock %} +{% block title %}{{ block.super }} – {% trans "Motion" %} {{ motion.identifier }}{% endblock %} {% block header %} @@ -16,8 +16,8 @@ {{ motion.title }}
    - {% if motion.number != None %} - {% trans "Motion" %} {{ motion.number }}, + {% if motion.identifier != None %} + {% trans "Motion" %} {{ motion.identifier }}, {% else %} [{% trans "no number" %}], {% endif %} diff --git a/openslides/motion/templates/motion/motion_list.html b/openslides/motion/templates/motion/motion_list.html index 09b8c8a34..bc5d630c9 100644 --- a/openslides/motion/templates/motion/motion_list.html +++ b/openslides/motion/templates/motion/motion_list.html @@ -61,7 +61,7 @@ {% for motion in motion_list %} - {{ motion.number }} + {{ motion.identifier }} {{ motion.title }} {% if min_supporters > 0 %} {# motion.count_supporters #} diff --git a/openslides/motion/templates/motion/poll_form.html b/openslides/motion/templates/motion/poll_form.html index f3bc4aa68..70030bf28 100644 --- a/openslides/motion/templates/motion/poll_form.html +++ b/openslides/motion/templates/motion/poll_form.html @@ -3,7 +3,7 @@ {% load i18n %} {% block title %} - {{ block.super }} - {% trans "Motion" %} {{ motion.number }}, {{ ballot }}. {% trans "Vote" %} + {{ block.super }} - {% trans "Motion" %} {{ motion.identifier }}, {{ ballot }}. {% trans "Vote" %} {% endblock %} {% block content %} @@ -11,7 +11,7 @@ {{ motion }}
    - {% trans "Motion" %} {{ motion.number }}, {{ ballot }}. {% trans "Vote" %} + {% trans "Motion" %} {{ motion.identifier }}, {{ ballot }}. {% trans "Vote" %}
    diff --git a/openslides/motion/templates/motion/widget.html b/openslides/motion/templates/motion/widget.html index 37302db51..3c9566403 100644 --- a/openslides/motion/templates/motion/widget.html +++ b/openslides/motion/templates/motion/widget.html @@ -17,8 +17,8 @@ {{ motion.public_version.title }} ({% trans "motion" %} - {% if motion.number %} - {{ motion.number }}) + {% if motion.identifier %} + {{ motion.identifier }}) {% else %} [{% trans "no number" %}]) {% endif %} diff --git a/openslides/motion/templates/projector/Motion.html b/openslides/motion/templates/projector/Motion.html index 8d2e6699e..20f2b97be 100644 --- a/openslides/motion/templates/projector/Motion.html +++ b/openslides/motion/templates/projector/Motion.html @@ -4,7 +4,7 @@ {% load i18n %} {% load staticfiles %} -{% block title %}{{ block.super }} - {% trans "Motion" %} {{ motion.number }}{% endblock %} +{% block title %}{{ block.super }} - {% trans "Motion" %} {{ motion.identifier }}{% endblock %} {% block content %}