Merge branch 'master' of github.com:OpenSlides/OpenSlides
This commit is contained in:
commit
93ee260856
@ -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,
|
||||||
|
@ -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.
@ -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")
|
||||||
|
|
||||||
|
@ -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')]
|
||||||
|
@ -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
|
||||||
|
@ -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')]
|
||||||
|
@ -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()
|
||||||
|
0
openslides/openslides_settings.py → openslides/openslides_global_settings.py
Executable file → Normal file
0
openslides/openslides_settings.py → openslides/openslides_global_settings.py
Executable file → Normal 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 {
|
||||||
|
@ -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;
|
@ -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' %}" />
|
||||||
|
@ -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',
|
||||||
|
Loading…
Reference in New Issue
Block a user