Merge branch 'master' of github.com:OpenSlides/OpenSlides

This commit is contained in:
Oskar Hahn 2012-09-11 19:30:50 +02:00
commit 93ee260856
14 changed files with 113 additions and 36 deletions

View File

@ -6,11 +6,15 @@ How to create a new portable Windows distribution of OpenSlides:
easy_install -Z django django-mptt reportlab pil easy_install -Z django django-mptt reportlab pil
2.) In the main directory of the OpenSlides checkout execute: 2.) Install OpenSlides by running python setup.py install in the top directory
of OpenSlides.
NOTE: This step must be repeated whenever you make changes to OpenSlides
3.) In the main directory of the OpenSlides checkout execute:
python extras/win32-portable/prepare_portable.py python extras/win32-portable/prepare_portable.py
3.) The portable OpenSlides distribution is now ready as a zip in the 4.) The portable OpenSlides distribution is now ready as a zip in the
'dist/' directory 'dist/' directory
Note: Creating the portable Windows distribution of OpenSlides is not possible, Note: Creating the portable Windows distribution of OpenSlides is not possible,

View File

@ -23,7 +23,7 @@ static const char *site_code =
static const char *run_openslides_code = static const char *run_openslides_code =
"import openslides.main;" "import openslides.main;"
"openslides.main.main()"; "openslides.main.win32_portable_main()";
/* determine the path to the executable /* determine the path to the executable
* NOTE: Py_GetFullProgramPath() can't be used because * NOTE: Py_GetFullProgramPath() can't be used because

Binary file not shown.

View File

@ -45,10 +45,6 @@ LIBEXCLUDE = [
r"^unittest/", r"^unittest/",
] ]
OPENSLIDES_EXCLUDE = [
r"^openslides/settings.py"
]
SITE_PACKAGES = { SITE_PACKAGES = {
"django": { "django": {
@ -84,7 +80,10 @@ SITE_PACKAGES = {
"pil": { "pil": {
# NOTE: PIL is a special case, see copy_pil # NOTE: PIL is a special case, see copy_pil
"copy": [], "copy": [],
} },
"openslides": {
"copy" : ["openslides"],
},
} }
PY_DLLS = [ PY_DLLS = [
@ -302,9 +301,6 @@ def main():
collect_lib(libdir, odir) collect_lib(libdir, odir)
collect_site_packages(sitedir, os.path.join(odir, "site-packages")) collect_site_packages(sitedir, os.path.join(odir, "site-packages"))
exclude = get_pkg_exclude("openslides", OPENSLIDES_EXCLUDE)
copy_dir_exclude(exclude, ".", "openslides", odir)
if not compile_openslides_launcher(): if not compile_openslides_launcher():
sys.stdout.write("Using prebuild openslides.exe\n") sys.stdout.write("Using prebuild openslides.exe\n")

View File

@ -99,10 +99,11 @@ def overview(request):
else: else:
sort = sortfilter['sort'] sort = sortfilter['sort']
query = query.order_by(sort) query = query.order_by(sort)
if sort.startswith('aversion_'): if sort.startswith('aversion_'):
# limit result to last version of an application # limit result to last version of an application
query = query.filter(aversion__id__in=[x.last_version.id for x in Application.objects.all()]) query = query.filter(aversion__id__in=[x.last_version.id for x in Application.objects.all()])
else:
query = query.order_by('number')
if 'reverse' in sortfilter: if 'reverse' in sortfilter:
query = query.reverse() query = query.reverse()
@ -920,5 +921,5 @@ def get_widgets(request):
Widget( Widget(
name='applications', name='applications',
template='application/widget.html', template='application/widget.html',
context={'applications': Application.objects.all()}, context={'applications': Application.objects.all().order_by('number')},
permission_required='application.can_manage_application')] permission_required='application.can_manage_application')]

View File

@ -33,6 +33,7 @@ class AssignmentCandidate(models.Model):
assignment = models.ForeignKey("Assignment") assignment = models.ForeignKey("Assignment")
person = PersonField(db_index=True) person = PersonField(db_index=True)
elected = models.BooleanField(default=False) elected = models.BooleanField(default=False)
blocked = models.BooleanField(default=False)
def __unicode__(self): def __unicode__(self):
return unicode(self.person) return unicode(self.person)
@ -72,6 +73,8 @@ class Assignment(models.Model, SlideMixin):
def run(self, candidate, person=None): def run(self, candidate, person=None):
""" """
run for a vote run for a vote
candidate: The user who will be a candidate
person: The user who chooses the candidate
""" """
# TODO: don't make any permission checks here. # TODO: don't make any permission checks here.
# Use other Exceptions # Use other Exceptions
@ -79,23 +82,39 @@ class Assignment(models.Model, SlideMixin):
raise NameError(_('<b>%s</b> is already a candidate.') % candidate) raise NameError(_('<b>%s</b> is already a candidate.') % candidate)
if not person.has_perm("assignment.can_manage_assignment") and self.status != 'sea': if not person.has_perm("assignment.can_manage_assignment") and self.status != 'sea':
raise NameError(_('The candidate list is already closed.')) raise NameError(_('The candidate list is already closed.'))
AssignmentCandidate(assignment=self, person=candidate, elected=False).save() candidation = self.assignment_candidats.filter(person=candidate)
if candidation and candidate != person:
# if the candidation is blocked and anotherone tries to run the
# candidate
raise NameError(
_('The %s does not want to be a candidate.') % candidate)
elif candidation and candidate == person:
candidation[0].blocked = False
candidation[0].save()
else:
AssignmentCandidate(assignment=self, person=candidate).save()
def delrun(self, candidate): def delrun(self, candidate, blocked=True):
""" """
stop running for a vote stop running for a vote
""" """
if self.is_candidate(candidate): if self.is_candidate(candidate):
self.assignment_candidats.get(person=candidate).delete() candidation = self.assignment_candidats.get(person=candidate)
if blocked:
candidation.blocked = True
candidation.save()
else:
candidation.delete()
else: else:
# TODO: Use an OpenSlides Error # TODO: Use an OpenSlides Error
raise Exception(_('%s is no candidate') % candidate) raise Exception(_('%s is no candidate') % candidate)
def is_candidate(self, person): def is_candidate(self, person):
if self.assignment_candidats.filter(person=person).exists(): """
return True return True, if person is a candidate.
else: """
return False return self.assignment_candidats.filter(person=person) \
.exclude(blocked=True).exists()
@property @property
def assignment_candidats(self): def assignment_candidats(self):
@ -110,7 +129,7 @@ class Assignment(models.Model, SlideMixin):
return self.get_participants(only_elected=True) return self.get_participants(only_elected=True)
def get_participants(self, only_elected=False, only_candidate=False): def get_participants(self, only_elected=False, only_candidate=False):
candidates = self.assignment_candidats candidates = self.assignment_candidats.exclude(blocked=True)
if only_elected and only_candidate: if only_elected and only_candidate:
# TODO: Use right Exception # TODO: Use right Exception

View File

@ -81,9 +81,12 @@ def view(request, assignment_id=None):
user = form.cleaned_data['candidate'] user = form.cleaned_data['candidate']
try: try:
assignment.run(user, request.user) assignment.run(user, request.user)
messages.success(request, _("Candidate <b>%s</b> was nominated successfully.") % (user))
except NameError, e: except NameError, e:
messages.error(request, e) messages.error(request, e)
else:
messages.success(request, _(
"Candidate <b>%s</b> was nominated successfully.")
% user)
else: else:
if request.user.has_perm('assignment.can_nominate_other'): if request.user.has_perm('assignment.can_nominate_other'):
form = AssignmentRunForm() form = AssignmentRunForm()
@ -184,7 +187,7 @@ def delrun(request, assignment_id):
assignment = Assignment.objects.get(pk=assignment_id) assignment = Assignment.objects.get(pk=assignment_id)
try: try:
if assignment.status == 'sea' or user.has_perm("assignment.can_manage_assignment"): if assignment.status == 'sea' or user.has_perm("assignment.can_manage_assignment"):
assignment.delrun(request.user) assignment.delrun(request.user, blocked=True)
else: else:
messages.error(request, _('The candidate list is already closed.')) messages.error(request, _('The candidate list is already closed.'))
except Exception, e: except Exception, e:
@ -201,7 +204,7 @@ def delother(request, assignment_id, user_id):
if request.method == 'POST': if request.method == 'POST':
try: try:
assignment.delrun(person) assignment.delrun(person, blocked=False)
except Exception, e: except Exception, e:
messages.error(request, e) messages.error(request, e)
else: else:
@ -660,5 +663,5 @@ def get_widgets(request):
Widget( Widget(
name=_('Assignments'), name=_('Assignments'),
template='assignment/widget.html', template='assignment/widget.html',
context={'assignments': Assignment.objects.all()}, context={'assignments': Assignment.objects.all().order_by('name')},
permission_required='assignment.can_manage_assignment')] permission_required='assignment.can_manage_assignment')]

View File

@ -25,8 +25,10 @@ import webbrowser
import django.conf import django.conf
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
CONFIG_TEMPLATE = """ CONFIG_TEMPLATE = """#!/usr/bin/env python
from openslides.openslides_settings import * # -*- coding: utf-8 -*-
from openslides.openslides_global_settings import *
# Use 'DEBUG = True' to get more details for server errors # Use 'DEBUG = True' to get more details for server errors
# (Default for relaeses: 'False') # (Default for relaeses: 'False')
@ -63,7 +65,14 @@ INSTALLED_APPS += INSTALLED_PLUGINS
KEY_LENGTH = 30 KEY_LENGTH = 30
def main(argv=None): _fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
def _fs2unicode(s):
if isinstance(s, unicode):
return s
return s.decode(_fs_encoding)
def main(argv=None, opt_defaults=None):
if argv is None: if argv is None:
argv = sys.argv[1:] argv = sys.argv[1:]
@ -81,6 +90,9 @@ def main(argv=None):
parser.add_option( parser.add_option(
"--no-reload", action="store_true", help="Do not reload the development server") "--no-reload", action="store_true", help="Do not reload the development server")
if not opt_defaults is None:
parser.set_defaults(**opt_defaults)
opts, args = parser.parse_args(argv) opts, args = parser.parse_args(argv)
if args: if args:
sys.stderr.write("This command does not take arguments!\n\n") sys.stderr.write("This command does not take arguments!\n\n")
@ -89,7 +101,7 @@ def main(argv=None):
# Find the path to the settings # Find the path to the settings
settings = opts.settings or \ settings = opts.settings or \
os.path.expanduser('~/.openslides/openslidessettings.py') os.path.join(os.path.expanduser('~'),'.openslides','openslides_personal_settings.py')
# Create settings if necessary # Create settings if necessary
if not os.path.exists(settings): if not os.path.exists(settings):
@ -128,7 +140,7 @@ def create_settings(settings):
setting_content = CONFIG_TEMPLATE % dict( setting_content = CONFIG_TEMPLATE % dict(
default_key=base64.b64encode(os.urandom(KEY_LENGTH)), default_key=base64.b64encode(os.urandom(KEY_LENGTH)),
dbpath=os.path.join(path_to_dir, 'database.db')) dbpath=_fs2unicode((os.path.join(path_to_dir, 'database.db'))))
if not os.path.exists(path_to_dir): if not os.path.exists(path_to_dir):
os.makedirs(path_to_dir) os.makedirs(path_to_dir)
@ -240,6 +252,47 @@ def start_browser(url):
t = threading.Thread(target=f) t = threading.Thread(target=f)
t.start() t.start()
def win32_portable_main(argv=None):
"""special entry point for the win32 portable version"""
import tempfile
# 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")
else:
import ctypes
shell32 = ctypes.WinDLL("shell32.dll")
SHGetFolderPath = shell32.SHGetFolderPathW
SHGetFolderPath.argtypes = (ctypes.c_void_p, ctypes.c_int,
ctypes.c_void_p, ctypes.c_uint32, ctypes.c_wchar_p)
SHGetFolderPath.restype = ctypes.c_uint32
CSIDL_LOCAL_APPDATA = 0x001c
MAX_PATH = 260
buf = ctypes.create_unicode_buffer(MAX_PATH)
res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf)
if res != 0:
raise Exception("Could not deterime APPDATA path")
default_settings = os.path.join(buf.value, "openslides",
"openslides_personal_settings.py")
main(argv, opt_defaults={ "settings": default_settings })
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -31,6 +31,7 @@ class ProjectorSlide(models.Model, SlideMixin):
title = models.CharField(max_length=256, verbose_name=_("Title")) title = models.CharField(max_length=256, verbose_name=_("Title"))
text = models.TextField(null=True, blank=True, verbose_name=_("Text")) text = models.TextField(null=True, blank=True, verbose_name=_("Text"))
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
def slide(self): def slide(self):
return { return {

View File

@ -9,7 +9,7 @@
.column { .column {
width: 50%; width: 50%;
float: left; float: left;
padding-bottom: 0px; padding-bottom: 100px;
} }
.portlet { .portlet {
margin: 0 10px 10px 0; margin: 0 10px 10px 0;

View File

@ -6,9 +6,9 @@
{% block header %} {% block header %}
<link type="text/css" href="{% static 'styles/humanity/jquery-ui-1.8.18.custom.css' %}" rel="stylesheet" /> <link type="text/css" href="{% static 'styles/humanity/jquery-ui-1.8.18.custom.css' %}" rel="stylesheet" />
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/projector-control.css' %}" /> <link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/dashboard.css' %}" />
<script type="text/javascript" src="{% static 'javascript/jquery-ui-1.8.18.custom.min.js' %}"></script> <script type="text/javascript" src="{% static 'javascript/jquery-ui-1.8.18.custom.min.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/projector-control.js' %}"></script> <script type="text/javascript" src="{% static 'javascript/dashboard.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/jquery.cookie.js' %}"></script> <script type="text/javascript" src="{% static 'javascript/jquery.cookie.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/jquery.form.js' %}"></script> <script type="text/javascript" src="{% static 'javascript/jquery.form.js' %}"></script>
{% endblock %} {% endblock %}
@ -17,7 +17,7 @@
<h1>{% trans 'Dashboard' %}</h1> <h1>{% trans 'Dashboard' %}</h1>
{% if perms.projector.can_manage_projector %} {% if perms.projector.can_manage_projector %}
<div style="text-align: right; padding: 0 10px 5px 0; margin-top:-20px;"> <div style="text-align: right; padding: 0 10px 5px 0; margin-top:-20px;">
<!-- projector control --> <!-- control projector view -->
{% trans "Adjust projector view" %}: {% trans "Adjust projector view" %}:
<a class="projector_edit" href="{% url projector_bigger %}" title="{% trans 'Zoom in' %}"> <a class="projector_edit" href="{% url projector_bigger %}" title="{% trans 'Zoom in' %}">
<img src="{% static 'images/icons/zoom-in.png' %}" /> <img src="{% static 'images/icons/zoom-in.png' %}" />

View File

@ -409,11 +409,11 @@ def get_widgets(request):
# Custom slide widget # Custom slide widget
context = { context = {
'slides': ProjectorSlide.objects.all(), 'slides': ProjectorSlide.objects.all().order_by('weight'),
'welcomepage_is_active': not bool(config["presentation"])} 'welcomepage_is_active': not bool(config["presentation"])}
widgets.append(Widget( widgets.append(Widget(
name='custom_slide', name='custom_slide',
display_name=_('Custom Slide'), display_name=_('Custom Slides'),
template='projector/custom_slide_widget.html', template='projector/custom_slide_widget.html',
context=context, context=context,
permission_required='projector.can_manage_projector', permission_required='projector.can_manage_projector',