From 777d2266bcf93b4dd3db2373e6bd668e1647e1e2 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Wed, 22 Aug 2012 09:39:20 +0200 Subject: [PATCH 01/13] Fixed drag'n'drop of widgets into empty dashboard column. Renamed projector-control{js|css} files into dashboard{js|css}. --- .../javascript/{projector-control.js => dashboard.js} | 0 .../static/styles/{projector-control.css => dashboard.css} | 2 +- openslides/projector/templates/projector/dashboard.html | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) rename openslides/projector/static/javascript/{projector-control.js => dashboard.js} (100%) rename openslides/projector/static/styles/{projector-control.css => dashboard.css} (98%) 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" %}: From 32cfa6c9143d06498ea0548ae3fb6a893a648b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Thu, 23 Aug 2012 23:34:30 +0200 Subject: [PATCH 02/13] Fix encoding error in settings.py #349 --- openslides/main.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/openslides/main.py b/openslides/main.py index 08593d8c6..443900abb 100644 --- a/openslides/main.py +++ b/openslides/main.py @@ -25,7 +25,9 @@ import webbrowser import django.conf from django.core.management import execute_from_command_line -CONFIG_TEMPLATE = """ +CONFIG_TEMPLATE = """#!/usr/bin/env python +# -*- coding: utf-8 -*- + from openslides.openslides_settings import * # Use 'DEBUG = True' to get more details for server errors @@ -63,6 +65,13 @@ 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) + + def main(argv=None): if argv is None: argv = sys.argv[1:] @@ -89,7 +98,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','settings.py') # Create settings if necessary if not os.path.exists(settings): @@ -128,7 +137,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) From 6d1c356ffd9689a1cfa52b1f0643e56a7462cda6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Fri, 24 Aug 2012 14:23:12 +0200 Subject: [PATCH 03/13] Name openslides_personal_settings.py and openslides_global_settings.oy --- openslides/main.py | 4 ++-- .../{openslides_settings.py => openslides_global_settings.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename openslides/{openslides_settings.py => openslides_global_settings.py} (100%) mode change 100755 => 100644 diff --git a/openslides/main.py b/openslides/main.py index 443900abb..6764ea859 100644 --- a/openslides/main.py +++ b/openslides/main.py @@ -28,7 +28,7 @@ from django.core.management import execute_from_command_line CONFIG_TEMPLATE = """#!/usr/bin/env python # -*- coding: utf-8 -*- -from openslides.openslides_settings import * +from openslides.openslides_global_settings import * # Use 'DEBUG = True' to get more details for server errors # (Default for relaeses: 'False') @@ -98,7 +98,7 @@ def main(argv=None): # Find the path to the settings settings = opts.settings or \ - os.path.join(os.path.expanduser('~'),'.openslides','settings.py') + os.path.join(os.path.expanduser('~'),'.openslides','openslides_personal_settings.py') # Create settings if necessary if not os.path.exists(settings): 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 From 14ba868df4ca9b62bb99499532271109c2e4c998 Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Mon, 27 Aug 2012 20:24:41 +0200 Subject: [PATCH 04/13] Treat openslides the same way as other packages OpenSlides will now be copied from (and to) site-packages in the same way as it's dependencies --- extras/win32-portable/prepare_portable.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) 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") From cc20ec345618fae0a195e11a4017b434e4315e8f Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Mon, 27 Aug 2012 21:48:46 +0200 Subject: [PATCH 05/13] Add special handling to determine default settings for portable version The portable version will prefer to create it's settings in it's own directory and only fallback to a user-specific path ($LOCAL_APPDATA/openslides) if this directory is not writable --- extras/win32-portable/openslides.c | 2 +- openslides/main.py | 46 +++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) 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/openslides/main.py b/openslides/main.py index 6764ea859..dde97b165 100644 --- a/openslides/main.py +++ b/openslides/main.py @@ -72,7 +72,7 @@ def _fs2unicode(s): return s.decode(_fs_encoding) -def main(argv=None): +def main(argv=None, opt_defaults = None): if argv is None: argv = sys.argv[1:] @@ -90,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") @@ -249,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() From 7f6f7a934033028852b2a3e6b32e15bb47b2f2ee Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Mon, 27 Aug 2012 21:49:05 +0200 Subject: [PATCH 06/13] Rebuild openslides.exe --- extras/win32-portable/openslides.exe | Bin 7168 -> 7168 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/extras/win32-portable/openslides.exe b/extras/win32-portable/openslides.exe index 55a24e895183383f158097748d77212bdbc8976e..3e86e8909b2b4399cd9b6e4236a3c85da42a412a 100644 GIT binary patch delta 763 zcmW+zUuaTM96m?y)$6~4*cDfpB1Oe?*wzKbSw19;C~z2+P_0D^XKO_$J?vqE37JN= z1F;7mcDV(6*n{8(5%{0tP8HC^do_x`rakF^*J1F3kK0?KN z>PgH?N=L;NP|ffF2B};oF8!k@Yoy{l4RTo06@hDkjYyb(3KOYSXEVB zRq|~nka7rp-3Uggy9WP|3BDw4{V;w({^-+qKo*5jv%0C33wNm0C!2!jh?+>X*?SBr z{Yp=`gmHm*joW~KjZ=WvOqT(dO)gv{o2FZa0v!3fm6M9ZYIZ@zZys6tItHdJI}F7w zhV}+Ssm?H11AJHzyDC}sRkBxA;x|+}T~^7n4pe%>w}J9ur0lB9DSN=L!-CRbI@=yl z+T@)OWB6n!`k)nDHTf+-1BdEOeg`7B2qZJRx$GXy$VD~WhY(&BZcMR}6BZ}7RVFMo zY+$o&=_Glp3E)B{c=Vm#`h0F-d~EvZlV{19`}4EYk7wo<&YnY+Z&nAc2)4fVKaD>P AvH$=8 delta 751 zcmW+zOK1~O6n!t5NhUun(kWJrDM9hmq{PoaLK@AYfg*}20X3kYpc3O}m4MJd10i6V zik~OaMHU&RLKa;V(n63dvIs^*&_xLavzSE|BkiIKMbYB(-eKUJd+xiR^WGH0#c-Vp zMs4vb6X<2@X*Xpl`qT15XkM~}EL3On!UcMSy%&532BA2~k~!Wlb)|AAXV1*AZDBFA zdL~YK!m%0GUik?QuWM2ZH00L6<5#~SJ0R-{g3lf*R9n-M7(d<7u$&d#^=6)0?yl$T)u5? zLN9q6mzP2H5_{0d)jIRrAEvO7%a0(R~dw zy@`To1GJx{gcuFDLn-!@>GpA&V}I;Ly2IwBDYrSPUr7wPVz3Pc=xA?GK+zs@Q|H^8*}^NB;uZ2=$r*po;IS~LiDa9 zDr<a lLuc75OOzIxvHfrDw#RoKJUMx)`F+esS)GnFrGvk%{{i?t2V(#L From ce87353cd43c6fa8b69e0a0308f75f6138c120eb Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Mon, 27 Aug 2012 21:54:12 +0200 Subject: [PATCH 07/13] Update instructions for building the portable --- extras/win32-portable/create_portable.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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, From 1b650442237be023d620426ab80ed0d10c6c3ab1 Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Tue, 28 Aug 2012 19:07:40 +0200 Subject: [PATCH 08/13] Remove a few spaces for pep8 conformance --- openslides/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openslides/main.py b/openslides/main.py index dde97b165..17d682eb8 100644 --- a/openslides/main.py +++ b/openslides/main.py @@ -72,7 +72,7 @@ def _fs2unicode(s): return s.decode(_fs_encoding) -def main(argv=None, opt_defaults = None): +def main(argv=None, opt_defaults=None): if argv is None: argv = sys.argv[1:] @@ -261,7 +261,7 @@ def win32_portable_main(argv=None): # python interpreter portable_dir = os.path.dirname(os.path.abspath(sys.executable)) try: - fd, test_file = tempfile.mkstemp(dir = portable_dir) + fd, test_file = tempfile.mkstemp(dir=portable_dir) except OSError: portable_dir_writeable = False else: @@ -291,7 +291,7 @@ def win32_portable_main(argv=None): default_settings = os.path.join(buf.value, "openslides", "openslides_personal_settings.py") - main(argv, opt_defaults = { "settings": default_settings }) + main(argv, opt_defaults={ "settings": default_settings }) if __name__ == "__main__": From 55073494fc20ea7937bed12d7b3d92b8ffe0a078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Wed, 5 Sep 2012 21:54:34 +0200 Subject: [PATCH 09/13] Insert weight field in custom slides and order custom slides in the widget --- openslides/projector/models.py | 1 + openslides/projector/views.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) 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/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', From d84a8d2bddbb36b67f48a08757b9951a2db013c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Wed, 5 Sep 2012 22:19:53 +0200 Subject: [PATCH 10/13] Sorting assignments by name in the widget --- openslides/assignment/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openslides/assignment/views.py b/openslides/assignment/views.py index 7bffaa18e..1e76e0999 100644 --- a/openslides/assignment/views.py +++ b/openslides/assignment/views.py @@ -660,5 +660,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')] From 2ecd552d2b93aca8c6e62e38d5a71a3ea137f93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Wed, 5 Sep 2012 22:38:40 +0200 Subject: [PATCH 11/13] Default Sorting of applications by number --- openslides/application/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openslides/application/views.py b/openslides/application/views.py index 37a0e08b4..c09b6a4a9 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() From d1c12ea0e59396e75caa52091893bb1243250ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Wed, 5 Sep 2012 22:42:44 +0200 Subject: [PATCH 12/13] Sorting applications in the widget by number --- openslides/application/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openslides/application/views.py b/openslides/application/views.py index c09b6a4a9..f702eb53b 100644 --- a/openslides/application/views.py +++ b/openslides/application/views.py @@ -921,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')] From 75eb59654799233ee63527cd9871261654ae4279 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Thu, 6 Sep 2012 15:57:42 +0200 Subject: [PATCH 13/13] block a user to be a candidate from a assignment, if he delete his candidation by him self --- openslides/assignment/models.py | 35 +++++++++++++++++++++++++-------- openslides/assignment/views.py | 9 ++++++--- 2 files changed, 33 insertions(+), 11 deletions(-) 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..bda689102 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: