Merge remote branch 'upstream/master'

This commit is contained in:
Emanuel Schuetze 2012-11-27 20:10:13 +01:00
commit 40984a6508
59 changed files with 875 additions and 851 deletions

4
.coveragerc Normal file
View File

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

6
.gitignore vendored
View File

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

12
.travis.yml Normal file
View 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

View File

@ -1,6 +1,5 @@
include AUTHORS
include CHANGELOG
include initial_data.json
include INSTALL.txt
include LICENSE
include manage.py
@ -12,16 +11,14 @@ recursive-include openslides/templates *
recursive-include openslides/agenda/templates *
recursive-include openslides/agenda/static *
recursive-include openslides/application/templates *
recursive-include openslides/application/static *
recursive-include openslides/motion/templates *
recursive-include openslides/assignment/templates *
recursive-include openslides/assignment/static *
recursive-include openslides/config/templates *
recursive-include openslides/config/static *
recursive-include openslides/participant/templates *
recursive-include openslides/participant/static *
include openslides/participant/fixtures/groups_de.json
recursive-include openslides/poll/templates *
recursive-include openslides/poll/static *
recursive-include openslides/projector/templates *
recursive-include openslides/projector/static *

View File

@ -1,6 +1,6 @@
==================================
English README file for OpenSlides
==================================
==================================
English README file for OpenSlides
==================================
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
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
@ -18,7 +18,7 @@ Getting started
Install and start OpenSlides as described in the INSTALL.txt.
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
@ -26,6 +26,7 @@ The start script of OpenSlides
Simply running
openslides.exe (on Windows) or
python start.py (on Linux/MacOS)
will start OpenSlides using djangos development server. It will also
try to open OpenSlides in your default webbrowser.

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

@ -87,6 +87,7 @@ PY_DLLS = [
"_sqlite3.pyd",
"_socket.pyd",
"select.pyd",
"_ctypes.pyd",
]
MSVCR_PUBLIC_KEY = "1fc8b3b9a1e18e3b"

View File

