Merge remote branch 'upstream/master'
This commit is contained in:
commit
40984a6508
4
.coveragerc
Normal file
4
.coveragerc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[run]
|
||||||
|
source=openslides
|
||||||
|
[report]
|
||||||
|
exclude_lines = def __(unicode|repr)__
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -12,3 +12,9 @@ docs/_build/*
|
|||||||
build/*
|
build/*
|
||||||
dist/*
|
dist/*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
settings.py
|
||||||
|
versiontools*
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
.coverage
|
||||||
|
htmlcov
|
||||||
|
12
.travis.yml
Normal file
12
.travis.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
language: python
|
||||||
|
python:
|
||||||
|
- "2.5"
|
||||||
|
- "2.6"
|
||||||
|
- "2.7"
|
||||||
|
install:
|
||||||
|
- pip install -r requirements.txt --use-mirrors
|
||||||
|
- pip install coverage django-discover-runner pep8
|
||||||
|
- python extras/scripts/create_local_settings.py
|
||||||
|
script:
|
||||||
|
- coverage run ./manage.py test tests && coverage report -m
|
||||||
|
- pep8 --max-line-length=150 --exclude="urls.py,motion/" --statistics openslides
|
@ -1,6 +1,5 @@
|
|||||||
include AUTHORS
|
include AUTHORS
|
||||||
include CHANGELOG
|
include CHANGELOG
|
||||||
include initial_data.json
|
|
||||||
include INSTALL.txt
|
include INSTALL.txt
|
||||||
include LICENSE
|
include LICENSE
|
||||||
include manage.py
|
include manage.py
|
||||||
@ -12,16 +11,14 @@ recursive-include openslides/templates *
|
|||||||
|
|
||||||
recursive-include openslides/agenda/templates *
|
recursive-include openslides/agenda/templates *
|
||||||
recursive-include openslides/agenda/static *
|
recursive-include openslides/agenda/static *
|
||||||
recursive-include openslides/application/templates *
|
recursive-include openslides/motion/templates *
|
||||||
recursive-include openslides/application/static *
|
|
||||||
recursive-include openslides/assignment/templates *
|
recursive-include openslides/assignment/templates *
|
||||||
recursive-include openslides/assignment/static *
|
recursive-include openslides/assignment/static *
|
||||||
recursive-include openslides/config/templates *
|
recursive-include openslides/config/templates *
|
||||||
recursive-include openslides/config/static *
|
|
||||||
recursive-include openslides/participant/templates *
|
recursive-include openslides/participant/templates *
|
||||||
recursive-include openslides/participant/static *
|
recursive-include openslides/participant/static *
|
||||||
|
include openslides/participant/fixtures/groups_de.json
|
||||||
recursive-include openslides/poll/templates *
|
recursive-include openslides/poll/templates *
|
||||||
recursive-include openslides/poll/static *
|
|
||||||
recursive-include openslides/projector/templates *
|
recursive-include openslides/projector/templates *
|
||||||
recursive-include openslides/projector/static *
|
recursive-include openslides/projector/static *
|
||||||
|
|
||||||
|
11
README.txt
11
README.txt
@ -1,6 +1,6 @@
|
|||||||
==================================
|
==================================
|
||||||
English README file for OpenSlides
|
English README file for OpenSlides
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
This is OpenSlides, version 1.3-beta2 (2012-11-09).
|
This is OpenSlides, version 1.3-beta2 (2012-11-09).
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ What is OpenSlides?
|
|||||||
OpenSlides is a free, web-based presentation system for displaying and
|
OpenSlides is a free, web-based presentation system for displaying and
|
||||||
controlling of agenda, applications and elections of an assembly.
|
controlling of agenda, applications and elections of an assembly.
|
||||||
|
|
||||||
See http://www.openslides.org for more information.
|
See http://openslides.org for more information.
|
||||||
|
|
||||||
|
|
||||||
Getting started
|
Getting started
|
||||||
@ -18,7 +18,7 @@ Getting started
|
|||||||
Install and start OpenSlides as described in the INSTALL.txt.
|
Install and start OpenSlides as described in the INSTALL.txt.
|
||||||
|
|
||||||
If you need help please contact the OpenSlides team on public mailing
|
If you need help please contact the OpenSlides team on public mailing
|
||||||
list or read the OpenSlides manual. See http://www.openslides.org.
|
list or read the OpenSlides manual. See http://openslides.org.
|
||||||
|
|
||||||
|
|
||||||
The start script of OpenSlides
|
The start script of OpenSlides
|
||||||
@ -26,6 +26,7 @@ The start script of OpenSlides
|
|||||||
Simply running
|
Simply running
|
||||||
openslides.exe (on Windows) or
|
openslides.exe (on Windows) or
|
||||||
python start.py (on Linux/MacOS)
|
python start.py (on Linux/MacOS)
|
||||||
|
|
||||||
will start OpenSlides using djangos development server. It will also
|
will start OpenSlides using djangos development server. It will also
|
||||||
try to open OpenSlides in your default webbrowser.
|
try to open OpenSlides in your default webbrowser.
|
||||||
|
|
||||||
|
15
extras/scripts/create_local_settings.py
Normal file
15
extras/scripts/create_local_settings.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
script_path = os.path.realpath(os.path.dirname(__file__))
|
||||||
|
sys.path.append(os.path.join(script_path, '..', '..'))
|
||||||
|
|
||||||
|
from openslides.main import create_settings
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cwd = os.getcwd()
|
||||||
|
create_settings(os.path.join(cwd, 'settings.py'),
|
||||||
|
os.path.join(cwd, 'database.sqlite'))
|
@ -87,6 +87,7 @@ PY_DLLS = [
|
|||||||
"_sqlite3.pyd",
|
"_sqlite3.pyd",
|
||||||
"_socket.pyd",
|
"_socket.pyd",
|
||||||
"select.pyd",
|
"select.pyd",
|
||||||
|
"_ctypes.pyd",
|
||||||
]
|
]
|
||||||
|
|
||||||
MSVCR_PUBLIC_KEY = "1fc8b3b9a1e18e3b"
|
MSVCR_PUBLIC_KEY = "1fc8b3b9a1e18e3b"
|
||||||
|
@ -29,12 +29,11 @@ def get_version(version=None):
|
|||||||
if version[3] != 'final':
|
if version[3] != 'final':
|
||||||
if version[3] == 'dev':
|
if version[3] == 'dev':
|
||||||
try:
|
try:
|
||||||
|
import os
|
||||||
git_head_path = '.git/' + open('.git/HEAD', 'r').read()[5:].rstrip()
|
git_head_path = '.git/' + open('.git/HEAD', 'r').read()[5:].rstrip()
|
||||||
|
git_commit_id = open(os.path.abspath(git_head_path), 'r').read().rstrip()
|
||||||
except IOError:
|
except IOError:
|
||||||
git_commit_id = 'unknown'
|
git_commit_id = 'unknown'
|
||||||
else:
|
|
||||||
import os
|
|
||||||
git_commit_id = open(os.path.abspath(git_head_path), 'r').read().rstrip()
|
|
||||||
sub = '-%s%s' % (version[3], git_commit_id)
|
sub = '-%s%s' % (version[3], git_commit_id)
|
||||||
else:
|
else:
|
||||||
sub = '-' + version[3] + str(version[4])
|
sub = '-' + version[3] + str(version[4])
|
||||||
|
@ -24,8 +24,8 @@ class ItemForm(forms.ModelForm, CssClassMixin):
|
|||||||
"""
|
"""
|
||||||
Form to create of update an item.
|
Form to create of update an item.
|
||||||
"""
|
"""
|
||||||
parent = TreeNodeChoiceField(queryset=Item.objects.all(),
|
parent = TreeNodeChoiceField(
|
||||||
label=_("Parent item"), required=False)
|
queryset=Item.objects.all(), label=_("Parent item"), required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Item
|
model = Item
|
||||||
|
@ -10,12 +10,6 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
|
||||||
import json
|
|
||||||
except ImportError:
|
|
||||||
# for python 2.5 support
|
|
||||||
import simplejson as json
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop, ugettext
|
from django.utils.translation import ugettext_lazy as _, ugettext_noop, ugettext
|
||||||
@ -23,11 +17,9 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop, ugettext
|
|||||||
from mptt.models import MPTTModel, TreeForeignKey
|
from mptt.models import MPTTModel, TreeForeignKey
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
|
|
||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
from openslides.projector.api import (register_slidemodel, get_slide_from_sid,
|
from openslides.projector.api import (
|
||||||
register_slidefunc, split_sid)
|
register_slidemodel, get_slide_from_sid, register_slidefunc)
|
||||||
|
|
||||||
from openslides.agenda.slides import agenda_show
|
from openslides.agenda.slides import agenda_show
|
||||||
|
|
||||||
|
|
||||||
@ -45,7 +37,7 @@ class Item(MPTTModel, SlideMixin):
|
|||||||
closed = models.BooleanField(default=False, verbose_name=_("Closed"))
|
closed = models.BooleanField(default=False, verbose_name=_("Closed"))
|
||||||
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
|
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
|
||||||
parent = TreeForeignKey('self', null=True, blank=True,
|
parent = TreeForeignKey('self', null=True, blank=True,
|
||||||
related_name='children')
|
related_name='children')
|
||||||
related_sid = models.CharField(null=True, blank=True, max_length=63)
|
related_sid = models.CharField(null=True, blank=True, max_length=63)
|
||||||
|
|
||||||
def get_related_slide(self):
|
def get_related_slide(self):
|
||||||
@ -84,7 +76,6 @@ class Item(MPTTModel, SlideMixin):
|
|||||||
return self.title
|
return self.title
|
||||||
return self.get_related_slide().get_agenda_title()
|
return self.get_related_slide().get_agenda_title()
|
||||||
|
|
||||||
|
|
||||||
def get_title_supplement(self):
|
def get_title_supplement(self):
|
||||||
"""
|
"""
|
||||||
return a supplement for the title.
|
return a supplement for the title.
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
"""
|
"""
|
||||||
from reportlab.platypus import Paragraph
|
from reportlab.platypus import Paragraph
|
||||||
|
|
||||||
from django.core.context_processors import csrf
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
@ -20,18 +19,15 @@ from django.utils.translation import ugettext as _, ugettext_lazy
|
|||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
from openslides.utils.views import (TemplateView, RedirectView, UpdateView,
|
from openslides.utils.views import (
|
||||||
CreateView, DeleteView, PDFView, DetailView)
|
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
|
||||||
|
DetailView)
|
||||||
from openslides.utils.template import Tab
|
from openslides.utils.template import Tab
|
||||||
from openslides.utils.utils import html_strong
|
from openslides.utils.utils import html_strong
|
||||||
|
|
||||||
from openslides.config.models import config
|
|
||||||
|
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide
|
||||||
from openslides.projector.projector import Widget, SLIDE
|
from openslides.projector.projector import Widget, SLIDE
|
||||||
|
from .models import Item
|
||||||
from openslides.agenda.models import Item
|
from .forms import ItemOrderForm, ItemForm
|
||||||
from openslides.agenda.forms import ItemOrderForm, ItemForm
|
|
||||||
|
|
||||||
|
|
||||||
class Overview(TemplateView):
|
class Overview(TemplateView):
|
||||||
@ -53,7 +49,8 @@ class Overview(TemplateView):
|
|||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
if not request.user.has_perm('agenda.can_manage_agenda'):
|
if not request.user.has_perm('agenda.can_manage_agenda'):
|
||||||
messages.error(request,
|
messages.error(
|
||||||
|
request,
|
||||||
_('You are not authorized to manage the agenda.'))
|
_('You are not authorized to manage the agenda.'))
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
@ -69,8 +66,8 @@ class Overview(TemplateView):
|
|||||||
Model.save(item)
|
Model.save(item)
|
||||||
else:
|
else:
|
||||||
transaction.rollback()
|
transaction.rollback()
|
||||||
messages.error(request,
|
messages.error(
|
||||||
_('Errors when reordering of the agenda'))
|
request, _('Errors when reordering of the agenda'))
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
Item.objects.rebuild()
|
Item.objects.rebuild()
|
||||||
# TODO: assure, that it is a valid tree
|
# TODO: assure, that it is a valid tree
|
||||||
@ -130,8 +127,8 @@ class ItemUpdate(UpdateView):
|
|||||||
apply_url = 'item_edit'
|
apply_url = 'item_edit'
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
messages.success(self.request,
|
messages.success(
|
||||||
_("Item %s was successfully modified.") \
|
self.request, _("Item %s was successfully modified.")
|
||||||
% html_strong(self.request.POST['title']))
|
% html_strong(self.request.POST['title']))
|
||||||
if 'apply' in self.request.POST:
|
if 'apply' in self.request.POST:
|
||||||
return ''
|
return ''
|
||||||
@ -151,8 +148,8 @@ class ItemCreate(CreateView):
|
|||||||
apply_url = 'item_edit'
|
apply_url = 'item_edit'
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
messages.success(self.request,
|
messages.success(
|
||||||
_("Item %s was successfully created.") \
|
self.request, _("Item %s was successfully created.")
|
||||||
% html_strong(self.request.POST['title']))
|
% html_strong(self.request.POST['title']))
|
||||||
if 'apply' in self.request.POST:
|
if 'apply' in self.request.POST:
|
||||||
return reverse(self.get_apply_url(), args=[self.object.id])
|
return reverse(self.get_apply_url(), args=[self.object.id])
|
||||||
@ -176,13 +173,13 @@ class ItemDelete(DeleteView):
|
|||||||
def pre_post_redirect(self, request, *args, **kwargs):
|
def pre_post_redirect(self, request, *args, **kwargs):
|
||||||
if self.get_answer() == 'all':
|
if self.get_answer() == 'all':
|
||||||
self.object.delete(with_children=True)
|
self.object.delete(with_children=True)
|
||||||
messages.success(request,
|
messages.success(
|
||||||
_("Item %s and his children were successfully deleted.")
|
request, _("Item %s and his children were successfully deleted.")
|
||||||
% html_strong(self.object))
|
% html_strong(self.object))
|
||||||
elif self.get_answer() == 'yes':
|
elif self.get_answer() == 'yes':
|
||||||
self.object.delete(with_children=False)
|
self.object.delete(with_children=False)
|
||||||
messages.success(request,
|
messages.success(
|
||||||
_("Item %s was successfully deleted.")
|
request, _("Item %s was successfully deleted.")
|
||||||
% html_strong(self.object))
|
% html_strong(self.object))
|
||||||
|
|
||||||
|
|
||||||
@ -199,7 +196,8 @@ class AgendaPDF(PDFView):
|
|||||||
ancestors = item.get_ancestors()
|
ancestors = item.get_ancestors()
|
||||||
if ancestors:
|
if ancestors:
|
||||||
space = " " * 6 * ancestors.count()
|
space = " " * 6 * ancestors.count()
|
||||||
story.append(Paragraph("%s%s" % (space, item.get_title()),
|
story.append(Paragraph(
|
||||||
|
"%s%s" % (space, item.get_title()),
|
||||||
stylesheet['Subitem']))
|
stylesheet['Subitem']))
|
||||||
else:
|
else:
|
||||||
story.append(Paragraph(item.get_title(), stylesheet['Item']))
|
story.append(Paragraph(item.get_title(), stylesheet['Item']))
|
||||||
@ -213,10 +211,9 @@ def register_tab(request):
|
|||||||
return Tab(
|
return Tab(
|
||||||
title=_('Agenda'),
|
title=_('Agenda'),
|
||||||
url=reverse('item_overview'),
|
url=reverse('item_overview'),
|
||||||
permission=request.user.has_perm('agenda.can_see_agenda')
|
permission=(request.user.has_perm('agenda.can_see_agenda') or
|
||||||
or request.user.has_perm('agenda.can_manage_agenda'),
|
request.user.has_perm('agenda.can_manage_agenda')),
|
||||||
selected=selected,
|
selected=selected)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_widgets(request):
|
def get_widgets(request):
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
from openslides.utils.person import PersonFormField
|
from openslides.utils.person import PersonFormField
|
||||||
@ -20,8 +20,8 @@ from openslides.assignment.models import Assignment
|
|||||||
|
|
||||||
|
|
||||||
class AssignmentForm(forms.ModelForm, CssClassMixin):
|
class AssignmentForm(forms.ModelForm, CssClassMixin):
|
||||||
posts = forms.IntegerField(min_value=1, initial=1,
|
posts = forms.IntegerField(
|
||||||
label=_("Number of available posts"))
|
min_value=1, initial=1, label=_("Number of available posts"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Assignment
|
model = Assignment
|
||||||
@ -39,8 +39,7 @@ class ConfigForm(forms.Form, CssClassMixin):
|
|||||||
assignment_publish_winner_results_only = forms.BooleanField(
|
assignment_publish_winner_results_only = forms.BooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
label=_("Only publish voting results for selected winners "
|
label=_("Only publish voting results for selected winners "
|
||||||
"(Projector view only)")
|
"(Projector view only)"))
|
||||||
)
|
|
||||||
assignment_pdf_ballot_papers_selection = forms.ChoiceField(
|
assignment_pdf_ballot_papers_selection = forms.ChoiceField(
|
||||||
widget=forms.Select(),
|
widget=forms.Select(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -48,31 +47,25 @@ class ConfigForm(forms.Form, CssClassMixin):
|
|||||||
choices=(
|
choices=(
|
||||||
("NUMBER_OF_DELEGATES", _("Number of all delegates")),
|
("NUMBER_OF_DELEGATES", _("Number of all delegates")),
|
||||||
("NUMBER_OF_ALL_PARTICIPANTS", _("Number of all participants")),
|
("NUMBER_OF_ALL_PARTICIPANTS", _("Number of all participants")),
|
||||||
("CUSTOM_NUMBER", _("Use the following custom number"))
|
("CUSTOM_NUMBER", _("Use the following custom number"))))
|
||||||
)
|
|
||||||
)
|
|
||||||
assignment_pdf_ballot_papers_number = forms.IntegerField(
|
assignment_pdf_ballot_papers_number = forms.IntegerField(
|
||||||
widget=forms.TextInput(attrs={'class':'small-input'}),
|
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||||
required=False,
|
required=False,
|
||||||
min_value=1,
|
min_value=1,
|
||||||
label=_("Custom number of ballot papers")
|
label=_("Custom number of ballot papers"))
|
||||||
)
|
|
||||||
assignment_pdf_title = forms.CharField(
|
assignment_pdf_title = forms.CharField(
|
||||||
widget=forms.TextInput(),
|
widget=forms.TextInput(),
|
||||||
required=False,
|
required=False,
|
||||||
label=_("Title for PDF document (all elections)")
|
label=_("Title for PDF document (all elections)"))
|
||||||
)
|
|
||||||
assignment_pdf_preamble = forms.CharField(
|
assignment_pdf_preamble = forms.CharField(
|
||||||
widget=forms.Textarea(),
|
widget=forms.Textarea(),
|
||||||
required=False,
|
required=False,
|
||||||
label=_("Preamble text for PDF document (all elections)")
|
label=_("Preamble text for PDF document (all elections)"))
|
||||||
)
|
assignment_poll_vote_values = forms.ChoiceField(
|
||||||
assignment_poll_vote_values = forms.ChoiceField(widget=forms.Select(),
|
widget=forms.Select(),
|
||||||
required=False,
|
required=False,
|
||||||
label=_("Election method"),
|
label=_("Election method"),
|
||||||
choices=(
|
choices=(
|
||||||
("auto", _("Automatic assign of method.")),
|
("auto", _("Automatic assign of method.")),
|
||||||
("votes", _("Always one option per candidate.")),
|
("votes", _("Always one option per candidate.")),
|
||||||
("yesnoabstain", _("Always Yes-No-Abstain per candidate.")),
|
("yesnoabstain", _("Always Yes-No-Abstain per candidate."))))
|
||||||
)
|
|
||||||
)
|
|
||||||
|
@ -16,16 +16,12 @@ from django.dispatch import receiver
|
|||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
||||||
|
|
||||||
from openslides.utils.person import PersonField
|
from openslides.utils.person import PersonField
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
from openslides.config.signals import default_config_value
|
from openslides.config.signals import default_config_value
|
||||||
|
|
||||||
from openslides.projector.api import register_slidemodel
|
from openslides.projector.api import register_slidemodel
|
||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
|
from openslides.poll.models import (
|
||||||
from openslides.poll.models import (BasePoll, CountInvalid, CountVotesCast,
|
BasePoll, CountInvalid, CountVotesCast, BaseOption, PublishPollMixin, BaseVote)
|
||||||
BaseOption, PublishPollMixin, BaseVote)
|
|
||||||
|
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
|
||||||
|
|
||||||
@ -51,11 +47,10 @@ class Assignment(models.Model, SlideMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
name = models.CharField(max_length=100, verbose_name=_("Name"))
|
name = models.CharField(max_length=100, verbose_name=_("Name"))
|
||||||
description = models.TextField(null=True, blank=True,
|
description = models.TextField(null=True, blank=True, verbose_name=_("Description"))
|
||||||
verbose_name=_("Description"))
|
posts = models.PositiveSmallIntegerField(verbose_name=_("Number of available posts"))
|
||||||
posts = models.PositiveSmallIntegerField(
|
polldescription = models.CharField(
|
||||||
verbose_name=_("Number of available posts"))
|
max_length=100, null=True, blank=True,
|
||||||
polldescription = models.CharField(max_length=100, null=True, blank=True,
|
|
||||||
verbose_name=_("Comment on the ballot paper"))
|
verbose_name=_("Comment on the ballot paper"))
|
||||||
status = models.CharField(max_length=3, choices=STATUS, default='sea')
|
status = models.CharField(max_length=3, choices=STATUS, default='sea')
|
||||||
|
|
||||||
@ -68,8 +63,8 @@ class Assignment(models.Model, SlideMixin):
|
|||||||
if error:
|
if error:
|
||||||
raise NameError(_('%s is not a valid status.') % status)
|
raise NameError(_('%s is not a valid status.') % status)
|
||||||
if self.status == status:
|
if self.status == status:
|
||||||
raise NameError(_('The assignment status is already %s.')
|
raise NameError(
|
||||||
% self.status)
|
_('The assignment status is already %s.') % self.status)
|
||||||
self.status = status
|
self.status = status
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
@ -116,14 +111,12 @@ class Assignment(models.Model, SlideMixin):
|
|||||||
else:
|
else:
|
||||||
candidature.delete()
|
candidature.delete()
|
||||||
|
|
||||||
|
|
||||||
def is_candidate(self, person):
|
def is_candidate(self, person):
|
||||||
"""
|
"""
|
||||||
return True, if person is a candidate.
|
return True, if person is a candidate.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self.assignment_candidates.filter(person=person) \
|
return self.assignment_candidates.filter(person=person).exclude(blocked=True).exists()
|
||||||
.exclude(blocked=True).exists()
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -131,8 +124,7 @@ class Assignment(models.Model, SlideMixin):
|
|||||||
"""
|
"""
|
||||||
return True, if the person is blockt for candidature.
|
return True, if the person is blockt for candidature.
|
||||||
"""
|
"""
|
||||||
return self.assignment_candidates.filter(person=person) \
|
return self.assignment_candidates.filter(person=person).filter(blocked=True).exists()
|
||||||
.filter(blocked=True).exists()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def assignment_candidates(self):
|
def assignment_candidates(self):
|
||||||
@ -167,7 +159,6 @@ class Assignment(models.Model, SlideMixin):
|
|||||||
return participants
|
return participants
|
||||||
#return candidates.values_list('person', flat=True)
|
#return candidates.values_list('person', flat=True)
|
||||||
|
|
||||||
|
|
||||||
def set_elected(self, person, value=True):
|
def set_elected(self, person, value=True):
|
||||||
candidate = self.assignment_candidates.get(person=person)
|
candidate = self.assignment_candidates.get(person=person)
|
||||||
candidate.elected = value
|
candidate.elected = value
|
||||||
@ -215,7 +206,6 @@ class Assignment(models.Model, SlideMixin):
|
|||||||
vote_results_dict[candidate].append(votes)
|
vote_results_dict[candidate].append(votes)
|
||||||
return vote_results_dict
|
return vote_results_dict
|
||||||
|
|
||||||
|
|
||||||
def get_agenda_title(self):
|
def get_agenda_title(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@ -301,8 +291,7 @@ class AssignmentPoll(BasePoll, CountInvalid, CountVotesCast, PublishPollMixin):
|
|||||||
self.yesnoabstain = False
|
self.yesnoabstain = False
|
||||||
self.save()
|
self.save()
|
||||||
if self.yesnoabstain:
|
if self.yesnoabstain:
|
||||||
return [ugettext_noop('Yes'), ugettext_noop('No'),
|
return [ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
|
||||||
ugettext_noop('Abstain')]
|
|
||||||
else:
|
else:
|
||||||
return [ugettext_noop('Votes')]
|
return [ugettext_noop('Votes')]
|
||||||
|
|
||||||
|
@ -13,39 +13,31 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from reportlab.lib import colors
|
from reportlab.lib import colors
|
||||||
from reportlab.platypus import (SimpleDocTemplate, PageBreak, Paragraph,
|
from reportlab.platypus import (
|
||||||
Spacer, Table, TableStyle)
|
SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle)
|
||||||
from reportlab.lib.units import cm
|
from reportlab.lib.units import cm
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.translation import ungettext, ugettext as _
|
from django.utils.translation import ungettext, ugettext as _
|
||||||
|
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
from openslides.utils.template import Tab
|
from openslides.utils.template import Tab
|
||||||
from openslides.utils.utils import (template, permission_required,
|
from openslides.utils.utils import (
|
||||||
gen_confirm_form, del_confirm_form, ajax_request)
|
template, permission_required, gen_confirm_form, del_confirm_form, ajax_request)
|
||||||
from openslides.utils.views import FormView, DeleteView, PDFView, RedirectView
|
from openslides.utils.views import FormView, DeleteView, PDFView, RedirectView
|
||||||
from openslides.utils.person import get_person
|
from openslides.utils.person import get_person
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
|
|
||||||
from openslides.participant.models import User
|
from openslides.participant.models import User
|
||||||
|
|
||||||
from openslides.projector.projector import Widget
|
from openslides.projector.projector import Widget
|
||||||
|
|
||||||
from openslides.poll.views import PollFormView
|
from openslides.poll.views import PollFormView
|
||||||
|
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
from openslides.assignment.models import Assignment, AssignmentPoll
|
||||||
from openslides.assignment.models import (Assignment, AssignmentPoll,
|
from openslides.assignment.forms import (
|
||||||
AssignmentOption)
|
AssignmentForm, AssignmentRunForm, ConfigForm)
|
||||||
from openslides.assignment.forms import (AssignmentForm, AssignmentRunForm,
|
|
||||||
ConfigForm)
|
|
||||||
|
|
||||||
|
|
||||||
@permission_required('assignment.can_see_assignment')
|
@permission_required('assignment.can_see_assignment')
|
||||||
@ -56,7 +48,7 @@ def get_overview(request):
|
|||||||
query = query.filter(status__iexact=request.GET['status'])
|
query = query.filter(status__iexact=request.GET['status'])
|
||||||
try:
|
try:
|
||||||
sort = request.GET['sort']
|
sort = request.GET['sort']
|
||||||
if sort in ['name','status']:
|
if sort in ['name', 'status']:
|
||||||
query = query.order_by(sort)
|
query = query.order_by(sort)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
@ -91,7 +83,6 @@ def view(request, assignment_id=None):
|
|||||||
if request.user.has_perm('assignment.can_nominate_other'):
|
if request.user.has_perm('assignment.can_nominate_other'):
|
||||||
form = AssignmentRunForm()
|
form = AssignmentRunForm()
|
||||||
|
|
||||||
|
|
||||||
polls = assignment.poll_set.all()
|
polls = assignment.poll_set.all()
|
||||||
if not request.user.has_perm('assignment.can_manage_assignment'):
|
if not request.user.has_perm('assignment.can_manage_assignment'):
|
||||||
polls = assignment.poll_set.filter(published=True)
|
polls = assignment.poll_set.filter(published=True)
|
||||||
@ -100,7 +91,8 @@ def view(request, assignment_id=None):
|
|||||||
polls = assignment.poll_set.all()
|
polls = assignment.poll_set.all()
|
||||||
vote_results = assignment.vote_results(only_published=False)
|
vote_results = assignment.vote_results(only_published=False)
|
||||||
|
|
||||||
blocked_candidates = [candidate.person for candidate in \
|
blocked_candidates = [
|
||||||
|
candidate.person for candidate in
|
||||||
assignment.assignment_candidates.filter(blocked=True)]
|
assignment.assignment_candidates.filter(blocked=True)]
|
||||||
return {
|
return {
|
||||||
'assignment': assignment,
|
'assignment': assignment,
|
||||||
@ -180,7 +172,7 @@ def run(request, assignment_id):
|
|||||||
assignment = Assignment.objects.get(pk=assignment_id)
|
assignment = Assignment.objects.get(pk=assignment_id)
|
||||||
try:
|
try:
|
||||||
assignment.run(request.user, request.user)
|
assignment.run(request.user, request.user)
|
||||||
messages.success(request, _('You have set your candidature successfully.') )
|
messages.success(request, _('You have set your candidature successfully.'))
|
||||||
except NameError, e:
|
except NameError, e:
|
||||||
messages.error(request, e)
|
messages.error(request, e)
|
||||||
return redirect(reverse('assignment_view', args=[assignment_id]))
|
return redirect(reverse('assignment_view', args=[assignment_id]))
|
||||||
@ -195,7 +187,8 @@ def delrun(request, assignment_id):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
messages.error(request, e)
|
messages.error(request, e)
|
||||||
else:
|
else:
|
||||||
messages.success(request,
|
messages.success(
|
||||||
|
request,
|
||||||
_("You have withdrawn your candidature successfully. "
|
_("You have withdrawn your candidature successfully. "
|
||||||
"You can not be nominated by other participants anymore."))
|
"You can not be nominated by other participants anymore."))
|
||||||
else:
|
else:
|
||||||
@ -240,7 +233,7 @@ def set_active(request, assignment_id):
|
|||||||
@permission_required('assignment.can_manage_assignment')
|
@permission_required('assignment.can_manage_assignment')
|
||||||
def gen_poll(request, assignment_id):
|
def gen_poll(request, assignment_id):
|
||||||
poll = Assignment.objects.get(pk=assignment_id).gen_poll()
|
poll = Assignment.objects.get(pk=assignment_id).gen_poll()
|
||||||
messages.success(request, _("New ballot was successfully created.") )
|
messages.success(request, _("New ballot was successfully created."))
|
||||||
return redirect(reverse('assignment_poll_view', args=[poll.id]))
|
return redirect(reverse('assignment_poll_view', args=[poll.id]))
|
||||||
|
|
||||||
|
|
||||||
@ -279,9 +272,9 @@ def set_publish_status(request, poll_id):
|
|||||||
return ajax_request({'published': poll.published})
|
return ajax_request({'published': poll.published})
|
||||||
|
|
||||||
if poll.published:
|
if poll.published:
|
||||||
messages.success(request, _("Ballot successfully published.") )
|
messages.success(request, _("Ballot successfully published."))
|
||||||
else:
|
else:
|
||||||
messages.success(request, _("Ballot successfully unpublished.") )
|
messages.success(request, _("Ballot successfully unpublished."))
|
||||||
return redirect(reverse('assignment_view', args=[poll.assignment.id]))
|
return redirect(reverse('assignment_view', args=[poll.assignment.id]))
|
||||||
|
|
||||||
|
|
||||||
@ -336,8 +329,9 @@ class AssignmentPDF(PDFView):
|
|||||||
try:
|
try:
|
||||||
assignment_id = self.kwargs['assignment_id']
|
assignment_id = self.kwargs['assignment_id']
|
||||||
assignment = Assignment.objects.get(id=assignment_id)
|
assignment = Assignment.objects.get(id=assignment_id)
|
||||||
filename = u'%s-%s' % (_("Assignment"),
|
filename = u'%s-%s' % (
|
||||||
assignment.name.replace(' ','_'))
|
_("Assignment"),
|
||||||
|
assignment.name.replace(' ', '_'))
|
||||||
except:
|
except:
|
||||||
filename = _("Elections")
|
filename = _("Elections")
|
||||||
return filename
|
return filename
|
||||||
@ -347,23 +341,24 @@ class AssignmentPDF(PDFView):
|
|||||||
assignment_id = self.kwargs['assignment_id']
|
assignment_id = self.kwargs['assignment_id']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
assignment_id = None
|
assignment_id = None
|
||||||
if assignment_id is None: #print all assignments
|
if assignment_id is None: # print all assignments
|
||||||
title = config["assignment_pdf_title"]
|
title = config["assignment_pdf_title"]
|
||||||
story.append(Paragraph(title, stylesheet['Heading1']))
|
story.append(Paragraph(title, stylesheet['Heading1']))
|
||||||
preamble = config["assignment_pdf_preamble"]
|
preamble = config["assignment_pdf_preamble"]
|
||||||
if preamble:
|
if preamble:
|
||||||
story.append(Paragraph("%s" % preamble.replace('\r\n', '<br/>'),
|
story.append(Paragraph(
|
||||||
|
"%s" % preamble.replace('\r\n', '<br/>'),
|
||||||
stylesheet['Paragraph']))
|
stylesheet['Paragraph']))
|
||||||
story.append(Spacer(0, 0.75 * cm))
|
story.append(Spacer(0, 0.75 * cm))
|
||||||
assignments = Assignment.objects.all()
|
assignments = Assignment.objects.all()
|
||||||
if not assignments: # No assignments existing
|
if not assignments: # No assignments existing
|
||||||
story.append(Paragraph(_("No assignments available."),
|
story.append(Paragraph(
|
||||||
stylesheet['Heading3']))
|
_("No assignments available."), stylesheet['Heading3']))
|
||||||
else: # Print all assignments
|
else: # Print all assignments
|
||||||
# List of assignments
|
# List of assignments
|
||||||
for assignment in assignments:
|
for assignment in assignments:
|
||||||
story.append(Paragraph(assignment.name,
|
story.append(Paragraph(
|
||||||
stylesheet['Heading3']))
|
assignment.name, stylesheet['Heading3']))
|
||||||
# Assignment details (each assignment on single page)
|
# Assignment details (each assignment on single page)
|
||||||
for assignment in assignments:
|
for assignment in assignments:
|
||||||
story.append(PageBreak())
|
story.append(PageBreak())
|
||||||
@ -376,28 +371,33 @@ class AssignmentPDF(PDFView):
|
|||||||
|
|
||||||
def get_assignment(self, assignment, story):
|
def get_assignment(self, assignment, story):
|
||||||
# title
|
# title
|
||||||
story.append(Paragraph(_("Election: %s") % assignment.name,
|
story.append(Paragraph(
|
||||||
stylesheet['Heading1']))
|
_("Election: %s") % assignment.name, stylesheet['Heading1']))
|
||||||
story.append(Spacer(0, 0.5 * cm))
|
story.append(Spacer(0, 0.5 * cm))
|
||||||
# posts
|
# posts
|
||||||
cell1a = []
|
cell1a = []
|
||||||
cell1a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font>" %
|
cell1a.append(Paragraph(
|
||||||
|
"<font name='Ubuntu-Bold'>%s:</font>" %
|
||||||
_("Number of available posts"), stylesheet['Bold']))
|
_("Number of available posts"), stylesheet['Bold']))
|
||||||
cell1b = []
|
cell1b = []
|
||||||
cell1b.append(Paragraph(str(assignment.posts), stylesheet['Paragraph']))
|
cell1b.append(Paragraph(str(assignment.posts), stylesheet['Paragraph']))
|
||||||
# candidates
|
# candidates
|
||||||
cell2a = []
|
cell2a = []
|
||||||
cell2a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font><seqreset" \
|
cell2a.append(Paragraph(
|
||||||
|
"<font name='Ubuntu-Bold'>%s:</font><seqreset"
|
||||||
" id='counter'>" % _("Candidates"), stylesheet['Heading4']))
|
" id='counter'>" % _("Candidates"), stylesheet['Heading4']))
|
||||||
cell2b = []
|
cell2b = []
|
||||||
for candidate in assignment.candidates:
|
for candidate in assignment.candidates:
|
||||||
cell2b.append(Paragraph("<seq id='counter'/>. %s" % candidate,
|
cell2b.append(Paragraph(
|
||||||
|
"<seq id='counter'/>. %s" % candidate,
|
||||||
stylesheet['Signaturefield']))
|
stylesheet['Signaturefield']))
|
||||||
if assignment.status == "sea":
|
if assignment.status == "sea":
|
||||||
for x in range(0, 2 * assignment.posts):
|
for x in range(0, 2 * assignment.posts):
|
||||||
cell2b.append(Paragraph("<seq id='counter'/>. "
|
cell2b.append(
|
||||||
"__________________________________________",
|
Paragraph(
|
||||||
stylesheet['Signaturefield']))
|
"<seq id='counter'/>. "
|
||||||
|
"__________________________________________",
|
||||||
|
stylesheet['Signaturefield']))
|
||||||
cell2b.append(Spacer(0, 0.2 * cm))
|
cell2b.append(Spacer(0, 0.2 * cm))
|
||||||
|
|
||||||
# Vote results
|
# Vote results
|
||||||
@ -409,15 +409,15 @@ class AssignmentPDF(PDFView):
|
|||||||
|
|
||||||
# Left side
|
# Left side
|
||||||
cell3a = []
|
cell3a = []
|
||||||
cell3a.append(Paragraph("%s:" % (_("Vote results")),
|
cell3a.append(Paragraph(
|
||||||
stylesheet['Heading4']))
|
"%s:" % (_("Vote results")), stylesheet['Heading4']))
|
||||||
|
|
||||||
if polls.count() == 1:
|
if polls.count() == 1:
|
||||||
cell3a.append(Paragraph("%s %s" % (polls.count(), _("ballot")),
|
cell3a.append(Paragraph(
|
||||||
stylesheet['Normal']))
|
"%s %s" % (polls.count(), _("ballot")), stylesheet['Normal']))
|
||||||
elif polls.count() > 1:
|
elif polls.count() > 1:
|
||||||
cell3a.append(Paragraph("%s %s" % (polls.count(), _("ballots")),
|
cell3a.append(Paragraph(
|
||||||
stylesheet['Normal']))
|
"%s %s" % (polls.count(), _("ballots")), stylesheet['Normal']))
|
||||||
|
|
||||||
# Add table head row
|
# Add table head row
|
||||||
headrow = []
|
headrow = []
|
||||||
@ -426,7 +426,6 @@ class AssignmentPDF(PDFView):
|
|||||||
headrow.append("%s." % poll.get_ballot())
|
headrow.append("%s." % poll.get_ballot())
|
||||||
data_votes.append(headrow)
|
data_votes.append(headrow)
|
||||||
|
|
||||||
|
|
||||||
# Add result rows
|
# Add result rows
|
||||||
elected_candidates = list(assignment.elected)
|
elected_candidates = list(assignment.elected)
|
||||||
for candidate, poll_list in vote_results.iteritems():
|
for candidate, poll_list in vote_results.iteritems():
|
||||||
@ -439,12 +438,13 @@ class AssignmentPDF(PDFView):
|
|||||||
candidate_string += "\n(%s)" % candidate.name_suffix
|
candidate_string += "\n(%s)" % candidate.name_suffix
|
||||||
row.append(candidate_string)
|
row.append(candidate_string)
|
||||||
for vote in poll_list:
|
for vote in poll_list:
|
||||||
if vote == None:
|
if vote is None:
|
||||||
row.append('–')
|
row.append('–')
|
||||||
elif 'Yes' in vote and 'No' in vote and 'Abstain' in vote:
|
elif 'Yes' in vote and 'No' in vote and 'Abstain' in vote:
|
||||||
row.append(_("Y: %(YES)s\nN: %(NO)s\nA: %(ABSTAIN)s")
|
row.append(
|
||||||
% {'YES':vote['Yes'], 'NO': vote['No'],
|
_("Y: %(YES)s\nN: %(NO)s\nA: %(ABSTAIN)s")
|
||||||
'ABSTAIN': vote['Abstain']})
|
% {'YES': vote['Yes'], 'NO': vote['No'],
|
||||||
|
'ABSTAIN': vote['Abstain']})
|
||||||
elif 'Votes' in vote:
|
elif 'Votes' in vote:
|
||||||
row.append(vote['Votes'])
|
row.append(vote['Votes'])
|
||||||
else:
|
else:
|
||||||
@ -465,15 +465,14 @@ class AssignmentPDF(PDFView):
|
|||||||
footrow_two.append(poll.print_votescast())
|
footrow_two.append(poll.print_votescast())
|
||||||
data_votes.append(footrow_two)
|
data_votes.append(footrow_two)
|
||||||
|
|
||||||
table_votes=Table(data_votes)
|
table_votes = Table(data_votes)
|
||||||
table_votes.setStyle( TableStyle([
|
table_votes.setStyle(TableStyle([
|
||||||
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
|
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
|
||||||
('VALIGN',(0, 0),(-1, -1), 'TOP'),
|
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||||
('LINEABOVE',(0, 0),(-1, 0), 2, colors.black),
|
('LINEABOVE', (0, 0), (-1, 0), 2, colors.black),
|
||||||
('LINEABOVE',(0, 1),(-1, 1), 1, colors.black),
|
('LINEABOVE', (0, 1), (-1, 1), 1, colors.black),
|
||||||
('LINEBELOW',(0, -1),(-1, -1), 2, colors.black),
|
('LINEBELOW', (0, -1), (-1, -1), 2, colors.black),
|
||||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), (colors.white, (.9, .9, .9))),
|
('ROWBACKGROUNDS', (0, 1), (-1, -1), (colors.white, (.9, .9, .9)))]))
|
||||||
]))
|
|
||||||
|
|
||||||
# table
|
# table
|
||||||
data = []
|
data = []
|
||||||
@ -484,17 +483,18 @@ class AssignmentPDF(PDFView):
|
|||||||
else:
|
else:
|
||||||
data.append([cell2a, cell2b])
|
data.append([cell2a, cell2b])
|
||||||
data.append([Spacer(0, 0.2 * cm), ''])
|
data.append([Spacer(0, 0.2 * cm), ''])
|
||||||
t=Table(data)
|
t = Table(data)
|
||||||
t._argW[0] = 4.5 * cm
|
t._argW[0] = 4.5 * cm
|
||||||
t._argW[1] = 11 * cm
|
t._argW[1] = 11 * cm
|
||||||
t.setStyle(TableStyle([ ('BOX', (0,0), (-1, -1), 1, colors.black),
|
t.setStyle(TableStyle([
|
||||||
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
('BOX', (0, 0), (-1, -1), 1, colors.black),
|
||||||
]))
|
('VALIGN', (0, 0), (-1, -1), 'TOP')]))
|
||||||
story.append(t)
|
story.append(t)
|
||||||
story.append(Spacer(0, 1 * cm))
|
story.append(Spacer(0, 1 * cm))
|
||||||
|
|
||||||
# text
|
# text
|
||||||
story.append(Paragraph("%s" % assignment.description.replace('\r\n',
|
story.append(Paragraph(
|
||||||
|
"%s" % assignment.description.replace('\r\n',
|
||||||
'<br/>'), stylesheet['Paragraph']))
|
'<br/>'), stylesheet['Paragraph']))
|
||||||
|
|
||||||
|
|
||||||
@ -519,13 +519,15 @@ class AssignmentPollPDF(PDFView):
|
|||||||
return super(AssignmentPollPDF, self).get(request, *args, **kwargs)
|
return super(AssignmentPollPDF, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_filename(self):
|
def get_filename(self):
|
||||||
filename = u'%s-%s_%s' % (_("Election"), self.poll.assignment.name.replace(' ', '_'),
|
filename = u'%s-%s_%s' % (
|
||||||
|
_("Election"), self.poll.assignment.name.replace(' ', '_'),
|
||||||
self.poll.get_ballot())
|
self.poll.get_ballot())
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
def get_template(self, buffer):
|
def get_template(self, buffer):
|
||||||
return SimpleDocTemplate(buffer, topMargin=-6, bottomMargin=-6,
|
return SimpleDocTemplate(
|
||||||
leftMargin=0, rightMargin=0, showBoundary=False)
|
buffer, topMargin=-6, bottomMargin=-6, leftMargin=0, rightMargin=0,
|
||||||
|
showBoundary=False)
|
||||||
|
|
||||||
def build_document(self, pdf_document, story):
|
def build_document(self, pdf_document, story):
|
||||||
pdf_document.build(story)
|
pdf_document.build(story)
|
||||||
@ -534,23 +536,27 @@ class AssignmentPollPDF(PDFView):
|
|||||||
imgpath = os.path.join(settings.SITE_ROOT, 'static/images/circle.png')
|
imgpath = os.path.join(settings.SITE_ROOT, 'static/images/circle.png')
|
||||||
circle = "<img src='%s' width='15' height='15'/> " % imgpath
|
circle = "<img src='%s' width='15' height='15'/> " % imgpath
|
||||||
cell = []
|
cell = []
|
||||||
cell.append(Spacer(0,0.8*cm))
|
cell.append(Spacer(0, 0.8 * cm))
|
||||||
cell.append(Paragraph(_("Election") + ": " + self.poll.assignment.name,
|
cell.append(Paragraph(
|
||||||
|
_("Election") + ": " + self.poll.assignment.name,
|
||||||
stylesheet['Ballot_title']))
|
stylesheet['Ballot_title']))
|
||||||
cell.append(Paragraph(self.poll.assignment.polldescription,
|
cell.append(Paragraph(
|
||||||
|
self.poll.assignment.polldescription,
|
||||||
stylesheet['Ballot_subtitle']))
|
stylesheet['Ballot_subtitle']))
|
||||||
options = self.poll.get_options()
|
options = self.poll.get_options()
|
||||||
|
|
||||||
ballot_string = _("%d. ballot") % self.poll.get_ballot()
|
ballot_string = _("%d. ballot") % self.poll.get_ballot()
|
||||||
candidate_string = ungettext("%d candidate", "%d candidates",
|
candidate_string = ungettext(
|
||||||
len(options)) % len(options)
|
"%d candidate", "%d candidates", len(options)) % len(options)
|
||||||
available_posts_string = ungettext("%d available post", "%d available posts",
|
available_posts_string = ungettext(
|
||||||
|
"%d available post", "%d available posts",
|
||||||
self.poll.assignment.posts) % self.poll.assignment.posts
|
self.poll.assignment.posts) % self.poll.assignment.posts
|
||||||
cell.append(Paragraph("%s, %s, %s" % (ballot_string, candidate_string,
|
cell.append(Paragraph(
|
||||||
|
"%s, %s, %s" % (ballot_string, candidate_string,
|
||||||
available_posts_string), stylesheet['Ballot_description']))
|
available_posts_string), stylesheet['Ballot_description']))
|
||||||
cell.append(Spacer(0, 0.4 * cm))
|
cell.append(Spacer(0, 0.4 * cm))
|
||||||
|
|
||||||
data= []
|
data = []
|
||||||
# get ballot papers config values
|
# get ballot papers config values
|
||||||
ballot_papers_selection = config["assignment_pdf_ballot_papers_selection"]
|
ballot_papers_selection = config["assignment_pdf_ballot_papers_selection"]
|
||||||
ballot_papers_number = config["assignment_pdf_ballot_papers_number"]
|
ballot_papers_number = config["assignment_pdf_ballot_papers_number"]
|
||||||
@ -560,7 +566,7 @@ class AssignmentPollPDF(PDFView):
|
|||||||
number = User.objects.filter(type__iexact="delegate").count()
|
number = User.objects.filter(type__iexact="delegate").count()
|
||||||
elif ballot_papers_selection == "NUMBER_OF_ALL_PARTICIPANTS":
|
elif ballot_papers_selection == "NUMBER_OF_ALL_PARTICIPANTS":
|
||||||
number = int(User.objects.count())
|
number = int(User.objects.count())
|
||||||
else: # ballot_papers_selection == "CUSTOM_NUMBER"
|
else: # ballot_papers_selection == "CUSTOM_NUMBER"
|
||||||
number = int(ballot_papers_number)
|
number = int(ballot_papers_number)
|
||||||
number = max(1, number)
|
number = max(1, number)
|
||||||
|
|
||||||
@ -568,16 +574,18 @@ class AssignmentPollPDF(PDFView):
|
|||||||
if self.poll.yesnoabstain:
|
if self.poll.yesnoabstain:
|
||||||
for option in options:
|
for option in options:
|
||||||
candidate = option.candidate
|
candidate = option.candidate
|
||||||
cell.append(Paragraph(candidate.clean_name,
|
cell.append(Paragraph(
|
||||||
stylesheet['Ballot_option_name']))
|
candidate.clean_name, stylesheet['Ballot_option_name']))
|
||||||
if candidate.name_suffix:
|
if candidate.name_suffix:
|
||||||
cell.append(Paragraph("(%s)" % candidate.name_suffix,
|
cell.append(Paragraph(
|
||||||
|
"(%s)" % candidate.name_suffix,
|
||||||
stylesheet['Ballot_option_group']))
|
stylesheet['Ballot_option_group']))
|
||||||
else:
|
else:
|
||||||
cell.append(Paragraph(" ",
|
cell.append(Paragraph(
|
||||||
stylesheet['Ballot_option_group']))
|
" ", stylesheet['Ballot_option_group']))
|
||||||
cell.append(Paragraph(circle + _("Yes") + " " * 3 + circle
|
cell.append(Paragraph(
|
||||||
+ _("No") + " " * 3 + circle+ _("Abstention"),
|
circle + _("Yes") + " " * 3 + circle
|
||||||
|
+ _("No") + " " * 3 + circle + _("Abstention"),
|
||||||
stylesheet['Ballot_option_YNA']))
|
stylesheet['Ballot_option_YNA']))
|
||||||
# print ballot papers
|
# print ballot papers
|
||||||
for user in xrange(number / 2):
|
for user in xrange(number / 2):
|
||||||
@ -594,14 +602,16 @@ class AssignmentPollPDF(PDFView):
|
|||||||
else:
|
else:
|
||||||
for option in options:
|
for option in options:
|
||||||
candidate = option.candidate
|
candidate = option.candidate
|
||||||
cell.append(Paragraph(circle + candidate.clean_name,
|
cell.append(Paragraph(
|
||||||
|
circle + candidate.clean_name,
|
||||||
stylesheet['Ballot_option_name']))
|
stylesheet['Ballot_option_name']))
|
||||||
if candidate.name_suffix:
|
if candidate.name_suffix:
|
||||||
cell.append(Paragraph("(%s)" % candidate.name_suffix,
|
cell.append(Paragraph(
|
||||||
|
"(%s)" % candidate.name_suffix,
|
||||||
stylesheet['Ballot_option_group_right']))
|
stylesheet['Ballot_option_group_right']))
|
||||||
else:
|
else:
|
||||||
cell.append(Paragraph(" ",
|
cell.append(Paragraph(
|
||||||
stylesheet['Ballot_option_group_right']))
|
" ", stylesheet['Ballot_option_group_right']))
|
||||||
# print ballot papers
|
# print ballot papers
|
||||||
for user in xrange(number / 2):
|
for user in xrange(number / 2):
|
||||||
data.append([cell, cell])
|
data.append([cell, cell])
|
||||||
@ -615,9 +625,9 @@ class AssignmentPollPDF(PDFView):
|
|||||||
else:
|
else:
|
||||||
t = Table(data, 10.5 * cm, 29.7 * cm)
|
t = Table(data, 10.5 * cm, 29.7 * cm)
|
||||||
|
|
||||||
t.setStyle(TableStyle([('GRID', (0, 0), (-1, -1), 0.25, colors.grey),
|
t.setStyle(TableStyle([
|
||||||
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
('GRID', (0, 0), (-1, -1), 0.25, colors.grey),
|
||||||
]))
|
('VALIGN', (0, 0), (-1, -1), 'TOP')]))
|
||||||
story.append(t)
|
story.append(t)
|
||||||
|
|
||||||
|
|
||||||
@ -629,16 +639,15 @@ class Config(FormView):
|
|||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {
|
return {
|
||||||
'assignment_publish_winner_results_only':
|
'assignment_publish_winner_results_only':
|
||||||
config['assignment_publish_winner_results_only'],
|
config['assignment_publish_winner_results_only'],
|
||||||
'assignment_pdf_ballot_papers_selection':
|
'assignment_pdf_ballot_papers_selection':
|
||||||
config['assignment_pdf_ballot_papers_selection'],
|
config['assignment_pdf_ballot_papers_selection'],
|
||||||
'assignment_pdf_ballot_papers_number':
|
'assignment_pdf_ballot_papers_number':
|
||||||
config['assignment_pdf_ballot_papers_number'],
|
config['assignment_pdf_ballot_papers_number'],
|
||||||
'assignment_pdf_title': config['assignment_pdf_title'],
|
'assignment_pdf_title': config['assignment_pdf_title'],
|
||||||
'assignment_pdf_preamble': config['assignment_pdf_preamble'],
|
'assignment_pdf_preamble': config['assignment_pdf_preamble'],
|
||||||
'assignment_poll_vote_values':
|
'assignment_poll_vote_values':
|
||||||
config['assignment_poll_vote_values'],
|
config['assignment_poll_vote_values']}
|
||||||
}
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
if form.cleaned_data['assignment_publish_winner_results_only']:
|
if form.cleaned_data['assignment_publish_winner_results_only']:
|
||||||
@ -655,8 +664,8 @@ class Config(FormView):
|
|||||||
form.cleaned_data['assignment_pdf_preamble']
|
form.cleaned_data['assignment_pdf_preamble']
|
||||||
config['assignment_poll_vote_values'] = \
|
config['assignment_poll_vote_values'] = \
|
||||||
form.cleaned_data['assignment_poll_vote_values']
|
form.cleaned_data['assignment_poll_vote_values']
|
||||||
messages.success(self.request,
|
messages.success(
|
||||||
_('Election settings successfully saved.'))
|
self.request, _('Election settings successfully saved.'))
|
||||||
return super(Config, self).form_valid(form)
|
return super(Config, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
@ -665,10 +674,11 @@ def register_tab(request):
|
|||||||
return Tab(
|
return Tab(
|
||||||
title=_('Elections'),
|
title=_('Elections'),
|
||||||
url=reverse('assignment_overview'),
|
url=reverse('assignment_overview'),
|
||||||
permission=request.user.has_perm('assignment.can_see_assignment')
|
permission=(
|
||||||
or request.user.has_perm('assignment.can_nominate_other')
|
request.user.has_perm('assignment.can_see_assignment') or
|
||||||
or request.user.has_perm('assignment.can_nominate_self')
|
request.user.has_perm('assignment.can_nominate_other') or
|
||||||
or request.user.has_perm('assignment.can_manage_assignment'),
|
request.user.has_perm('assignment.can_nominate_self') or
|
||||||
|
request.user.has_perm('assignment.can_manage_assignment')),
|
||||||
selected=selected,
|
selected=selected,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
|
|
||||||
from openslides.config.models import config
|
|
||||||
|
|
||||||
|
|
||||||
class GeneralConfigForm(forms.Form, CssClassMixin):
|
class GeneralConfigForm(forms.Form, CssClassMixin):
|
||||||
event_name = forms.CharField(
|
event_name = forms.CharField(
|
||||||
|
@ -51,7 +51,7 @@ class Config(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
for receiver, value in default_config_value.send(sender='config',
|
for receiver, value in default_config_value.send(sender='config',
|
||||||
key=key):
|
key=key):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
return value
|
return value
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
@ -69,7 +69,6 @@ class Config(object):
|
|||||||
def __contains__(self, item):
|
def __contains__(self, item):
|
||||||
return ConfigStore.objects.filter(key=item).exists()
|
return ConfigStore.objects.filter(key=item).exists()
|
||||||
|
|
||||||
|
|
||||||
config = Config()
|
config = Config()
|
||||||
|
|
||||||
|
|
||||||
@ -81,7 +80,7 @@ def default_config(sender, key, **kwargs):
|
|||||||
return {
|
return {
|
||||||
'event_name': 'OpenSlides',
|
'event_name': 'OpenSlides',
|
||||||
'event_description':
|
'event_description':
|
||||||
_('Presentation and assembly system'),
|
_('Presentation and assembly system'),
|
||||||
'event_date': '',
|
'event_date': '',
|
||||||
'event_location': '',
|
'event_location': '',
|
||||||
'event_organizer': '',
|
'event_organizer': '',
|
||||||
@ -123,11 +122,9 @@ def set_submenu(sender, request, context, **kwargs):
|
|||||||
(reverse('config_%s' % appname), _(title), selected)
|
(reverse('config_%s' % appname), _(title), selected)
|
||||||
)
|
)
|
||||||
|
|
||||||
menu_links.append (
|
menu_links.append((
|
||||||
(reverse('config_version'), _('Version'),
|
reverse('config_version'), _('Version'),
|
||||||
request.path == reverse('config_version'))
|
request.path == reverse('config_version')))
|
||||||
)
|
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'menu_links': menu_links,
|
'menu_links': menu_links})
|
||||||
})
|
|
||||||
|
@ -12,18 +12,18 @@
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import Group, Permission
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides import get_version
|
from openslides import get_version
|
||||||
|
|
||||||
from openslides.utils.template import Tab
|
from openslides.utils.template import Tab
|
||||||
from openslides.utils.views import FormView, TemplateView
|
from openslides.utils.views import FormView, TemplateView
|
||||||
|
from .forms import GeneralConfigForm
|
||||||
|
from .models import config
|
||||||
|
|
||||||
from openslides.config.forms import GeneralConfigForm
|
# TODO: Do not import the participant module in config
|
||||||
from openslides.config.models import config
|
from openslides.participant.api import get_or_create_anonymous_group
|
||||||
|
|
||||||
|
|
||||||
class GeneralConfig(FormView):
|
class GeneralConfig(FormView):
|
||||||
@ -61,27 +61,12 @@ class GeneralConfig(FormView):
|
|||||||
# system
|
# system
|
||||||
if form.cleaned_data['system_enable_anonymous']:
|
if form.cleaned_data['system_enable_anonymous']:
|
||||||
config['system_enable_anonymous'] = True
|
config['system_enable_anonymous'] = True
|
||||||
# check for Anonymous group and (re)create it as needed
|
get_or_create_anonymous_group()
|
||||||
try:
|
|
||||||
anonymous = Group.objects.get(name='Anonymous')
|
|
||||||
except Group.DoesNotExist:
|
|
||||||
default_perms = ['can_see_agenda', 'can_see_projector',
|
|
||||||
'can_see_motion', 'can_see_assignment',
|
|
||||||
'can_see_dashboard']
|
|
||||||
anonymous = Group()
|
|
||||||
anonymous.name = 'Anonymous'
|
|
||||||
anonymous.save()
|
|
||||||
anonymous.permissions = Permission.objects.filter(
|
|
||||||
codename__in=default_perms)
|
|
||||||
anonymous.save()
|
|
||||||
messages.success(self.request,
|
|
||||||
_('Anonymous access enabled. Please modify the "Anonymous" ' \
|
|
||||||
'group to fit your required permissions.'))
|
|
||||||
else:
|
else:
|
||||||
config['system_enable_anonymous'] = False
|
config['system_enable_anonymous'] = False
|
||||||
|
|
||||||
messages.success(self.request,
|
messages.success(
|
||||||
_('General settings successfully saved.'))
|
self.request, _('General settings successfully saved.'))
|
||||||
return super(GeneralConfig, self).form_valid(form)
|
return super(GeneralConfig, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,15 +13,12 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
_fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
|
from openslides.main import fs2unicode
|
||||||
def _fs2unicode(s):
|
|
||||||
if isinstance(s, unicode):
|
|
||||||
return s
|
|
||||||
return s.decode(_fs_encoding)
|
|
||||||
|
|
||||||
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
|
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',
|
AUTHENTICATION_BACKENDS = (
|
||||||
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
'openslides.utils.auth.AnonymousAuth',)
|
'openslides.utils.auth.AnonymousAuth',)
|
||||||
|
|
||||||
LOGIN_URL = '/login/'
|
LOGIN_URL = '/login/'
|
||||||
@ -48,12 +45,12 @@ USE_I18N = True
|
|||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
|
|
||||||
LOCALE_PATHS = (
|
LOCALE_PATHS = (
|
||||||
_fs2unicode(os.path.join(SITE_ROOT, 'locale')),
|
fs2unicode(os.path.join(SITE_ROOT, 'locale')),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Absolute path to the directory that holds media.
|
# Absolute path to the directory that holds media.
|
||||||
# Example: "/home/media/media.lawrence.com/"
|
# Example: "/home/media/media.lawrence.com/"
|
||||||
MEDIA_ROOT = _fs2unicode(os.path.join(SITE_ROOT, './static/'))
|
MEDIA_ROOT = fs2unicode(os.path.join(SITE_ROOT, './static/'))
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||||
# trailing slash if there is a path component (optional in other cases).
|
# trailing slash if there is a path component (optional in other cases).
|
||||||
@ -62,17 +59,17 @@ MEDIA_URL = ''
|
|||||||
|
|
||||||
# Absolute path to the directory that holds static media from ``collectstatic``
|
# Absolute path to the directory that holds static media from ``collectstatic``
|
||||||
# Example: "/home/media/static.lawrence.com/"
|
# Example: "/home/media/static.lawrence.com/"
|
||||||
STATIC_ROOT = _fs2unicode(os.path.join(SITE_ROOT, '../site-static'))
|
STATIC_ROOT = fs2unicode(os.path.join(SITE_ROOT, '../site-static'))
|
||||||
|
|
||||||
# URL that handles the media served from STATIC_ROOT. Make sure to use a
|
# URL that handles the media served from STATIC_ROOT. Make sure to use a
|
||||||
# trailing slash if there is a path component (optional in other cases).
|
# trailing slash if there is a path component (optional in other cases).
|
||||||
# Examples: "http://static.lawrence.com", "http://example.com/static/"
|
# Examples: "http://static.lawrence.com", "http://example.com/static/"
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
# Additional directories containing static files (not application specific)
|
# Additional directories containing static files (not application specific)
|
||||||
# Examples: "/home/media/lawrence.com/extra-static/"
|
# Examples: "/home/media/lawrence.com/extra-static/"
|
||||||
STATICFILES_DIRS = (
|
STATICFILES_DIRS = (
|
||||||
_fs2unicode(os.path.join(SITE_ROOT, 'static')),
|
fs2unicode(os.path.join(SITE_ROOT, 'static')),
|
||||||
)
|
)
|
||||||
|
|
||||||
#XXX: Note this setting (as well as our workaround finder)
|
#XXX: Note this setting (as well as our workaround finder)
|
||||||
@ -106,7 +103,7 @@ TEMPLATE_DIRS = (
|
|||||||
# "C:/www/django/templates".
|
# "C:/www/django/templates".
|
||||||
# Always use forward slashes, even on Windows.
|
# Always use forward slashes, even on Windows.
|
||||||
# Don't forget to use absolute paths, not relative paths.
|
# Don't forget to use absolute paths, not relative paths.
|
||||||
_fs2unicode(os.path.join(SITE_ROOT, 'templates')),
|
fs2unicode(os.path.join(SITE_ROOT, 'templates')),
|
||||||
)
|
)
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
@ -142,3 +139,6 @@ CACHES = {
|
|||||||
'LOCATION': 'openslidecache'
|
'LOCATION': 'openslidecache'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_RUNNER = 'discover_runner.DiscoverRunner'
|
||||||
|
TEST_DISCOVER_TOP_LEVEL = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
@ -13,13 +13,15 @@
|
|||||||
# for python 2.5 support
|
# for python 2.5 support
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import optparse
|
|
||||||
import socket
|
|
||||||
import time
|
|
||||||
import threading
|
|
||||||
import base64
|
import base64
|
||||||
|
import ctypes
|
||||||
|
import optparse
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
import django.conf
|
import django.conf
|
||||||
@ -28,14 +30,15 @@ from django.core.management import execute_from_command_line
|
|||||||
CONFIG_TEMPLATE = """#!/usr/bin/env python
|
CONFIG_TEMPLATE = """#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import openslides.main
|
||||||
from openslides.global_settings import *
|
from openslides.global_settings import *
|
||||||
|
|
||||||
# Use 'DEBUG = True' to get more details for server errors
|
# Use 'DEBUG = True' to get more details for server errors
|
||||||
# (Default for relaeses: 'False')
|
# (Default for releases: 'False')
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
TEMPLATE_DEBUG = DEBUG
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
DBPATH = %(dbpath)r
|
DBPATH = %(dbpath)s
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
@ -64,15 +67,12 @@ INSTALLED_APPS += INSTALLED_PLUGINS
|
|||||||
|
|
||||||
KEY_LENGTH = 30
|
KEY_LENGTH = 30
|
||||||
|
|
||||||
|
# sentinel used to signal that the database ought to be stored
|
||||||
_fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
|
# relative to the portable's directory
|
||||||
def _fs2unicode(s):
|
_portable_db_path = object()
|
||||||
if isinstance(s, unicode):
|
|
||||||
return s
|
|
||||||
return s.decode(_fs_encoding)
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None, opt_defaults=None, database_path=None):
|
def process_options(argv=None):
|
||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv[1:]
|
argv = sys.argv[1:]
|
||||||
|
|
||||||
@ -86,12 +86,11 @@ def main(argv=None, opt_defaults=None, database_path=None):
|
|||||||
parser.add_option(
|
parser.add_option(
|
||||||
"--reset-admin", action="store_true",
|
"--reset-admin", action="store_true",
|
||||||
help="Make sure the user 'admin' exists and uses 'admin' as password")
|
help="Make sure the user 'admin' exists and uses 'admin' as password")
|
||||||
parser.add_option("-s", "--settings", help="Path to the openslides configuration.")
|
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"--no-reload", action="store_true", help="Do not reload the development server")
|
"-s", "--settings", help="Path to the openslides configuration.")
|
||||||
|
parser.add_option(
|
||||||
if not opt_defaults is None:
|
"--no-reload", action="store_true",
|
||||||
parser.set_defaults(**opt_defaults)
|
help="Do not reload the development server")
|
||||||
|
|
||||||
opts, args = parser.parse_args(argv)
|
opts, args = parser.parse_args(argv)
|
||||||
if args:
|
if args:
|
||||||
@ -99,12 +98,45 @@ def main(argv=None, opt_defaults=None, database_path=None):
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
return opts
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
opts = process_options(argv)
|
||||||
|
_main(opts)
|
||||||
|
|
||||||
|
|
||||||
|
def win32_portable_main(argv=None):
|
||||||
|
"""special entry point for the win32 portable version"""
|
||||||
|
|
||||||
|
opts = process_options(argv)
|
||||||
|
|
||||||
|
database_path = None
|
||||||
|
|
||||||
|
if opts.settings is None:
|
||||||
|
portable_dir = get_portable_path()
|
||||||
|
try:
|
||||||
|
fd, test_file = tempfile.mkstemp(dir=portable_dir)
|
||||||
|
except OSError:
|
||||||
|
portable_dir_writeable = False
|
||||||
|
else:
|
||||||
|
portable_dir_writeable = True
|
||||||
|
os.close(fd)
|
||||||
|
os.unlink(test_file)
|
||||||
|
|
||||||
|
if portable_dir_writeable:
|
||||||
|
opts.settings = os.path.join(
|
||||||
|
portable_dir, "openslides", "settings.py")
|
||||||
|
database_path = _portable_db_path
|
||||||
|
|
||||||
|
_main(opts, database_path=database_path)
|
||||||
|
|
||||||
|
|
||||||
|
def _main(opts, database_path=None):
|
||||||
# Find the path to the settings
|
# Find the path to the settings
|
||||||
settings_path = opts.settings
|
settings_path = opts.settings
|
||||||
if settings_path is None:
|
if settings_path is None:
|
||||||
config_home = os.environ.get('XDG_CONFIG_HOME', \
|
settings_path = get_user_config_path('openslides', 'settings.py')
|
||||||
os.path.join(os.path.expanduser('~'), '.config'))
|
|
||||||
settings_path = os.path.join(config_home, 'openslides', 'settings.py')
|
|
||||||
|
|
||||||
# Create settings if necessary
|
# Create settings if necessary
|
||||||
if not os.path.exists(settings_path):
|
if not os.path.exists(settings_path):
|
||||||
@ -141,14 +173,17 @@ def main(argv=None, opt_defaults=None, database_path=None):
|
|||||||
def create_settings(settings_path, database_path=None):
|
def create_settings(settings_path, database_path=None):
|
||||||
settings_module = os.path.dirname(settings_path)
|
settings_module = os.path.dirname(settings_path)
|
||||||
|
|
||||||
if database_path is None:
|
if database_path is _portable_db_path:
|
||||||
data_home = os.environ.get('XDG_DATA_HOME', \
|
database_path = get_portable_db_path()
|
||||||
os.path.join(os.path.expanduser('~'), '.local', 'share'))
|
dbpath_value = 'openslides.main.get_portable_db_path()'
|
||||||
database_path = os.path.join(data_home, 'openslides', 'database.sqlite')
|
else:
|
||||||
|
if database_path is None:
|
||||||
|
database_path = get_user_data_path('openslides', 'database.sqlite')
|
||||||
|
dbpath_value = repr(fs2unicode(database_path))
|
||||||
|
|
||||||
settings_content = CONFIG_TEMPLATE % dict(
|
settings_content = CONFIG_TEMPLATE % dict(
|
||||||
default_key=base64.b64encode(os.urandom(KEY_LENGTH)),
|
default_key=base64.b64encode(os.urandom(KEY_LENGTH)),
|
||||||
dbpath=_fs2unicode(database_path))
|
dbpath=dbpath_value)
|
||||||
|
|
||||||
if not os.path.exists(settings_module):
|
if not os.path.exists(settings_module):
|
||||||
os.makedirs(settings_module)
|
os.makedirs(settings_module)
|
||||||
@ -217,6 +252,7 @@ def run_syncdb():
|
|||||||
# now initialize the database
|
# now initialize the database
|
||||||
argv = ["", "syncdb", "--noinput"]
|
argv = ["", "syncdb", "--noinput"]
|
||||||
execute_from_command_line(argv)
|
execute_from_command_line(argv)
|
||||||
|
execute_from_command_line(["", "loaddata", "groups_de"])
|
||||||
|
|
||||||
|
|
||||||
def set_system_url(url):
|
def set_system_url(url):
|
||||||
@ -269,51 +305,71 @@ def start_browser(url):
|
|||||||
t = threading.Thread(target=f)
|
t = threading.Thread(target=f)
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
def win32_portable_main(argv=None):
|
|
||||||
"""special entry point for the win32 portable version"""
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
|
def fs2unicode(s):
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
return s
|
||||||
|
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
|
||||||
|
return s.decode(fs_encoding)
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_config_path(*args):
|
||||||
|
if sys.platform == "win32":
|
||||||
|
return win32_get_app_data_path(*args)
|
||||||
|
|
||||||
|
config_home = os.environ.get(
|
||||||
|
'XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config'))
|
||||||
|
|
||||||
|
return os.path.join(fs2unicode(config_home), *args)
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_data_path(*args):
|
||||||
|
if sys.platform == "win32":
|
||||||
|
return win32_get_app_data_path(*args)
|
||||||
|
|
||||||
|
data_home = os.environ.get(
|
||||||
|
'XDG_DATA_HOME', os.path.join(
|
||||||
|
os.path.expanduser('~'), '.local', 'share'))
|
||||||
|
|
||||||
|
return os.path.join(fs2unicode(data_home), *args)
|
||||||
|
|
||||||
|
|
||||||
|
def get_portable_path(*args):
|
||||||
# NOTE: sys.executable will be the path to openslides.exe
|
# NOTE: sys.executable will be the path to openslides.exe
|
||||||
# since it is essentially a small wrapper that embeds the
|
# since it is essentially a small wrapper that embeds the
|
||||||
# python interpreter
|
# python interpreter
|
||||||
portable_dir = os.path.dirname(os.path.abspath(sys.executable))
|
|
||||||
try:
|
|
||||||
fd, test_file = tempfile.mkstemp(dir=portable_dir)
|
|
||||||
except OSError:
|
|
||||||
portable_dir_writeable = False
|
|
||||||
else:
|
|
||||||
portable_dir_writeable = True
|
|
||||||
os.close(fd)
|
|
||||||
os.unlink(test_file)
|
|
||||||
|
|
||||||
if portable_dir_writeable:
|
exename = os.path.basename(sys.executable).lower()
|
||||||
default_settings = os.path.join(portable_dir, "openslides",
|
if exename != "openslides.exe":
|
||||||
"openslides_personal_settings.py")
|
raise Exception(
|
||||||
database_path = os.path.join(portable_dir, "openslides",
|
"Cannot determine portable path when "
|
||||||
"database.sqlite")
|
"not running as portable")
|
||||||
else:
|
|
||||||
import ctypes
|
|
||||||
|
|
||||||
shell32 = ctypes.WinDLL("shell32.dll")
|
portable_dir = fs2unicode(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
SHGetFolderPath = shell32.SHGetFolderPathW
|
return os.path.join(portable_dir, *args)
|
||||||
SHGetFolderPath.argtypes = (ctypes.c_void_p, ctypes.c_int,
|
|
||||||
ctypes.c_void_p, ctypes.c_uint32, ctypes.c_wchar_p)
|
|
||||||
SHGetFolderPath.restype = ctypes.c_uint32
|
|
||||||
|
|
||||||
CSIDL_LOCAL_APPDATA = 0x001c
|
|
||||||
MAX_PATH = 260
|
|
||||||
|
|
||||||
buf = ctypes.create_unicode_buffer(MAX_PATH)
|
def get_portable_db_path():
|
||||||
res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf)
|
return get_portable_path('openslides', 'database.sqlite')
|
||||||
if res != 0:
|
|
||||||
raise Exception("Could not deterime APPDATA path")
|
|
||||||
default_settings = os.path.join(buf.value, "openslides",
|
|
||||||
"openslides_personal_settings.py")
|
|
||||||
database_path = os.path.join(buf.value, "openslides",
|
|
||||||
"database.sqlite")
|
|
||||||
|
|
||||||
main(argv, opt_defaults={ "settings": default_settings },
|
|
||||||
database_path=database_path)
|
def win32_get_app_data_path(*args):
|
||||||
|
shell32 = ctypes.WinDLL("shell32.dll")
|
||||||
|
SHGetFolderPath = shell32.SHGetFolderPathW
|
||||||
|
SHGetFolderPath.argtypes = (
|
||||||
|
ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_uint32,
|
||||||
|
ctypes.c_wchar_p)
|
||||||
|
SHGetFolderPath.restype = ctypes.c_uint32
|
||||||
|
|
||||||
|
CSIDL_LOCAL_APPDATA = 0x001c
|
||||||
|
MAX_PATH = 260
|
||||||
|
|
||||||
|
buf = ctypes.create_unicode_buffer(MAX_PATH)
|
||||||
|
res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf)
|
||||||
|
if res != 0:
|
||||||
|
raise Exception("Could not deterime APPDATA path")
|
||||||
|
|
||||||
|
return os.path.join(buf.value, *args)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
from openslides.utils.person import PersonFormField, MultiplePersonFormField
|
from openslides.utils.person import PersonFormField, MultiplePersonFormField
|
||||||
@ -21,18 +21,18 @@ from openslides.motion.models import Motion
|
|||||||
class MotionForm(forms.Form, CssClassMixin):
|
class MotionForm(forms.Form, CssClassMixin):
|
||||||
title = forms.CharField(widget=forms.TextInput(), label=_("Title"))
|
title = forms.CharField(widget=forms.TextInput(), label=_("Title"))
|
||||||
text = forms.CharField(widget=forms.Textarea(), label=_("Text"))
|
text = forms.CharField(widget=forms.Textarea(), label=_("Text"))
|
||||||
reason = forms.CharField(widget=forms.Textarea(), required=False,
|
reason = forms.CharField(
|
||||||
label=_("Reason"))
|
widget=forms.Textarea(), required=False, label=_("Reason"))
|
||||||
|
|
||||||
|
|
||||||
class MotionFormTrivialChanges(MotionForm):
|
class MotionFormTrivialChanges(MotionForm):
|
||||||
trivial_change = forms.BooleanField(required=False,
|
trivial_change = forms.BooleanField(
|
||||||
label=_("Trivial change"),
|
required=False, label=_("Trivial change"),
|
||||||
help_text=_("Trivial changes don't create a new version."))
|
help_text=_("Trivial changes don't create a new version."))
|
||||||
|
|
||||||
|
|
||||||
class MotionManagerForm(forms.ModelForm, CssClassMixin):
|
class MotionManagerForm(forms.ModelForm, CssClassMixin):
|
||||||
submitter = PersonFormField(label = _("Submitter"))
|
submitter = PersonFormField(label=_("Submitter"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Motion
|
model = Motion
|
||||||
@ -46,20 +46,20 @@ class MotionManagerFormSupporter(MotionManagerForm):
|
|||||||
|
|
||||||
class MotionImportForm(forms.Form, CssClassMixin):
|
class MotionImportForm(forms.Form, CssClassMixin):
|
||||||
csvfile = forms.FileField(
|
csvfile = forms.FileField(
|
||||||
widget=forms.FileInput(attrs={'size':'50'}),
|
widget=forms.FileInput(attrs={'size': '50'}),
|
||||||
label=_("CSV File"),
|
label=_("CSV File"),
|
||||||
)
|
)
|
||||||
import_permitted = forms.BooleanField(
|
import_permitted = forms.BooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
label=_("Import motions with status \"authorized\""),
|
label=_("Import motions with status \"authorized\""),
|
||||||
help_text=_('Set the initial status for each motion to '
|
help_text=_('Set the initial status for each motion to '
|
||||||
'"authorized"'),
|
'"authorized"'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConfigForm(forms.Form, CssClassMixin):
|
class ConfigForm(forms.Form, CssClassMixin):
|
||||||
motion_min_supporters = forms.IntegerField(
|
motion_min_supporters = forms.IntegerField(
|
||||||
widget=forms.TextInput(attrs={'class':'small-input'}),
|
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||||
label=_("Number of (minimum) required supporters for a motion"),
|
label=_("Number of (minimum) required supporters for a motion"),
|
||||||
initial=4,
|
initial=4,
|
||||||
min_value=0,
|
min_value=0,
|
||||||
@ -82,7 +82,7 @@ class ConfigForm(forms.Form, CssClassMixin):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
motion_pdf_ballot_papers_number = forms.IntegerField(
|
motion_pdf_ballot_papers_number = forms.IntegerField(
|
||||||
widget=forms.TextInput(attrs={'class':'small-input'}),
|
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||||
required=False,
|
required=False,
|
||||||
min_value=1,
|
min_value=1,
|
||||||
label=_("Custom number of ballot papers")
|
label=_("Custom number of ballot papers")
|
||||||
@ -101,6 +101,6 @@ class ConfigForm(forms.Form, CssClassMixin):
|
|||||||
motion_allow_trivial_change = forms.BooleanField(
|
motion_allow_trivial_change = forms.BooleanField(
|
||||||
label=_("Allow trivial changes"),
|
label=_("Allow trivial changes"),
|
||||||
help_text=_('Warning: Trivial changes undermine the motions '
|
help_text=_('Warning: Trivial changes undermine the motions '
|
||||||
'autorisation system.'),
|
'autorisation system.'),
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
@ -21,18 +21,13 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop, ugettext
|
|||||||
|
|
||||||
from openslides.utils.utils import _propper_unicode
|
from openslides.utils.utils import _propper_unicode
|
||||||
from openslides.utils.person import PersonField
|
from openslides.utils.person import PersonField
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
from openslides.config.signals import default_config_value
|
from openslides.config.signals import default_config_value
|
||||||
|
from openslides.poll.models import (
|
||||||
from openslides.poll.models import (BaseOption, BasePoll, CountVotesCast,
|
BaseOption, BasePoll, CountVotesCast, CountInvalid, BaseVote)
|
||||||
CountInvalid, BaseVote)
|
from openslides.participant.models import User
|
||||||
|
|
||||||
from openslides.participant.models import User, Group
|
|
||||||
|
|
||||||
from openslides.projector.api import register_slidemodel
|
from openslides.projector.api import register_slidemodel
|
||||||
from openslides.projector.models import SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
|
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
|
||||||
|
|
||||||
@ -53,7 +48,7 @@ class Motion(models.Model, SlideMixin):
|
|||||||
('noc', _('Not Concerned')),
|
('noc', _('Not Concerned')),
|
||||||
('com', _('Commited a bill')),
|
('com', _('Commited a bill')),
|
||||||
('nop', _('Rejected (not authorized)')),
|
('nop', _('Rejected (not authorized)')),
|
||||||
('rev', _('Needs Review')), # Where is this status used?
|
('rev', _('Needs Review')), # Where is this status used?
|
||||||
#additional actions:
|
#additional actions:
|
||||||
# edit
|
# edit
|
||||||
# delete
|
# delete
|
||||||
@ -67,9 +62,9 @@ class Motion(models.Model, SlideMixin):
|
|||||||
|
|
||||||
submitter = PersonField(verbose_name=_("Submitter"))
|
submitter = PersonField(verbose_name=_("Submitter"))
|
||||||
number = models.PositiveSmallIntegerField(blank=True, null=True,
|
number = models.PositiveSmallIntegerField(blank=True, null=True,
|
||||||
unique=True)
|
unique=True)
|
||||||
status = models.CharField(max_length=3, choices=STATUS, default='pub')
|
status = models.CharField(max_length=3, choices=STATUS, default='pub')
|
||||||
permitted = models.ForeignKey('AVersion', related_name='permitted', \
|
permitted = models.ForeignKey('AVersion', related_name='permitted',
|
||||||
null=True, blank=True)
|
null=True, blank=True)
|
||||||
log = models.TextField(blank=True, null=True)
|
log = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
@ -94,7 +89,7 @@ class Motion(models.Model, SlideMixin):
|
|||||||
else:
|
else:
|
||||||
return self.last_version
|
return self.last_version
|
||||||
|
|
||||||
def accept_version(self, version, user = None):
|
def accept_version(self, version, user=None):
|
||||||
"""
|
"""
|
||||||
accept a Version
|
accept a Version
|
||||||
"""
|
"""
|
||||||
@ -102,15 +97,15 @@ class Motion(models.Model, SlideMixin):
|
|||||||
self.save(nonewversion=True)
|
self.save(nonewversion=True)
|
||||||
version.rejected = False
|
version.rejected = False
|
||||||
version.save()
|
version.save()
|
||||||
self.writelog(_("Version %d authorized") % (version.aid, ),
|
self.writelog(_("Version %d authorized") % version.aid, user)
|
||||||
user)
|
|
||||||
|
|
||||||
def reject_version(self, version, user = None):
|
def reject_version(self, version, user=None):
|
||||||
if version.id > self.permitted.id:
|
if version.id > self.permitted.id:
|
||||||
version.rejected = True
|
version.rejected = True
|
||||||
version.save()
|
version.save()
|
||||||
self.writelog(pgettext("Rejected means not authorized", "Version %d rejected")
|
self.writelog(pgettext(
|
||||||
% (version.aid, ), user)
|
"Rejected means not authorized", "Version %d rejected")
|
||||||
|
% version.aid, user)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -154,8 +149,8 @@ class Motion(models.Model, SlideMixin):
|
|||||||
is not the lastone and the lastone is not rejected.
|
is not the lastone and the lastone is not rejected.
|
||||||
TODO: rename the property in unchecked__changes
|
TODO: rename the property in unchecked__changes
|
||||||
"""
|
"""
|
||||||
if (self.last_version != self.permitted
|
if (self.last_version != self.permitted and
|
||||||
and not self.last_version.rejected):
|
not self.last_version.rejected):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@ -207,7 +202,8 @@ class Motion(models.Model, SlideMixin):
|
|||||||
last_version = self.last_version
|
last_version = self.last_version
|
||||||
fields = ["text", "title", "reason"]
|
fields = ["text", "title", "reason"]
|
||||||
if last_version is not None:
|
if last_version is not None:
|
||||||
changed_fields = [f for f in fields
|
changed_fields = [
|
||||||
|
f for f in fields
|
||||||
if getattr(last_version, f) != getattr(self, f)]
|
if getattr(last_version, f) != getattr(self, f)]
|
||||||
if not changed_fields:
|
if not changed_fields:
|
||||||
return # No changes
|
return # No changes
|
||||||
@ -219,19 +215,22 @@ class Motion(models.Model, SlideMixin):
|
|||||||
last_version.save()
|
last_version.save()
|
||||||
|
|
||||||
meta = AVersion._meta
|
meta = AVersion._meta
|
||||||
field_names = [unicode(meta.get_field(f).verbose_name)
|
field_names = [
|
||||||
|
unicode(meta.get_field(f).verbose_name)
|
||||||
for f in changed_fields]
|
for f in changed_fields]
|
||||||
|
|
||||||
self.writelog(_("Trivial changes to version %(version)d; "
|
self.writelog(
|
||||||
"changed fields: %(changed_fields)s")
|
_("Trivial changes to version %(version)d; "
|
||||||
% dict(version = last_version.aid,
|
"changed fields: %(changed_fields)s")
|
||||||
changed_fields = ", ".join(field_names)))
|
% dict(version=last_version.aid,
|
||||||
return # Done
|
changed_fields=", ".join(field_names)))
|
||||||
|
return # Done
|
||||||
|
|
||||||
version = AVersion(title=getattr(self, 'title', ''),
|
version = AVersion(
|
||||||
text=getattr(self, 'text', ''),
|
title=getattr(self, 'title', ''),
|
||||||
reason=getattr(self, 'reason', ''),
|
text=getattr(self, 'text', ''),
|
||||||
motion=self)
|
reason=getattr(self, 'reason', ''),
|
||||||
|
motion=self)
|
||||||
version.save()
|
version.save()
|
||||||
self.writelog(_("Version %s created") % version.aid, user)
|
self.writelog(_("Version %s created") % version.aid, user)
|
||||||
is_manager = user.has_perm('motion.can_manage_motion')
|
is_manager = user.has_perm('motion.can_manage_motion')
|
||||||
@ -239,9 +238,8 @@ class Motion(models.Model, SlideMixin):
|
|||||||
is_manager = False
|
is_manager = False
|
||||||
|
|
||||||
supporters = self.motionsupporter_set.all()
|
supporters = self.motionsupporter_set.all()
|
||||||
if (self.status == "pub"
|
if (self.status == "pub" and
|
||||||
and supporters
|
supporters and not is_manager):
|
||||||
and not is_manager):
|
|
||||||
supporters.delete()
|
supporters.delete()
|
||||||
self.writelog(_("Supporters removed"), user)
|
self.writelog(_("Supporters removed"), user)
|
||||||
|
|
||||||
@ -272,7 +270,7 @@ class Motion(models.Model, SlideMixin):
|
|||||||
remove a supporter from the list of supporters of the motion
|
remove a supporter from the list of supporters of the motion
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
object = self.motionsupporter_set.get(person=person).delete()
|
self.motionsupporter_set.get(person=person).delete()
|
||||||
except MotionSupporter.DoesNotExist:
|
except MotionSupporter.DoesNotExist:
|
||||||
# TODO: Don't do nothing but raise a precise exception for the view
|
# TODO: Don't do nothing but raise a precise exception for the view
|
||||||
pass
|
pass
|
||||||
@ -288,8 +286,7 @@ class Motion(models.Model, SlideMixin):
|
|||||||
raise NameError('This motion has already a number.')
|
raise NameError('This motion has already a number.')
|
||||||
if number is None:
|
if number is None:
|
||||||
try:
|
try:
|
||||||
number = Motion.objects.aggregate(Max('number')) \
|
number = Motion.objects.aggregate(Max('number'))['number__max'] + 1
|
||||||
['number__max'] + 1
|
|
||||||
except TypeError:
|
except TypeError:
|
||||||
number = 1
|
number = 1
|
||||||
self.number = number
|
self.number = number
|
||||||
@ -316,8 +313,6 @@ class Motion(models.Model, SlideMixin):
|
|||||||
"""
|
"""
|
||||||
self.set_status(user, "nop")
|
self.set_status(user, "nop")
|
||||||
#TODO: reject last version
|
#TODO: reject last version
|
||||||
aversion = self.last_version
|
|
||||||
#self.permitted = aversion
|
|
||||||
if self.number is None:
|
if self.number is None:
|
||||||
self.set_number()
|
self.set_number()
|
||||||
self.save()
|
self.save()
|
||||||
@ -333,11 +328,11 @@ class Motion(models.Model, SlideMixin):
|
|||||||
error = False
|
error = False
|
||||||
break
|
break
|
||||||
if error:
|
if error:
|
||||||
#TODO: Use the Right Error
|
# TODO: Use the Right Error
|
||||||
raise NameError(_('%s is not a valid status.') % status)
|
raise NameError(_('%s is not a valid status.') % status)
|
||||||
if self.status == status:
|
if self.status == status:
|
||||||
#TODO: Use the Right Error
|
# TODO: Use the Right Error
|
||||||
raise NameError(_('The motion status is already \'%s.\'') \
|
raise NameError(_('The motion status is already \'%s.\'')
|
||||||
% self.status)
|
% self.status)
|
||||||
|
|
||||||
actions = []
|
actions = []
|
||||||
@ -353,7 +348,7 @@ class Motion(models.Model, SlideMixin):
|
|||||||
oldstatus = self.get_status_display()
|
oldstatus = self.get_status_display()
|
||||||
self.status = status
|
self.status = status
|
||||||
self.save()
|
self.save()
|
||||||
self.writelog(_("Status modified")+": %s -> %s" \
|
self.writelog(_("Status modified") + ": %s -> %s"
|
||||||
% (oldstatus, self.get_status_display()), user)
|
% (oldstatus, self.get_status_display()), user)
|
||||||
|
|
||||||
def get_allowed_actions(self, user):
|
def get_allowed_actions(self, user):
|
||||||
@ -432,10 +427,9 @@ class Motion(models.Model, SlideMixin):
|
|||||||
allready a number
|
allready a number
|
||||||
"""
|
"""
|
||||||
if self.number and not force:
|
if self.number and not force:
|
||||||
raise NameError('The motion has already a number. ' \
|
raise NameError('The motion has already a number. '
|
||||||
'You can not delete it.')
|
'You can not delete it.')
|
||||||
|
|
||||||
|
|
||||||
for item in Item.objects.filter(related_sid=self.sid):
|
for item in Item.objects.filter(related_sid=self.sid):
|
||||||
item.delete()
|
item.delete()
|
||||||
super(Motion, self).delete()
|
super(Motion, self).delete()
|
||||||
@ -501,12 +495,12 @@ class Motion(models.Model, SlideMixin):
|
|||||||
for poll in self.polls:
|
for poll in self.polls:
|
||||||
for option in poll.get_options():
|
for option in poll.get_options():
|
||||||
if option.get_votes().exists():
|
if option.get_votes().exists():
|
||||||
results.append((option['Yes'], option['No'],
|
results.append((
|
||||||
|
option['Yes'], option['No'],
|
||||||
option['Abstain'], poll.print_votesinvalid(),
|
option['Abstain'], poll.print_votesinvalid(),
|
||||||
poll.print_votescast()))
|
poll.print_votescast()))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def slide(self):
|
def slide(self):
|
||||||
"""
|
"""
|
||||||
return the slide dict
|
return the slide dict
|
||||||
@ -542,10 +536,10 @@ class Motion(models.Model, SlideMixin):
|
|||||||
|
|
||||||
|
|
||||||
class AVersion(models.Model):
|
class AVersion(models.Model):
|
||||||
title = models.CharField(max_length=100, verbose_name = _("Title"))
|
title = models.CharField(max_length=100, verbose_name=_("Title"))
|
||||||
text = models.TextField(verbose_name = _("Text"))
|
text = models.TextField(verbose_name=_("Text"))
|
||||||
reason = models.TextField(null=True, blank=True, verbose_name = _("Reason"))
|
reason = models.TextField(null=True, blank=True, verbose_name=_("Reason"))
|
||||||
rejected = models.BooleanField() # = Not Permitted
|
rejected = models.BooleanField() # = Not Permitted
|
||||||
time = models.DateTimeField(auto_now=True)
|
time = models.DateTimeField(auto_now=True)
|
||||||
motion = models.ForeignKey(Motion)
|
motion = models.ForeignKey(Motion)
|
||||||
|
|
||||||
@ -576,8 +570,8 @@ class MotionOption(BaseOption):
|
|||||||
|
|
||||||
class MotionPoll(BasePoll, CountInvalid, CountVotesCast):
|
class MotionPoll(BasePoll, CountInvalid, CountVotesCast):
|
||||||
option_class = MotionOption
|
option_class = MotionOption
|
||||||
vote_values = [ugettext_noop('Yes'), ugettext_noop('No'),
|
vote_values = [
|
||||||
ugettext_noop('Abstain')]
|
ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
|
||||||
|
|
||||||
motion = models.ForeignKey(Motion)
|
motion = models.ForeignKey(Motion)
|
||||||
|
|
||||||
|
@ -18,19 +18,17 @@ import os
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from urlparse import parse_qs
|
from urlparse import parse_qs
|
||||||
except ImportError: # python <= 2.5
|
except ImportError: # python <= 2.5
|
||||||
from cgi import parse_qs
|
from cgi import parse_qs
|
||||||
|
|
||||||
from reportlab.lib import colors
|
from reportlab.lib import colors
|
||||||
from reportlab.lib.units import cm
|
from reportlab.lib.units import cm
|
||||||
from reportlab.platypus import (SimpleDocTemplate, PageBreak, Paragraph, Spacer,
|
from reportlab.platypus import (
|
||||||
Table, TableStyle)
|
SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle)
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.core.context_processors import csrf
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
@ -39,26 +37,21 @@ from django.utils.translation import ugettext as _, ungettext
|
|||||||
from openslides.utils import csv_ext
|
from openslides.utils import csv_ext
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
from openslides.utils.template import Tab
|
from openslides.utils.template import Tab
|
||||||
from openslides.utils.utils import (template, permission_required,
|
from openslides.utils.utils import (
|
||||||
del_confirm_form, gen_confirm_form)
|
template, permission_required, del_confirm_form, gen_confirm_form)
|
||||||
from openslides.utils.views import (PDFView, RedirectView, DeleteView,
|
from openslides.utils.views import (
|
||||||
FormView, SingleObjectMixin, QuestionMixin)
|
PDFView, RedirectView, DeleteView, FormView, SingleObjectMixin,
|
||||||
|
QuestionMixin)
|
||||||
from openslides.utils.person import get_person
|
from openslides.utils.person import get_person
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
|
|
||||||
from openslides.projector.projector import Widget
|
from openslides.projector.projector import Widget
|
||||||
|
|
||||||
from openslides.poll.views import PollFormView
|
from openslides.poll.views import PollFormView
|
||||||
|
|
||||||
from openslides.participant.api import gen_username, gen_password
|
from openslides.participant.api import gen_username, gen_password
|
||||||
from openslides.participant.models import User, Group
|
from openslides.participant.models import User, Group
|
||||||
|
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
|
||||||
from openslides.motion.models import Motion, AVersion, MotionPoll
|
from openslides.motion.models import Motion, AVersion, MotionPoll
|
||||||
from openslides.motion.forms import (MotionForm,
|
from openslides.motion.forms import (
|
||||||
MotionFormTrivialChanges, MotionManagerForm,
|
MotionForm, MotionFormTrivialChanges, MotionManagerForm,
|
||||||
MotionManagerFormSupporter, MotionImportForm, ConfigForm)
|
MotionManagerFormSupporter, MotionImportForm, ConfigForm)
|
||||||
|
|
||||||
|
|
||||||
@ -124,14 +117,14 @@ def overview(request):
|
|||||||
for (i, motion) in enumerate(motions):
|
for (i, motion) in enumerate(motions):
|
||||||
try:
|
try:
|
||||||
motions[i] = {
|
motions[i] = {
|
||||||
'actions' : motion.get_allowed_actions(request.user),
|
'actions': motion.get_allowed_actions(request.user),
|
||||||
'motion' : motion
|
'motion': motion
|
||||||
}
|
}
|
||||||
except:
|
except:
|
||||||
# todo: except what?
|
# todo: except what?
|
||||||
motions[i] = {
|
motions[i] = {
|
||||||
'actions' : [],
|
'actions': [],
|
||||||
'motion' : motion
|
'motion': motion
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -210,12 +203,7 @@ def edit(request, motion_id=None):
|
|||||||
managerform = None
|
managerform = None
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
del_supporters = True
|
|
||||||
if is_manager:
|
if is_manager:
|
||||||
if motion: # Edit motion
|
|
||||||
original_supporters = list(motion.supporters)
|
|
||||||
else:
|
|
||||||
original_supporters = []
|
|
||||||
motion = managerform.save(commit=False)
|
motion = managerform.save(commit=False)
|
||||||
elif motion_id is None:
|
elif motion_id is None:
|
||||||
motion = Motion(submitter=request.user)
|
motion = Motion(submitter=request.user)
|
||||||
@ -694,7 +682,7 @@ def motion_import(request):
|
|||||||
return redirect(reverse('motion_overview'))
|
return redirect(reverse('motion_overview'))
|
||||||
|
|
||||||
except csv.Error:
|
except csv.Error:
|
||||||
message.error(request, _('Import aborted because of severe errors in the input file.'))
|
messages.error(request, _('Import aborted because of severe errors in the input file.'))
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
messages.error(request, _('Import file has wrong character encoding, only UTF-8 is supported!'))
|
messages.error(request, _('Import file has wrong character encoding, only UTF-8 is supported!'))
|
||||||
else:
|
else:
|
||||||
|
@ -14,15 +14,20 @@
|
|||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
from random import choice
|
from random import choice
|
||||||
import string
|
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import Permission
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides.utils import csv_ext
|
from openslides.utils import csv_ext
|
||||||
|
|
||||||
from openslides.participant.models import User
|
from openslides.participant.models import User, Group
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_PERMS = ['can_see_agenda', 'can_see_projector',
|
||||||
|
'can_see_motion', 'can_see_assignment',
|
||||||
|
'can_see_dashboard']
|
||||||
|
|
||||||
|
|
||||||
def gen_password():
|
def gen_password():
|
||||||
@ -73,7 +78,7 @@ def import_users(csv_file):
|
|||||||
try:
|
try:
|
||||||
(first_name, last_name, gender, structure_level, type, committee, comment) = line[:7]
|
(first_name, last_name, gender, structure_level, type, committee, comment) = line[:7]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
error_messages.append(_('Ignoring malformed line %d in import file.') % line_no + 1)
|
error_messages.append(_('Ignoring malformed line %d in import file.') % (line_no + 1))
|
||||||
continue
|
continue
|
||||||
user = User()
|
user = User()
|
||||||
user.last_name = last_name
|
user.last_name = last_name
|
||||||
@ -93,3 +98,23 @@ def import_users(csv_file):
|
|||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
error_messages.appen(_('Import file has wrong character encoding, only UTF-8 is supported!'))
|
error_messages.appen(_('Import file has wrong character encoding, only UTF-8 is supported!'))
|
||||||
return (count_success, error_messages)
|
return (count_success, error_messages)
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_registered_group():
|
||||||
|
registered, created = Group.objects.get_or_create(
|
||||||
|
name__iexact='Registered', defaults={'name': 'Registered'})
|
||||||
|
if created:
|
||||||
|
registered.permissions = Permission.objects.filter(
|
||||||
|
codename__in=DEFAULT_PERMS)
|
||||||
|
registered.save()
|
||||||
|
return registered
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_anonymous_group():
|
||||||
|
anonymous, created = Group.objects.get_or_create(
|
||||||
|
name__iexact='Anonymous', defaults={'name': 'Anonymous'})
|
||||||
|
if created:
|
||||||
|
anonymous.permissions = Permission.objects.filter(
|
||||||
|
codename__in=DEFAULT_PERMS)
|
||||||
|
anonymous.save()
|
||||||
|
return anonymous
|
||||||
|
@ -18,6 +18,7 @@ from openslides.utils.forms import (
|
|||||||
CssClassMixin, LocalizedModelMultipleChoiceField)
|
CssClassMixin, LocalizedModelMultipleChoiceField)
|
||||||
|
|
||||||
from openslides.participant.models import User, Group
|
from openslides.participant.models import User, Group
|
||||||
|
from openslides.participant.api import get_or_create_registered_group
|
||||||
|
|
||||||
|
|
||||||
class UserCreateForm(forms.ModelForm, CssClassMixin):
|
class UserCreateForm(forms.ModelForm, CssClassMixin):
|
||||||
@ -25,6 +26,13 @@ class UserCreateForm(forms.ModelForm, CssClassMixin):
|
|||||||
queryset=Group.objects.exclude(name__iexact='anonymous'),
|
queryset=Group.objects.exclude(name__iexact='anonymous'),
|
||||||
label=_("Groups"), required=False)
|
label=_("Groups"), required=False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if kwargs.get('instance', None) is None:
|
||||||
|
initial = kwargs.setdefault('initial', {})
|
||||||
|
registered = get_or_create_registered_group()
|
||||||
|
initial['groups'] = [registered.pk]
|
||||||
|
super(UserCreateForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ('first_name', 'last_name', 'is_active', 'groups', 'structure_level',
|
fields = ('first_name', 'last_name', 'is_active', 'groups', 'structure_level',
|
||||||
@ -58,12 +66,13 @@ class GroupForm(forms.ModelForm, CssClassMixin):
|
|||||||
instance = forms.ModelForm.save(self, False)
|
instance = forms.ModelForm.save(self, False)
|
||||||
|
|
||||||
old_save_m2m = self.save_m2m
|
old_save_m2m = self.save_m2m
|
||||||
def save_m2m():
|
|
||||||
old_save_m2m()
|
|
||||||
|
|
||||||
instance.user_set.clear()
|
def save_m2m():
|
||||||
for user in self.cleaned_data['users']:
|
old_save_m2m()
|
||||||
instance.user_set.add(user)
|
|
||||||
|
instance.user_set.clear()
|
||||||
|
for user in self.cleaned_data['users']:
|
||||||
|
instance.user_set.add(user)
|
||||||
self.save_m2m = save_m2m
|
self.save_m2m = save_m2m
|
||||||
|
|
||||||
if commit:
|
if commit:
|
||||||
@ -76,13 +85,13 @@ class GroupForm(forms.ModelForm, CssClassMixin):
|
|||||||
# Do not allow to change the name "anonymous" or give another group
|
# Do not allow to change the name "anonymous" or give another group
|
||||||
# this name
|
# this name
|
||||||
data = self.cleaned_data['name']
|
data = self.cleaned_data['name']
|
||||||
if self.instance.name.lower() == 'anonymous':
|
if self.instance.name.lower() in ['anonymous', 'registered']:
|
||||||
# Editing the anonymous-user
|
# Editing the anonymous-user
|
||||||
if self.instance.name.lower() != data.lower():
|
if self.instance.name.lower() != data.lower():
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
_('You can not edit the name for the anonymous user'))
|
_('You can not edit the name for this group.'))
|
||||||
else:
|
else:
|
||||||
if data.lower() == 'anonymous':
|
if data.lower() in ['anonymous', 'registered']:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
_('Group name "%s" is reserved for internal use.') % data)
|
_('Group name "%s" is reserved for internal use.') % data)
|
||||||
return data
|
return data
|
||||||
@ -94,7 +103,7 @@ class GroupForm(forms.ModelForm, CssClassMixin):
|
|||||||
class UsersettingsForm(forms.ModelForm, CssClassMixin):
|
class UsersettingsForm(forms.ModelForm, CssClassMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ('username', 'first_name', 'last_name', 'gender', 'email', 'committee', 'about_me' )
|
fields = ('username', 'first_name', 'last_name', 'gender', 'email', 'committee', 'about_me')
|
||||||
|
|
||||||
|
|
||||||
class UserImportForm(forms.Form, CssClassMixin):
|
class UserImportForm(forms.Form, CssClassMixin):
|
||||||
|
@ -25,8 +25,9 @@ from openslides.config.signals import default_config_value
|
|||||||
from openslides.projector.api import register_slidemodel
|
from openslides.projector.api import register_slidemodel
|
||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
|
|
||||||
|
|
||||||
class User(DjangoUser, PersonMixin, Person, SlideMixin):
|
class User(DjangoUser, PersonMixin, Person, SlideMixin):
|
||||||
prefix = 'user' # This is for the slides
|
prefix = 'user' # This is for the slides
|
||||||
person_prefix = 'user'
|
person_prefix = 'user'
|
||||||
GENDER_CHOICES = (
|
GENDER_CHOICES = (
|
||||||
('male', _('Male')),
|
('male', _('Male')),
|
||||||
@ -131,12 +132,15 @@ class User(DjangoUser, PersonMixin, Person, SlideMixin):
|
|||||||
|
|
||||||
register_slidemodel(User)
|
register_slidemodel(User)
|
||||||
|
|
||||||
|
|
||||||
class Group(DjangoGroup, PersonMixin, Person, SlideMixin):
|
class Group(DjangoGroup, PersonMixin, Person, SlideMixin):
|
||||||
prefix = 'group' # This is for the slides
|
prefix = 'group' # This is for the slides
|
||||||
person_prefix = 'group'
|
person_prefix = 'group'
|
||||||
|
|
||||||
django_group = models.OneToOneField(DjangoGroup, editable=False, parent_link=True)
|
django_group = models.OneToOneField(DjangoGroup, editable=False, parent_link=True)
|
||||||
group_as_person = models.BooleanField(default=False, verbose_name=_("Use this group as participant"), help_text=_('For example as submitter of a motion.'))
|
group_as_person = models.BooleanField(
|
||||||
|
default=False, verbose_name=_("Use this group as participant"),
|
||||||
|
help_text=_('For example as submitter of a motion.'))
|
||||||
description = models.TextField(blank=True, verbose_name=_("Description"))
|
description = models.TextField(blank=True, verbose_name=_("Description"))
|
||||||
|
|
||||||
@models.permalink
|
@models.permalink
|
||||||
@ -173,6 +177,7 @@ class Group(DjangoGroup, PersonMixin, Person, SlideMixin):
|
|||||||
|
|
||||||
register_slidemodel(Group)
|
register_slidemodel(Group)
|
||||||
|
|
||||||
|
|
||||||
class UsersAndGroupsToPersons(object):
|
class UsersAndGroupsToPersons(object):
|
||||||
"""
|
"""
|
||||||
Object to send all Users and Groups or a special User or Group to
|
Object to send all Users and Groups or a special User or Group to
|
||||||
@ -216,8 +221,9 @@ def receive_persons(sender, **kwargs):
|
|||||||
"""
|
"""
|
||||||
Answers to the Person-API
|
Answers to the Person-API
|
||||||
"""
|
"""
|
||||||
return UsersAndGroupsToPersons(person_prefix_filter=kwargs['person_prefix_filter'],
|
return UsersAndGroupsToPersons(
|
||||||
id_filter=kwargs['id_filter'])
|
person_prefix_filter=kwargs['person_prefix_filter'],
|
||||||
|
id_filter=kwargs['id_filter'])
|
||||||
|
|
||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="participant_default_config")
|
@receiver(default_config_value, dispatch_uid="participant_default_config")
|
||||||
@ -234,7 +240,7 @@ def default_config(sender, key, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
@receiver(signals.post_save, sender=DjangoUser)
|
@receiver(signals.post_save, sender=DjangoUser)
|
||||||
def user_post_save(sender, instance, signal, *args, **kwargs):
|
def djangouser_post_save(sender, instance, signal, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
instance.user
|
instance.user
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
@ -242,8 +248,18 @@ def user_post_save(sender, instance, signal, *args, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
@receiver(signals.post_save, sender=DjangoGroup)
|
@receiver(signals.post_save, sender=DjangoGroup)
|
||||||
def group_post_save(sender, instance, signal, *args, **kwargs):
|
def djangogroup_post_save(sender, instance, signal, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
instance.group
|
instance.group
|
||||||
except Group.DoesNotExist:
|
except Group.DoesNotExist:
|
||||||
Group(django_group=instance).save_base(raw=True)
|
Group(django_group=instance).save_base(raw=True)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(signals.post_save, sender=User)
|
||||||
|
def user_post_save(sender, instance, *args, **kwargs):
|
||||||
|
from openslides.participant.api import get_or_create_registered_group
|
||||||
|
if not kwargs['created']:
|
||||||
|
return
|
||||||
|
registered = get_or_create_registered_group()
|
||||||
|
instance.groups.add(registered)
|
||||||
|
instance.save()
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<tr class="{% cycle '' 'odd' %}">
|
<tr class="{% cycle '' 'odd' %}">
|
||||||
<td><a href="{% model_url group 'view' %}">{{ group.name }}</a></td>
|
<td><a href="{% model_url group 'view' %}">{{ group.name }}</a></td>
|
||||||
<td><a href="{% url user_group_edit group.id %}"><img src="{% static 'images/icons/edit.png' %}" title="{% trans 'Edit group' %}"></a>
|
<td><a href="{% url user_group_edit group.id %}"><img src="{% static 'images/icons/edit.png' %}" title="{% trans 'Edit group' %}"></a>
|
||||||
{% if group.name|lower != 'anonymous' %}
|
{% if group.name|lower != 'anonymous' and group.name|lower != 'registered' %}
|
||||||
<a href="{% url user_group_delete group.id %}"><img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Delete group' %}"></a>
|
<a href="{% url user_group_delete group.id %}"><img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Delete group' %}"></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf.urls.defaults import url, patterns
|
from django.conf.urls.defaults import url, patterns
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
|
|
||||||
from openslides.participant.views import (
|
from openslides.participant.views import (
|
||||||
UserOverview, UserCreateView, UserDetailView, UserUpdateView,
|
UserOverview, UserCreateView, UserDetailView, UserUpdateView,
|
||||||
|
@ -28,7 +28,6 @@ from reportlab.platypus import (
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.forms import PasswordChangeForm
|
from django.contrib.auth.forms import PasswordChangeForm
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib.auth.views import login as django_login
|
from django.contrib.auth.views import login as django_login
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
@ -39,16 +38,12 @@ from openslides.utils.template import Tab
|
|||||||
from openslides.utils.utils import (
|
from openslides.utils.utils import (
|
||||||
template, decodedict, encodedict, delete_default_permissions, html_strong)
|
template, decodedict, encodedict, delete_default_permissions, html_strong)
|
||||||
from openslides.utils.views import (
|
from openslides.utils.views import (
|
||||||
FormView, PDFView, CreateView, UpdateView, DeleteView,
|
FormView, PDFView, CreateView, UpdateView, DeleteView, PermissionMixin,
|
||||||
RedirectView, SingleObjectMixin, ListView, QuestionMixin)
|
RedirectView, SingleObjectMixin, ListView, QuestionMixin, DetailView)
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
|
|
||||||
from openslides.projector.projector import Widget
|
from openslides.projector.projector import Widget
|
||||||
|
|
||||||
from openslides.motion.models import Motion
|
from openslides.motion.models import Motion
|
||||||
from openslides.assignment.models import Assignment
|
from openslides.assignment.models import Assignment
|
||||||
|
|
||||||
from openslides.participant.api import gen_username, gen_password, import_users
|
from openslides.participant.api import gen_username, gen_password, import_users
|
||||||
from openslides.participant.forms import (
|
from openslides.participant.forms import (
|
||||||
UserCreateForm, UserUpdateForm, UsersettingsForm,
|
UserCreateForm, UserUpdateForm, UsersettingsForm,
|
||||||
@ -125,11 +120,13 @@ class UserOverview(ListView):
|
|||||||
percent = 0
|
percent = 0
|
||||||
|
|
||||||
# list of all existing categories
|
# list of all existing categories
|
||||||
structure_levels = [p['structure_level'] for p in User.objects.values('structure_level')
|
structure_levels = [
|
||||||
.exclude(structure_level='').distinct()]
|
p['structure_level'] for p in
|
||||||
|
User.objects.values('structure_level').exclude(structure_level='').distinct()]
|
||||||
# list of all existing committees
|
# list of all existing committees
|
||||||
committees = [p['committee'] for p in User.objects.values('committee')
|
committees = [
|
||||||
.exclude(committee='').distinct()]
|
p['committee'] for p in
|
||||||
|
User.objects.values('committee').exclude(committee='').distinct()]
|
||||||
# context vars
|
# context vars
|
||||||
context.update({
|
context.update({
|
||||||
'allusers': all_users,
|
'allusers': all_users,
|
||||||
@ -137,13 +134,13 @@ class UserOverview(ListView):
|
|||||||
'percent': round(percent, 1),
|
'percent': round(percent, 1),
|
||||||
'structure_levels': structure_levels,
|
'structure_levels': structure_levels,
|
||||||
'committees': committees,
|
'committees': committees,
|
||||||
'cookie': ['participant_sortfilter', urlencode(decodedict(self.sortfilter),
|
'cookie': [
|
||||||
|
'participant_sortfilter', urlencode(decodedict(self.sortfilter),
|
||||||
doseq=True)],
|
doseq=True)],
|
||||||
'sortfilter': self.sortfilter})
|
'sortfilter': self.sortfilter})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
from openslides.utils.views import DetailView, PermissionMixin
|
|
||||||
class UserDetailView(DetailView, PermissionMixin):
|
class UserDetailView(DetailView, PermissionMixin):
|
||||||
"""
|
"""
|
||||||
Classed based view to show a specific user in the interface.
|
Classed based view to show a specific user in the interface.
|
||||||
@ -177,8 +174,8 @@ class UserCreateView(CreateView):
|
|||||||
apply_url = 'user_edit'
|
apply_url = 'user_edit'
|
||||||
|
|
||||||
def manipulate_object(self, form):
|
def manipulate_object(self, form):
|
||||||
self.object.username = gen_username(form.cleaned_data['first_name'],
|
self.object.username = gen_username(
|
||||||
form.cleaned_data['last_name'])
|
form.cleaned_data['first_name'], form.cleaned_data['last_name'])
|
||||||
if not self.object.default_password:
|
if not self.object.default_password:
|
||||||
self.object.default_password = gen_password()
|
self.object.default_password = gen_password()
|
||||||
self.object.set_password(self.object.default_password)
|
self.object.set_password(self.object.default_password)
|
||||||
@ -211,6 +208,7 @@ class UserDeleteView(DeleteView):
|
|||||||
else:
|
else:
|
||||||
super(UserDeleteView, self).pre_redirect(request, *args, **kwargs)
|
super(UserDeleteView, self).pre_redirect(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SetUserStatusView(RedirectView, SingleObjectMixin):
|
class SetUserStatusView(RedirectView, SingleObjectMixin):
|
||||||
"""
|
"""
|
||||||
Activate or deactivate an user.
|
Activate or deactivate an user.
|
||||||
@ -547,8 +545,9 @@ def register_tab(request):
|
|||||||
return Tab(
|
return Tab(
|
||||||
title=_('Participants'),
|
title=_('Participants'),
|
||||||
url=reverse('user_overview'),
|
url=reverse('user_overview'),
|
||||||
permission=request.user.has_perm('participant.can_see_participant') or
|
permission=(
|
||||||
request.user.has_perm('participant.can_manage_participant'),
|
request.user.has_perm('participant.can_see_participant') or
|
||||||
|
request.user.has_perm('participant.can_manage_participant')),
|
||||||
selected=selected)
|
selected=selected)
|
||||||
|
|
||||||
|
|
||||||
@ -569,12 +568,12 @@ def get_personal_info_widget(request):
|
|||||||
and where you are supporter or candidate.
|
and where you are supporter or candidate.
|
||||||
"""
|
"""
|
||||||
personal_info_context = {
|
personal_info_context = {
|
||||||
'submitted_motions': Motion.objects.filter(submitter=request.user),
|
'submitted_motions': Motion.objects.filter(submitter=request.user),
|
||||||
'config_motion_min_supporters': config['motion_min_supporters'],
|
'config_motion_min_supporters': config['motion_min_supporters'],
|
||||||
'supported_motions': Motion.objects.filter(motionsupporter=request.user),
|
'supported_motions': Motion.objects.filter(motionsupporter=request.user),
|
||||||
'assignments': Assignment.objects.filter(
|
'assignments': Assignment.objects.filter(
|
||||||
assignmentcandidate__person=request.user,
|
assignmentcandidate__person=request.user,
|
||||||
assignmentcandidate__blocked=False),}
|
assignmentcandidate__blocked=False)}
|
||||||
return Widget(
|
return Widget(
|
||||||
name='personal_info',
|
name='personal_info',
|
||||||
display_name=_('My motions and elections'),
|
display_name=_('My motions and elections'),
|
||||||
@ -593,7 +592,7 @@ def get_user_widget(request):
|
|||||||
name='user',
|
name='user',
|
||||||
display_name=_('Participants'),
|
display_name=_('Participants'),
|
||||||
template='participant/user_widget.html',
|
template='participant/user_widget.html',
|
||||||
context={'users': User.objects.all(),},
|
context={'users': User.objects.all()},
|
||||||
permission_required='projector.can_manage_projector',
|
permission_required='projector.can_manage_projector',
|
||||||
default_column=1)
|
default_column=1)
|
||||||
|
|
||||||
@ -607,6 +606,6 @@ def get_group_widget(request):
|
|||||||
name='group',
|
name='group',
|
||||||
display_name=_('Groups'),
|
display_name=_('Groups'),
|
||||||
template='participant/group_widget.html',
|
template='participant/group_widget.html',
|
||||||
context={'groups': Group.objects.all(),},
|
context={'groups': Group.objects.all()},
|
||||||
permission_required='projector.can_manage_projector',
|
permission_required='projector.can_manage_projector',
|
||||||
default_column=1)
|
default_column=1)
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class BaseVote(models.Model):
|
|||||||
Subclasses have to define a option-field, which are a subclass of
|
Subclasses have to define a option-field, which are a subclass of
|
||||||
BaseOption.
|
BaseOption.
|
||||||
"""
|
"""
|
||||||
weight = models.IntegerField(default=1, null=True) # Use MinMaxIntegerField
|
weight = models.IntegerField(default=1, null=True) # Use MinMaxIntegerField
|
||||||
value = models.CharField(max_length=255, null=True)
|
value = models.CharField(max_length=255, null=True)
|
||||||
|
|
||||||
def print_weight(self, raw=False):
|
def print_weight(self, raw=False):
|
||||||
@ -73,7 +73,7 @@ class BaseVote(models.Model):
|
|||||||
|
|
||||||
class CountVotesCast(models.Model):
|
class CountVotesCast(models.Model):
|
||||||
votescast = MinMaxIntegerField(null=True, blank=True, min_value=-2,
|
votescast = MinMaxIntegerField(null=True, blank=True, min_value=-2,
|
||||||
verbose_name=_("Votes cast"))
|
verbose_name=_("Votes cast"))
|
||||||
|
|
||||||
def append_pollform_fields(self, fields):
|
def append_pollform_fields(self, fields):
|
||||||
fields.append('votescast')
|
fields.append('votescast')
|
||||||
@ -92,7 +92,7 @@ class CountVotesCast(models.Model):
|
|||||||
|
|
||||||
class CountInvalid(models.Model):
|
class CountInvalid(models.Model):
|
||||||
votesinvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
|
votesinvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
|
||||||
verbose_name=_("Votes invalid"))
|
verbose_name=_("Votes invalid"))
|
||||||
|
|
||||||
def append_pollform_fields(self, fields):
|
def append_pollform_fields(self, fields):
|
||||||
fields.append('votesinvalid')
|
fields.append('votesinvalid')
|
||||||
@ -164,7 +164,6 @@ class BasePoll(models.Model):
|
|||||||
"""
|
"""
|
||||||
return self.vote_values
|
return self.vote_values
|
||||||
|
|
||||||
|
|
||||||
def get_vote_class(self):
|
def get_vote_class(self):
|
||||||
"""
|
"""
|
||||||
Return the releatet vote class.
|
Return the releatet vote class.
|
||||||
@ -212,7 +211,7 @@ class BasePoll(models.Model):
|
|||||||
"""
|
"""
|
||||||
from openslides.poll.forms import OptionForm
|
from openslides.poll.forms import OptionForm
|
||||||
return OptionForm(extra=self.get_form_values(kwargs['formid']),
|
return OptionForm(extra=self.get_form_values(kwargs['formid']),
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
def get_vote_forms(self, **kwargs):
|
def get_vote_forms(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -35,7 +35,7 @@ class PollFormView(TemplateView):
|
|||||||
context['forms'] = self.poll.get_vote_forms()
|
context['forms'] = self.poll.get_vote_forms()
|
||||||
FormClass = self.get_modelform_class()
|
FormClass = self.get_modelform_class()
|
||||||
context['pollform'] = FormClass(instance=self.poll,
|
context['pollform'] = FormClass(instance=self.poll,
|
||||||
prefix='pollform')
|
prefix='pollform')
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
@ -52,7 +52,7 @@ class PollFormView(TemplateView):
|
|||||||
|
|
||||||
FormClass = self.get_modelform_class()
|
FormClass = self.get_modelform_class()
|
||||||
pollform = FormClass(data=self.request.POST, instance=self.poll,
|
pollform = FormClass(data=self.request.POST, instance=self.poll,
|
||||||
prefix='pollform')
|
prefix='pollform')
|
||||||
|
|
||||||
error = False
|
error = False
|
||||||
for form in option_forms:
|
for form in option_forms:
|
||||||
|
@ -12,12 +12,11 @@
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.template.loader import render_to_string
|
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
from openslides.projector.projector import SLIDE, Slide, Widget
|
from openslides.projector.projector import SLIDE, Slide
|
||||||
|
|
||||||
|
|
||||||
def split_sid(sid):
|
def split_sid(sid):
|
||||||
@ -95,27 +94,18 @@ def clear_projector_cache():
|
|||||||
cache.delete('projector_data')
|
cache.delete('projector_data')
|
||||||
|
|
||||||
|
|
||||||
def register_slidemodel(model, model_name=None, control_template=None,
|
def register_slidemodel(model, model_name=None, control_template=None, weight=0):
|
||||||
weight=0):
|
|
||||||
"""
|
"""
|
||||||
Register a Model as a slide.
|
Register a Model as a slide.
|
||||||
"""
|
"""
|
||||||
|
# TODO: control_template should never be None
|
||||||
if model_name is None:
|
if model_name is None:
|
||||||
model_name = model.prefix
|
model_name = model.prefix
|
||||||
|
|
||||||
if control_template is None:
|
|
||||||
control_template = 'projector/default_control_slidemodel.html'
|
|
||||||
|
|
||||||
category = model.__module__.split('.')[0]
|
category = model.__module__.split('.')[0]
|
||||||
SLIDE[model_name] = Slide(
|
SLIDE[model_name] = Slide(model_slide=True, model=model, category=category,
|
||||||
model_slide=True,
|
key=model.prefix, model_name=model_name,
|
||||||
model=model,
|
control_template=control_template, weight=weight)
|
||||||
category=category,
|
|
||||||
key=model.prefix,
|
|
||||||
model_name=model_name,
|
|
||||||
control_template=control_template,
|
|
||||||
weight=weight,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def register_slidefunc(key, func, control_template=None, weight=0, name=''):
|
def register_slidefunc(key, func, control_template=None, weight=0, name=''):
|
||||||
@ -125,15 +115,9 @@ def register_slidefunc(key, func, control_template=None, weight=0, name=''):
|
|||||||
if control_template is None:
|
if control_template is None:
|
||||||
control_template = 'projector/default_control_slidefunc.html'
|
control_template = 'projector/default_control_slidefunc.html'
|
||||||
category = func.__module__.split('.')[0]
|
category = func.__module__.split('.')[0]
|
||||||
SLIDE[key] = Slide(
|
SLIDE[key] = Slide(model_slide=False, func=func, category=category,
|
||||||
model_slide=False,
|
key=key, control_template=control_template, weight=weight,
|
||||||
func=func,
|
name=name,)
|
||||||
category=category,
|
|
||||||
key=key,
|
|
||||||
control_template=control_template,
|
|
||||||
weight=weight,
|
|
||||||
name=name,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def projector_message_set(message, sid=None):
|
def projector_message_set(message, sid=None):
|
||||||
@ -147,7 +131,7 @@ def projector_message_set(message, sid=None):
|
|||||||
overlay = ProjectorOverlay.objects.get(def_name='Message')
|
overlay = ProjectorOverlay.objects.get(def_name='Message')
|
||||||
except ProjectorOverlay.DoesNotExist:
|
except ProjectorOverlay.DoesNotExist:
|
||||||
overlay = ProjectorOverlay(def_name='Message', active=False)
|
overlay = ProjectorOverlay(def_name='Message', active=False)
|
||||||
overlay.sid=sid
|
overlay.sid = sid
|
||||||
overlay.save()
|
overlay.save()
|
||||||
|
|
||||||
|
|
||||||
@ -166,7 +150,6 @@ def get_all_widgets(request, session=False):
|
|||||||
mod = import_module(app + '.views')
|
mod = import_module(app + '.views')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
continue
|
continue
|
||||||
appname = mod.__name__.split('.')[0]
|
|
||||||
try:
|
try:
|
||||||
modul_widgets = mod.get_widgets(request)
|
modul_widgets = mod.get_widgets(request)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
|
|
||||||
|
@ -19,9 +19,6 @@ from openslides.config.signals import default_config_value
|
|||||||
from openslides.projector.api import register_slidemodel
|
from openslides.projector.api import register_slidemodel
|
||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
|
|
||||||
from openslides.config.models import config
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectorSlide(models.Model, SlideMixin):
|
class ProjectorSlide(models.Model, SlideMixin):
|
||||||
"""
|
"""
|
||||||
@ -56,8 +53,7 @@ class ProjectorSlide(models.Model, SlideMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
register_slidemodel(ProjectorSlide,
|
register_slidemodel(ProjectorSlide, control_template='projector/control_customslide.html')
|
||||||
control_template='projector/control_customslide.html')
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectorOverlay(models.Model):
|
class ProjectorOverlay(models.Model):
|
||||||
|
@ -19,9 +19,9 @@ from openslides.config.models import config
|
|||||||
|
|
||||||
from openslides.projector.signals import projector_overlays
|
from openslides.projector.signals import projector_overlays
|
||||||
|
|
||||||
|
|
||||||
SLIDE = {}
|
SLIDE = {}
|
||||||
|
|
||||||
|
|
||||||
class SlideMixin(object):
|
class SlideMixin(object):
|
||||||
"""
|
"""
|
||||||
A Mixin for a Django-Model, for making the model a slide.
|
A Mixin for a Django-Model, for making the model a slide.
|
||||||
@ -49,13 +49,16 @@ class SlideMixin(object):
|
|||||||
"""
|
"""
|
||||||
Return True, if the the slide is the active slide.
|
Return True, if the the slide is the active slide.
|
||||||
"""
|
"""
|
||||||
from api import get_active_slide
|
if self.id is None:
|
||||||
|
return False
|
||||||
|
from openslides.projector.api import get_active_slide
|
||||||
return get_active_slide(only_sid=True) == self.sid
|
return get_active_slide(only_sid=True) == self.sid
|
||||||
|
|
||||||
def set_active(self):
|
def set_active(self):
|
||||||
"""
|
"""
|
||||||
Appoint this item as the active slide.
|
Appoint this item as the active slide.
|
||||||
"""
|
"""
|
||||||
|
from openslides.projector.api import set_active_slide
|
||||||
set_active_slide(self.sid)
|
set_active_slide(self.sid)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
@ -112,7 +115,7 @@ class Widget(object):
|
|||||||
Class for a Widget for the Projector-Tab.
|
Class for a Widget for the Projector-Tab.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, html=None, template=None, context={},
|
def __init__(self, name, html=None, template=None, context={},
|
||||||
permission_required=None, display_name=None, default_column=1):
|
permission_required=None, display_name=None, default_column=1):
|
||||||
self.name = name
|
self.name = name
|
||||||
if display_name is None:
|
if display_name is None:
|
||||||
self.display_name = name.capitalize()
|
self.display_name = name.capitalize()
|
||||||
|
@ -13,34 +13,28 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.context_processors import csrf
|
from django.core.context_processors import csrf
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.dispatch import receiver
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.utils.datastructures import SortedDict
|
|
||||||
from django.utils.importlib import import_module
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from openslides.utils.template import render_block_to_string, Tab
|
from openslides.utils.template import render_block_to_string, Tab
|
||||||
from openslides.utils.utils import html_strong
|
from openslides.utils.views import (
|
||||||
from openslides.utils.views import (TemplateView, RedirectView, CreateView,
|
TemplateView, RedirectView, CreateView, UpdateView, DeleteView, AjaxMixin)
|
||||||
UpdateView, DeleteView, AjaxMixin)
|
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
|
from .api import (
|
||||||
from openslides.projector.api import (get_active_slide, set_active_slide,
|
get_active_slide, set_active_slide, projector_message_set,
|
||||||
projector_message_set, projector_message_delete, get_slide_from_sid,
|
projector_message_delete, get_slide_from_sid, get_all_widgets,
|
||||||
get_all_widgets, clear_projector_cache)
|
clear_projector_cache)
|
||||||
from openslides.projector.forms import SelectWidgetsForm
|
from .forms import SelectWidgetsForm
|
||||||
from openslides.projector.models import ProjectorOverlay, ProjectorSlide
|
from .models import ProjectorOverlay, ProjectorSlide
|
||||||
from openslides.projector.projector import SLIDE, Widget
|
from .projector import Widget
|
||||||
from openslides.projector.signals import projector_overlays
|
from .signals import projector_overlays
|
||||||
|
|
||||||
|
|
||||||
class DashboardView(TemplateView, AjaxMixin):
|
class DashboardView(TemplateView, AjaxMixin):
|
||||||
@ -73,7 +67,7 @@ class Projector(TemplateView, AjaxMixin):
|
|||||||
if sid is None:
|
if sid is None:
|
||||||
try:
|
try:
|
||||||
data = get_active_slide()
|
data = get_active_slide()
|
||||||
except AttributeError: #TODO: It has to be an Slide.DoesNotExist
|
except AttributeError: # TODO: It has to be an Slide.DoesNotExist
|
||||||
data = None
|
data = None
|
||||||
ajax = 'on'
|
ajax = 'on'
|
||||||
active_sid = get_active_slide(True)
|
active_sid = get_active_slide(True)
|
||||||
@ -92,10 +86,10 @@ class Projector(TemplateView, AjaxMixin):
|
|||||||
# Projector Overlays
|
# Projector Overlays
|
||||||
if self.kwargs['sid'] is None:
|
if self.kwargs['sid'] is None:
|
||||||
active_defs = ProjectorOverlay.objects.filter(active=True) \
|
active_defs = ProjectorOverlay.objects.filter(active=True) \
|
||||||
.filter(Q(sid=active_sid) | Q(sid=None)).values_list('def_name',
|
.filter(Q(sid=active_sid) | Q(sid=None)).values_list(
|
||||||
flat=True)
|
'def_name', flat=True)
|
||||||
for receiver, response in projector_overlays.send(sender=sid,
|
for receiver, response in projector_overlays.send(
|
||||||
register=False, call=active_defs):
|
sender=sid, register=False, call=active_defs):
|
||||||
if response is not None:
|
if response is not None:
|
||||||
data['overlays'].append(response)
|
data['overlays'].append(response)
|
||||||
self._data = data
|
self._data = data
|
||||||
@ -124,7 +118,6 @@ class Projector(TemplateView, AjaxMixin):
|
|||||||
'scrollcontent', self.data)
|
'scrollcontent', self.data)
|
||||||
cache.set('projector_scrollcontent', scrollcontent, 1)
|
cache.set('projector_scrollcontent', scrollcontent, 1)
|
||||||
|
|
||||||
|
|
||||||
# TODO: do not call the hole data-methode, if we only need some vars
|
# TODO: do not call the hole data-methode, if we only need some vars
|
||||||
data = cache.get('projector_data')
|
data = cache.get('projector_data')
|
||||||
if not data:
|
if not data:
|
||||||
@ -301,7 +294,6 @@ class OverlayMessageView(RedirectView):
|
|||||||
elif 'message-clean' in request.POST:
|
elif 'message-clean' in request.POST:
|
||||||
projector_message_delete()
|
projector_message_delete()
|
||||||
|
|
||||||
|
|
||||||
def get_ajax_context(self, **kwargs):
|
def get_ajax_context(self, **kwargs):
|
||||||
clear_projector_cache()
|
clear_projector_cache()
|
||||||
return {
|
return {
|
||||||
@ -309,7 +301,6 @@ class OverlayMessageView(RedirectView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ActivateOverlay(RedirectView):
|
class ActivateOverlay(RedirectView):
|
||||||
"""
|
"""
|
||||||
Activate or deactivate an overlay.
|
Activate or deactivate an overlay.
|
||||||
@ -379,12 +370,11 @@ def register_tab(request):
|
|||||||
"""
|
"""
|
||||||
Register the projector tab.
|
Register the projector tab.
|
||||||
"""
|
"""
|
||||||
selected = True if request.path.startswith('/projector/') else False
|
selected = request.path.startswith('/projector/')
|
||||||
return Tab(
|
return Tab(
|
||||||
title=_('Dashboard'),
|
title=_('Dashboard'),
|
||||||
url=reverse('dashboard'),
|
url=reverse('dashboard'),
|
||||||
permission=request.user.has_perm('projector.can_manage_projector') or
|
permission=request.user.has_perm('projector.can_see_dashboard'),
|
||||||
request.user.has_perm('projector.can_see_dashboard'),
|
|
||||||
selected=selected,
|
selected=selected,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -411,7 +401,7 @@ def get_widgets(request):
|
|||||||
name='live_view',
|
name='live_view',
|
||||||
display_name=_('Projector live view'),
|
display_name=_('Projector live view'),
|
||||||
template='projector/live_view_widget.html',
|
template='projector/live_view_widget.html',
|
||||||
context = RequestContext(request, {}),
|
context=RequestContext(request, {}),
|
||||||
permission_required='projector.can_see_projector',
|
permission_required='projector.can_see_projector',
|
||||||
default_column=2))
|
default_column=2))
|
||||||
|
|
||||||
@ -424,15 +414,14 @@ def get_widgets(request):
|
|||||||
projector_overlay = ProjectorOverlay.objects.get(
|
projector_overlay = ProjectorOverlay.objects.get(
|
||||||
def_name=name)
|
def_name=name)
|
||||||
except ProjectorOverlay.DoesNotExist:
|
except ProjectorOverlay.DoesNotExist:
|
||||||
projector_overlay = ProjectorOverlay(def_name=name,
|
projector_overlay = ProjectorOverlay(def_name=name, active=False)
|
||||||
active=False)
|
|
||||||
projector_overlay.save()
|
projector_overlay.save()
|
||||||
overlays.append(projector_overlay)
|
overlays.append(projector_overlay)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'overlays':overlays,
|
'overlays': overlays,
|
||||||
'countdown_time': config['countdown_time'],
|
'countdown_time': config['countdown_time'],
|
||||||
'countdown_state' : config['countdown_state']}
|
'countdown_state': config['countdown_state']}
|
||||||
context.update(csrf(request))
|
context.update(csrf(request))
|
||||||
widgets.append(Widget(
|
widgets.append(Widget(
|
||||||
name='overlays',
|
name='overlays',
|
||||||
@ -442,7 +431,6 @@ def get_widgets(request):
|
|||||||
default_column=2,
|
default_column=2,
|
||||||
context=context))
|
context=context))
|
||||||
|
|
||||||
|
|
||||||
# Custom slide widget
|
# Custom slide widget
|
||||||
context = {
|
context = {
|
||||||
'slides': ProjectorSlide.objects.all().order_by('weight'),
|
'slides': ProjectorSlide.objects.all().order_by('weight'),
|
||||||
|
@ -12,8 +12,6 @@
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls.defaults import patterns, url, include
|
from django.conf.urls.defaults import patterns, url, include
|
||||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
from openslides.utils.views import RedirectView
|
from openslides.utils.views import RedirectView
|
||||||
@ -34,7 +32,7 @@ urlpatterns = patterns('',
|
|||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns += patterns('django.contrib.staticfiles.views',
|
urlpatterns += patterns('django.contrib.staticfiles.views',
|
||||||
url(r'^static/(?P<path>.*)$', 'serve', {'insecure':True}),
|
url(r'^static/(?P<path>.*)$', 'serve', {'insecure': True}),
|
||||||
)
|
)
|
||||||
|
|
||||||
js_info_dict = {
|
js_info_dict = {
|
||||||
|
@ -37,8 +37,8 @@ class AnonymousAuth(object):
|
|||||||
|
|
||||||
- try to return the permissions for the 'Anonymous' group
|
- try to return the permissions for the 'Anonymous' group
|
||||||
"""
|
"""
|
||||||
if not user_obj.is_anonymous() or obj is not None or \
|
if (not user_obj.is_anonymous() or obj is not None or
|
||||||
not config['system_enable_anonymous']:
|
not config['system_enable_anonymous']):
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
perms = Permission.objects.filter(group__name='Anonymous')
|
perms = Permission.objects.filter(group__name='Anonymous')
|
||||||
@ -60,8 +60,8 @@ class AnonymousAuth(object):
|
|||||||
"""
|
"""
|
||||||
Check if the user as a specific permission
|
Check if the user as a specific permission
|
||||||
"""
|
"""
|
||||||
if not user_obj.is_anonymous() or obj is not None or \
|
if (not user_obj.is_anonymous() or obj is not None or
|
||||||
not config['system_enable_anonymous']:
|
not config['system_enable_anonymous']):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return (perm in self.get_all_permissions(user_obj))
|
return (perm in self.get_all_permissions(user_obj))
|
||||||
@ -70,8 +70,8 @@ class AnonymousAuth(object):
|
|||||||
"""
|
"""
|
||||||
Check if the user has permissions on the module app_label
|
Check if the user has permissions on the module app_label
|
||||||
"""
|
"""
|
||||||
if not user_obj.is_anonymous() or \
|
if (not user_obj.is_anonymous() or
|
||||||
not config['system_enable_anonymous']:
|
not config['system_enable_anonymous']):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for perm in self.get_all_permissions(user_obj):
|
for perm in self.get_all_permissions(user_obj):
|
||||||
@ -87,10 +87,10 @@ class AnonymousAuth(object):
|
|||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def anonymous_context_additions(RequestContext):
|
def anonymous_context_additions(RequestContext):
|
||||||
"""
|
"""
|
||||||
Add a variable to the request context that will indicate
|
Add a variable to the request context that will indicate
|
||||||
if anonymous login is possible at all.
|
if anonymous login is possible at all.
|
||||||
"""
|
"""
|
||||||
return {'os_enable_anonymous_login' : config['system_enable_anonymous']}
|
return {'os_enable_anonymous_login': config['system_enable_anonymous']}
|
||||||
|
|
||||||
|
@ -25,10 +25,9 @@ class excel_semikolon(Dialect):
|
|||||||
def patchup(dialect):
|
def patchup(dialect):
|
||||||
if dialect:
|
if dialect:
|
||||||
if dialect.delimiter in [excel_semikolon.delimiter, excel.delimiter] and \
|
if dialect.delimiter in [excel_semikolon.delimiter, excel.delimiter] and \
|
||||||
dialect.quotechar == excel_semikolon.quotechar:
|
dialect.quotechar == excel_semikolon.quotechar:
|
||||||
# walks like a duck and talks like a duck.. must be one
|
# walks like a duck and talks like a duck.. must be one
|
||||||
dialect.doublequote = True
|
dialect.doublequote = True
|
||||||
return dialect
|
return dialect
|
||||||
|
|
||||||
register_dialect("excel_semikolon", excel_semikolon)
|
register_dialect("excel_semikolon", excel_semikolon)
|
||||||
|
|
||||||
|
@ -1 +1,3 @@
|
|||||||
from fields import JSONField
|
from fields import JSONField
|
||||||
|
|
||||||
|
__all__ = ['JSONField']
|
||||||
|
@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.forms.fields import Field
|
from django.forms.fields import Field
|
||||||
from django.forms.util import ValidationError as FormValidationError
|
from django.forms.util import ValidationError as FormValidationError
|
||||||
|
|
||||||
|
|
||||||
class JSONFormField(Field):
|
class JSONFormField(Field):
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ class JSONFormField(Field):
|
|||||||
raise FormValidationError(_("Enter valid JSON"))
|
raise FormValidationError(_("Enter valid JSON"))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class JSONField(models.TextField):
|
class JSONField(models.TextField):
|
||||||
"""JSONField is a generic textfield that serializes/unserializes JSON objects"""
|
"""JSONField is a generic textfield that serializes/unserializes JSON objects"""
|
||||||
|
|
||||||
|
@ -1,108 +0,0 @@
|
|||||||
from django.db import models
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.utils import simplejson as json
|
|
||||||
|
|
||||||
from fields import JSONField
|
|
||||||
|
|
||||||
|
|
||||||
class JsonModel(models.Model):
|
|
||||||
json = JSONField()
|
|
||||||
|
|
||||||
|
|
||||||
class ComplexEncoder(json.JSONEncoder):
|
|
||||||
def default(self, obj):
|
|
||||||
if isinstance(obj, complex):
|
|
||||||
return {
|
|
||||||
'__complex__': True,
|
|
||||||
'real': obj.real,
|
|
||||||
'imag': obj.imag,
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.JSONEncoder.default(self, obj)
|
|
||||||
|
|
||||||
|
|
||||||
def as_complex(dct):
|
|
||||||
if '__complex__' in dct:
|
|
||||||
return complex(dct['real'], dct['imag'])
|
|
||||||
return dct
|
|
||||||
|
|
||||||
|
|
||||||
class JSONModelCustomEncoders(models.Model):
|
|
||||||
# A JSON field that can store complex numbers
|
|
||||||
json = JSONField(
|
|
||||||
dump_kwargs={'cls': ComplexEncoder},
|
|
||||||
load_kwargs={'object_hook': as_complex},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class JSONFieldTest(TestCase):
|
|
||||||
"""JSONField Wrapper Tests"""
|
|
||||||
|
|
||||||
def test_json_field_create(self):
|
|
||||||
"""Test saving a JSON object in our JSONField"""
|
|
||||||
|
|
||||||
json_obj = {
|
|
||||||
"item_1": "this is a json blah",
|
|
||||||
"blergh": "hey, hey, hey"}
|
|
||||||
|
|
||||||
obj = JsonModel.objects.create(json=json_obj)
|
|
||||||
new_obj = JsonModel.objects.get(id=obj.id)
|
|
||||||
|
|
||||||
self.failUnlessEqual(new_obj.json, json_obj)
|
|
||||||
|
|
||||||
def test_json_field_modify(self):
|
|
||||||
"""Test modifying a JSON object in our JSONField"""
|
|
||||||
|
|
||||||
json_obj_1 = {'a': 1, 'b': 2}
|
|
||||||
json_obj_2 = {'a': 3, 'b': 4}
|
|
||||||
|
|
||||||
obj = JsonModel.objects.create(json=json_obj_1)
|
|
||||||
|
|
||||||
self.failUnlessEqual(obj.json, json_obj_1)
|
|
||||||
|
|
||||||
obj.json = json_obj_2
|
|
||||||
|
|
||||||
self.failUnlessEqual(obj.json, json_obj_2)
|
|
||||||
|
|
||||||
obj.save()
|
|
||||||
|
|
||||||
self.failUnlessEqual(obj.json, json_obj_2)
|
|
||||||
|
|
||||||
self.assert_(obj)
|
|
||||||
|
|
||||||
def test_json_field_load(self):
|
|
||||||
"""Test loading a JSON object from the DB"""
|
|
||||||
|
|
||||||
json_obj_1 = {'a': 1, 'b': 2}
|
|
||||||
|
|
||||||
obj = JsonModel.objects.create(json=json_obj_1)
|
|
||||||
|
|
||||||
new_obj = JsonModel.objects.get(id=obj.id)
|
|
||||||
|
|
||||||
self.failUnlessEqual(new_obj.json, json_obj_1)
|
|
||||||
|
|
||||||
def test_json_list(self):
|
|
||||||
"""Test storing a JSON list"""
|
|
||||||
|
|
||||||
json_obj = ["my", "list", "of", 1, "objs", {"hello": "there"}]
|
|
||||||
|
|
||||||
obj = JsonModel.objects.create(json=json_obj)
|
|
||||||
new_obj = JsonModel.objects.get(id=obj.id)
|
|
||||||
self.failUnlessEqual(new_obj.json, json_obj)
|
|
||||||
|
|
||||||
def test_empty_objects(self):
|
|
||||||
"""Test storing empty objects"""
|
|
||||||
|
|
||||||
for json_obj in [{}, [], 0, '', False]:
|
|
||||||
obj = JsonModel.objects.create(json=json_obj)
|
|
||||||
new_obj = JsonModel.objects.get(id=obj.id)
|
|
||||||
self.failUnlessEqual(json_obj, obj.json)
|
|
||||||
self.failUnlessEqual(json_obj, new_obj.json)
|
|
||||||
|
|
||||||
def test_custom_encoder(self):
|
|
||||||
"""Test encoder_cls and object_hook"""
|
|
||||||
value = 1 + 3j # A complex number
|
|
||||||
|
|
||||||
obj = JSONModelCustomEncoders.objects.create(json=value)
|
|
||||||
new_obj = JSONModelCustomEncoders.objects.get(pk=obj.pk)
|
|
||||||
self.failUnlessEqual(value, new_obj.json)
|
|
@ -12,12 +12,13 @@
|
|||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class MinMaxIntegerField(models.IntegerField):
|
class MinMaxIntegerField(models.IntegerField):
|
||||||
def __init__(self, min_value=None, max_value=None, *args, **kwargs):
|
def __init__(self, min_value=None, max_value=None, *args, **kwargs):
|
||||||
self.min_value, self.max_value = min_value, max_value
|
self.min_value, self.max_value = min_value, max_value
|
||||||
super(MinMaxIntegerField, self).__init__(*args, **kwargs)
|
super(MinMaxIntegerField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
defaults = {'min_value': self.min_value, 'max_value' : self.max_value}
|
defaults = {'min_value': self.min_value, 'max_value': self.max_value}
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return super(MinMaxIntegerField, self).formfield(**defaults)
|
return super(MinMaxIntegerField, self).formfield(**defaults)
|
||||||
|
@ -28,16 +28,16 @@ from openslides.config.models import config
|
|||||||
|
|
||||||
|
|
||||||
# register new truetype fonts
|
# register new truetype fonts
|
||||||
pdfmetrics.registerFont(TTFont('Ubuntu', path_join(settings.SITE_ROOT,
|
pdfmetrics.registerFont(TTFont(
|
||||||
'static/fonts/Ubuntu-R.ttf')))
|
'Ubuntu', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-R.ttf')))
|
||||||
pdfmetrics.registerFont(TTFont('Ubuntu-Bold', path_join(settings.SITE_ROOT,
|
pdfmetrics.registerFont(TTFont(
|
||||||
'static/fonts/Ubuntu-B.ttf')))
|
'Ubuntu-Bold', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-B.ttf')))
|
||||||
pdfmetrics.registerFont(TTFont('Ubuntu-Italic', path_join(settings.SITE_ROOT,
|
pdfmetrics.registerFont(TTFont(
|
||||||
'static/fonts/Ubuntu-RI.ttf')))
|
'Ubuntu-Italic', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-RI.ttf')))
|
||||||
|
|
||||||
|
|
||||||
# set style information
|
# set style information
|
||||||
PAGE_HEIGHT = defaultPageSize[1];
|
PAGE_HEIGHT = defaultPageSize[1]
|
||||||
PAGE_WIDTH = defaultPageSize[0]
|
PAGE_WIDTH = defaultPageSize[0]
|
||||||
|
|
||||||
|
|
||||||
@ -105,104 +105,104 @@ stylesheet.add(ParagraphStyle(
|
|||||||
leftIndent=0,
|
leftIndent=0,
|
||||||
spaceAfter=15,
|
spaceAfter=15,
|
||||||
))
|
))
|
||||||
stylesheet.add(ParagraphStyle(name = 'Subitem',
|
stylesheet.add(ParagraphStyle(
|
||||||
parent = stylesheet['Normal'],
|
name='Subitem',
|
||||||
fontSize = 10,
|
parent=stylesheet['Normal'],
|
||||||
leading = 10,
|
fontSize=10,
|
||||||
leftIndent = 20,
|
leading=10,
|
||||||
spaceAfter = 15)
|
leftIndent=20,
|
||||||
)
|
spaceAfter=15))
|
||||||
stylesheet.add(ParagraphStyle(name = 'Tablecell',
|
stylesheet.add(ParagraphStyle(
|
||||||
parent = stylesheet['Normal'],
|
name='Tablecell',
|
||||||
fontSize = 9)
|
parent=stylesheet['Normal'],
|
||||||
)
|
fontSize=9))
|
||||||
stylesheet.add(ParagraphStyle(name = 'Signaturefield',
|
stylesheet.add(ParagraphStyle(name='Signaturefield',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
spaceBefore = 15)
|
spaceBefore=15)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ballot stylesheets
|
# Ballot stylesheets
|
||||||
stylesheet.add(ParagraphStyle(name = 'Ballot_title',
|
stylesheet.add(ParagraphStyle(name='Ballot_title',
|
||||||
parent = stylesheet['Bold'],
|
parent=stylesheet['Bold'],
|
||||||
fontSize = 12,
|
fontSize=12,
|
||||||
leading = 14,
|
leading=14,
|
||||||
leftIndent = 30),
|
leftIndent=30),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Ballot_subtitle',
|
stylesheet.add(ParagraphStyle(name='Ballot_subtitle',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
fontSize = 10,
|
fontSize=10,
|
||||||
leading = 12,
|
leading=12,
|
||||||
leftIndent = 30,
|
leftIndent=30,
|
||||||
rightIndent = 20,
|
rightIndent=20,
|
||||||
spaceAfter = 5),
|
spaceAfter=5),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Ballot_description',
|
stylesheet.add(ParagraphStyle(name='Ballot_description',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
fontSize = 7,
|
fontSize=7,
|
||||||
leading = 10,
|
leading=10,
|
||||||
leftIndent = 30),
|
leftIndent=30),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Ballot_option',
|
stylesheet.add(ParagraphStyle(name='Ballot_option',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
fontSize = 12,
|
fontSize=12,
|
||||||
leading = 24,
|
leading=24,
|
||||||
leftIndent = 30),
|
leftIndent=30),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Monotype',
|
stylesheet.add(ParagraphStyle(name='Monotype',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
fontName = 'Courier',
|
fontName='Courier',
|
||||||
fontSize = 12,
|
fontSize=12,
|
||||||
leading = 24,
|
leading=24,
|
||||||
leftIndent = 30),
|
leftIndent=30),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Ballot_option_name',
|
stylesheet.add(ParagraphStyle(name='Ballot_option_name',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
fontSize = 12,
|
fontSize=12,
|
||||||
leading = 15,
|
leading=15,
|
||||||
leftIndent = 30),
|
leftIndent=30),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Ballot_option_group',
|
stylesheet.add(ParagraphStyle(name='Ballot_option_group',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
fontSize = 8,
|
fontSize=8,
|
||||||
leading = 15,
|
leading=15,
|
||||||
leftIndent = 30),
|
leftIndent=30),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Ballot_option_YNA',
|
stylesheet.add(ParagraphStyle(name='Ballot_option_YNA',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
fontSize = 12,
|
fontSize=12,
|
||||||
leading = 15,
|
leading=15,
|
||||||
leftIndent = 49,
|
leftIndent=49,
|
||||||
spaceAfter = 18),
|
spaceAfter=18),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Ballot_option_group_right',
|
stylesheet.add(ParagraphStyle(name='Ballot_option_group_right',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
fontSize = 8,
|
fontSize=8,
|
||||||
leading = 16,
|
leading=16,
|
||||||
leftIndent = 49),
|
leftIndent=49),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Badge_title',
|
stylesheet.add(ParagraphStyle(name='Badge_title',
|
||||||
parent = stylesheet['Bold'],
|
parent=stylesheet['Bold'],
|
||||||
fontSize = 16,
|
fontSize=16,
|
||||||
leading = 22,
|
leading=22,
|
||||||
leftIndent = 30),
|
leftIndent=30),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(name = 'Badge_subtitle',
|
stylesheet.add(ParagraphStyle(name='Badge_subtitle',
|
||||||
parent = stylesheet['Normal'],
|
parent=stylesheet['Normal'],
|
||||||
fontSize = 12,
|
fontSize=12,
|
||||||
leading = 24,
|
leading=24,
|
||||||
leftIndent = 30),
|
leftIndent=30),
|
||||||
)
|
)
|
||||||
stylesheet.add(ParagraphStyle(
|
stylesheet.add(ParagraphStyle(
|
||||||
name = 'Badge_italic',
|
name='Badge_italic',
|
||||||
parent = stylesheet['Italic'],
|
parent=stylesheet['Italic'],
|
||||||
fontSize = 12,
|
fontSize=12,
|
||||||
leading = 24,
|
leading=24,
|
||||||
leftIndent = 30,
|
leftIndent=30,
|
||||||
))
|
))
|
||||||
stylesheet.add(ParagraphStyle(
|
stylesheet.add(ParagraphStyle(
|
||||||
name = 'Badge_qrcode',
|
name='Badge_qrcode',
|
||||||
fontSize = 12,
|
fontSize=12,
|
||||||
leftIndent = 190,
|
leftIndent=190,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
@ -213,13 +213,13 @@ def firstPage(canvas, doc):
|
|||||||
canvas.setFillGray(0.4)
|
canvas.setFillGray(0.4)
|
||||||
|
|
||||||
title_line = u"%s | %s" % (config["event_name"],
|
title_line = u"%s | %s" % (config["event_name"],
|
||||||
config["event_description"])
|
config["event_description"])
|
||||||
if len(title_line) > 75:
|
if len(title_line) > 75:
|
||||||
title_line = "%s ..." % title_line[:70]
|
title_line = "%s ..." % title_line[:70]
|
||||||
canvas.drawString(2.75 * cm, 28 * cm, title_line)
|
canvas.drawString(2.75 * cm, 28 * cm, title_line)
|
||||||
if config["event_date"] and config["event_location"]:
|
if config["event_date"] and config["event_location"]:
|
||||||
canvas.drawString(2.75 * cm, 27.6 * cm, u"%s, %s"
|
canvas.drawString(2.75 * cm, 27.6 * cm, u"%s, %s"
|
||||||
% (config["event_date"], config["event_location"]))
|
% (config["event_date"], config["event_location"]))
|
||||||
|
|
||||||
# time
|
# time
|
||||||
canvas.setFont('Ubuntu', 7)
|
canvas.setFont('Ubuntu', 7)
|
||||||
|
@ -11,11 +11,15 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from openslides.utils.person.signals import receive_persons
|
from openslides.utils.person.signals import receive_persons
|
||||||
from openslides.utils.person.api import (generate_person_id, get_person,
|
from openslides.utils.person.api import (
|
||||||
Person, Persons)
|
generate_person_id, get_person, Person, Persons)
|
||||||
from openslides.utils.person.forms import PersonFormField, MultiplePersonFormField
|
from openslides.utils.person.forms import PersonFormField, MultiplePersonFormField
|
||||||
from openslides.utils.person.models import PersonField, PersonMixin
|
from openslides.utils.person.models import PersonField, PersonMixin
|
||||||
|
|
||||||
|
__all__ = ['receive_persons', 'generate_person_id', 'get_person', 'Person',
|
||||||
|
'Persons', 'PersonFormField', 'MultiplePersonFormField',
|
||||||
|
'PersonField', 'PersonMixin', 'EmptyPerson']
|
||||||
|
|
||||||
|
|
||||||
class EmptyPerson(PersonMixin):
|
class EmptyPerson(PersonMixin):
|
||||||
@property
|
@property
|
||||||
|
@ -61,8 +61,8 @@ class MultiplePersonFormField(PersonFormField):
|
|||||||
widget = forms.widgets.SelectMultiple
|
widget = forms.widgets.SelectMultiple
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(MultiplePersonFormField, self).__init__(empty_label=None,
|
super(MultiplePersonFormField, self).__init__(
|
||||||
*args, **kwargs)
|
empty_label=None, *args, **kwargs)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if hasattr(value, '__iter__'):
|
if hasattr(value, '__iter__'):
|
||||||
|
@ -10,8 +10,7 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.http import HttpResponse
|
from django.template import loader, Context
|
||||||
from django.template import loader, Context, RequestContext, TextNode
|
|
||||||
from django.template.loader_tags import BlockNode, ExtendsNode
|
from django.template.loader_tags import BlockNode, ExtendsNode
|
||||||
|
|
||||||
|
|
||||||
@ -24,6 +23,10 @@ class Tab(object):
|
|||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
|
|
||||||
|
## All following function are only needed to render a block from a template
|
||||||
|
## and could be removed, if the template worked with an include-statement instead.
|
||||||
|
## Its only used for ajax-request from the projector.
|
||||||
|
|
||||||
def get_template(template):
|
def get_template(template):
|
||||||
if isinstance(template, (tuple, list)):
|
if isinstance(template, (tuple, list)):
|
||||||
return loader.select_template(template)
|
return loader.select_template(template)
|
||||||
@ -49,22 +52,22 @@ def render_template_block_nodelist(nodelist, block, context):
|
|||||||
for key in ('nodelist', 'nodelist_true', 'nodelist_false'):
|
for key in ('nodelist', 'nodelist_true', 'nodelist_false'):
|
||||||
if hasattr(node, key):
|
if hasattr(node, key):
|
||||||
try:
|
try:
|
||||||
return render_template_block_nodelist(getattr(node, key),
|
return render_template_block_nodelist(
|
||||||
block, context)
|
getattr(node, key), block, context)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
for node in nodelist:
|
for node in nodelist:
|
||||||
if isinstance(node, ExtendsNode):
|
if isinstance(node, ExtendsNode):
|
||||||
try:
|
try:
|
||||||
return render_template_block(node.get_parent(context), block,
|
return render_template_block(
|
||||||
context)
|
node.get_parent(context), block, context)
|
||||||
except BlockNotFound:
|
except BlockNotFound:
|
||||||
pass
|
pass
|
||||||
raise BlockNotFound
|
raise BlockNotFound
|
||||||
|
|
||||||
|
|
||||||
def render_block_to_string(template_name, block, dictionary=None,
|
def render_block_to_string(template_name, block, dictionary=None,
|
||||||
context_instance=None):
|
context_instance=None):
|
||||||
"""
|
"""
|
||||||
Loads the given template_name and renders the given block with the given
|
Loads the given template_name and renders the given block with the given
|
||||||
dictionary as context. Returns a string.
|
dictionary as context. Returns a string.
|
||||||
@ -77,23 +80,3 @@ def render_block_to_string(template_name, block, dictionary=None,
|
|||||||
context_instance = Context(dictionary)
|
context_instance = Context(dictionary)
|
||||||
t.render(context_instance)
|
t.render(context_instance)
|
||||||
return render_template_block(t, block, context_instance)
|
return render_template_block(t, block, context_instance)
|
||||||
|
|
||||||
|
|
||||||
def direct_block_to_template(request, template, block, extra_context=None,
|
|
||||||
mimetype=None, **kwargs):
|
|
||||||
"""
|
|
||||||
Render a given block in a given template with any extra URL parameters in
|
|
||||||
the context as ``{{ params }}``.
|
|
||||||
"""
|
|
||||||
if extra_context is None:
|
|
||||||
extra_context = {}
|
|
||||||
dictionary = {'params': kwargs}
|
|
||||||
for key, value in extra_context.items():
|
|
||||||
if callable(value):
|
|
||||||
dictionary[key] = value()
|
|
||||||
else:
|
|
||||||
dictionary[key] = value
|
|
||||||
c = RequestContext(request, dictionary)
|
|
||||||
t = get_template(template)
|
|
||||||
t.render(c)
|
|
||||||
return HttpResponse(render_template_block(t, block, c), mimetype=mimetype)
|
|
||||||
|
@ -10,12 +10,13 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import json
|
import json
|
||||||
except ImportError: # For python 2.5 support
|
except ImportError: # For python 2.5 support
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.core.context_processors import csrf
|
from django.core.context_processors import csrf
|
||||||
@ -41,16 +42,17 @@ def gen_confirm_form(request, message, url):
|
|||||||
|
|
||||||
Deprecated. Use Class base Views instead.
|
Deprecated. Use Class base Views instead.
|
||||||
"""
|
"""
|
||||||
messages.warning(request,
|
messages.warning(
|
||||||
"""
|
request,
|
||||||
%s
|
"""
|
||||||
<form action="%s" method="post">
|
%s
|
||||||
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
|
<form action="%s" method="post">
|
||||||
<input type="submit" value="%s">
|
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
|
||||||
<input type="button" value="%s">
|
<input type="submit" value="%s">
|
||||||
</form>
|
<input type="button" value="%s">
|
||||||
"""
|
</form>
|
||||||
% (message, url, csrf(request)['csrf_token'], _("Yes"), _("No")))
|
"""
|
||||||
|
% (message, url, csrf(request)['csrf_token'], _("Yes"), _("No")))
|
||||||
|
|
||||||
|
|
||||||
def del_confirm_form(request, object, name=None, delete_link=None):
|
def del_confirm_form(request, object, name=None, delete_link=None):
|
||||||
@ -63,27 +65,28 @@ def del_confirm_form(request, object, name=None, delete_link=None):
|
|||||||
name = object
|
name = object
|
||||||
if delete_link is None:
|
if delete_link is None:
|
||||||
delete_link = object.get_absolute_url('delete')
|
delete_link = object.get_absolute_url('delete')
|
||||||
gen_confirm_form(request, _('Do you really want to delete %s?')
|
gen_confirm_form(
|
||||||
|
request, _('Do you really want to delete %s?')
|
||||||
% html_strong(name), delete_link)
|
% html_strong(name), delete_link)
|
||||||
|
|
||||||
|
|
||||||
def render_response(req, *args, **kwargs):
|
|
||||||
kwargs['context_instance'] = RequestContext(req)
|
|
||||||
return render_to_response(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def template(template_name):
|
def template(template_name):
|
||||||
|
"""
|
||||||
|
Decorator to set a template for a view.
|
||||||
|
|
||||||
|
Deprecated. Use class based views instead.
|
||||||
|
"""
|
||||||
def renderer(func):
|
def renderer(func):
|
||||||
def wrapper(request, *args, **kwargs):
|
def wrapper(request, *args, **kwargs):
|
||||||
output = func(request, *args, **kwargs)
|
output = func(request, *args, **kwargs)
|
||||||
if not isinstance(output, dict):
|
if not isinstance(output, dict):
|
||||||
return output
|
return output
|
||||||
context = {}
|
context = {}
|
||||||
template_manipulation.send(sender='utils_template', request=request,
|
template_manipulation.send(
|
||||||
context=context)
|
sender='utils_template', request=request, context=context)
|
||||||
output.update(context)
|
output.update(context)
|
||||||
response = render_to_response(template_name, output,
|
response = render_to_response(
|
||||||
context_instance=RequestContext(request))
|
template_name, output, context_instance=RequestContext(request))
|
||||||
if 'cookie' in output:
|
if 'cookie' in output:
|
||||||
response.set_cookie(output['cookie'][0], output['cookie'][1])
|
response.set_cookie(output['cookie'][0], output['cookie'][1])
|
||||||
return response
|
return response
|
||||||
@ -95,6 +98,8 @@ def permission_required(perm, login_url=None):
|
|||||||
"""
|
"""
|
||||||
Decorator for views that checks whether a user has a particular permission
|
Decorator for views that checks whether a user has a particular permission
|
||||||
enabled, redirecting to the log-in page if necessary.
|
enabled, redirecting to the log-in page if necessary.
|
||||||
|
|
||||||
|
Deprecated.
|
||||||
"""
|
"""
|
||||||
def renderer(func):
|
def renderer(func):
|
||||||
def wrapper(request, *args, **kw):
|
def wrapper(request, *args, **kw):
|
||||||
@ -107,10 +112,12 @@ def permission_required(perm, login_url=None):
|
|||||||
return renderer
|
return renderer
|
||||||
|
|
||||||
|
|
||||||
def render_to_forbidden(request, error=
|
def render_to_forbidden(request,
|
||||||
ugettext_lazy("Sorry, you have no rights to see this page.")):
|
error=ugettext_lazy("Sorry, you have no rights to see this page.")):
|
||||||
return HttpResponseForbidden(render_to_string('403.html',
|
# TODO: Integrate this function into the PermissionMixin once the
|
||||||
{'error': error}, context_instance=RequestContext(request)))
|
# above function is deleted.
|
||||||
|
return HttpResponseForbidden(render_to_string(
|
||||||
|
'403.html', {'error': error}, context_instance=RequestContext(request)))
|
||||||
|
|
||||||
|
|
||||||
def delete_default_permissions(**kwargs):
|
def delete_default_permissions(**kwargs):
|
||||||
@ -118,16 +125,18 @@ def delete_default_permissions(**kwargs):
|
|||||||
Deletes the permissions, django creates by default for the admin.
|
Deletes the permissions, django creates by default for the admin.
|
||||||
"""
|
"""
|
||||||
for p in Permission.objects.all():
|
for p in Permission.objects.all():
|
||||||
if p.codename.startswith('add') \
|
if (p.codename.startswith('add') or
|
||||||
or p.codename.startswith('delete') \
|
p.codename.startswith('delete') or
|
||||||
or p.codename.startswith('change'):
|
p.codename.startswith('change')):
|
||||||
p.delete()
|
p.delete()
|
||||||
|
|
||||||
|
|
||||||
def ajax_request(data):
|
def ajax_request(data):
|
||||||
"""
|
"""
|
||||||
generates a HTTPResponse-Object with json-Data for a
|
generates a HTTPResponse-Object with json-Data for a
|
||||||
ajax response
|
ajax response.
|
||||||
|
|
||||||
|
Deprecated.
|
||||||
"""
|
"""
|
||||||
return HttpResponse(json.dumps(data))
|
return HttpResponse(json.dumps(data))
|
||||||
|
|
||||||
|
@ -22,8 +22,7 @@ except ImportError:
|
|||||||
# Is this exception realy necessary?
|
# Is this exception realy necessary?
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Frame, PageBreak,
|
from reportlab.platypus import SimpleDocTemplate, Spacer
|
||||||
Spacer, Table, LongTable, TableStyle, Image)
|
|
||||||
from reportlab.lib.units import cm
|
from reportlab.lib.units import cm
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
@ -34,9 +33,9 @@ from django.conf import settings
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.http import HttpResponseServerError, HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponseServerError, HttpResponse, HttpResponseRedirect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import ugettext as _, ugettext_noop, ugettext_lazy
|
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
from django.template import loader, RequestContext
|
from django.template import RequestContext
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.views.generic import (
|
from django.views.generic import (
|
||||||
TemplateView as _TemplateView,
|
TemplateView as _TemplateView,
|
||||||
@ -50,8 +49,6 @@ from django.views.generic import (
|
|||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
from django.views.generic.list import TemplateResponseMixin
|
from django.views.generic.list import TemplateResponseMixin
|
||||||
|
|
||||||
from openslides.config.models import config
|
|
||||||
|
|
||||||
from openslides.utils.utils import render_to_forbidden, html_strong
|
from openslides.utils.utils import render_to_forbidden, html_strong
|
||||||
from openslides.utils.signals import template_manipulation
|
from openslides.utils.signals import template_manipulation
|
||||||
from openslides.utils.pdf import firstPage, laterPages
|
from openslides.utils.pdf import firstPage, laterPages
|
||||||
@ -64,8 +61,8 @@ View = _View
|
|||||||
|
|
||||||
class SetCookieMixin(object):
|
class SetCookieMixin(object):
|
||||||
def render_to_response(self, context, **response_kwargs):
|
def render_to_response(self, context, **response_kwargs):
|
||||||
response = TemplateResponseMixin.render_to_response(self, context,
|
response = TemplateResponseMixin.render_to_response(
|
||||||
**response_kwargs)
|
self, context, **response_kwargs)
|
||||||
if 'cookie' in context:
|
if 'cookie' in context:
|
||||||
response.set_cookie(context['cookie'][0], context['cookie'][1])
|
response.set_cookie(context['cookie'][0], context['cookie'][1])
|
||||||
return response
|
return response
|
||||||
@ -90,8 +87,8 @@ class PermissionMixin(object):
|
|||||||
if not self.has_permission(request, *args, **kwargs):
|
if not self.has_permission(request, *args, **kwargs):
|
||||||
if not request.user.is_authenticated():
|
if not request.user.is_authenticated():
|
||||||
path = request.get_full_path()
|
path = request.get_full_path()
|
||||||
return HttpResponseRedirect("%s?next=%s" % (settings.LOGIN_URL,
|
return HttpResponseRedirect(
|
||||||
path))
|
"%s?next=%s" % (settings.LOGIN_URL, path))
|
||||||
else:
|
else:
|
||||||
return render_to_forbidden(request)
|
return render_to_forbidden(request)
|
||||||
return _View.dispatch(self, request, *args, **kwargs)
|
return _View.dispatch(self, request, *args, **kwargs)
|
||||||
@ -130,18 +127,18 @@ class QuestionMixin(object):
|
|||||||
option_fields = "\n".join([
|
option_fields = "\n".join([
|
||||||
'<input type="submit" name="%s" value="%s">' % (option[0], unicode(option[1]))
|
'<input type="submit" name="%s" value="%s">' % (option[0], unicode(option[1]))
|
||||||
for option in self.get_answer_options()])
|
for option in self.get_answer_options()])
|
||||||
messages.warning(self.request,
|
messages.warning(
|
||||||
|
self.request,
|
||||||
"""
|
"""
|
||||||
%(message)s
|
%(message)s
|
||||||
<form action="%(url)s" method="post">
|
<form action="%(url)s" method="post">
|
||||||
<input type="hidden" value="%(csrf)s" name="csrfmiddlewaretoken">
|
<input type="hidden" value="%(csrf)s" name="csrfmiddlewaretoken">
|
||||||
%(option_fields)s
|
%(option_fields)s
|
||||||
</form>
|
</form>
|
||||||
""" % {
|
""" % {'message': self.get_question(),
|
||||||
'message': self.get_question(),
|
'url': self.get_answer_url(),
|
||||||
'url': self.get_answer_url(),
|
'csrf': csrf(self.request)['csrf_token'],
|
||||||
'csrf': csrf(self.request)['csrf_token'],
|
'option_fields': option_fields})
|
||||||
'option_fields': option_fields})
|
|
||||||
|
|
||||||
def pre_post_redirect(self, request, *args, **kwargs):
|
def pre_post_redirect(self, request, *args, **kwargs):
|
||||||
# Reacts on the response of the user in a POST-request.
|
# Reacts on the response of the user in a POST-request.
|
||||||
@ -167,16 +164,16 @@ class QuestionMixin(object):
|
|||||||
class TemplateView(PermissionMixin, _TemplateView):
|
class TemplateView(PermissionMixin, _TemplateView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(TemplateView, self).get_context_data(**kwargs)
|
context = super(TemplateView, self).get_context_data(**kwargs)
|
||||||
template_manipulation.send(sender=self.__class__, request=self.request,
|
template_manipulation.send(
|
||||||
context=context)
|
sender=self.__class__, request=self.request, context=context)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class ListView(PermissionMixin, SetCookieMixin, _ListView):
|
class ListView(PermissionMixin, SetCookieMixin, _ListView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(ListView, self).get_context_data(**kwargs)
|
context = super(ListView, self).get_context_data(**kwargs)
|
||||||
template_manipulation.send(sender=self.__class__, request=self.request,
|
template_manipulation.send(
|
||||||
context=context)
|
sender=self.__class__, request=self.request, context=context)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -217,8 +214,8 @@ class FormView(PermissionMixin, _FormView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(FormView, self).get_context_data(**kwargs)
|
context = super(FormView, self).get_context_data(**kwargs)
|
||||||
template_manipulation.send(sender=self.__class__, request=self.request,
|
template_manipulation.send(
|
||||||
context=context)
|
sender=self.__class__, request=self.request, context=context)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_invalid(self, form):
|
def form_invalid(self, form):
|
||||||
@ -235,8 +232,8 @@ class UpdateView(PermissionMixin, _UpdateView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(UpdateView, self).get_context_data(**kwargs)
|
context = super(UpdateView, self).get_context_data(**kwargs)
|
||||||
template_manipulation.send(sender=self.__class__, request=self.request,
|
template_manipulation.send(
|
||||||
context=context)
|
sender=self.__class__, request=self.request, context=context)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_invalid(self, form):
|
def form_invalid(self, form):
|
||||||
@ -256,8 +253,8 @@ class CreateView(PermissionMixin, _CreateView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(CreateView, self).get_context_data(**kwargs)
|
context = super(CreateView, self).get_context_data(**kwargs)
|
||||||
template_manipulation.send(sender=self.__class__, request=self.request,
|
template_manipulation.send(
|
||||||
context=context)
|
sender=self.__class__, request=self.request, context=context)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_apply_url(self):
|
def get_apply_url(self):
|
||||||
@ -299,7 +296,6 @@ class DeleteView(SingleObjectMixin, QuestionMixin, RedirectView):
|
|||||||
class DetailView(TemplateView, SingleObjectMixin):
|
class DetailView(TemplateView, SingleObjectMixin):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
context = self.get_context_data(object=self.object)
|
|
||||||
return super(DetailView, self).get(request, *args, **kwargs)
|
return super(DetailView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
@ -329,8 +325,8 @@ class PDFView(PermissionMixin, View):
|
|||||||
return SimpleDocTemplate(buffer)
|
return SimpleDocTemplate(buffer)
|
||||||
|
|
||||||
def build_document(self, pdf_document, story):
|
def build_document(self, pdf_document, story):
|
||||||
pdf_document.build(story, onFirstPage=firstPage,
|
pdf_document.build(
|
||||||
onLaterPages=laterPages)
|
story, onFirstPage=firstPage, onLaterPages=laterPages)
|
||||||
|
|
||||||
def render_to_response(self, filename):
|
def render_to_response(self, filename):
|
||||||
response = HttpResponse(mimetype='application/pdf')
|
response = HttpResponse(mimetype='application/pdf')
|
||||||
@ -340,7 +336,7 @@ class PDFView(PermissionMixin, View):
|
|||||||
buffer = StringIO()
|
buffer = StringIO()
|
||||||
pdf_document = self.get_template(buffer)
|
pdf_document = self.get_template(buffer)
|
||||||
pdf_document.title = self.get_document_title()
|
pdf_document.title = self.get_document_title()
|
||||||
story = [Spacer(1, self.get_top_space()*cm)]
|
story = [Spacer(1, self.get_top_space() * cm)]
|
||||||
|
|
||||||
self.append_to_pdf(story)
|
self.append_to_pdf(story)
|
||||||
|
|
||||||
@ -351,9 +347,6 @@ class PDFView(PermissionMixin, View):
|
|||||||
response.write(pdf)
|
response.write(pdf)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def get_filename(self):
|
|
||||||
return self.filename
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
return self.render_to_response(self.get_filename())
|
return self.render_to_response(self.get_filename())
|
||||||
|
|
||||||
@ -364,9 +357,8 @@ def server_error(request, template_name='500.html'):
|
|||||||
|
|
||||||
Templates: `500.html`
|
Templates: `500.html`
|
||||||
"""
|
"""
|
||||||
t = loader.get_template("500.html")
|
return HttpResponseServerError(render_to_string(
|
||||||
return HttpResponseServerError(render_to_string('500.html',
|
template_name, context_instance=RequestContext(request)))
|
||||||
context_instance=RequestContext(request)))
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(template_manipulation, dispatch_uid="send_register_tab")
|
@receiver(template_manipulation, dispatch_uid="send_register_tab")
|
||||||
|
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Django==1.4.2
|
||||||
|
django-mptt
|
||||||
|
reportlab
|
||||||
|
PIL
|
||||||
|
simplejson
|
6
setup.py
6
setup.py
@ -12,15 +12,19 @@ from setuptools import find_packages
|
|||||||
from openslides import get_version
|
from openslides import get_version
|
||||||
|
|
||||||
|
|
||||||
|
with open('README.txt') as file:
|
||||||
|
long_description = file.read()
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='openslides',
|
name='openslides',
|
||||||
description='Presentation-System',
|
description='Presentation-System',
|
||||||
|
long_description=long_description,
|
||||||
version=get_version(),
|
version=get_version(),
|
||||||
url='http://openslides.org',
|
url='http://openslides.org',
|
||||||
author='OpenSlides-Team',
|
author='OpenSlides-Team',
|
||||||
author_email='support@openslides.org',
|
author_email='support@openslides.org',
|
||||||
license='GPL2+',
|
license='GPL2+',
|
||||||
packages=find_packages(),
|
packages=find_packages(exclude=['tests']),
|
||||||
include_package_data = True,
|
include_package_data = True,
|
||||||
classifiers = [
|
classifiers = [
|
||||||
# http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
# http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||||
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
0
tests/agenda/__init__.py
Normal file
0
tests/agenda/__init__.py
Normal file
18
tests/agenda/models.py
Normal file
18
tests/agenda/models.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from openslides.projector.projector import SlideMixin
|
||||||
|
from openslides.projector.api import register_slidemodel
|
||||||
|
|
||||||
|
|
||||||
|
class ReleatedItem(SlideMixin, models.Model):
|
||||||
|
prefix = 'releateditem'
|
||||||
|
|
||||||
|
name = models.CharField(max_length='255')
|
||||||
|
|
||||||
|
def get_agenda_title(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def get_agenda_title_supplement(self):
|
||||||
|
return 'test item'
|
||||||
|
|
||||||
|
register_slidemodel(ReleatedItem)
|
@ -12,12 +12,15 @@
|
|||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.db.models.query import EmptyQuerySet
|
from django.db.models.query import EmptyQuerySet
|
||||||
|
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide
|
||||||
|
from openslides.participant.models import User
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
from openslides.agenda.slides import agenda_show
|
||||||
|
|
||||||
|
from .models import ReleatedItem
|
||||||
|
|
||||||
|
|
||||||
class ItemTest(TestCase):
|
class ItemTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -25,6 +28,8 @@ class ItemTest(TestCase):
|
|||||||
self.item2 = Item.objects.create(title='item2')
|
self.item2 = Item.objects.create(title='item2')
|
||||||
self.item3 = Item.objects.create(title='item1A', parent=self.item1)
|
self.item3 = Item.objects.create(title='item1A', parent=self.item1)
|
||||||
self.item4 = Item.objects.create(title='item1Aa', parent=self.item3)
|
self.item4 = Item.objects.create(title='item1Aa', parent=self.item3)
|
||||||
|
self.releated = ReleatedItem.objects.create(name='foo')
|
||||||
|
self.item5 = Item.objects.create(title='item5', related_sid=self.releated.sid)
|
||||||
|
|
||||||
def testClosed(self):
|
def testClosed(self):
|
||||||
self.assertFalse(self.item1.closed)
|
self.assertFalse(self.item1.closed)
|
||||||
@ -46,10 +51,6 @@ class ItemTest(TestCase):
|
|||||||
self.assertTrue(self.item3 in self.item1.get_children())
|
self.assertTrue(self.item3 in self.item1.get_children())
|
||||||
self.assertFalse(self.item4 in self.item1.get_children())
|
self.assertFalse(self.item4 in self.item1.get_children())
|
||||||
|
|
||||||
l = Item.objects.all()
|
|
||||||
self.assertEqual(str(l),
|
|
||||||
"[<Item: item1>, <Item: item1A>, <Item: item1Aa>, <Item: item2>]")
|
|
||||||
|
|
||||||
def testForms(self):
|
def testForms(self):
|
||||||
for item in Item.objects.all():
|
for item in Item.objects.all():
|
||||||
initial = item.weight_form.initial
|
initial = item.weight_form.initial
|
||||||
@ -64,6 +65,36 @@ class ItemTest(TestCase):
|
|||||||
self.item1.related_sid = 'foobar'
|
self.item1.related_sid = 'foobar'
|
||||||
self.assertFalse(self.item1.get_related_slide() is None)
|
self.assertFalse(self.item1.get_related_slide() is None)
|
||||||
|
|
||||||
|
def test_title_supplement(self):
|
||||||
|
self.assertEqual(self.item1.get_title_supplement(), '')
|
||||||
|
|
||||||
|
def test_delete_item(self):
|
||||||
|
new_item1 = Item.objects.create()
|
||||||
|
new_item2 = Item.objects.create(parent=new_item1)
|
||||||
|
new_item3 = Item.objects.create(parent=new_item2)
|
||||||
|
new_item1.delete()
|
||||||
|
self.assertTrue(new_item3 in Item.objects.all())
|
||||||
|
new_item2.delete(with_children=True)
|
||||||
|
self.assertFalse(new_item3 in Item.objects.all())
|
||||||
|
|
||||||
|
def test_absolute_url(self):
|
||||||
|
self.assertEqual(self.item1.get_absolute_url(), '/agenda/1/')
|
||||||
|
self.assertEqual(self.item1.get_absolute_url('edit'), '/agenda/1/edit/')
|
||||||
|
self.assertEqual(self.item1.get_absolute_url('delete'), '/agenda/1/del/')
|
||||||
|
|
||||||
|
def test_agenda_slide(self):
|
||||||
|
data = agenda_show()
|
||||||
|
self.assertEqual(list(data['items']), list(Item.objects.all().filter(parent=None)))
|
||||||
|
self.assertEqual(data['template'], 'projector/AgendaSummary.html')
|
||||||
|
self.assertEqual(data['title'], 'Agenda')
|
||||||
|
|
||||||
|
def test_releated_item(self):
|
||||||
|
self.assertEqual(self.item5.get_title(), self.releated.name)
|
||||||
|
self.assertEqual(self.item5.get_title_supplement(), 'test item')
|
||||||
|
self.assertEqual(self.item5.get_related_type(), 'releateditem')
|
||||||
|
self.assertEqual(self.item5.print_related_type(), 'Releateditem')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ViewTest(TestCase):
|
class ViewTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -71,8 +102,10 @@ class ViewTest(TestCase):
|
|||||||
self.item2 = Item.objects.create(title='item2')
|
self.item2 = Item.objects.create(title='item2')
|
||||||
self.refreshItems()
|
self.refreshItems()
|
||||||
|
|
||||||
self.admin = User.objects.create_user('testadmin', '', 'default')
|
self.admin, created = User.objects.get_or_create(username='testadmin')
|
||||||
self.anonym = User.objects.create_user('testanoym', '', 'default')
|
self.anonym, created = User.objects.get_or_create(username='testanonym')
|
||||||
|
self.admin.reset_password('default')
|
||||||
|
self.anonym.reset_password('default')
|
||||||
|
|
||||||
self.admin.is_superuser = True
|
self.admin.is_superuser = True
|
||||||
self.admin.save()
|
self.admin.save()
|
||||||
@ -91,6 +124,13 @@ class ViewTest(TestCase):
|
|||||||
def anonymClient(self):
|
def anonymClient(self):
|
||||||
return Client()
|
return Client()
|
||||||
|
|
||||||
|
def testOverview(self):
|
||||||
|
c = self.adminClient
|
||||||
|
|
||||||
|
response = c.get('/agenda/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(len(response.context['items']), len(Item.objects.all()))
|
||||||
|
|
||||||
def testActivate(self):
|
def testActivate(self):
|
||||||
c = self.adminClient
|
c = self.adminClient
|
||||||
|
|
||||||
@ -122,6 +162,14 @@ class ViewTest(TestCase):
|
|||||||
self.refreshItems()
|
self.refreshItems()
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
# Test ajax
|
||||||
|
response = c.get('/agenda/%d/close/' % self.item1.id,
|
||||||
|
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
response = c.get('/agenda/%d/open/' % self.item1.id,
|
||||||
|
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def testEdit(self):
|
def testEdit(self):
|
||||||
c = self.adminClient
|
c = self.adminClient
|
||||||
|
|
||||||
@ -131,7 +179,7 @@ class ViewTest(TestCase):
|
|||||||
response = c.get('/agenda/%d/edit/' % 1000)
|
response = c.get('/agenda/%d/edit/' % 1000)
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
data = {'title': 'newitem1', 'text': 'item1-text', 'weight':'0'}
|
data = {'title': 'newitem1', 'text': 'item1-text', 'weight': '0'}
|
||||||
response = c.post('/agenda/%d/edit/' % self.item1.id, data)
|
response = c.post('/agenda/%d/edit/' % self.item1.id, data)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.refreshItems()
|
self.refreshItems()
|
||||||
@ -143,4 +191,3 @@ class ViewTest(TestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.refreshItems()
|
self.refreshItems()
|
||||||
self.assertEqual(self.item1.title, 'newitem1')
|
self.assertEqual(self.item1.title, 'newitem1')
|
||||||
|
|
23
tests/test_init.py
Normal file
23
tests/test_init.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Unit test for OpenSlides __init__.py
|
||||||
|
|
||||||
|
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from openslides import get_version
|
||||||
|
|
||||||
|
class InitTest(TestCase):
|
||||||
|
def test_get_version(self):
|
||||||
|
self.assertEqual(get_version((1, 3, 0, 'beta', 2)), '1.3-beta2')
|
||||||
|
self.assertEqual(get_version((1, 0, 0, 'final', 0)), '1.0')
|
||||||
|
self.assertEqual(get_version((2, 5, 3, 'alpha', 0)), '2.5.3-alpha0')
|
||||||
|
git_version = get_version((2, 5, 0, 'dev', 0))
|
||||||
|
if 'unknown' in git_version:
|
||||||
|
self.assertEqual(len(git_version), 14)
|
||||||
|
else:
|
||||||
|
self.assertEqual(len(git_version), 47)
|
@ -11,10 +11,10 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import Client
|
|
||||||
|
|
||||||
from openslides.participant.models import User
|
from openslides.participant.models import User
|
||||||
from openslides.motion.models import Motion, AVersion
|
from openslides.motion.models import Motion
|
||||||
|
|
||||||
|
|
||||||
class MotionTest(TestCase):
|
class MotionTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -39,4 +39,3 @@ class MotionTest(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(self.app1.versions.count(), 2)
|
self.assertEqual(self.app1.versions.count(), 2)
|
||||||
self.assertEqual(self.app1.last_version, self.app1.versions[1])
|
self.assertEqual(self.app1.last_version, self.app1.versions[1])
|
||||||
|
|
@ -11,8 +11,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import Client
|
|
||||||
from django.contrib.auth.hashers import check_password
|
|
||||||
|
|
||||||
from openslides.utils.person import get_person, Persons
|
from openslides.utils.person import get_person, Persons
|
||||||
from openslides.participant.api import gen_username, gen_password
|
from openslides.participant.api import gen_username, gen_password
|
||||||
@ -38,9 +36,9 @@ class UserTest(TestCase):
|
|||||||
self.assertEqual(unicode(self.user1), 'Max Mustermann')
|
self.assertEqual(unicode(self.user1), 'Max Mustermann')
|
||||||
|
|
||||||
def test_name_suffix(self):
|
def test_name_suffix(self):
|
||||||
self.user1.structure_level = 'München'
|
self.user1.structure_level = u'München'
|
||||||
self.user1.save()
|
self.user1.save()
|
||||||
self.assertEqual(unicode(self.user1), 'Max Mustermann (München)')
|
self.assertEqual(unicode(self.user1), u'Max Mustermann (München)')
|
||||||
|
|
||||||
def test_reset_password(self):
|
def test_reset_password(self):
|
||||||
self.assertIsInstance(self.user1.default_password, basestring)
|
self.assertIsInstance(self.user1.default_password, basestring)
|
Loading…
Reference in New Issue
Block a user