diff --git a/CHANGELOG b/CHANGELOG index d5524d69a..c1271cbbb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,14 @@ Version 1.5.1 (unreleased) ========================== [https://github.com/OpenSlides/OpenSlides/issues?milestone=15] +Participant: +- Added permission to see participants also to the manager group. +Files: +- Fixed error when a file was removed from filesystem. +Other: +- Fixed http status code when requesting a non-existing static page using + Tordado web server. + Version 1.5 (2013-11-25) ======================== diff --git a/openslides/agenda/signals.py b/openslides/agenda/signals.py index 2c7f093c1..c4e144fab 100644 --- a/openslides/agenda/signals.py +++ b/openslides/agenda/signals.py @@ -98,7 +98,7 @@ def agenda_list_of_speakers(sender, **kwargs): list-of-speakers slide. """ slide = get_active_object() - if isinstance(slide, Item): + if slide is None or isinstance(slide, Item): item = slide else: # TODO: If there are more the one items, use the first one in the diff --git a/openslides/mediafile/models.py b/openslides/mediafile/models.py index e2f2ec3f8..28ec44dca 100644 --- a/openslides/mediafile/models.py +++ b/openslides/mediafile/models.py @@ -4,6 +4,7 @@ import mimetypes from django.core.urlresolvers import reverse from django.db import models +from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop from openslides.projector.models import SlideMixin @@ -80,15 +81,20 @@ class Mediafile(SlideMixin, models.Model): def get_filesize(self): """ - Transforms Bytes to Kilobytes or Megabytes. Returns the size as string. + Transforms bytes to kilobytes or megabytes. Returns the size as string. """ # TODO: Read http://stackoverflow.com/a/1094933 and think about it. - size = self.mediafile.size - if size < 1024: - return '< 1 kB' - if size >= 1024 * 1024: - mB = size / 1024 / 1024 - return '%d MB' % mB + try: + size = self.mediafile.size + except OSError: + size_string = _('unknown') else: - kB = size / 1024 - return '%d kB' % kB + if size < 1024: + size_string = '< 1 kB' + elif size >= 1024 * 1024: + mB = size / 1024 / 1024 + size_string = '%d MB' % mB + else: + kB = size / 1024 + size_string = '%d kB' % kB + return size_string diff --git a/openslides/mediafile/templates/mediafile/mediafile_list.html b/openslides/mediafile/templates/mediafile/mediafile_list.html index 518b93f46..9f2d1d370 100644 --- a/openslides/mediafile/templates/mediafile/mediafile_list.html +++ b/openslides/mediafile/templates/mediafile/mediafile_list.html @@ -27,7 +27,7 @@ {% for mediafile in mediafile_list %} {{ mediafile }} - {{ mediafile.filetype }} + {% trans mediafile.filetype %} {{ mediafile.get_filesize }} {{ mediafile.timestamp }} {{ mediafile.uploader }} diff --git a/openslides/participant/signals.py b/openslides/participant/signals.py index 54cafa5c1..c76387680 100644 --- a/openslides/participant/signals.py +++ b/openslides/participant/signals.py @@ -118,6 +118,7 @@ def create_builtin_groups_and_admin(sender, **kwargs): group_staff = Group.objects.create(name=ugettext_noop('Staff'), pk=4) group_staff.permissions.add(perm_7, perm_9, perm_10, perm_10a, perm_11, perm_12, perm_13, perm_14, perm_15, perm_15a, perm_16) + group_staff.permissions.add(perm_6) # TODO: Remove this redundancy after cleanup of the permission system # Admin user create_or_reset_admin_user() diff --git a/openslides/projector/exceptions.py b/openslides/projector/exceptions.py new file mode 100644 index 000000000..88bf6dc00 --- /dev/null +++ b/openslides/projector/exceptions.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + + +class ProjectorExceptionWarning(RuntimeWarning): + pass diff --git a/openslides/projector/projector.py b/openslides/projector/projector.py index b1a73a8a1..b569284e7 100644 --- a/openslides/projector/projector.py +++ b/openslides/projector/projector.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- -from django.conf import settings +import warnings + from openslides.config.api import config +from .exceptions import ProjectorExceptionWarning + class Overlay(object): """ @@ -18,6 +21,9 @@ class Overlay(object): self.javascript_callback = get_javascript self.allways_active = allways_active + def __repr__(self): + return self.name + def get_widget_html(self): """ Returns the html code for the overlay widget. @@ -36,11 +42,10 @@ class Overlay(object): try: value = self.get_html_wrapper(self.projector_html_callback()) except Exception as exception: - if settings.DEBUG: - raise exception - else: - # Catch all errors, so an overlay can not kill the projector - value = '' + warnings.warn('%s in overlay "%s": %s' + % (type(exception).__name__, self, exception), + ProjectorExceptionWarning) + value = '' return value def get_javascript(self): diff --git a/openslides/projector/templates/projector/default_slide.html b/openslides/projector/templates/projector/default_slide.html index 71971372a..a44f04e56 100644 --- a/openslides/projector/templates/projector/default_slide.html +++ b/openslides/projector/templates/projector/default_slide.html @@ -2,5 +2,4 @@

{{ 'welcome_title'|get_config }} -

diff --git a/openslides/projector/templates/projector/projectorslide_slide.html b/openslides/projector/templates/projector/projectorslide_slide.html index 80c33eaaf..3eb47df89 100644 --- a/openslides/projector/templates/projector/projectorslide_slide.html +++ b/openslides/projector/templates/projector/projectorslide_slide.html @@ -2,7 +2,6 @@

{{ slide.title }} -

{% if slide.text %} diff --git a/openslides/static/javascript/utils.js b/openslides/static/javascript/utils.js index c45061b22..7c7602fa4 100644 --- a/openslides/static/javascript/utils.js +++ b/openslides/static/javascript/utils.js @@ -72,7 +72,7 @@ $(function () { function new_message(text, type) { var message = $('#dummy-notification').clone(true); - $(message).removeAttr('id').addClass(type).children('em').html(text); + $(message).removeAttr('id').addClass(type).html(text); $('#notifications').append(message); message.slideDown('fast'); } diff --git a/openslides/utils/tornado_webserver.py b/openslides/utils/tornado_webserver.py index 6b64df3b0..2558f141a 100644 --- a/openslides/utils/tornado_webserver.py +++ b/openslides/utils/tornado_webserver.py @@ -43,10 +43,10 @@ class DjangoStaticFileHandler(StaticFileHandler): # a shared root prefix # - we do not handle self.default_filename (we do not use it and it # does not make much sense here anyway) - if not os.path.exists(absolute_path): + if absolute_path is None or not os.path.exists(absolute_path): raise HTTPError(404) if not os.path.isfile(absolute_path): - raise HTTPError(403, "%s is not a file", self.path) + raise HTTPError(403, 'The requested resource is not a file.') return absolute_path diff --git a/tests/agenda/test_list_of_speakers.py b/tests/agenda/test_list_of_speakers.py index b4e240ac0..a6d4b50d0 100644 --- a/tests/agenda/test_list_of_speakers.py +++ b/tests/agenda/test_list_of_speakers.py @@ -5,6 +5,7 @@ from django.test.client import Client from mock import patch, MagicMock from openslides.agenda.models import Item, Speaker +from openslides.agenda.signals import agenda_list_of_speakers from openslides.config.api import config from openslides.participant.models import Group, User from openslides.projector.api import set_active_slide @@ -300,3 +301,16 @@ class GlobalListOfSpeakersLinks(SpeakerViewTestCase): response = self.admin_client.get('/agenda/list_of_speakers/end_speach/') self.assertRedirects(response, '/dashboard/') self.assertTrue(Speaker.objects.get(item__pk='1').end_time is not None) + + +class TestOverlay(TestCase): + def test_overlay_with_no_model_slide(self): + """ + When a slide is active, that is not a model (for example the agenda) + an Attribute Error was raised. + """ + config['projector_active_slide'] = {'callback': None} + + value = agenda_list_of_speakers(sender='test').get_projector_html() + + self.assertEqual(value, '') diff --git a/tests/mediafile/tests.py b/tests/mediafile/tests.py index 46106f190..507c4691a 100644 --- a/tests/mediafile/tests.py +++ b/tests/mediafile/tests.py @@ -255,4 +255,5 @@ class MediafileTest(TestCase): bigfile.seek(1048575) bigfile.write('0') self.assertEqual(object_4.get_filesize(), '1 MB') - object_4.mediafile.delete() + os.remove(mediafile_4_path) + self.assertEqual(object_4.get_filesize(), 'unknown') diff --git a/tests/participant/test_views.py b/tests/participant/test_views.py index 9c00db5ce..13e7c1800 100644 --- a/tests/participant/test_views.py +++ b/tests/participant/test_views.py @@ -2,9 +2,12 @@ import re +from django.contrib.auth.models import Permission +from django.contrib.contenttypes.models import ContentType from django.test.client import Client from openslides.config.api import config +from openslides.participant.api import get_registered_group from openslides.participant.models import get_protected_perm, Group, User from openslides.utils.test import TestCase @@ -93,7 +96,9 @@ class GroupViews(TestCase): class LockoutProtection(TestCase): """ Tests that a manager user can not lockout himself by doing - something that removes his last permission to manage participants. + something that removes his last permission to manage participants. Tests + also that he can see the participant app (although there is no absolute + protection). """ def setUp(self): self.user = User.objects.get(pk=1) @@ -159,6 +164,17 @@ class LockoutProtection(TestCase): field=None, errors='You can not remove the permission to manage participants from the last group you are in.') + def test_remove_permission_can_see_participant_from_registered(self): + self.assertTrue(self.user.has_perm('participant.can_see_participant')) + # Remove perm from registered group + can_see_perm = Permission.objects.get( + content_type=ContentType.objects.get(app_label='participant', model='user'), + codename='can_see_participant') + get_registered_group().permissions.remove(can_see_perm) + # Reload user + self.user = User.objects.get(pk=1) + self.assertTrue(self.user.has_perm('participant.can_see_participant')) + class TestUserSettings(TestCase): def setUp(self): diff --git a/tests/projector/test_overlays.py b/tests/projector/test_overlays.py index c59927c50..c9e8fdddf 100644 --- a/tests/projector/test_overlays.py +++ b/tests/projector/test_overlays.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- -from mock import MagicMock, patch +import warnings + +from mock import MagicMock from openslides.projector.projector import Overlay from openslides.utils.test import TestCase @@ -9,18 +11,11 @@ from openslides.utils.test import TestCase class OverlayTest(TestCase): def test_error_in_html(self): """ - Tests that the methof get_projector_html does not raise any errors. + Tests that the method get_projector_html does not raise any errors. """ get_projector_html = MagicMock(side_effect=Exception('no good error')) overlay = Overlay('test_overlay', lambda: 'widget_html', get_projector_html) - # Test in productive mode - with patch('openslides.projector.projector.settings.DEBUG', False): - self.assertEqual(overlay.get_projector_html(), '') - - # Test in debug mode - with patch('openslides.projector.projector.settings.DEBUG', True): - self.assertRaisesMessage( - Exception, - 'no good error', - overlay.get_projector_html) + with warnings.catch_warnings(record=True) as warning: + overlay.get_projector_html() + self.assertEqual(warning[0].message.message, 'Exception in overlay "test_overlay": no good error')