diff --git a/extras/win32-portable/create_portable.txt b/extras/win32-portable/create_portable.txt index 9818f6ec9..6770efd3f 100644 --- a/extras/win32-portable/create_portable.txt +++ b/extras/win32-portable/create_portable.txt @@ -6,11 +6,15 @@ How to create a new portable Windows distribution of OpenSlides: 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 -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 Note: Creating the portable Windows distribution of OpenSlides is not possible, diff --git a/extras/win32-portable/openslides.c b/extras/win32-portable/openslides.c index 997baf123..6df2f8e7f 100644 --- a/extras/win32-portable/openslides.c +++ b/extras/win32-portable/openslides.c @@ -23,7 +23,7 @@ static const char *site_code = static const char *run_openslides_code = "import openslides.main;" - "openslides.main.main()"; + "openslides.main.win32_portable_main()"; /* determine the path to the executable * NOTE: Py_GetFullProgramPath() can't be used because diff --git a/extras/win32-portable/openslides.exe b/extras/win32-portable/openslides.exe index 55a24e895..3e86e8909 100644 Binary files a/extras/win32-portable/openslides.exe and b/extras/win32-portable/openslides.exe differ diff --git a/extras/win32-portable/prepare_portable.py b/extras/win32-portable/prepare_portable.py index 574a4cfba..04c1e0cc7 100644 --- a/extras/win32-portable/prepare_portable.py +++ b/extras/win32-portable/prepare_portable.py @@ -45,10 +45,6 @@ LIBEXCLUDE = [ r"^unittest/", ] -OPENSLIDES_EXCLUDE = [ - r"^openslides/settings.py" -] - SITE_PACKAGES = { "django": { @@ -84,7 +80,10 @@ SITE_PACKAGES = { "pil": { # NOTE: PIL is a special case, see copy_pil "copy": [], - } + }, + "openslides": { + "copy" : ["openslides"], + }, } PY_DLLS = [ @@ -302,9 +301,6 @@ def main(): collect_lib(libdir, odir) 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(): sys.stdout.write("Using prebuild openslides.exe\n") diff --git a/openslides/application/views.py b/openslides/application/views.py index 37a0e08b4..f702eb53b 100644 --- a/openslides/application/views.py +++ b/openslides/application/views.py @@ -99,10 +99,11 @@ def overview(request): else: sort = sortfilter['sort'] query = query.order_by(sort) - if sort.startswith('aversion_'): # limit result to last version of an application 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: query = query.reverse() @@ -920,5 +921,5 @@ def get_widgets(request): Widget( name='applications', template='application/widget.html', - context={'applications': Application.objects.all()}, + context={'applications': Application.objects.all().order_by('number')}, permission_required='application.can_manage_application')] diff --git a/openslides/assignment/models.py b/openslides/assignment/models.py index bf288cd8e..accfc2105 100644 --- a/openslides/assignment/models.py +++ b/openslides/assignment/models.py @@ -33,6 +33,7 @@ class AssignmentCandidate(models.Model): assignment = models.ForeignKey("Assignment") person = PersonField(db_index=True) elected = models.BooleanField(default=False) + blocked = models.BooleanField(default=False) def __unicode__(self): return unicode(self.person) @@ -72,6 +73,8 @@ class Assignment(models.Model, SlideMixin): def run(self, candidate, person=None): """ 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. # Use other Exceptions @@ -79,23 +82,39 @@ class Assignment(models.Model, SlideMixin): raise NameError(_('%s is already a candidate.') % candidate) if not person.has_perm("assignment.can_manage_assignment") and self.status != 'sea': 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 """ 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: # TODO: Use an OpenSlides Error raise Exception(_('%s is no candidate') % candidate) def is_candidate(self, person): - if self.assignment_candidats.filter(person=person).exists(): - return True - else: - return False + """ + return True, if person is a candidate. + """ + return self.assignment_candidats.filter(person=person) \ + .exclude(blocked=True).exists() @property def assignment_candidats(self): @@ -110,7 +129,7 @@ class Assignment(models.Model, SlideMixin): return self.get_participants(only_elected=True) 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: # TODO: Use right Exception diff --git a/openslides/assignment/views.py b/openslides/assignment/views.py index 7bffaa18e..83b5e04c5 100644 --- a/openslides/assignment/views.py +++ b/openslides/assignment/views.py @@ -81,9 +81,12 @@ def view(request, assignment_id=None): user = form.cleaned_data['candidate'] try: assignment.run(user, request.user) - messages.success(request, _("Candidate %s was nominated successfully.") % (user)) except NameError, e: messages.error(request, e) + else: + messages.success(request, _( + "Candidate %s was nominated successfully.") + % user) else: if request.user.has_perm('assignment.can_nominate_other'): form = AssignmentRunForm() @@ -184,7 +187,7 @@ def delrun(request, assignment_id): assignment = Assignment.objects.get(pk=assignment_id) try: if assignment.status == 'sea' or user.has_perm("assignment.can_manage_assignment"): - assignment.delrun(request.user) + assignment.delrun(request.user, blocked=True) else: messages.error(request, _('The candidate list is already closed.')) except Exception, e: @@ -201,7 +204,7 @@ def delother(request, assignment_id, user_id): if request.method == 'POST': try: - assignment.delrun(person) + assignment.delrun(person, blocked=False) except Exception, e: messages.error(request, e) else: @@ -660,5 +663,5 @@ def get_widgets(request): Widget( name=_('Assignments'), template='assignment/widget.html', - context={'assignments': Assignment.objects.all()}, + context={'assignments': Assignment.objects.all().order_by('name')}, permission_required='assignment.can_manage_assignment')] diff --git a/openslides/main.py b/openslides/main.py index 08593d8c6..17d682eb8 100644 --- a/openslides/main.py +++ b/openslides/main.py @@ -25,8 +25,10 @@ import webbrowser import django.conf from django.core.management import execute_from_command_line -CONFIG_TEMPLATE = """ -from openslides.openslides_settings import * +CONFIG_TEMPLATE = """#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from openslides.openslides_global_settings import * # Use 'DEBUG = True' to get more details for server errors # (Default for relaeses: 'False') @@ -63,7 +65,14 @@ INSTALLED_APPS += INSTALLED_PLUGINS 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: argv = sys.argv[1:] @@ -81,6 +90,9 @@ def main(argv=None): 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) + opts, args = parser.parse_args(argv) if args: 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 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 if not os.path.exists(settings): @@ -128,7 +140,7 @@ def create_settings(settings): setting_content = CONFIG_TEMPLATE % dict( 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): os.makedirs(path_to_dir) @@ -240,6 +252,47 @@ 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 + + # 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__": main() diff --git a/openslides/openslides_settings.py b/openslides/openslides_global_settings.py old mode 100755 new mode 100644 similarity index 100% rename from openslides/openslides_settings.py rename to openslides/openslides_global_settings.py diff --git a/openslides/projector/models.py b/openslides/projector/models.py index e6e51716a..299b41afb 100644 --- a/openslides/projector/models.py +++ b/openslides/projector/models.py @@ -31,6 +31,7 @@ class ProjectorSlide(models.Model, SlideMixin): title = models.CharField(max_length=256, verbose_name=_("Title")) text = models.TextField(null=True, blank=True, verbose_name=_("Text")) + weight = models.IntegerField(default=0, verbose_name=_("Weight")) def slide(self): return { diff --git a/openslides/projector/static/javascript/projector-control.js b/openslides/projector/static/javascript/dashboard.js similarity index 100% rename from openslides/projector/static/javascript/projector-control.js rename to openslides/projector/static/javascript/dashboard.js diff --git a/openslides/projector/static/styles/projector-control.css b/openslides/projector/static/styles/dashboard.css similarity index 98% rename from openslides/projector/static/styles/projector-control.css rename to openslides/projector/static/styles/dashboard.css index b131114fd..1ff1b62ed 100644 --- a/openslides/projector/static/styles/projector-control.css +++ b/openslides/projector/static/styles/dashboard.css @@ -9,7 +9,7 @@ .column { width: 50%; float: left; - padding-bottom: 0px; + padding-bottom: 100px; } .portlet { margin: 0 10px 10px 0; diff --git a/openslides/projector/templates/projector/dashboard.html b/openslides/projector/templates/projector/dashboard.html index bc2669780..5b6b9091e 100644 --- a/openslides/projector/templates/projector/dashboard.html +++ b/openslides/projector/templates/projector/dashboard.html @@ -6,9 +6,9 @@ {% block header %} - + - + {% endblock %} @@ -17,7 +17,7 @@

{% trans 'Dashboard' %}

{% if perms.projector.can_manage_projector %}
- + {% trans "Adjust projector view" %}: diff --git a/openslides/projector/views.py b/openslides/projector/views.py index 96af29ed6..59b660e2b 100644 --- a/openslides/projector/views.py +++ b/openslides/projector/views.py @@ -409,11 +409,11 @@ def get_widgets(request): # Custom slide widget context = { - 'slides': ProjectorSlide.objects.all(), + 'slides': ProjectorSlide.objects.all().order_by('weight'), 'welcomepage_is_active': not bool(config["presentation"])} widgets.append(Widget( name='custom_slide', - display_name=_('Custom Slide'), + display_name=_('Custom Slides'), template='projector/custom_slide_widget.html', context=context, permission_required='projector.can_manage_projector',