Merge remote-tracking branch 'origin/master' into bug-409

Conflicts:
	openslides/main.py
This commit is contained in:
Andy Kittner 2012-11-24 20:49:04 +01:00
commit 5979b23b04
48 changed files with 533 additions and 580 deletions

4
.coveragerc Normal file
View File

@ -0,0 +1,4 @@
[run]
source=openslides
[report]
exclude_lines = def __(unicode|repr)__

5
.gitignore vendored
View File

@ -12,3 +12,8 @@ docs/_build/*
build/*
dist/*
.DS_Store
settings.py
# Unit test / coverage reports
.coverage
htmlcov

10
.travis.yml Normal file
View File

@ -0,0 +1,10 @@
language: python
python:
- "2.5"
- "2.6"
- "2.7"
install:
- pip install -r requirements.txt --use-mirrors
- pip install coverage django-discover-runner
- python extras/scripts/create_local_settings.py
script: coverage run ./manage.py test tests && coverage report -m

View 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'))

View File

@ -24,8 +24,8 @@ class ItemForm(forms.ModelForm, CssClassMixin):
"""
Form to create of update an item.
"""
parent = TreeNodeChoiceField(queryset=Item.objects.all(),
label=_("Parent item"), required=False)
parent = TreeNodeChoiceField(
queryset=Item.objects.all(), label=_("Parent item"), required=False)
class Meta:
model = Item

View File

@ -10,12 +10,6 @@
: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.core.urlresolvers import reverse
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 openslides.config.models import config
from openslides.projector.projector import SlideMixin
from openslides.projector.api import (register_slidemodel, get_slide_from_sid,
register_slidefunc, split_sid)
from openslides.projector.api import (
register_slidemodel, get_slide_from_sid, register_slidefunc)
from openslides.agenda.slides import agenda_show
@ -45,7 +37,7 @@ class Item(MPTTModel, SlideMixin):
closed = models.BooleanField(default=False, verbose_name=_("Closed"))
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
parent = TreeForeignKey('self', null=True, blank=True,
related_name='children')
related_name='children')
related_sid = models.CharField(null=True, blank=True, max_length=63)
def get_related_slide(self):
@ -84,7 +76,6 @@ class Item(MPTTModel, SlideMixin):
return self.title
return self.get_related_slide().get_agenda_title()
def get_title_supplement(self):
"""
return a supplement for the title.

View File

@ -11,7 +11,6 @@
"""
from reportlab.platypus import Paragraph
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
from django.contrib import messages
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 openslides.utils.pdf import stylesheet
from openslides.utils.views import (TemplateView, RedirectView, UpdateView,
CreateView, DeleteView, PDFView, DetailView)
from openslides.utils.views import (
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
DetailView)
from openslides.utils.template import Tab
from openslides.utils.utils import html_strong
from openslides.config.models import config
from openslides.projector.api import get_active_slide
from openslides.projector.projector import Widget, SLIDE
from openslides.agenda.models import Item
from openslides.agenda.forms import ItemOrderForm, ItemForm
from .models import Item
from .forms import ItemOrderForm, ItemForm
class Overview(TemplateView):
@ -53,7 +49,8 @@ class Overview(TemplateView):
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
if not request.user.has_perm('agenda.can_manage_agenda'):
messages.error(request,
messages.error(
request,
_('You are not authorized to manage the agenda.'))
return self.render_to_response(context)
transaction.commit()
@ -69,8 +66,8 @@ class Overview(TemplateView):
Model.save(item)
else:
transaction.rollback()
messages.error(request,
_('Errors when reordering of the agenda'))
messages.error(
request, _('Errors when reordering of the agenda'))
return self.render_to_response(context)
Item.objects.rebuild()
# TODO: assure, that it is a valid tree
@ -130,8 +127,8 @@ class ItemUpdate(UpdateView):
apply_url = 'item_edit'
def get_success_url(self):
messages.success(self.request,
_("Item %s was successfully modified.") \
messages.success(
self.request, _("Item %s was successfully modified.")
% html_strong(self.request.POST['title']))
if 'apply' in self.request.POST:
return ''
@ -151,8 +148,8 @@ class ItemCreate(CreateView):
apply_url = 'item_edit'
def get_success_url(self):
messages.success(self.request,
_("Item %s was successfully created.") \
messages.success(
self.request, _("Item %s was successfully created.")
% html_strong(self.request.POST['title']))
if 'apply' in self.request.POST:
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):
if self.get_answer() == 'all':
self.object.delete(with_children=True)
messages.success(request,
_("Item %s and his children were successfully deleted.")
messages.success(
request, _("Item %s and his children were successfully deleted.")
% html_strong(self.object))
elif self.get_answer() == 'yes':
self.object.delete(with_children=False)
messages.success(request,
_("Item %s was successfully deleted.")
messages.success(
request, _("Item %s was successfully deleted.")
% html_strong(self.object))
@ -199,7 +196,8 @@ class AgendaPDF(PDFView):
ancestors = item.get_ancestors()
if ancestors:
space = " " * 6 * ancestors.count()
story.append(Paragraph("%s%s" % (space, item.get_title()),
story.append(Paragraph(
"%s%s" % (space, item.get_title()),
stylesheet['Subitem']))
else:
story.append(Paragraph(item.get_title(), stylesheet['Item']))
@ -213,10 +211,9 @@ def register_tab(request):
return Tab(
title=_('Agenda'),
url=reverse('item_overview'),
permission=request.user.has_perm('agenda.can_see_agenda')
or request.user.has_perm('agenda.can_manage_agenda'),
selected=selected,
)
permission=(request.user.has_perm('agenda.can_see_agenda') or
request.user.has_perm('agenda.can_manage_agenda')),
selected=selected)
def get_widgets(request):

View File

@ -11,7 +11,7 @@
"""
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.person import PersonFormField
@ -20,8 +20,8 @@ from openslides.assignment.models import Assignment
class AssignmentForm(forms.ModelForm, CssClassMixin):
posts = forms.IntegerField(min_value=1, initial=1,
label=_("Number of available posts"))
posts = forms.IntegerField(
min_value=1, initial=1, label=_("Number of available posts"))
class Meta:
model = Assignment
@ -39,8 +39,7 @@ class ConfigForm(forms.Form, CssClassMixin):
assignment_publish_winner_results_only = forms.BooleanField(
required=False,
label=_("Only publish voting results for selected winners "
"(Projector view only)")
)
"(Projector view only)"))
assignment_pdf_ballot_papers_selection = forms.ChoiceField(
widget=forms.Select(),
required=False,
@ -48,31 +47,25 @@ class ConfigForm(forms.Form, CssClassMixin):
choices=(
("NUMBER_OF_DELEGATES", _("Number of all delegates")),
("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(
widget=forms.TextInput(attrs={'class':'small-input'}),
widget=forms.TextInput(attrs={'class': 'small-input'}),
required=False,
min_value=1,
label=_("Custom number of ballot papers")
)
label=_("Custom number of ballot papers"))
assignment_pdf_title = forms.CharField(
widget=forms.TextInput(),
required=False,
label=_("Title for PDF document (all elections)")
)
label=_("Title for PDF document (all elections)"))
assignment_pdf_preamble = forms.CharField(
widget=forms.Textarea(),
required=False,
label=_("Preamble text for PDF document (all elections)")
)
assignment_poll_vote_values = forms.ChoiceField(widget=forms.Select(),
label=_("Preamble text for PDF document (all elections)"))
assignment_poll_vote_values = forms.ChoiceField(
widget=forms.Select(),
required=False,
label=_("Election method"),
choices=(
("auto", _("Automatic assign of method.")),
("votes", _("Always one option per candidate.")),
("yesnoabstain", _("Always Yes-No-Abstain per candidate.")),
)
)
("yesnoabstain", _("Always Yes-No-Abstain per candidate."))))

View File

@ -16,16 +16,12 @@ from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _, ugettext_noop
from openslides.utils.person import PersonField
from openslides.config.models import config
from openslides.config.signals import default_config_value
from openslides.projector.api import register_slidemodel
from openslides.projector.projector import SlideMixin
from openslides.poll.models import (BasePoll, CountInvalid, CountVotesCast,
BaseOption, PublishPollMixin, BaseVote)
from openslides.poll.models import (
BasePoll, CountInvalid, CountVotesCast, BaseOption, PublishPollMixin, BaseVote)
from openslides.agenda.models import Item
@ -51,11 +47,10 @@ class Assignment(models.Model, SlideMixin):
)
name = models.CharField(max_length=100, verbose_name=_("Name"))
description = models.TextField(null=True, blank=True,
verbose_name=_("Description"))
posts = models.PositiveSmallIntegerField(
verbose_name=_("Number of available posts"))
polldescription = models.CharField(max_length=100, null=True, blank=True,
description = models.TextField(null=True, blank=True, verbose_name=_("Description"))
posts = models.PositiveSmallIntegerField(verbose_name=_("Number of available posts"))
polldescription = models.CharField(
max_length=100, null=True, blank=True,
verbose_name=_("Comment on the ballot paper"))
status = models.CharField(max_length=3, choices=STATUS, default='sea')
@ -68,8 +63,8 @@ class Assignment(models.Model, SlideMixin):
if error:
raise NameError(_('%s is not a valid status.') % status)
if self.status == status:
raise NameError(_('The assignment status is already %s.')
% self.status)
raise NameError(
_('The assignment status is already %s.') % self.status)
self.status = status
self.save()
@ -116,14 +111,12 @@ class Assignment(models.Model, SlideMixin):
else:
candidation.delete()
def is_candidate(self, person):
"""
return True, if person is a candidate.
"""
try:
return self.assignment_candidates.filter(person=person) \
.exclude(blocked=True).exists()
return self.assignment_candidates.filter(person=person).exclude(blocked=True).exists()
except AttributeError:
return False
@ -131,8 +124,7 @@ class Assignment(models.Model, SlideMixin):
"""
return True, if the person is blockt for candidation.
"""
return self.assignment_candidates.filter(person=person) \
.filter(blocked=True).exists()
return self.assignment_candidates.filter(person=person).filter(blocked=True).exists()
@property
def assignment_candidates(self):
@ -164,7 +156,6 @@ class Assignment(models.Model, SlideMixin):
return participants
#return candidates.values_list('person', flat=True)
def set_elected(self, person, value=True):
candidate = self.assignment_candidates.get(person=person)
candidate.elected = value
@ -212,7 +203,6 @@ class Assignment(models.Model, SlideMixin):
vote_results_dict[candidate].append(votes)
return vote_results_dict
def get_agenda_title(self):
return self.name
@ -298,8 +288,7 @@ class AssignmentPoll(BasePoll, CountInvalid, CountVotesCast, PublishPollMixin):
self.yesnoabstain = False
self.save()
if self.yesnoabstain:
return [ugettext_noop('Yes'), ugettext_noop('No'),
ugettext_noop('Abstain')]
return [ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
else:
return [ugettext_noop('Votes')]

View File

@ -13,39 +13,31 @@
import os
from reportlab.lib import colors
from reportlab.platypus import (SimpleDocTemplate, PageBreak, Paragraph,
Spacer, Table, TableStyle)
from reportlab.platypus import (
SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle)
from reportlab.lib.units import cm
from django.conf import settings
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.shortcuts import redirect
from django.utils.translation import ungettext, ugettext as _
from openslides.utils.pdf import stylesheet
from openslides.utils.template import Tab
from openslides.utils.utils import (template, permission_required,
gen_confirm_form, del_confirm_form, ajax_request)
from openslides.utils.utils import (
template, permission_required, gen_confirm_form, del_confirm_form, ajax_request)
from openslides.utils.views import FormView, DeleteView, PDFView, RedirectView
from openslides.utils.person import get_person
from openslides.config.models import config
from openslides.participant.models import User
from openslides.projector.projector import Widget
from openslides.poll.views import PollFormView
from openslides.agenda.models import Item
from openslides.assignment.models import (Assignment, AssignmentPoll,
AssignmentOption)
from openslides.assignment.forms import (AssignmentForm, AssignmentRunForm,
ConfigForm)
from openslides.assignment.models import Assignment, AssignmentPoll
from openslides.assignment.forms import (
AssignmentForm, AssignmentRunForm, ConfigForm)
@permission_required('assignment.can_see_assignment')
@ -56,7 +48,7 @@ def get_overview(request):
query = query.filter(status__iexact=request.GET['status'])
try:
sort = request.GET['sort']
if sort in ['name','status']:
if sort in ['name', 'status']:
query = query.order_by(sort)
except KeyError:
pass
@ -91,7 +83,6 @@ def view(request, assignment_id=None):
if request.user.has_perm('assignment.can_nominate_other'):
form = AssignmentRunForm()
polls = assignment.poll_set.all()
if not request.user.has_perm('assignment.can_manage_assignment'):
polls = assignment.poll_set.filter(published=True)
@ -100,7 +91,8 @@ def view(request, assignment_id=None):
polls = assignment.poll_set.all()
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)]
return {
'assignment': assignment,
@ -180,7 +172,7 @@ def run(request, assignment_id):
assignment = Assignment.objects.get(pk=assignment_id)
try:
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:
messages.error(request, e)
return redirect(reverse('assignment_view', args=[assignment_id]))
@ -195,7 +187,8 @@ def delrun(request, assignment_id):
except Exception, e:
messages.error(request, e)
else:
messages.success(request,
messages.success(
request,
_("You have withdrawn your candidature successfully. "
"You can not be nominated by other participants anymore."))
else:
@ -240,7 +233,7 @@ def set_active(request, assignment_id):
@permission_required('assignment.can_manage_assignment')
def gen_poll(request, assignment_id):
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]))
@ -279,9 +272,9 @@ def set_publish_status(request, poll_id):
return ajax_request({'published': poll.published})
if poll.published:
messages.success(request, _("Ballot successfully published.") )
messages.success(request, _("Ballot successfully published."))
else:
messages.success(request, _("Ballot successfully unpublished.") )
messages.success(request, _("Ballot successfully unpublished."))
return redirect(reverse('assignment_view', args=[poll.assignment.id]))
@ -336,8 +329,9 @@ class AssignmentPDF(PDFView):
try:
assignment_id = self.kwargs['assignment_id']
assignment = Assignment.objects.get(id=assignment_id)
filename = u'%s-%s' % (_("Assignment"),
assignment.name.replace(' ','_'))
filename = u'%s-%s' % (
_("Assignment"),
assignment.name.replace(' ', '_'))
except:
filename = _("Elections")
return filename
@ -347,23 +341,24 @@ class AssignmentPDF(PDFView):
assignment_id = self.kwargs['assignment_id']
except KeyError:
assignment_id = None
if assignment_id is None: #print all assignments
if assignment_id is None: # print all assignments
title = config["assignment_pdf_title"]
story.append(Paragraph(title, stylesheet['Heading1']))
preamble = config["assignment_pdf_preamble"]
if preamble:
story.append(Paragraph("%s" % preamble.replace('\r\n', '<br/>'),
story.append(Paragraph(
"%s" % preamble.replace('\r\n', '<br/>'),
stylesheet['Paragraph']))
story.append(Spacer(0, 0.75 * cm))
assignments = Assignment.objects.all()
if not assignments: # No assignments existing
story.append(Paragraph(_("No assignments available."),
stylesheet['Heading3']))
else: # Print all assignments
if not assignments: # No assignments existing
story.append(Paragraph(
_("No assignments available."), stylesheet['Heading3']))
else: # Print all assignments
# List of assignments
for assignment in assignments:
story.append(Paragraph(assignment.name,
stylesheet['Heading3']))
story.append(Paragraph(
assignment.name, stylesheet['Heading3']))
# Assignment details (each assignment on single page)
for assignment in assignments:
story.append(PageBreak())
@ -376,28 +371,33 @@ class AssignmentPDF(PDFView):
def get_assignment(self, assignment, story):
# title
story.append(Paragraph(_("Election: %s") % assignment.name,
stylesheet['Heading1']))
story.append(Paragraph(
_("Election: %s") % assignment.name, stylesheet['Heading1']))
story.append(Spacer(0, 0.5 * cm))
# posts
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']))
cell1b = []
cell1b.append(Paragraph(str(assignment.posts), stylesheet['Paragraph']))
# candidates
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']))
cell2b = []
for candidate in assignment.candidates:
cell2b.append(Paragraph("<seq id='counter'/>.&nbsp; %s" % candidate,
cell2b.append(Paragraph(
"<seq id='counter'/>.&nbsp; %s" % candidate,
stylesheet['Signaturefield']))
if assignment.status == "sea":
for x in range(0, 2 * assignment.posts):
cell2b.append(Paragraph("<seq id='counter'/>.&nbsp; "
"__________________________________________",
stylesheet['Signaturefield']))
cell2b.append(
Paragraph(
"<seq id='counter'/>.&nbsp; "
"__________________________________________",
stylesheet['Signaturefield']))
cell2b.append(Spacer(0, 0.2 * cm))
# Vote results
@ -409,15 +409,15 @@ class AssignmentPDF(PDFView):
# Left side
cell3a = []
cell3a.append(Paragraph("%s:" % (_("Vote results")),
stylesheet['Heading4']))
cell3a.append(Paragraph(
"%s:" % (_("Vote results")), stylesheet['Heading4']))
if polls.count() == 1:
cell3a.append(Paragraph("%s %s" % (polls.count(), _("ballot")),
stylesheet['Normal']))
cell3a.append(Paragraph(
"%s %s" % (polls.count(), _("ballot")), stylesheet['Normal']))
elif polls.count() > 1:
cell3a.append(Paragraph("%s %s" % (polls.count(), _("ballots")),
stylesheet['Normal']))
cell3a.append(Paragraph(
"%s %s" % (polls.count(), _("ballots")), stylesheet['Normal']))
# Add table head row
headrow = []
@ -426,7 +426,6 @@ class AssignmentPDF(PDFView):
headrow.append("%s." % poll.get_ballot())
data_votes.append(headrow)
# Add result rows
elected_candidates = list(assignment.elected)
for candidate, poll_list in vote_results.iteritems():
@ -439,12 +438,13 @@ class AssignmentPDF(PDFView):
candidate_string += "\n(%s)" % candidate.name_suffix
row.append(candidate_string)
for vote in poll_list:
if vote == None:
if vote is None:
row.append('')
elif 'Yes' in vote and 'No' in vote and 'Abstain' in vote:
row.append(_("Y: %(YES)s\nN: %(NO)s\nA: %(ABSTAIN)s")
% {'YES':vote['Yes'], 'NO': vote['No'],
'ABSTAIN': vote['Abstain']})
row.append(
_("Y: %(YES)s\nN: %(NO)s\nA: %(ABSTAIN)s")
% {'YES': vote['Yes'], 'NO': vote['No'],
'ABSTAIN': vote['Abstain']})
elif 'Votes' in vote:
row.append(vote['Votes'])
else:
@ -465,15 +465,14 @@ class AssignmentPDF(PDFView):
footrow_two.append(poll.print_votescast())
data_votes.append(footrow_two)
table_votes=Table(data_votes)
table_votes.setStyle( TableStyle([
table_votes = Table(data_votes)
table_votes.setStyle(TableStyle([
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('VALIGN',(0, 0),(-1, -1), 'TOP'),
('LINEABOVE',(0, 0),(-1, 0), 2, colors.black),
('LINEABOVE',(0, 1),(-1, 1), 1, colors.black),
('LINEBELOW',(0, -1),(-1, -1), 2, colors.black),
('ROWBACKGROUNDS', (0, 1), (-1, -1), (colors.white, (.9, .9, .9))),
]))
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('LINEABOVE', (0, 0), (-1, 0), 2, colors.black),
('LINEABOVE', (0, 1), (-1, 1), 1, colors.black),
('LINEBELOW', (0, -1), (-1, -1), 2, colors.black),
('ROWBACKGROUNDS', (0, 1), (-1, -1), (colors.white, (.9, .9, .9)))]))
# table
data = []
@ -484,17 +483,18 @@ class AssignmentPDF(PDFView):
else:
data.append([cell2a, cell2b])
data.append([Spacer(0, 0.2 * cm), ''])
t=Table(data)
t = Table(data)
t._argW[0] = 4.5 * cm
t._argW[1] = 11 * cm
t.setStyle(TableStyle([ ('BOX', (0,0), (-1, -1), 1, colors.black),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
]))
t.setStyle(TableStyle([
('BOX', (0, 0), (-1, -1), 1, colors.black),
('VALIGN', (0, 0), (-1, -1), 'TOP')]))
story.append(t)
story.append(Spacer(0, 1 * cm))
# text
story.append(Paragraph("%s" % assignment.description.replace('\r\n',
story.append(Paragraph(
"%s" % assignment.description.replace('\r\n',
'<br/>'), stylesheet['Paragraph']))
@ -519,13 +519,15 @@ class AssignmentPollPDF(PDFView):
return super(AssignmentPollPDF, self).get(request, *args, **kwargs)
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())
return filename
def get_template(self, buffer):
return SimpleDocTemplate(buffer, topMargin=-6, bottomMargin=-6,
leftMargin=0, rightMargin=0, showBoundary=False)
return SimpleDocTemplate(
buffer, topMargin=-6, bottomMargin=-6, leftMargin=0, rightMargin=0,
showBoundary=False)
def build_document(self, pdf_document, story):
pdf_document.build(story)
@ -534,23 +536,27 @@ class AssignmentPollPDF(PDFView):
imgpath = os.path.join(settings.SITE_ROOT, 'static/images/circle.png')
circle = "<img src='%s' width='15' height='15'/>&nbsp;&nbsp;" % imgpath
cell = []
cell.append(Spacer(0,0.8*cm))
cell.append(Paragraph(_("Election") + ": " + self.poll.assignment.name,
cell.append(Spacer(0, 0.8 * cm))
cell.append(Paragraph(
_("Election") + ": " + self.poll.assignment.name,
stylesheet['Ballot_title']))
cell.append(Paragraph(self.poll.assignment.polldescription,
cell.append(Paragraph(
self.poll.assignment.polldescription,
stylesheet['Ballot_subtitle']))
options = self.poll.get_options()
ballot_string = _("%d. ballot") % self.poll.get_ballot()
candidate_string = ungettext("%d candidate", "%d candidates",
len(options)) % len(options)
available_posts_string = ungettext("%d available post", "%d available posts",
candidate_string = ungettext(
"%d candidate", "%d candidates", len(options)) % len(options)
available_posts_string = ungettext(
"%d available post", "%d available 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']))
cell.append(Spacer(0, 0.4 * cm))
data= []
data = []
# get ballot papers config values
ballot_papers_selection = config["assignment_pdf_ballot_papers_selection"]
ballot_papers_number = config["assignment_pdf_ballot_papers_number"]
@ -560,7 +566,7 @@ class AssignmentPollPDF(PDFView):
number = User.objects.filter(type__iexact="delegate").count()
elif ballot_papers_selection == "NUMBER_OF_ALL_PARTICIPANTS":
number = int(User.objects.count())
else: # ballot_papers_selection == "CUSTOM_NUMBER"
else: # ballot_papers_selection == "CUSTOM_NUMBER"
number = int(ballot_papers_number)
number = max(1, number)
@ -568,16 +574,18 @@ class AssignmentPollPDF(PDFView):
if self.poll.yesnoabstain:
for option in options:
candidate = option.candidate
cell.append(Paragraph(candidate.clean_name,
stylesheet['Ballot_option_name']))
cell.append(Paragraph(
candidate.clean_name, stylesheet['Ballot_option_name']))
if candidate.name_suffix:
cell.append(Paragraph("(%s)" % candidate.name_suffix,
cell.append(Paragraph(
"(%s)" % candidate.name_suffix,
stylesheet['Ballot_option_group']))
else:
cell.append(Paragraph("&nbsp;",
stylesheet['Ballot_option_group']))
cell.append(Paragraph(circle + _("Yes") + "&nbsp; " * 3 + circle
+ _("No") + "&nbsp; " * 3 + circle+ _("Abstention"),
cell.append(Paragraph(
"&nbsp;", stylesheet['Ballot_option_group']))
cell.append(Paragraph(
circle + _("Yes") + "&nbsp; " * 3 + circle
+ _("No") + "&nbsp; " * 3 + circle + _("Abstention"),
stylesheet['Ballot_option_YNA']))
# print ballot papers
for user in xrange(number / 2):
@ -594,14 +602,16 @@ class AssignmentPollPDF(PDFView):
else:
for option in options:
candidate = option.candidate
cell.append(Paragraph(circle + candidate.clean_name,
cell.append(Paragraph(
circle + candidate.clean_name,
stylesheet['Ballot_option_name']))
if candidate.name_suffix:
cell.append(Paragraph("(%s)" % candidate.name_suffix,
cell.append(Paragraph(
"(%s)" % candidate.name_suffix,
stylesheet['Ballot_option_group_right']))
else:
cell.append(Paragraph("&nbsp;",
stylesheet['Ballot_option_group_right']))
cell.append(Paragraph(
"&nbsp;", stylesheet['Ballot_option_group_right']))
# print ballot papers
for user in xrange(number / 2):
data.append([cell, cell])
@ -615,9 +625,9 @@ class AssignmentPollPDF(PDFView):
else:
t = Table(data, 10.5 * cm, 29.7 * cm)
t.setStyle(TableStyle([('GRID', (0, 0), (-1, -1), 0.25, colors.grey),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
]))
t.setStyle(TableStyle([
('GRID', (0, 0), (-1, -1), 0.25, colors.grey),
('VALIGN', (0, 0), (-1, -1), 'TOP')]))
story.append(t)
@ -629,16 +639,15 @@ class Config(FormView):
def get_initial(self):
return {
'assignment_publish_winner_results_only':
config['assignment_publish_winner_results_only'],
config['assignment_publish_winner_results_only'],
'assignment_pdf_ballot_papers_selection':
config['assignment_pdf_ballot_papers_selection'],
config['assignment_pdf_ballot_papers_selection'],
'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_preamble': config['assignment_pdf_preamble'],
'assignment_poll_vote_values':
config['assignment_poll_vote_values'],
}
config['assignment_poll_vote_values']}
def form_valid(self, form):
if form.cleaned_data['assignment_publish_winner_results_only']:
@ -655,8 +664,8 @@ class Config(FormView):
form.cleaned_data['assignment_pdf_preamble']
config['assignment_poll_vote_values'] = \
form.cleaned_data['assignment_poll_vote_values']
messages.success(self.request,
_('Election settings successfully saved.'))
messages.success(
self.request, _('Election settings successfully saved.'))
return super(Config, self).form_valid(form)
@ -665,10 +674,11 @@ def register_tab(request):
return Tab(
title=_('Elections'),
url=reverse('assignment_overview'),
permission=request.user.has_perm('assignment.can_see_assignment')
or request.user.has_perm('assignment.can_nominate_other')
or request.user.has_perm('assignment.can_nominate_self')
or request.user.has_perm('assignment.can_manage_assignment'),
permission=(
request.user.has_perm('assignment.can_see_assignment') or
request.user.has_perm('assignment.can_nominate_other') or
request.user.has_perm('assignment.can_nominate_self') or
request.user.has_perm('assignment.can_manage_assignment')),
selected=selected,
)

View File

@ -15,8 +15,6 @@ from django.utils.translation import ugettext_lazy as _
from openslides.utils.forms import CssClassMixin
from openslides.config.models import config
class GeneralConfigForm(forms.Form, CssClassMixin):
event_name = forms.CharField(

View File

@ -51,7 +51,7 @@ class Config(object):
pass
for receiver, value in default_config_value.send(sender='config',
key=key):
key=key):
if value is not None:
return value
if settings.DEBUG:
@ -69,7 +69,6 @@ class Config(object):
def __contains__(self, item):
return ConfigStore.objects.filter(key=item).exists()
config = Config()
@ -81,7 +80,7 @@ def default_config(sender, key, **kwargs):
return {
'event_name': 'OpenSlides',
'event_description':
_('Presentation and assembly system'),
_('Presentation and assembly system'),
'event_date': '',
'event_location': '',
'event_organizer': '',
@ -123,11 +122,9 @@ def set_submenu(sender, request, context, **kwargs):
(reverse('config_%s' % appname), _(title), selected)
)
menu_links.append (
(reverse('config_version'), _('Version'),
request.path == reverse('config_version'))
)
menu_links.append((
reverse('config_version'), _('Version'),
request.path == reverse('config_version')))
context.update({
'menu_links': menu_links,
})
'menu_links': menu_links})

View File

@ -17,12 +17,12 @@ from django.utils.importlib import import_module
from django.utils.translation import ugettext as _
from openslides import get_version
from openslides.utils.template import Tab
from openslides.utils.views import FormView, TemplateView
from .forms import GeneralConfigForm
from .models import config
from openslides.config.forms import GeneralConfigForm
from openslides.config.models import config
# TODO: Do not import the participant module in config
from openslides.participant.api import get_or_create_anonymous_group
@ -61,12 +61,12 @@ class GeneralConfig(FormView):
# system
if form.cleaned_data['system_enable_anonymous']:
config['system_enable_anonymous'] = True
anonymous = get_or_create_anonymous_group()
get_or_create_anonymous_group()
else:
config['system_enable_anonymous'] = False
messages.success(self.request,
_('General settings successfully saved.'))
messages.success(
self.request, _('General settings successfully saved.'))
return super(GeneralConfig, self).form_valid(form)

View File

@ -13,15 +13,12 @@
import os
import sys
_fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
def _fs2unicode(s):
if isinstance(s, unicode):
return s
return s.decode(_fs_encoding)
from openslides.main import fs2unicode
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',)
LOGIN_URL = '/login/'
@ -48,12 +45,12 @@ USE_I18N = True
USE_L10N = True
LOCALE_PATHS = (
_fs2unicode(os.path.join(SITE_ROOT, 'locale')),
fs2unicode(os.path.join(SITE_ROOT, 'locale')),
)
# Absolute path to the directory that holds media.
# 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
# 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``
# 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
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://static.lawrence.com", "http://example.com/static/"
STATIC_URL = '/static/'
STATIC_URL = '/static/'
# Additional directories containing static files (not application specific)
# Examples: "/home/media/lawrence.com/extra-static/"
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)
@ -106,7 +103,7 @@ TEMPLATE_DIRS = (
# "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# 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 = (
@ -142,3 +139,6 @@ CACHES = {
'LOCATION': 'openslidecache'
}
}
TEST_RUNNER = 'discover_runner.DiscoverRunner'
TEST_DISCOVER_TOP_LEVEL = os.path.dirname(os.path.dirname(__file__))

View File

@ -72,15 +72,6 @@ KEY_LENGTH = 30
_portable_db_path = object()
_fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
def _fs2unicode(s):
if isinstance(s, unicode):
return s
return s.decode(_fs_encoding)
def process_options(argv=None):
if argv is None:
argv = sys.argv[1:]
@ -95,9 +86,11 @@ def process_options(argv=None):
parser.add_option(
"--reset-admin", action="store_true",
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(
"--no-reload", action="store_true", help="Do not reload the development server")
"-s", "--settings", help="Path to the openslides configuration.")
parser.add_option(
"--no-reload", action="store_true",
help="Do not reload the development server")
opts, args = parser.parse_args(argv)
if args:
@ -186,7 +179,7 @@ def create_settings(settings_path, database_path=None):
else:
if database_path is None:
database_path = get_user_data_path('openslides', 'database.sqlite')
dbpath_value = repr(_fs2unicode(database_path))
dbpath_value = repr(fs2unicode(database_path))
settings_content = CONFIG_TEMPLATE % dict(
default_key=base64.b64encode(os.urandom(KEY_LENGTH)),
@ -312,6 +305,13 @@ def start_browser(url):
t.start()
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)
@ -319,7 +319,7 @@ def get_user_config_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)
return os.path.join(fs2unicode(config_home), *args)
def get_user_data_path(*args):
@ -330,7 +330,7 @@ def get_user_data_path(*args):
'XDG_DATA_HOME', os.path.join(
os.path.expanduser('~'), '.local', 'share'))
return os.path.join(_fs2unicode(data_home), *args)
return os.path.join(fs2unicode(data_home), *args)
def get_portable_path(*args):
@ -344,7 +344,7 @@ def get_portable_path(*args):
"Cannot determine portable path when "
"not running as portable")
portable_dir = _fs2unicode(os.path.dirname(os.path.abspath(sys.executable)))
portable_dir = fs2unicode(os.path.dirname(os.path.abspath(sys.executable)))
return os.path.join(portable_dir, *args)

View File

@ -11,7 +11,7 @@
"""
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.person import PersonFormField, MultiplePersonFormField
@ -21,18 +21,18 @@ from openslides.motion.models import Motion
class MotionForm(forms.Form, CssClassMixin):
title = forms.CharField(widget=forms.TextInput(), label=_("Title"))
text = forms.CharField(widget=forms.Textarea(), label=_("Text"))
reason = forms.CharField(widget=forms.Textarea(), required=False,
label=_("Reason"))
reason = forms.CharField(
widget=forms.Textarea(), required=False, label=_("Reason"))
class MotionFormTrivialChanges(MotionForm):
trivial_change = forms.BooleanField(required=False,
label=_("Trivial change"),
trivial_change = forms.BooleanField(
required=False, label=_("Trivial change"),
help_text=_("Trivial changes don't create a new version."))
class MotionManagerForm(forms.ModelForm, CssClassMixin):
submitter = PersonFormField(label = _("Submitter"))
submitter = PersonFormField(label=_("Submitter"))
class Meta:
model = Motion
@ -46,20 +46,20 @@ class MotionManagerFormSupporter(MotionManagerForm):
class MotionImportForm(forms.Form, CssClassMixin):
csvfile = forms.FileField(
widget=forms.FileInput(attrs={'size':'50'}),
widget=forms.FileInput(attrs={'size': '50'}),
label=_("CSV File"),
)
import_permitted = forms.BooleanField(
required=False,
label=_("Import motions with status \"authorized\""),
help_text=_('Set the initial status for each motion to '
'"authorized"'),
'"authorized"'),
)
class ConfigForm(forms.Form, CssClassMixin):
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"),
initial=4,
min_value=0,
@ -82,7 +82,7 @@ class ConfigForm(forms.Form, CssClassMixin):
]
)
motion_pdf_ballot_papers_number = forms.IntegerField(
widget=forms.TextInput(attrs={'class':'small-input'}),
widget=forms.TextInput(attrs={'class': 'small-input'}),
required=False,
min_value=1,
label=_("Custom number of ballot papers")
@ -101,6 +101,6 @@ class ConfigForm(forms.Form, CssClassMixin):
motion_allow_trivial_change = forms.BooleanField(
label=_("Allow trivial changes"),
help_text=_('Warning: Trivial changes undermine the motions '
'autorisation system.'),
'autorisation system.'),
required=False,
)

View File

@ -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.person import PersonField
from openslides.config.models import config
from openslides.config.signals import default_config_value
from openslides.poll.models import (BaseOption, BasePoll, CountVotesCast,
CountInvalid, BaseVote)
from openslides.participant.models import User, Group
from openslides.poll.models import (
BaseOption, BasePoll, CountVotesCast, CountInvalid, BaseVote)
from openslides.participant.models import User
from openslides.projector.api import register_slidemodel
from openslides.projector.models import SlideMixin
from openslides.agenda.models import Item
@ -53,7 +48,7 @@ class Motion(models.Model, SlideMixin):
('noc', _('Not Concerned')),
('com', _('Commited a bill')),
('nop', _('Rejected (not authorized)')),
('rev', _('Needs Review')), # Where is this status used?
('rev', _('Needs Review')), # Where is this status used?
#additional actions:
# edit
# delete
@ -67,9 +62,9 @@ class Motion(models.Model, SlideMixin):
submitter = PersonField(verbose_name=_("Submitter"))
number = models.PositiveSmallIntegerField(blank=True, null=True,
unique=True)
unique=True)
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)
log = models.TextField(blank=True, null=True)
@ -94,7 +89,7 @@ class Motion(models.Model, SlideMixin):
else:
return self.last_version
def accept_version(self, version, user = None):
def accept_version(self, version, user=None):
"""
accept a Version
"""
@ -102,15 +97,15 @@ class Motion(models.Model, SlideMixin):
self.save(nonewversion=True)
version.rejected = False
version.save()
self.writelog(_("Version %d authorized") % (version.aid, ),
user)
self.writelog(_("Version %d authorized") % version.aid, user)
def reject_version(self, version, user = None):
def reject_version(self, version, user=None):
if version.id > self.permitted.id:
version.rejected = True
version.save()
self.writelog(pgettext("Rejected means not authorized", "Version %d rejected")
% (version.aid, ), user)
self.writelog(pgettext(
"Rejected means not authorized", "Version %d rejected")
% version.aid, user)
return True
return False
@ -154,8 +149,8 @@ class Motion(models.Model, SlideMixin):
is not the lastone and the lastone is not rejected.
TODO: rename the property in unchecked__changes
"""
if (self.last_version != self.permitted
and not self.last_version.rejected):
if (self.last_version != self.permitted and
not self.last_version.rejected):
return True
else:
return False
@ -207,7 +202,8 @@ class Motion(models.Model, SlideMixin):
last_version = self.last_version
fields = ["text", "title", "reason"]
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 not changed_fields:
return # No changes
@ -219,19 +215,22 @@ class Motion(models.Model, SlideMixin):
last_version.save()
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]
self.writelog(_("Trivial changes to version %(version)d; "
"changed fields: %(changed_fields)s")
% dict(version = last_version.aid,
changed_fields = ", ".join(field_names)))
return # Done
self.writelog(
_("Trivial changes to version %(version)d; "
"changed fields: %(changed_fields)s")
% dict(version=last_version.aid,
changed_fields=", ".join(field_names)))
return # Done
version = AVersion(title=getattr(self, 'title', ''),
text=getattr(self, 'text', ''),
reason=getattr(self, 'reason', ''),
motion=self)
version = AVersion(
title=getattr(self, 'title', ''),
text=getattr(self, 'text', ''),
reason=getattr(self, 'reason', ''),
motion=self)
version.save()
self.writelog(_("Version %s created") % version.aid, user)
is_manager = user.has_perm('motion.can_manage_motion')
@ -239,9 +238,8 @@ class Motion(models.Model, SlideMixin):
is_manager = False
supporters = self.motionsupporter_set.all()
if (self.status == "pub"
and supporters
and not is_manager):
if (self.status == "pub" and
supporters and not is_manager):
supporters.delete()
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
"""
try:
object = self.motionsupporter_set.get(person=person).delete()
self.motionsupporter_set.get(person=person).delete()
except MotionSupporter.DoesNotExist:
# TODO: Don't do nothing but raise a precise exception for the view
pass
@ -288,8 +286,7 @@ class Motion(models.Model, SlideMixin):
raise NameError('This motion has already a number.')
if number is None:
try:
number = Motion.objects.aggregate(Max('number')) \
['number__max'] + 1
number = Motion.objects.aggregate(Max('number'))['number__max'] + 1
except TypeError:
number = 1
self.number = number
@ -316,8 +313,6 @@ class Motion(models.Model, SlideMixin):
"""
self.set_status(user, "nop")
#TODO: reject last version
aversion = self.last_version
#self.permitted = aversion
if self.number is None:
self.set_number()
self.save()
@ -333,11 +328,11 @@ class Motion(models.Model, SlideMixin):
error = False
break
if error:
#TODO: Use the Right Error
# TODO: Use the Right Error
raise NameError(_('%s is not a valid status.') % status)
if self.status == status:
#TODO: Use the Right Error
raise NameError(_('The motion status is already \'%s.\'') \
# TODO: Use the Right Error
raise NameError(_('The motion status is already \'%s.\'')
% self.status)
actions = []
@ -353,7 +348,7 @@ class Motion(models.Model, SlideMixin):
oldstatus = self.get_status_display()
self.status = status
self.save()
self.writelog(_("Status modified")+": %s -> %s" \
self.writelog(_("Status modified") + ": %s -> %s"
% (oldstatus, self.get_status_display()), user)
def get_allowed_actions(self, user):
@ -432,10 +427,9 @@ class Motion(models.Model, SlideMixin):
allready a number
"""
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.')
for item in Item.objects.filter(related_sid=self.sid):
item.delete()
super(Motion, self).delete()
@ -501,12 +495,12 @@ class Motion(models.Model, SlideMixin):
for poll in self.polls:
for option in poll.get_options():
if option.get_votes().exists():
results.append((option['Yes'], option['No'],
results.append((
option['Yes'], option['No'],
option['Abstain'], poll.print_votesinvalid(),
poll.print_votescast()))
return results
def slide(self):
"""
return the slide dict
@ -542,10 +536,10 @@ class Motion(models.Model, SlideMixin):
class AVersion(models.Model):
title = models.CharField(max_length=100, verbose_name = _("Title"))
text = models.TextField(verbose_name = _("Text"))
reason = models.TextField(null=True, blank=True, verbose_name = _("Reason"))
rejected = models.BooleanField() # = Not Permitted
title = models.CharField(max_length=100, verbose_name=_("Title"))
text = models.TextField(verbose_name=_("Text"))
reason = models.TextField(null=True, blank=True, verbose_name=_("Reason"))
rejected = models.BooleanField() # = Not Permitted
time = models.DateTimeField(auto_now=True)
motion = models.ForeignKey(Motion)
@ -576,8 +570,8 @@ class MotionOption(BaseOption):
class MotionPoll(BasePoll, CountInvalid, CountVotesCast):
option_class = MotionOption
vote_values = [ugettext_noop('Yes'), ugettext_noop('No'),
ugettext_noop('Abstain')]
vote_values = [
ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
motion = models.ForeignKey(Motion)

View File

@ -18,19 +18,17 @@ import os
try:
from urlparse import parse_qs
except ImportError: # python <= 2.5
except ImportError: # python <= 2.5
from cgi import parse_qs
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.platypus import (SimpleDocTemplate, PageBreak, Paragraph, Spacer,
Table, TableStyle)
from reportlab.platypus import (
SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle)
from django.conf import settings
from django.contrib import messages
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.db import transaction
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.pdf import stylesheet
from openslides.utils.template import Tab
from openslides.utils.utils import (template, permission_required,
del_confirm_form, gen_confirm_form)
from openslides.utils.views import (PDFView, RedirectView, DeleteView,
FormView, SingleObjectMixin, QuestionMixin)
from openslides.utils.utils import (
template, permission_required, del_confirm_form, gen_confirm_form)
from openslides.utils.views import (
PDFView, RedirectView, DeleteView, FormView, SingleObjectMixin,
QuestionMixin)
from openslides.utils.person import get_person
from openslides.config.models import config
from openslides.projector.projector import Widget
from openslides.poll.views import PollFormView
from openslides.participant.api import gen_username, gen_password
from openslides.participant.models import User, Group
from openslides.agenda.models import Item
from openslides.motion.models import Motion, AVersion, MotionPoll
from openslides.motion.forms import (MotionForm,
MotionFormTrivialChanges, MotionManagerForm,
from openslides.motion.forms import (
MotionForm, MotionFormTrivialChanges, MotionManagerForm,
MotionManagerFormSupporter, MotionImportForm, ConfigForm)
@ -124,14 +117,14 @@ def overview(request):
for (i, motion) in enumerate(motions):
try:
motions[i] = {
'actions' : motion.get_allowed_actions(request.user),
'motion' : motion
'actions': motion.get_allowed_actions(request.user),
'motion': motion
}
except:
# todo: except what?
motions[i] = {
'actions' : [],
'motion' : motion
'actions': [],
'motion': motion
}
return {
@ -210,12 +203,7 @@ def edit(request, motion_id=None):
managerform = None
if valid:
del_supporters = True
if is_manager:
if motion: # Edit motion
original_supporters = list(motion.supporters)
else:
original_supporters = []
motion = managerform.save(commit=False)
elif motion_id is None:
motion = Motion(submitter=request.user)
@ -611,7 +599,7 @@ def motion_import(request):
except ValueError:
messages.error(request, _('Ignoring malformed line %d in import file.') % (lno + 1))
continue
if is_group:
# fetch existing groups or issue an error message
try:
@ -694,7 +682,7 @@ def motion_import(request):
return redirect(reverse('motion_overview'))
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:
messages.error(request, _('Import file has wrong character encoding, only UTF-8 is supported!'))
else:

View File

@ -14,11 +14,11 @@
from __future__ import with_statement
from random import choice
import string
import csv
from django.contrib.auth.models import Permission
from django.db import transaction
from django.utils.translation import ugettext as _
from openslides.utils import csv_ext
@ -78,7 +78,7 @@ def import_users(csv_file):
try:
(first_name, last_name, gender, structure_level, type, committee, comment) = line[:7]
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
user = User()
user.last_name = last_name
@ -105,7 +105,7 @@ def get_or_create_registered_group():
name__iexact='Registered', defaults={'name': 'Registered'})
if created:
registered.permissions = Permission.objects.filter(
codename__in=DEFAULT_PERMS)
codename__in=DEFAULT_PERMS)
registered.save()
return registered
@ -115,6 +115,6 @@ def get_or_create_anonymous_group():
name__iexact='Anonymous', defaults={'name': 'Anonymous'})
if created:
anonymous.permissions = Permission.objects.filter(
codename__in=DEFAULT_PERMS)
codename__in=DEFAULT_PERMS)
anonymous.save()
return anonymous

View File

@ -66,12 +66,13 @@ class GroupForm(forms.ModelForm, CssClassMixin):
instance = forms.ModelForm.save(self, False)
old_save_m2m = self.save_m2m
def save_m2m():
old_save_m2m()
instance.user_set.clear()
for user in self.cleaned_data['users']:
instance.user_set.add(user)
def save_m2m():
old_save_m2m()
instance.user_set.clear()
for user in self.cleaned_data['users']:
instance.user_set.add(user)
self.save_m2m = save_m2m
if commit:
@ -102,7 +103,7 @@ class GroupForm(forms.ModelForm, CssClassMixin):
class UsersettingsForm(forms.ModelForm, CssClassMixin):
class Meta:
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):

View File

@ -25,8 +25,9 @@ from openslides.config.signals import default_config_value
from openslides.projector.api import register_slidemodel
from openslides.projector.projector import SlideMixin
class User(DjangoUser, PersonMixin, Person, SlideMixin):
prefix = 'user' # This is for the slides
prefix = 'user' # This is for the slides
person_prefix = 'user'
GENDER_CHOICES = (
('male', _('Male')),
@ -131,8 +132,9 @@ class User(DjangoUser, PersonMixin, Person, SlideMixin):
register_slidemodel(User)
class Group(DjangoGroup, PersonMixin, Person, SlideMixin):
prefix = 'group' # This is for the slides
prefix = 'group' # This is for the slides
person_prefix = 'group'
django_group = models.OneToOneField(DjangoGroup, editable=False, parent_link=True)
@ -173,6 +175,7 @@ class Group(DjangoGroup, PersonMixin, Person, SlideMixin):
register_slidemodel(Group)
class UsersAndGroupsToPersons(object):
"""
Object to send all Users and Groups or a special User or Group to
@ -216,8 +219,9 @@ def receive_persons(sender, **kwargs):
"""
Answers to the Person-API
"""
return UsersAndGroupsToPersons(person_prefix_filter=kwargs['person_prefix_filter'],
id_filter=kwargs['id_filter'])
return UsersAndGroupsToPersons(
person_prefix_filter=kwargs['person_prefix_filter'],
id_filter=kwargs['id_filter'])
@receiver(default_config_value, dispatch_uid="participant_default_config")

View File

@ -11,7 +11,6 @@
"""
from django.conf.urls.defaults import url, patterns
from django.core.urlresolvers import reverse
from openslides.participant.views import (
UserOverview, UserCreateView, UserDetailView, UserUpdateView,

View File

@ -28,7 +28,6 @@ from reportlab.platypus import (
from django.contrib import messages
from django.contrib.auth.decorators import login_required
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.core.urlresolvers import reverse
from django.shortcuts import redirect
@ -39,16 +38,12 @@ from openslides.utils.template import Tab
from openslides.utils.utils import (
template, decodedict, encodedict, delete_default_permissions, html_strong)
from openslides.utils.views import (
FormView, PDFView, CreateView, UpdateView, DeleteView,
RedirectView, SingleObjectMixin, ListView, QuestionMixin)
FormView, PDFView, CreateView, UpdateView, DeleteView, PermissionMixin,
RedirectView, SingleObjectMixin, ListView, QuestionMixin, DetailView)
from openslides.config.models import config
from openslides.projector.projector import Widget
from openslides.motion.models import Motion
from openslides.assignment.models import Assignment
from openslides.participant.api import gen_username, gen_password, import_users
from openslides.participant.forms import (
UserCreateForm, UserUpdateForm, UsersettingsForm,
@ -125,11 +120,13 @@ class UserOverview(ListView):
percent = 0
# list of all existing categories
structure_levels = [p['structure_level'] for p in User.objects.values('structure_level')
.exclude(structure_level='').distinct()]
structure_levels = [
p['structure_level'] for p in
User.objects.values('structure_level').exclude(structure_level='').distinct()]
# list of all existing committees
committees = [p['committee'] for p in User.objects.values('committee')
.exclude(committee='').distinct()]
committees = [
p['committee'] for p in
User.objects.values('committee').exclude(committee='').distinct()]
# context vars
context.update({
'allusers': all_users,
@ -137,13 +134,13 @@ class UserOverview(ListView):
'percent': round(percent, 1),
'structure_levels': structure_levels,
'committees': committees,
'cookie': ['participant_sortfilter', urlencode(decodedict(self.sortfilter),
'cookie': [
'participant_sortfilter', urlencode(decodedict(self.sortfilter),
doseq=True)],
'sortfilter': self.sortfilter})
return context
from openslides.utils.views import DetailView, PermissionMixin
class UserDetailView(DetailView, PermissionMixin):
"""
Classed based view to show a specific user in the interface.
@ -177,8 +174,8 @@ class UserCreateView(CreateView):
apply_url = 'user_edit'
def manipulate_object(self, form):
self.object.username = gen_username(form.cleaned_data['first_name'],
form.cleaned_data['last_name'])
self.object.username = gen_username(
form.cleaned_data['first_name'], form.cleaned_data['last_name'])
if not self.object.default_password:
self.object.default_password = gen_password()
self.object.set_password(self.object.default_password)
@ -211,6 +208,7 @@ class UserDeleteView(DeleteView):
else:
super(UserDeleteView, self).pre_redirect(request, *args, **kwargs)
class SetUserStatusView(RedirectView, SingleObjectMixin):
"""
Activate or deactivate an user.
@ -547,8 +545,9 @@ def register_tab(request):
return Tab(
title=_('Participants'),
url=reverse('user_overview'),
permission=request.user.has_perm('participant.can_see_participant') or
request.user.has_perm('participant.can_manage_participant'),
permission=(
request.user.has_perm('participant.can_see_participant') or
request.user.has_perm('participant.can_manage_participant')),
selected=selected)
@ -569,12 +568,12 @@ def get_personal_info_widget(request):
and where you are supporter or candidate.
"""
personal_info_context = {
'submitted_motions': Motion.objects.filter(submitter=request.user),
'config_motion_min_supporters': config['motion_min_supporters'],
'supported_motions': Motion.objects.filter(motionsupporter=request.user),
'assignments': Assignment.objects.filter(
assignmentcandidate__person=request.user,
assignmentcandidate__blocked=False),}
'submitted_motions': Motion.objects.filter(submitter=request.user),
'config_motion_min_supporters': config['motion_min_supporters'],
'supported_motions': Motion.objects.filter(motionsupporter=request.user),
'assignments': Assignment.objects.filter(
assignmentcandidate__person=request.user,
assignmentcandidate__blocked=False)}
return Widget(
name='personal_info',
display_name=_('My motions and elections'),
@ -593,7 +592,7 @@ def get_user_widget(request):
name='user',
display_name=_('Participants'),
template='participant/user_widget.html',
context={'users': User.objects.all(),},
context={'users': User.objects.all()},
permission_required='projector.can_manage_projector',
default_column=1)
@ -607,6 +606,6 @@ def get_group_widget(request):
name='group',
display_name=_('Groups'),
template='participant/group_widget.html',
context={'groups': Group.objects.all(),},
context={'groups': Group.objects.all()},
permission_required='projector.can_manage_projector',
default_column=1)

View File

@ -11,7 +11,6 @@
"""
from django import forms
from django.utils.translation import ugettext_lazy as _
from openslides.utils.forms import CssClassMixin

View File

@ -47,7 +47,7 @@ class BaseVote(models.Model):
Subclasses have to define a option-field, which are a subclass of
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)
def print_weight(self, raw=False):
@ -73,7 +73,7 @@ class BaseVote(models.Model):
class CountVotesCast(models.Model):
votescast = MinMaxIntegerField(null=True, blank=True, min_value=-2,
verbose_name=_("Votes cast"))
verbose_name=_("Votes cast"))
def append_pollform_fields(self, fields):
fields.append('votescast')
@ -92,7 +92,7 @@ class CountVotesCast(models.Model):
class CountInvalid(models.Model):
votesinvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
verbose_name=_("Votes invalid"))
verbose_name=_("Votes invalid"))
def append_pollform_fields(self, fields):
fields.append('votesinvalid')
@ -164,7 +164,6 @@ class BasePoll(models.Model):
"""
return self.vote_values
def get_vote_class(self):
"""
Return the releatet vote class.
@ -212,7 +211,7 @@ class BasePoll(models.Model):
"""
from openslides.poll.forms import OptionForm
return OptionForm(extra=self.get_form_values(kwargs['formid']),
**kwargs)
**kwargs)
def get_vote_forms(self, **kwargs):
"""

View File

@ -35,7 +35,7 @@ class PollFormView(TemplateView):
context['forms'] = self.poll.get_vote_forms()
FormClass = self.get_modelform_class()
context['pollform'] = FormClass(instance=self.poll,
prefix='pollform')
prefix='pollform')
return context
def get_success_url(self):
@ -52,7 +52,7 @@ class PollFormView(TemplateView):
FormClass = self.get_modelform_class()
pollform = FormClass(data=self.request.POST, instance=self.poll,
prefix='pollform')
prefix='pollform')
error = False
for form in option_forms:

View File

@ -12,12 +12,11 @@
from django.conf import settings
from django.core.cache import cache
from django.template.loader import render_to_string
from django.utils.datastructures import SortedDict
from django.utils.importlib import import_module
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):
@ -95,27 +94,18 @@ def clear_projector_cache():
cache.delete('projector_data')
def register_slidemodel(model, model_name=None, control_template=None,
weight=0):
def register_slidemodel(model, model_name=None, control_template=None, weight=0):
"""
Register a Model as a slide.
"""
# TODO: control_template should never be None
if model_name is None:
model_name = model.prefix
if control_template is None:
control_template = 'projector/default_control_slidemodel.html'
category = model.__module__.split('.')[0]
SLIDE[model_name] = Slide(
model_slide=True,
model=model,
category=category,
key=model.prefix,
model_name=model_name,
control_template=control_template,
weight=weight,
)
SLIDE[model_name] = Slide(model_slide=True, model=model, 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=''):
@ -125,15 +115,9 @@ def register_slidefunc(key, func, control_template=None, weight=0, name=''):
if control_template is None:
control_template = 'projector/default_control_slidefunc.html'
category = func.__module__.split('.')[0]
SLIDE[key] = Slide(
model_slide=False,
func=func,
category=category,
key=key,
control_template=control_template,
weight=weight,
name=name,
)
SLIDE[key] = Slide(model_slide=False, func=func, category=category,
key=key, control_template=control_template, weight=weight,
name=name,)
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')
except ProjectorOverlay.DoesNotExist:
overlay = ProjectorOverlay(def_name='Message', active=False)
overlay.sid=sid
overlay.sid = sid
overlay.save()
@ -166,7 +150,6 @@ def get_all_widgets(request, session=False):
mod = import_module(app + '.views')
except ImportError:
continue
appname = mod.__name__.split('.')[0]
try:
modul_widgets = mod.get_widgets(request)
except AttributeError:

View File

@ -11,7 +11,6 @@
"""
from django import forms
from django.utils.translation import ugettext_lazy as _
from openslides.utils.forms import CssClassMixin

View File

@ -19,9 +19,6 @@ from openslides.config.signals import default_config_value
from openslides.projector.api import register_slidemodel
from openslides.projector.projector import SlideMixin
from openslides.config.models import config
class ProjectorSlide(models.Model, SlideMixin):
"""
@ -56,8 +53,7 @@ class ProjectorSlide(models.Model, SlideMixin):
)
register_slidemodel(ProjectorSlide,
control_template='projector/control_customslide.html')
register_slidemodel(ProjectorSlide, control_template='projector/control_customslide.html')
class ProjectorOverlay(models.Model):

View File

@ -19,9 +19,9 @@ from openslides.config.models import config
from openslides.projector.signals import projector_overlays
SLIDE = {}
class SlideMixin(object):
"""
A Mixin for a Django-Model, for making the model a slide.
@ -49,13 +49,14 @@ class SlideMixin(object):
"""
Return True, if the the slide is the active slide.
"""
from api import get_active_slide
from openslides.projector.api import get_active_slide
return get_active_slide(only_sid=True) == self.sid
def set_active(self):
"""
Appoint this item as the active slide.
"""
from openslides.projector.api import set_active_slide
set_active_slide(self.sid)
def save(self, *args, **kwargs):
@ -112,7 +113,7 @@ class Widget(object):
Class for a Widget for the Projector-Tab.
"""
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
if display_name is None:
self.display_name = name.capitalize()

View File

@ -13,34 +13,28 @@
from datetime import datetime
from time import time
from django.conf import settings
from django.contrib import messages
from django.core.cache import cache
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
from django.db import transaction
from django.db.models import Q
from django.dispatch import receiver
from django.shortcuts import redirect
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 openslides.utils.template import render_block_to_string, Tab
from openslides.utils.utils import html_strong
from openslides.utils.views import (TemplateView, RedirectView, CreateView,
UpdateView, DeleteView, AjaxMixin)
from openslides.utils.views import (
TemplateView, RedirectView, CreateView, UpdateView, DeleteView, AjaxMixin)
from openslides.config.models import config
from openslides.projector.api import (get_active_slide, set_active_slide,
projector_message_set, projector_message_delete, get_slide_from_sid,
get_all_widgets, clear_projector_cache)
from openslides.projector.forms import SelectWidgetsForm
from openslides.projector.models import ProjectorOverlay, ProjectorSlide
from openslides.projector.projector import SLIDE, Widget
from openslides.projector.signals import projector_overlays
from .api import (
get_active_slide, set_active_slide, projector_message_set,
projector_message_delete, get_slide_from_sid, get_all_widgets,
clear_projector_cache)
from .forms import SelectWidgetsForm
from .models import ProjectorOverlay, ProjectorSlide
from .projector import Widget
from .signals import projector_overlays
class DashboardView(TemplateView, AjaxMixin):
@ -73,7 +67,7 @@ class Projector(TemplateView, AjaxMixin):
if sid is None:
try:
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
ajax = 'on'
active_sid = get_active_slide(True)
@ -92,10 +86,10 @@ class Projector(TemplateView, AjaxMixin):
# Projector Overlays
if self.kwargs['sid'] is None:
active_defs = ProjectorOverlay.objects.filter(active=True) \
.filter(Q(sid=active_sid) | Q(sid=None)).values_list('def_name',
flat=True)
for receiver, response in projector_overlays.send(sender=sid,
register=False, call=active_defs):
.filter(Q(sid=active_sid) | Q(sid=None)).values_list(
'def_name', flat=True)
for receiver, response in projector_overlays.send(
sender=sid, register=False, call=active_defs):
if response is not None:
data['overlays'].append(response)
self._data = data
@ -124,7 +118,6 @@ class Projector(TemplateView, AjaxMixin):
'scrollcontent', self.data)
cache.set('projector_scrollcontent', scrollcontent, 1)
# TODO: do not call the hole data-methode, if we only need some vars
data = cache.get('projector_data')
if not data:
@ -301,7 +294,6 @@ class OverlayMessageView(RedirectView):
elif 'message-clean' in request.POST:
projector_message_delete()
def get_ajax_context(self, **kwargs):
clear_projector_cache()
return {
@ -309,7 +301,6 @@ class OverlayMessageView(RedirectView):
}
class ActivateOverlay(RedirectView):
"""
Activate or deactivate an overlay.
@ -379,12 +370,11 @@ def register_tab(request):
"""
Register the projector tab.
"""
selected = True if request.path.startswith('/projector/') else False
selected = request.path.startswith('/projector/')
return Tab(
title=_('Dashboard'),
url=reverse('dashboard'),
permission=request.user.has_perm('projector.can_manage_projector') or
request.user.has_perm('projector.can_see_dashboard'),
permission=request.user.has_perm('projector.can_see_dashboard'),
selected=selected,
)
@ -411,7 +401,7 @@ def get_widgets(request):
name='live_view',
display_name=_('Projector live view'),
template='projector/live_view_widget.html',
context = RequestContext(request, {}),
context=RequestContext(request, {}),
permission_required='projector.can_see_projector',
default_column=2))
@ -424,15 +414,14 @@ def get_widgets(request):
projector_overlay = ProjectorOverlay.objects.get(
def_name=name)
except ProjectorOverlay.DoesNotExist:
projector_overlay = ProjectorOverlay(def_name=name,
active=False)
projector_overlay = ProjectorOverlay(def_name=name, active=False)
projector_overlay.save()
overlays.append(projector_overlay)
context = {
'overlays':overlays,
'countdown_time': config['countdown_time'],
'countdown_state' : config['countdown_state']}
'overlays': overlays,
'countdown_time': config['countdown_time'],
'countdown_state': config['countdown_state']}
context.update(csrf(request))
widgets.append(Widget(
name='overlays',
@ -442,7 +431,6 @@ def get_widgets(request):
default_column=2,
context=context))
# Custom slide widget
context = {
'slides': ProjectorSlide.objects.all().order_by('weight'),

View File

@ -12,8 +12,6 @@
from django.conf import settings
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 openslides.utils.views import RedirectView
@ -34,7 +32,7 @@ urlpatterns = patterns('',
)
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 = {

View File

@ -37,8 +37,8 @@ class AnonymousAuth(object):
- try to return the permissions for the 'Anonymous' group
"""
if not user_obj.is_anonymous() or obj is not None or \
not config['system_enable_anonymous']:
if (not user_obj.is_anonymous() or obj is not None or
not config['system_enable_anonymous']):
return set()
perms = Permission.objects.filter(group__name='Anonymous')
@ -60,8 +60,8 @@ class AnonymousAuth(object):
"""
Check if the user as a specific permission
"""
if not user_obj.is_anonymous() or obj is not None or \
not config['system_enable_anonymous']:
if (not user_obj.is_anonymous() or obj is not None or
not config['system_enable_anonymous']):
return False
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
"""
if not user_obj.is_anonymous() or \
not config['system_enable_anonymous']:
if (not user_obj.is_anonymous() or
not config['system_enable_anonymous']):
return False
for perm in self.get_all_permissions(user_obj):
@ -87,10 +87,10 @@ class AnonymousAuth(object):
"""
return None
def anonymous_context_additions(RequestContext):
"""
Add a variable to the request context that will indicate
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']}

View File

@ -25,10 +25,9 @@ class excel_semikolon(Dialect):
def patchup(dialect):
if dialect:
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
dialect.doublequote = True
return dialect
register_dialect("excel_semikolon", excel_semikolon)

View File

@ -1 +1,3 @@
from fields import JSONField
from fields import JSONField
__all__ = ['JSONField']

View File

@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _
from django.forms.fields import Field
from django.forms.util import ValidationError as FormValidationError
class JSONFormField(Field):
def clean(self, value):
@ -21,6 +22,7 @@ class JSONFormField(Field):
raise FormValidationError(_("Enter valid JSON"))
return value
class JSONField(models.TextField):
"""JSONField is a generic textfield that serializes/unserializes JSON objects"""

View File

@ -12,12 +12,13 @@
from django.db import models
class MinMaxIntegerField(models.IntegerField):
def __init__(self, min_value=None, max_value=None, *args, **kwargs):
self.min_value, self.max_value = min_value, max_value
super(MinMaxIntegerField, self).__init__(*args, **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)
return super(MinMaxIntegerField, self).formfield(**defaults)

View File

@ -28,16 +28,16 @@ from openslides.config.models import config
# register new truetype fonts
pdfmetrics.registerFont(TTFont('Ubuntu', path_join(settings.SITE_ROOT,
'static/fonts/Ubuntu-R.ttf')))
pdfmetrics.registerFont(TTFont('Ubuntu-Bold', path_join(settings.SITE_ROOT,
'static/fonts/Ubuntu-B.ttf')))
pdfmetrics.registerFont(TTFont('Ubuntu-Italic', path_join(settings.SITE_ROOT,
'static/fonts/Ubuntu-RI.ttf')))
pdfmetrics.registerFont(TTFont(
'Ubuntu', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-R.ttf')))
pdfmetrics.registerFont(TTFont(
'Ubuntu-Bold', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-B.ttf')))
pdfmetrics.registerFont(TTFont(
'Ubuntu-Italic', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-RI.ttf')))
# set style information
PAGE_HEIGHT = defaultPageSize[1];
PAGE_HEIGHT = defaultPageSize[1]
PAGE_WIDTH = defaultPageSize[0]
@ -105,17 +105,17 @@ stylesheet.add(ParagraphStyle(
leftIndent=0,
spaceAfter=15,
))
stylesheet.add(ParagraphStyle(name = 'Subitem',
parent = stylesheet['Normal'],
fontSize = 10,
leading = 10,
leftIndent = 20,
spaceAfter = 15)
)
stylesheet.add(ParagraphStyle(name = 'Tablecell',
parent = stylesheet['Normal'],
fontSize = 9)
)
stylesheet.add(ParagraphStyle(
name='Subitem',
parent=stylesheet['Normal'],
fontSize=10,
leading=10,
leftIndent=20,
spaceAfter=15))
stylesheet.add(ParagraphStyle(
name='Tablecell',
parent=stylesheet['Normal'],
fontSize=9))
stylesheet.add(ParagraphStyle(name = 'Signaturefield',
parent = stylesheet['Normal'],
spaceBefore = 15)

View File

@ -11,11 +11,15 @@
"""
from openslides.utils.person.signals import receive_persons
from openslides.utils.person.api import (generate_person_id, get_person,
Person, Persons)
from openslides.utils.person.api import (
generate_person_id, get_person, Person, Persons)
from openslides.utils.person.forms import PersonFormField, MultiplePersonFormField
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):
@property

View File

@ -61,8 +61,8 @@ class MultiplePersonFormField(PersonFormField):
widget = forms.widgets.SelectMultiple
def __init__(self, *args, **kwargs):
super(MultiplePersonFormField, self).__init__(empty_label=None,
*args, **kwargs)
super(MultiplePersonFormField, self).__init__(
empty_label=None, *args, **kwargs)
def to_python(self, value):
if hasattr(value, '__iter__'):

View File

@ -10,8 +10,7 @@
:license: GNU GPL, see LICENSE for more details.
"""
from django.http import HttpResponse
from django.template import loader, Context, RequestContext, TextNode
from django.template import loader, Context
from django.template.loader_tags import BlockNode, ExtendsNode
@ -24,6 +23,10 @@ class Tab(object):
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):
if isinstance(template, (tuple, list)):
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'):
if hasattr(node, key):
try:
return render_template_block_nodelist(getattr(node, key),
block, context)
return render_template_block_nodelist(
getattr(node, key), block, context)
except:
pass
for node in nodelist:
if isinstance(node, ExtendsNode):
try:
return render_template_block(node.get_parent(context), block,
context)
return render_template_block(
node.get_parent(context), block, context)
except BlockNotFound:
pass
raise BlockNotFound
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
dictionary as context. Returns a string.
@ -77,23 +80,3 @@ def render_block_to_string(template_name, block, dictionary=None,
context_instance = Context(dictionary)
t.render(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)

View File

@ -10,12 +10,13 @@
:license: GNU GPL, see LICENSE for more details.
"""
import sys
try:
import json
except ImportError: # For python 2.5 support
except ImportError: # For python 2.5 support
import simplejson as json
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import Permission
from django.core.context_processors import csrf
@ -41,16 +42,17 @@ def gen_confirm_form(request, message, url):
Deprecated. Use Class base Views instead.
"""
messages.warning(request,
"""
%s
<form action="%s" method="post">
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
<input type="submit" value="%s">
<input type="button" value="%s">
</form>
"""
% (message, url, csrf(request)['csrf_token'], _("Yes"), _("No")))
messages.warning(
request,
"""
%s
<form action="%s" method="post">
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
<input type="submit" value="%s">
<input type="button" value="%s">
</form>
"""
% (message, url, csrf(request)['csrf_token'], _("Yes"), _("No")))
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
if delete_link is None:
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)
def render_response(req, *args, **kwargs):
kwargs['context_instance'] = RequestContext(req)
return render_to_response(*args, **kwargs)
def template(template_name):
"""
Decorator to set a template for a view.
Deprecated. Use class based views instead.
"""
def renderer(func):
def wrapper(request, *args, **kwargs):
output = func(request, *args, **kwargs)
if not isinstance(output, dict):
return output
context = {}
template_manipulation.send(sender='utils_template', request=request,
context=context)
template_manipulation.send(
sender='utils_template', request=request, context=context)
output.update(context)
response = render_to_response(template_name, output,
context_instance=RequestContext(request))
response = render_to_response(
template_name, output, context_instance=RequestContext(request))
if 'cookie' in output:
response.set_cookie(output['cookie'][0], output['cookie'][1])
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
enabled, redirecting to the log-in page if necessary.
Deprecated.
"""
def renderer(func):
def wrapper(request, *args, **kw):
@ -107,10 +112,12 @@ def permission_required(perm, login_url=None):
return renderer
def render_to_forbidden(request, error=
ugettext_lazy("Sorry, you have no rights to see this page.")):
return HttpResponseForbidden(render_to_string('403.html',
{'error': error}, context_instance=RequestContext(request)))
def render_to_forbidden(request,
error=ugettext_lazy("Sorry, you have no rights to see this page.")):
# TODO: Integrate this function into the PermissionMixin once the
# above function is deleted.
return HttpResponseForbidden(render_to_string(
'403.html', {'error': error}, context_instance=RequestContext(request)))
def delete_default_permissions(**kwargs):
@ -118,16 +125,18 @@ def delete_default_permissions(**kwargs):
Deletes the permissions, django creates by default for the admin.
"""
for p in Permission.objects.all():
if p.codename.startswith('add') \
or p.codename.startswith('delete') \
or p.codename.startswith('change'):
if (p.codename.startswith('add') or
p.codename.startswith('delete') or
p.codename.startswith('change')):
p.delete()
def ajax_request(data):
"""
generates a HTTPResponse-Object with json-Data for a
ajax response
ajax response.
Deprecated.
"""
return HttpResponse(json.dumps(data))

View File

@ -22,8 +22,7 @@ except ImportError:
# Is this exception realy necessary?
from StringIO import StringIO
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Frame, PageBreak,
Spacer, Table, LongTable, TableStyle, Image)
from reportlab.platypus import SimpleDocTemplate, Spacer
from reportlab.lib.units import cm
from django.contrib import messages
@ -34,9 +33,9 @@ from django.conf import settings
from django.dispatch import receiver
from django.http import HttpResponseServerError, HttpResponse, HttpResponseRedirect
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.template import loader, RequestContext
from django.template import RequestContext
from django.template.loader import render_to_string
from django.views.generic import (
TemplateView as _TemplateView,
@ -50,8 +49,6 @@ from django.views.generic import (
from django.views.generic.detail import SingleObjectMixin
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.signals import template_manipulation
from openslides.utils.pdf import firstPage, laterPages
@ -64,8 +61,8 @@ View = _View
class SetCookieMixin(object):
def render_to_response(self, context, **response_kwargs):
response = TemplateResponseMixin.render_to_response(self, context,
**response_kwargs)
response = TemplateResponseMixin.render_to_response(
self, context, **response_kwargs)
if 'cookie' in context:
response.set_cookie(context['cookie'][0], context['cookie'][1])
return response
@ -90,8 +87,8 @@ class PermissionMixin(object):
if not self.has_permission(request, *args, **kwargs):
if not request.user.is_authenticated():
path = request.get_full_path()
return HttpResponseRedirect("%s?next=%s" % (settings.LOGIN_URL,
path))
return HttpResponseRedirect(
"%s?next=%s" % (settings.LOGIN_URL, path))
else:
return render_to_forbidden(request)
return _View.dispatch(self, request, *args, **kwargs)
@ -130,18 +127,18 @@ class QuestionMixin(object):
option_fields = "\n".join([
'<input type="submit" name="%s" value="%s">' % (option[0], unicode(option[1]))
for option in self.get_answer_options()])
messages.warning(self.request,
messages.warning(
self.request,
"""
%(message)s
<form action="%(url)s" method="post">
<input type="hidden" value="%(csrf)s" name="csrfmiddlewaretoken">
%(option_fields)s
</form>
""" % {
'message': self.get_question(),
'url': self.get_answer_url(),
'csrf': csrf(self.request)['csrf_token'],
'option_fields': option_fields})
""" % {'message': self.get_question(),
'url': self.get_answer_url(),
'csrf': csrf(self.request)['csrf_token'],
'option_fields': option_fields})
def pre_post_redirect(self, request, *args, **kwargs):
# Reacts on the response of the user in a POST-request.
@ -167,16 +164,16 @@ class QuestionMixin(object):
class TemplateView(PermissionMixin, _TemplateView):
def get_context_data(self, **kwargs):
context = super(TemplateView, self).get_context_data(**kwargs)
template_manipulation.send(sender=self.__class__, request=self.request,
context=context)
template_manipulation.send(
sender=self.__class__, request=self.request, context=context)
return context
class ListView(PermissionMixin, SetCookieMixin, _ListView):
def get_context_data(self, **kwargs):
context = super(ListView, self).get_context_data(**kwargs)
template_manipulation.send(sender=self.__class__, request=self.request,
context=context)
template_manipulation.send(
sender=self.__class__, request=self.request, context=context)
return context
@ -217,8 +214,8 @@ class FormView(PermissionMixin, _FormView):
def get_context_data(self, **kwargs):
context = super(FormView, self).get_context_data(**kwargs)
template_manipulation.send(sender=self.__class__, request=self.request,
context=context)
template_manipulation.send(
sender=self.__class__, request=self.request, context=context)
return context
def form_invalid(self, form):
@ -235,8 +232,8 @@ class UpdateView(PermissionMixin, _UpdateView):
def get_context_data(self, **kwargs):
context = super(UpdateView, self).get_context_data(**kwargs)
template_manipulation.send(sender=self.__class__, request=self.request,
context=context)
template_manipulation.send(
sender=self.__class__, request=self.request, context=context)
return context
def form_invalid(self, form):
@ -256,8 +253,8 @@ class CreateView(PermissionMixin, _CreateView):
def get_context_data(self, **kwargs):
context = super(CreateView, self).get_context_data(**kwargs)
template_manipulation.send(sender=self.__class__, request=self.request,
context=context)
template_manipulation.send(
sender=self.__class__, request=self.request, context=context)
return context
def get_apply_url(self):
@ -299,7 +296,6 @@ class DeleteView(SingleObjectMixin, QuestionMixin, RedirectView):
class DetailView(TemplateView, SingleObjectMixin):
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return super(DetailView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
@ -329,8 +325,8 @@ class PDFView(PermissionMixin, View):
return SimpleDocTemplate(buffer)
def build_document(self, pdf_document, story):
pdf_document.build(story, onFirstPage=firstPage,
onLaterPages=laterPages)
pdf_document.build(
story, onFirstPage=firstPage, onLaterPages=laterPages)
def render_to_response(self, filename):
response = HttpResponse(mimetype='application/pdf')
@ -340,7 +336,7 @@ class PDFView(PermissionMixin, View):
buffer = StringIO()
pdf_document = self.get_template(buffer)
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)
@ -351,9 +347,6 @@ class PDFView(PermissionMixin, View):
response.write(pdf)
return response
def get_filename(self):
return self.filename
def get(self, request, *args, **kwargs):
return self.render_to_response(self.get_filename())
@ -364,9 +357,8 @@ def server_error(request, template_name='500.html'):
Templates: `500.html`
"""
t = loader.get_template("500.html")
return HttpResponseServerError(render_to_string('500.html',
context_instance=RequestContext(request)))
return HttpResponseServerError(render_to_string(
template_name, context_instance=RequestContext(request)))
@receiver(template_manipulation, dispatch_uid="send_register_tab")

5
requirements.txt Normal file
View File

@ -0,0 +1,5 @@
Django==1.4.2
django-mptt
reportlab
PIL
simplejson

0
tests/__init__.py Normal file
View File

View File

@ -12,13 +12,13 @@
from django.test import TestCase
from django.test.client import Client
from django.contrib.auth.models import User
from django.db.models.query import EmptyQuerySet
from openslides.projector.api import get_active_slide
from openslides.participant.models import User
from openslides.agenda.models import Item
class ItemTest(TestCase):
def setUp(self):
self.item1 = Item.objects.create(title='item1')
@ -47,8 +47,9 @@ class ItemTest(TestCase):
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>]")
self.assertEqual(
str(l),
"[<Item: item1>, <Item: item1A>, <Item: item1Aa>, <Item: item2>]")
def testForms(self):
for item in Item.objects.all():
@ -71,8 +72,10 @@ class ViewTest(TestCase):
self.item2 = Item.objects.create(title='item2')
self.refreshItems()
self.admin = User.objects.create_user('testadmin', '', 'default')
self.anonym = User.objects.create_user('testanoym', '', 'default')
self.admin, created = User.objects.get_or_create(username='testadmin')
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.save()
@ -131,7 +134,7 @@ class ViewTest(TestCase):
response = c.get('/agenda/%d/edit/' % 1000)
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)
self.assertEqual(response.status_code, 302)
self.refreshItems()
@ -143,4 +146,3 @@ class ViewTest(TestCase):
self.assertEqual(response.status_code, 200)
self.refreshItems()
self.assertEqual(self.item1.title, 'newitem1')

View File

@ -11,10 +11,10 @@
"""
from django.test import TestCase
from django.test.client import Client
from openslides.participant.models import User
from openslides.motion.models import Motion, AVersion
from openslides.motion.models import Motion
class MotionTest(TestCase):
def setUp(self):
@ -39,4 +39,3 @@ class MotionTest(TestCase):
self.assertEqual(self.app1.versions.count(), 2)
self.assertEqual(self.app1.last_version, self.app1.versions[1])

View File

@ -11,8 +11,6 @@
"""
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.participant.api import gen_username, gen_password
@ -38,9 +36,9 @@ class UserTest(TestCase):
self.assertEqual(unicode(self.user1), 'Max Mustermann')
def test_name_suffix(self):
self.user1.structure_level = 'München'
self.user1.structure_level = u'München'
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):
self.assertIsInstance(self.user1.default_password, basestring)