@ -29,12 +29,11 @@ def get_version(version=None):
if version[3] != 'final':
if version[3] == 'dev':
try:
import os
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:
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)
else:
sub = '-' + version[3] + str(version[4])

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
@ -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:
candidature.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 candidature.
"""
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):
@ -167,7 +159,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
@ -215,7 +206,6 @@ class Assignment(models.Model, SlideMixin):
vote_results_dict[candidate].append(votes)
return vote_results_dict
def get_agenda_title(self):
return self.name
@ -301,8 +291,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']))
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,26 +371,31 @@ 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; "
cell2b.append(
Paragraph(
"<seq id='counter'/>.&nbsp; "
"__________________________________________",
stylesheet['Signaturefield']))
cell2b.append(Spacer(0, 0.2 * cm))
@ -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,11 +438,12 @@ 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'],
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'])
@ -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"]
@ -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)
@ -637,8 +647,7 @@ class Config(FormView):
'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

@ -69,7 +69,6 @@ class Config(object):
def __contains__(self, item):
return ConfigStore.objects.filter(key=item).exists()
config = Config()
@ -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

@ -12,18 +12,18 @@
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import Group, Permission
from django.core.urlresolvers import reverse
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
class GeneralConfig(FormView):
@ -61,27 +61,12 @@ class GeneralConfig(FormView):
# system
if form.cleaned_data['system_enable_anonymous']:
config['system_enable_anonymous'] = True
# check for Anonymous group and (re)create it as needed
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.'))
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,7 +59,7 @@ 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).
@ -72,7 +69,7 @@ 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

@ -13,13 +13,15 @@
# for python 2.5 support
from __future__ import with_statement
import os
import sys
import optparse
import socket
import time
import threading
import base64
import ctypes
import optparse
import os
import socket
import sys
import tempfile
import threading
import time
import webbrowser
import django.conf
@ -28,14 +30,15 @@ from django.core.management import execute_from_command_line
CONFIG_TEMPLATE = """#!/usr/bin/env python
# -*- coding: utf-8 -*-
import openslides.main
from openslides.global_settings import *
# Use 'DEBUG = True' to get more details for server errors
# (Default for relaeses: 'False')
# (Default for releases: 'False')
DEBUG = False
TEMPLATE_DEBUG = DEBUG
DBPATH = %(dbpath)r
DBPATH = %(dbpath)s
DATABASES = {
'default': {
@ -64,15 +67,12 @@ INSTALLED_APPS += INSTALLED_PLUGINS
KEY_LENGTH = 30
_fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
def _fs2unicode(s):
if isinstance(s, unicode):
return s
return s.decode(_fs_encoding)
# sentinel used to signal that the database ought to be stored
# relative to the portable's directory
_portable_db_path = object()
def main(argv=None, opt_defaults=None, database_path=None):
def process_options(argv=None):
if argv is None:
argv = sys.argv[1:]
@ -86,12 +86,11 @@ def main(argv=None, opt_defaults=None, database_path=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")
if not opt_defaults is None:
parser.set_defaults(**opt_defaults)
"-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:
@ -99,12 +98,45 @@ def main(argv=None, opt_defaults=None, database_path=None):
parser.print_help()
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
settings_path = opts.settings
if settings_path is None:
config_home = os.environ.get('XDG_CONFIG_HOME', \
os.path.join(os.path.expanduser('~'), '.config'))
settings_path = os.path.join(config_home, 'openslides', 'settings.py')
settings_path = get_user_config_path('openslides', 'settings.py')
# Create settings if necessary
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):
settings_module = os.path.dirname(settings_path)
if database_path is _portable_db_path:
database_path = get_portable_db_path()
dbpath_value = 'openslides.main.get_portable_db_path()'
else:
if database_path is None:
data_home = os.environ.get('XDG_DATA_HOME', \
os.path.join(os.path.expanduser('~'), '.local', 'share'))
database_path = os.path.join(data_home, 'openslides', 'database.sqlite')
database_path = get_user_data_path('openslides', 'database.sqlite')
dbpath_value = repr(fs2unicode(database_path))
settings_content = CONFIG_TEMPLATE % dict(
default_key=base64.b64encode(os.urandom(KEY_LENGTH)),
dbpath=_fs2unicode(database_path))
dbpath=dbpath_value)
if not os.path.exists(settings_module):
os.makedirs(settings_module)
@ -217,6 +252,7 @@ def run_syncdb():
# now initialize the database
argv = ["", "syncdb", "--noinput"]
execute_from_command_line(argv)
execute_from_command_line(["", "loaddata", "groups_de"])
def set_system_url(url):
@ -269,35 +305,60 @@ def start_browser(url):
t = threading.Thread(target=f)
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
# since it is essentially a small wrapper that embeds the
# 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:
default_settings = os.path.join(portable_dir, "openslides",
"openslides_personal_settings.py")
database_path = os.path.join(portable_dir, "openslides",
"database.sqlite")
else:
import ctypes
exename = os.path.basename(sys.executable).lower()
if exename != "openslides.exe":
raise Exception(
"Cannot determine portable path when "
"not running as portable")
portable_dir = fs2unicode(os.path.dirname(os.path.abspath(sys.executable)))
return os.path.join(portable_dir, *args)
def get_portable_db_path():
return get_portable_path('openslides', 'database.sqlite')
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.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
@ -307,13 +368,8 @@ def win32_portable_main(argv=None):
res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf)
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)
return os.path.join(buf.value, *args)
if __name__ == "__main__":

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,7 +46,7 @@ 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(
@ -59,7 +59,7 @@ class MotionImportForm(forms.Form, CssClassMixin):
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")

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
@ -69,7 +64,7 @@ class Motion(models.Model, SlideMixin):
number = models.PositiveSmallIntegerField(blank=True, null=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,16 +215,19 @@ 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; "
self.writelog(
_("Trivial changes to version %(version)d; "
"changed fields: %(changed_fields)s")
% dict(version = last_version.aid,
changed_fields = ", ".join(field_names)))
% dict(version=last_version.aid,
changed_fields=", ".join(field_names)))
return # Done
version = AVersion(title=getattr(self, 'title', ''),
version = AVersion(
title=getattr(self, 'title', ''),
text=getattr(self, 'text', ''),
reason=getattr(self, 'reason', ''),
motion=self)
@ -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,9 +536,9 @@ 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"))
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

@ -23,14 +23,12 @@ except ImportError: # python <= 2.5
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)
@ -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,15 +14,20 @@
from __future__ import with_statement
from random import choice
import string
import csv
from django.contrib.auth.models import User
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
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():
@ -73,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
@ -93,3 +98,23 @@ def import_users(csv_file):
except UnicodeDecodeError:
error_messages.appen(_('Import file has wrong character encoding, only UTF-8 is supported!'))
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

View File

@ -18,6 +18,7 @@ from openslides.utils.forms import (
CssClassMixin, LocalizedModelMultipleChoiceField)
from openslides.participant.models import User, Group
from openslides.participant.api import get_or_create_registered_group
class UserCreateForm(forms.ModelForm, CssClassMixin):
@ -25,6 +26,13 @@ class UserCreateForm(forms.ModelForm, CssClassMixin):
queryset=Group.objects.exclude(name__iexact='anonymous'),
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:
model = User
fields = ('first_name', 'last_name', 'is_active', 'groups', 'structure_level',
@ -58,6 +66,7 @@ class GroupForm(forms.ModelForm, CssClassMixin):
instance = forms.ModelForm.save(self, False)
old_save_m2m = self.save_m2m
def save_m2m():
old_save_m2m()
@ -76,13 +85,13 @@ class GroupForm(forms.ModelForm, CssClassMixin):
# Do not allow to change the name "anonymous" or give another group
# this name
data = self.cleaned_data['name']
if self.instance.name.lower() == 'anonymous':
if self.instance.name.lower() in ['anonymous', 'registered']:
# Editing the anonymous-user
if self.instance.name.lower() != data.lower():
raise forms.ValidationError(
_('You can not edit the name for the anonymous user'))
_('You can not edit the name for this group.'))
else:
if data.lower() == 'anonymous':
if data.lower() in ['anonymous', 'registered']:
raise forms.ValidationError(
_('Group name "%s" is reserved for internal use.') % data)
return data
@ -94,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,6 +25,7 @@ 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
person_prefix = 'user'
@ -131,12 +132,15 @@ class User(DjangoUser, PersonMixin, Person, SlideMixin):
register_slidemodel(User)
class Group(DjangoGroup, PersonMixin, Person, SlideMixin):
prefix = 'group' # This is for the slides
person_prefix = 'group'
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"))
@models.permalink
@ -173,6 +177,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,7 +221,8 @@ def receive_persons(sender, **kwargs):
"""
Answers to the Person-API
"""
return UsersAndGroupsToPersons(person_prefix_filter=kwargs['person_prefix_filter'],
return UsersAndGroupsToPersons(
person_prefix_filter=kwargs['person_prefix_filter'],
id_filter=kwargs['id_filter'])
@ -234,7 +240,7 @@ def default_config(sender, key, **kwargs):
@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:
instance.user
except User.DoesNotExist:
@ -242,8 +248,18 @@ def user_post_save(sender, instance, signal, *args, **kwargs):
@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:
instance.group
except Group.DoesNotExist:
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()

View File

@ -17,7 +17,7 @@
<tr class="{% cycle '' 'odd' %}">
<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>
{% 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>
{% endif %}
</td>

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)
@ -574,7 +573,7 @@ def get_personal_info_widget(request):
'supported_motions': Motion.objects.filter(motionsupporter=request.user),
'assignments': Assignment.objects.filter(
assignmentcandidate__person=request.user,
assignmentcandidate__blocked=False),}
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

@ -164,7 +164,6 @@ class BasePoll(models.Model):
"""
return self.vote_values
def get_vote_class(self):
"""
Return the releatet vote class.

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,16 @@ class SlideMixin(object):
"""
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
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):

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,
'overlays': overlays,
'countdown_time': config['countdown_time'],
'countdown_state' : config['countdown_state']}
'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

@ -31,4 +31,3 @@ def patchup(dialect):
return dialect
register_dialect("excel_semikolon", excel_semikolon)

View File

@ -1 +1,3 @@
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

@ -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)

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,104 +105,104 @@ 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 = 'Signaturefield',
parent = stylesheet['Normal'],
spaceBefore = 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='Signaturefield',
parent=stylesheet['Normal'],
spaceBefore=15)
)
# Ballot stylesheets
stylesheet.add(ParagraphStyle(name = 'Ballot_title',
parent = stylesheet['Bold'],
fontSize = 12,
leading = 14,
leftIndent = 30),
stylesheet.add(ParagraphStyle(name='Ballot_title',
parent=stylesheet['Bold'],
fontSize=12,
leading=14,
leftIndent=30),
)
stylesheet.add(ParagraphStyle(name = 'Ballot_subtitle',
parent = stylesheet['Normal'],
fontSize = 10,
leading = 12,
leftIndent = 30,
rightIndent = 20,
spaceAfter = 5),
stylesheet.add(ParagraphStyle(name='Ballot_subtitle',
parent=stylesheet['Normal'],
fontSize=10,
leading=12,
leftIndent=30,
rightIndent=20,
spaceAfter=5),
)
stylesheet.add(ParagraphStyle(name = 'Ballot_description',
parent = stylesheet['Normal'],
fontSize = 7,
leading = 10,
leftIndent = 30),
stylesheet.add(ParagraphStyle(name='Ballot_description',
parent=stylesheet['Normal'],
fontSize=7,
leading=10,
leftIndent=30),
)
stylesheet.add(ParagraphStyle(name = 'Ballot_option',
parent = stylesheet['Normal'],
fontSize = 12,
leading = 24,
leftIndent = 30),
stylesheet.add(ParagraphStyle(name='Ballot_option',
parent=stylesheet['Normal'],
fontSize=12,
leading=24,
leftIndent=30),
)
stylesheet.add(ParagraphStyle(name = 'Monotype',
parent = stylesheet['Normal'],
fontName = 'Courier',
fontSize = 12,
leading = 24,
leftIndent = 30),
stylesheet.add(ParagraphStyle(name='Monotype',
parent=stylesheet['Normal'],
fontName='Courier',
fontSize=12,
leading=24,
leftIndent=30),
)
stylesheet.add(ParagraphStyle(name = 'Ballot_option_name',
parent = stylesheet['Normal'],
fontSize = 12,
leading = 15,
leftIndent = 30),
stylesheet.add(ParagraphStyle(name='Ballot_option_name',
parent=stylesheet['Normal'],
fontSize=12,
leading=15,
leftIndent=30),
)
stylesheet.add(ParagraphStyle(name = 'Ballot_option_group',
parent = stylesheet['Normal'],
fontSize = 8,
leading = 15,
leftIndent = 30),
stylesheet.add(ParagraphStyle(name='Ballot_option_group',
parent=stylesheet['Normal'],
fontSize=8,
leading=15,
leftIndent=30),
)
stylesheet.add(ParagraphStyle(name = 'Ballot_option_YNA',
parent = stylesheet['Normal'],
fontSize = 12,
leading = 15,
leftIndent = 49,
spaceAfter = 18),
stylesheet.add(ParagraphStyle(name='Ballot_option_YNA',
parent=stylesheet['Normal'],
fontSize=12,
leading=15,
leftIndent=49,
spaceAfter=18),
)
stylesheet.add(ParagraphStyle(name = 'Ballot_option_group_right',
parent = stylesheet['Normal'],
fontSize = 8,
leading = 16,
leftIndent = 49),
stylesheet.add(ParagraphStyle(name='Ballot_option_group_right',
parent=stylesheet['Normal'],
fontSize=8,
leading=16,
leftIndent=49),
)
stylesheet.add(ParagraphStyle(name = 'Badge_title',
parent = stylesheet['Bold'],
fontSize = 16,
leading = 22,
leftIndent = 30),
stylesheet.add(ParagraphStyle(name='Badge_title',
parent=stylesheet['Bold'],
fontSize=16,
leading=22,
leftIndent=30),
)
stylesheet.add(ParagraphStyle(name = 'Badge_subtitle',
parent = stylesheet['Normal'],
fontSize = 12,
leading = 24,
leftIndent = 30),
stylesheet.add(ParagraphStyle(name='Badge_subtitle',
parent=stylesheet['Normal'],
fontSize=12,
leading=24,
leftIndent=30),
)
stylesheet.add(ParagraphStyle(
name = 'Badge_italic',
parent = stylesheet['Italic'],
fontSize = 12,
leading = 24,
leftIndent = 30,
name='Badge_italic',
parent=stylesheet['Italic'],
fontSize=12,
leading=24,
leftIndent=30,
))
stylesheet.add(ParagraphStyle(
name = 'Badge_qrcode',
fontSize = 12,
leftIndent = 190,
name='Badge_qrcode',
fontSize=12,
leftIndent=190,
))

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,15 +52,15 @@ 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
@ -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
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,7 +42,8 @@ def gen_confirm_form(request, message, url):
Deprecated. Use Class base Views instead.
"""
messages.warning(request,
messages.warning(
request,
"""
%s
<form action="%s" method="post">
@ -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,15 +127,15 @@ 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(),
""" % {'message': self.get_question(),
'url': self.get_answer_url(),
'csrf': csrf(self.request)['csrf_token'],
'option_fields': option_fields})
@ -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

View File

@ -12,15 +12,19 @@ from setuptools import find_packages
from openslides import get_version
with open('README.txt') as file:
long_description = file.read()
setup(
name='openslides',
description='Presentation-System',
long_description=long_description,
version=get_version(),
url='http://openslides.org',
author='OpenSlides-Team',
author_email='support@openslides.org',
license='GPL2+',
packages=find_packages(),
packages=find_packages(exclude=['tests']),
include_package_data = True,
classifiers = [
# http://pypi.python.org/pypi?%3Aaction=list_classifiers

0
tests/__init__.py Normal file
View File

0
tests/agenda/__init__.py Normal file
View File

18
tests/agenda/models.py Normal file
View 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)

View File

@ -12,12 +12,15 @@
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
from openslides.agenda.slides import agenda_show
from .models import ReleatedItem
class ItemTest(TestCase):
def setUp(self):
@ -25,6 +28,8 @@ class ItemTest(TestCase):
self.item2 = Item.objects.create(title='item2')
self.item3 = Item.objects.create(title='item1A', parent=self.item1)
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):
self.assertFalse(self.item1.closed)
@ -46,10 +51,6 @@ class ItemTest(TestCase):
self.assertTrue(self.item3 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):
for item in Item.objects.all():
initial = item.weight_form.initial
@ -64,6 +65,36 @@ class ItemTest(TestCase):
self.item1.related_sid = 'foobar'
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):
def setUp(self):
@ -71,8 +102,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()
@ -91,6 +124,13 @@ class ViewTest(TestCase):
def anonymClient(self):
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):
c = self.adminClient
@ -122,6 +162,14 @@ class ViewTest(TestCase):
self.refreshItems()
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):
c = self.adminClient
@ -131,7 +179,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 +191,3 @@ class ViewTest(TestCase):
self.assertEqual(response.status_code, 200)
self.refreshItems()
self.assertEqual(self.item1.title, 'newitem1')

23
tests/test_init.py Normal file
View 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)

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)