Merge remote-tracking branch 'openslides/stable/1.4.x'

Conflicts:
	openslides/__init__.py
This commit is contained in:
Oskar Hahn 2013-07-29 20:48:12 +02:00
commit b8d23164f7
19 changed files with 2984 additions and 29 deletions

View File

@ -10,3 +10,4 @@ Authors of OpenSlides in chronological order of first contribution:
Stefan Frauenknecht <stefan@frauenknecht.net>
Roland Geider <roland@geider.net>
Tobias Hößl <tobias@hoessl.eu>
Pavel Fric <pavelfric@seznam.cz> (Czech translation)

View File

@ -4,9 +4,24 @@
http://openslides.org
Version 1.4.1 (unreleased)
Version 1.4.2 (unreleased)
==========================
Version 1.4.1 (2013-07-29)
==========================
[https://github.com/OpenSlides/OpenSlides/issues?milestone=11]
- Fix tooltip which shows the end of each agenda item.
- Fix duration of agenda with closed agenda items.
- Disable deleting active version of a motion.
- Start browser on custom IP address.
- Fix wrong URLs to polls in motion detail view.
- Add Czech translation.
Version 1.4 (2013-07-10)
========================
[https://github.com/OpenSlides/OpenSlides/issues?milestone=7]
Agenda:

View File

@ -1,6 +1,6 @@
==============================================
Installation instructions for OpenSlides 1.4
==============================================
================================================
Installation instructions for OpenSlides 1.4.1
================================================
Content
=======
@ -90,7 +90,7 @@ II. Installation on GNU/Linux and MacOSX using the package from openslides.org
2. Follow the same steps as in I. but use in step 3.
$ pip install openslides-1.4.tar.gz
$ pip install openslides-1.4.1.tar.gz
instead of $ pip install openslides.

View File

@ -2,7 +2,7 @@
English README file for OpenSlides
====================================
This is OpenSlides, version 1.4.1 (unreleased).
This is OpenSlides, version 1.4.2 (unreleased).
What is OpenSlides?

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
:copyright: 2011 2013 by OpenSlides team, see AUTHORS.
:copyright: 20112013 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""

View File

@ -45,9 +45,9 @@
<div class="duration">
{% if node.duration %}
{{ node.duration }} h
{% if start and end %}
{% if node.tooltip %}
<a class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'End' %}:
{{ end|date:"DATETIME_FORMAT" }}"><i class="icon-clock"></i>
{{ node.tooltip|date:"DATETIME_FORMAT" }}"><i class="icon-clock"></i>
</a>
{% endif %}
{% endif %}

View File

@ -111,7 +111,7 @@
{% if items %}
<ol class="agenda_list {% if perms.agenda.can_manage_agenda %}sortable{% endif %}">
{% recursetree items %}
<li class="draggable{% if item.active %} activeline{% endif %}{% if item.closed %} offline{% endif %}">
<li class="draggable">
{% include "agenda/item_row.html" %}
{% if not node.is_leaf_node %}
<ol>

View File

@ -50,6 +50,13 @@ class Overview(TemplateView):
else:
items = Item.objects.filter(type__exact=Item.AGENDA_ITEM)
# Save the items as a list (not a queryset). This is important,
# because in other case, django-mtpp reloads the items in the
# template. But we add some attributes (in this function), which are
# not in the database and would be lost if the items were reloaded.
# TODO: Try to remove this line in later versions of django-mptt
items = list(items)
start = config['agenda_start_event_date_time']
if start is None or len(start) == 0:
start = None
@ -59,8 +66,8 @@ class Overview(TemplateView):
duration = timedelta()
for item in items:
if not item.closed and (item.duration is not None
and len(item.duration) > 0):
if (item.duration is not None and
len(item.duration) > 0):
duration_list = item.duration.split(':')
duration += timedelta(hours=int(duration_list[0]),
minutes=int(duration_list[1]))

View File

@ -33,6 +33,7 @@ LANGUAGES = (
('de', ugettext('German')),
('en', ugettext('English')),
('fr', ugettext('French')),
('cs', ugettext('Czech')),
)
# If you set this to False, Django will make some optimizations so as not

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,260 @@
# Language file (for JavaScript) of OpenSlides used by transifex:
# https://www.transifex.com/projects/p/openslides/
# Copyright (C) 20112013 by OpenSlides team, see AUTHORS.
# This file is distributed under the same license as the OpenSlides package.
# Translators:
# emanuel <emanuel@intevation.de>, 2013
# emanuelschuetze <emanuelschuetze@gmail.com>, 2013
# ostcar <mail@oshahn.de>, 2012
# fri <pavelfric@seznam.cz>, 2013
msgid ""
msgstr ""
"Project-Id-Version: OpenSlides\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-07-07 23:49+0200\n"
"PO-Revision-Date: 2013-07-15 23:01+0000\n"
"Last-Translator: fri <pavelfric@seznam.cz>\n"
"Language-Team: Czech (http://www.transifex.com/projects/p/openslides/language/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: agenda/static/javascript/agenda-config-datepicker.js:9
#: agenda/static/javascript/agenda-config-datepicker.js:39
msgid "en"
msgstr "en"
#: agenda/static/javascript/agenda-config-datepicker.js:10
msgid "previous month"
msgstr "Předchozí měsíc"
#: agenda/static/javascript/agenda-config-datepicker.js:11
msgid "next month"
msgstr "Další měsíc"
#: agenda/static/javascript/agenda-config-datepicker.js:13
msgid "January"
msgstr "Leden"
#: agenda/static/javascript/agenda-config-datepicker.js:13
msgid "February"
msgstr "Únor"
#: agenda/static/javascript/agenda-config-datepicker.js:13
msgid "March"
msgstr "Březen"
#: agenda/static/javascript/agenda-config-datepicker.js:14
msgid "April"
msgstr "Duben"
#: agenda/static/javascript/agenda-config-datepicker.js:14
#: agenda/static/javascript/agenda-config-datepicker.js:20
msgid "May"
msgstr "Květen"
#: agenda/static/javascript/agenda-config-datepicker.js:14
msgid "June"
msgstr "Červen"
#: agenda/static/javascript/agenda-config-datepicker.js:15
msgid "July"
msgstr "Červenec"
#: agenda/static/javascript/agenda-config-datepicker.js:15
msgid "August"
msgstr "Srpen"
#: agenda/static/javascript/agenda-config-datepicker.js:15
msgid "September"
msgstr "Září"
#: agenda/static/javascript/agenda-config-datepicker.js:16
msgid "October"
msgstr "Říjen"
#: agenda/static/javascript/agenda-config-datepicker.js:16
msgid "November"
msgstr "Listopad"
#: agenda/static/javascript/agenda-config-datepicker.js:16
msgid "December"
msgstr "Prosinec"
#: agenda/static/javascript/agenda-config-datepicker.js:19
msgid "Jan"
msgstr "Led"
#: agenda/static/javascript/agenda-config-datepicker.js:19
msgid "Feb"
msgstr "Úno"
#: agenda/static/javascript/agenda-config-datepicker.js:19
msgid "Mar"
msgstr "Bře"
#: agenda/static/javascript/agenda-config-datepicker.js:20
msgid "Apr"
msgstr "Dub"
#: agenda/static/javascript/agenda-config-datepicker.js:20
msgid "Jun"
msgstr "Čer"
#: agenda/static/javascript/agenda-config-datepicker.js:21
msgid "Jul"
msgstr "Čec"
#: agenda/static/javascript/agenda-config-datepicker.js:21
msgid "Aug"
msgstr "Srp"
#: agenda/static/javascript/agenda-config-datepicker.js:21
msgid "Sep"
msgstr "Zář"
#: agenda/static/javascript/agenda-config-datepicker.js:22
msgid "Oct"
msgstr "Říj"
#: agenda/static/javascript/agenda-config-datepicker.js:22
msgid "Nov"
msgstr "Lis"
#: agenda/static/javascript/agenda-config-datepicker.js:22
msgid "Dec"
msgstr "Pro"
#: agenda/static/javascript/agenda-config-datepicker.js:25
msgid "Sunday"
msgstr "Neděle"
#: agenda/static/javascript/agenda-config-datepicker.js:25
msgid "Monday"
msgstr "Pondělí"
#: agenda/static/javascript/agenda-config-datepicker.js:25
msgid "Tuesdey"
msgstr "Úterý"
#: agenda/static/javascript/agenda-config-datepicker.js:25
msgid "Wednesday"
msgstr "Středa"
#: agenda/static/javascript/agenda-config-datepicker.js:26
msgid "Thursday"
msgstr "Čtvrtek"
#: agenda/static/javascript/agenda-config-datepicker.js:26
msgid "Friday"
msgstr "Pátek"
#: agenda/static/javascript/agenda-config-datepicker.js:26
msgid "Saturday"
msgstr "Sobota"
#: agenda/static/javascript/agenda-config-datepicker.js:29
#: agenda/static/javascript/agenda-config-datepicker.js:33
msgid "Su"
msgstr "Ne"
#: agenda/static/javascript/agenda-config-datepicker.js:29
#: agenda/static/javascript/agenda-config-datepicker.js:33
msgid "Mo"
msgstr "Po"
#: agenda/static/javascript/agenda-config-datepicker.js:29
#: agenda/static/javascript/agenda-config-datepicker.js:33
msgid "Tu"
msgstr "Út"
#: agenda/static/javascript/agenda-config-datepicker.js:29
#: agenda/static/javascript/agenda-config-datepicker.js:33
msgid "We"
msgstr "St"
#: agenda/static/javascript/agenda-config-datepicker.js:30
#: agenda/static/javascript/agenda-config-datepicker.js:34
msgid "Th"
msgstr "Čt"
#: agenda/static/javascript/agenda-config-datepicker.js:30
#: agenda/static/javascript/agenda-config-datepicker.js:34
msgid "Fr"
msgstr "Pá"
#: agenda/static/javascript/agenda-config-datepicker.js:30
#: agenda/static/javascript/agenda-config-datepicker.js:34
msgid "Sa"
msgstr "So"
#: agenda/static/javascript/agenda-config-datepicker.js:45
msgid "Time"
msgstr "Čas"
#: agenda/static/javascript/agenda-config-datepicker.js:46
msgid "Hour"
msgstr "Hodina"
#: agenda/static/javascript/agenda-config-datepicker.js:47
msgid "Minute"
msgstr "Minuta"
#: agenda/static/javascript/agenda-config-datepicker.js:48
msgid "Current time"
msgstr "Nynější čas"
#: agenda/static/javascript/agenda-config-datepicker.js:49
msgid "Close"
msgstr "Zavřít"
#: agenda/static/javascript/agenda.js:17
#, c-format
msgid ", of which %s are hidden."
msgstr ", z toho %s skryto."
#: static/javascript/dataTables.bootstrap.js:14
msgid "All"
msgstr "Vše"
#: static/javascript/dataTables.bootstrap.js:19
msgid "_MENU_ entries per page"
msgstr "_MENU_ záznamů na stranu"
#: static/javascript/dataTables.bootstrap.js:20
msgid "Search:"
msgstr "Hledání:"
#: static/javascript/dataTables.bootstrap.js:21
msgid "Showing _START_ to _END_ of _TOTAL_ entries"
msgstr "_START_ až _END_ z _TOTAL_ záznamů"
#: static/javascript/dataTables.bootstrap.js:22
msgid "Showing 0 entries"
msgstr "0 záznamů"
#: static/javascript/dataTables.bootstrap.js:23
msgid "(filtered from _MAX_ total entries)"
msgstr "(filtrováno z _MAX_ záznamů)"
#: static/javascript/dataTables.bootstrap.js:24
msgid "No matching records found"
msgstr "Nebyly nalezeny žádné odpovídající záznamy"
#: static/javascript/dataTables.bootstrap.js:26
msgid "First"
msgstr "První"
#: static/javascript/dataTables.bootstrap.js:27
msgid "Last"
msgstr "Poslední"
#: static/javascript/dataTables.bootstrap.js:28
msgid "Next"
msgstr "Další"
#: static/javascript/dataTables.bootstrap.js:29
msgid "Previous"
msgstr "Předchozí"

View File

@ -203,11 +203,15 @@ def _main(opts, database_path=None):
reload = False
if opts.start_browser:
if opts.address:
prefix = opts.address
else:
prefix = 'localhost'
if port == 80:
suffix = ""
else:
suffix = ":%d" % port
start_browser("http://localhost%s" % suffix)
start_browser("http://%s%s" % (prefix, suffix))
# Start the server
run_tornado(addr, port, reload)

View File

@ -100,7 +100,7 @@ class Motion(SlideMixin, models.Model):
"""
Return a human readable name of this motion.
"""
return self.active_version.title
return self.get_active_version().title
# TODO: Use transaction
def save(self, use_version=None, *args, **kwargs):

View File

@ -69,18 +69,18 @@
{# TODO: show only for workflow with versioning #}
{% with last_version=motion.get_last_version active_version=motion.get_active_version %}
{% if version.version_number != last_version.version_number %}
<span class="label label-warning">
<p><span class="label label-warning">
<i class="icon-warning-sign icon-white"></i> {% trans "This is not the newest version." %}
</span>
<a href="{% model_url last_version %}" class="btn btn-small">{% trans "Go to the newest version" %}
(# {{ last_version.version_number }})</a>
(# {{ last_version.version_number }})</a></p>
{% endif %}
{% if version.version_number != active_version.version_number %}
<span class="label label-warning">
<p><span class="label label-warning">
<i class="icon-warning-sign icon-white"></i> {% trans "This version is not authorized." %}
</span>
<a href="{% model_url active_version %}" class="btn btn-small">{% trans "Go to the authorized version" %}
(# {{ active_version.version_number }})</a>
(# {{ active_version.version_number }})</a></p>
{% endif %}
{% endwith %}
@ -130,9 +130,11 @@
<a href="{% model_url version %}" title="{% trans 'Show' %}" class="btn btn-mini">
<i class="icon-search"></i>
</a>
<a href="{% model_url version 'delete' %}" title="{% trans 'Delete' %}" class="btn btn-mini">
<i class="icon-remove"></i>
</a>
{% if version != motion.active_version %}
<a href="{% model_url version 'delete' %}" title="{% trans 'Delete' %}" class="btn btn-mini">
<i class="icon-remove"></i>
</a>
{% endif %}
</td>
</tr>
{% if forloop.last %}
@ -198,8 +200,8 @@
{% if perms.motion.can_manage_motion or poll.has_votes %}
<li><b>{{ poll.poll_number|ordinal|safe }} {% trans "vote" %}</b>
{% if perms.motion.can_manage_motion %}
<a class="btn btn-mini" href="{% url 'motion_poll_edit' motion.id poll.id %}" title="{% trans 'Edit Vote' %}"><i class="icon-pencil"></i></a>
<a class="btn btn-mini" href="{% url 'motion_poll_delete' motion.id poll.id %}" title="{% trans 'Delete Vote' %}"><i class="icon-remove"></i></a>
<a class="btn btn-mini" href="{% model_url poll 'edit' %}" title="{% trans 'Edit Vote' %}"><i class="icon-pencil"></i></a>
<a class="btn btn-mini" href="{% model_url poll 'delete' %}" title="{% trans 'Delete Vote' %}"><i class="icon-remove"></i></a>
{% endif %}
<br>
{% if poll.has_votes %}

View File

@ -303,10 +303,19 @@ class VersionDeleteView(DeleteView):
success_url_name = 'motion_detail'
def get_object(self):
motion_id = int(self.kwargs.get('pk'))
version_number = int(self.kwargs.get('version_number'))
return MotionVersion.objects.get(motion=motion_id,
version_number=version_number)
try:
motion = Motion.objects.get(pk=int(self.kwargs.get('pk')))
except Motion.DoesNotExist:
raise Http404('Motion %s not found.' % self.kwargs.get('pk'))
try:
version = MotionVersion.objects.get(
motion=motion,
version_number=int(self.kwargs.get('version_number')))
except MotionVersion.DoesNotExist:
raise Http404('Version %s not found.' % self.kwargs.get('version_number'))
if version == motion.active_version:
raise Http404('You can not delete the active version of a motion.')
return version
def get_success_url_name_args(self):
return (self.object.motion_id, )
@ -333,7 +342,7 @@ class VersionPermitView(SingleObjectMixin, QuestionMixin, RedirectView):
try:
self.version = self.object.versions.get(version_number=int(version_number))
except MotionVersion.DoesNotExist:
raise Http404('Version %s not found' % version_number)
raise Http404('Version %s not found.' % version_number)
return super(VersionPermitView, self).get(*args, **kwargs)
def get_url_name_args(self):

View File

@ -146,6 +146,12 @@ class ModelTest(TestCase):
motion.save(use_version=False)
self.assertEqual(motion.versions.count(), 2)
def test_unicode_with_no_active_version(self):
motion = Motion.objects.create(title='foo', text='bar', identifier='')
motion.active_version = None
motion.save(update_fields=['active_version'])
self.assertEqual(str(motion), 'foo') # motion.__unicode__() raised an AttributeError
class ConfigTest(TestCase):
def test_stop_submitting(self):

View File

@ -412,6 +412,7 @@ class TestVersionPermitView(MotionViewTestCase):
class TestVersionDeleteView(MotionViewTestCase):
def test_get(self):
self.motion1.save(use_version=self.motion1.get_new_version(title='new', text='new'))
response = self.check_url('/motion/1/version/1/del/', self.admin_client, 302)
self.assertRedirects(response, '/motion/1/version/1/')
@ -424,3 +425,10 @@ class TestVersionDeleteView(MotionViewTestCase):
response = self.admin_client.post('/motion/1/version/2/del/', {'yes': 1})
self.assertRedirects(response, '/motion/1/')
self.assertEqual(self.motion1.versions.count(), 2)
def test_delete_active_version(self):
self.motion1.save(use_version=self.motion1.get_new_version(title='new_title_yae6Aequaiw5saeb8suG', text='new'))
motion = Motion.objects.all()[0]
self.assertEqual(motion.get_active_version().title, 'new_title_yae6Aequaiw5saeb8suG')
response = self.admin_client.post('/motion/1/version/2/del/', {'yes': 1})
self.assertEqual(response.status_code, 404)