Insert new app to upload files via the frontend. Let tornado server media files. Insert icon-mediafile css class. Insert extra_stylfiles context variable.
This commit is contained in:
parent
571649d5a6
commit
ad0e157bd1
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,7 +1,12 @@
|
||||
.venv/*
|
||||
# General
|
||||
*.pyc
|
||||
*.swp
|
||||
*~
|
||||
|
||||
# Virtual Environment
|
||||
.venv/*
|
||||
|
||||
# Development settings and database
|
||||
settings.py
|
||||
database.sqlite
|
||||
!tests/settings.py
|
||||
@ -14,6 +19,6 @@ dist/*
|
||||
.DS_Store
|
||||
versiontools*
|
||||
|
||||
# Unit test / coverage reports
|
||||
# Unit test and coverage reports
|
||||
.coverage
|
||||
htmlcov
|
||||
|
@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
openslides.openslides_settings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
openslides.global_settings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
OpenSlides default settings.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
@ -15,6 +15,7 @@ import sys
|
||||
|
||||
from openslides.main import fs2unicode
|
||||
|
||||
|
||||
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
@ -34,7 +35,6 @@ LANGUAGES = (
|
||||
('fr', ugettext('French')),
|
||||
)
|
||||
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = True
|
||||
@ -47,18 +47,14 @@ LOCALE_PATHS = (
|
||||
fs2unicode(os.path.join(SITE_ROOT, 'locale')),
|
||||
)
|
||||
|
||||
# Absolute path to the directory that holds media.
|
||||
# Example: "/home/media/media.lawrence.com/"
|
||||
MEDIA_ROOT = fs2unicode(os.path.join(SITE_ROOT, './static/'))
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash if there is a path component (optional in other cases).
|
||||
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
||||
MEDIA_URL = ''
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
# Absolute path to the directory that holds static media from ``collectstatic``
|
||||
# Example: "/home/media/static.lawrence.com/"
|
||||
STATIC_ROOT = fs2unicode(os.path.join(SITE_ROOT, '../site-static'))
|
||||
STATIC_ROOT = fs2unicode(os.path.join(SITE_ROOT, '../collected-site-static'))
|
||||
|
||||
# URL that handles the media served from STATIC_ROOT. Make sure to use a
|
||||
# trailing slash if there is a path component (optional in other cases).
|
||||
@ -119,6 +115,7 @@ INSTALLED_APPS = (
|
||||
'openslides.motion',
|
||||
'openslides.assignment',
|
||||
'openslides.participant',
|
||||
'openslides.mediafile',
|
||||
'openslides.config',
|
||||
)
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Main script to start and set up OpenSlides.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
@ -64,6 +64,11 @@ INSTALLED_PLUGINS = (
|
||||
)
|
||||
|
||||
INSTALLED_APPS += INSTALLED_PLUGINS
|
||||
|
||||
# Absolute path to the directory that holds media.
|
||||
# Example: "/home/media/media.lawrence.com/"
|
||||
MEDIA_ROOT = %(media_root_path)s
|
||||
|
||||
"""
|
||||
|
||||
KEY_LENGTH = 30
|
||||
@ -195,14 +200,17 @@ def create_settings(settings_path, database_path=None):
|
||||
if database_path is _portable_db_path:
|
||||
database_path = get_portable_db_path()
|
||||
dbpath_value = 'openslides.main.get_portable_db_path()'
|
||||
media_root_path_value = 'openslides.main.get_portable_media_root_path()'
|
||||
else:
|
||||
if database_path is None:
|
||||
database_path = get_user_data_path('openslides', 'database.sqlite')
|
||||
dbpath_value = repr(fs2unicode(database_path))
|
||||
media_root_path_value = repr(fs2unicode(get_user_data_path('openslides', 'media', '')))
|
||||
|
||||
settings_content = CONFIG_TEMPLATE % dict(
|
||||
default_key=base64.b64encode(os.urandom(KEY_LENGTH)),
|
||||
dbpath=dbpath_value)
|
||||
dbpath=dbpath_value,
|
||||
media_root_path=media_root_path_value)
|
||||
|
||||
if not os.path.exists(settings_module):
|
||||
os.makedirs(settings_module)
|
||||
@ -370,6 +378,10 @@ def get_portable_db_path():
|
||||
return get_portable_path('openslides', 'database.sqlite')
|
||||
|
||||
|
||||
def get_portable_media_root_path():
|
||||
return get_portable_path('openslides', 'media', '')
|
||||
|
||||
|
||||
def win32_get_app_data_path(*args):
|
||||
shell32 = ctypes.WinDLL("shell32.dll")
|
||||
SHGetFolderPath = shell32.SHGetFolderPathW
|
||||
|
0
openslides/mediafile/__init__.py
Normal file
0
openslides/mediafile/__init__.py
Normal file
50
openslides/mediafile/forms.py
Normal file
50
openslides/mediafile/forms.py
Normal file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
openslides.mediafile.forms
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Forms for the mediafile app.
|
||||
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from django.forms import ModelForm
|
||||
|
||||
from openslides.utils.forms import CssClassMixin
|
||||
|
||||
from .models import Mediafile
|
||||
|
||||
|
||||
class MediafileNormalUserCreateForm(CssClassMixin, ModelForm):
|
||||
"""Form to create a media file.
|
||||
|
||||
This form is only used by normal users, not by managers.
|
||||
|
||||
"""
|
||||
class Meta:
|
||||
model = Mediafile
|
||||
exclude = ('uploader',)
|
||||
|
||||
|
||||
class MediafileUpdateForm(CssClassMixin, ModelForm):
|
||||
"""Form to edit mediafile entries.
|
||||
|
||||
This form is only for managers to update the mediafile entry.
|
||||
|
||||
"""
|
||||
class Meta:
|
||||
model = Mediafile
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Method to save the form.
|
||||
|
||||
Here the overwrite is to delete old files.
|
||||
|
||||
"""
|
||||
if not self.instance.pk is None:
|
||||
old_file = Mediafile.objects.get(pk=self.instance.pk).mediafile
|
||||
if not old_file == self.instance.mediafile:
|
||||
old_file.delete()
|
||||
return super(MediafileUpdateForm, self).save(*args, **kwargs)
|
85
openslides/mediafile/models.py
Normal file
85
openslides/mediafile/models.py
Normal file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
openslides.mediafile.models
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Models for the mediafile app.
|
||||
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import mimetypes
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_noop
|
||||
|
||||
from openslides.utils.person.models import PersonField
|
||||
|
||||
|
||||
class Mediafile(models.Model):
|
||||
"""The Mediafile class
|
||||
|
||||
Class for uploaded files which can be delivered under a certain url.
|
||||
|
||||
"""
|
||||
mediafile = models.FileField(upload_to='file')
|
||||
"""A FileField
|
||||
|
||||
See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield
|
||||
for more information.
|
||||
|
||||
"""
|
||||
title = models.CharField(max_length=255, unique=True)
|
||||
"""A string representing the title of the file."""
|
||||
|
||||
uploader = PersonField(blank=True)
|
||||
"""A person – the uploader of a file."""
|
||||
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
"""A DateTimeField to save the upload date and time."""
|
||||
|
||||
filetype = models.CharField(max_length=255, editable=False)
|
||||
"""A string used to show the type of the file."""
|
||||
|
||||
class Meta:
|
||||
"""Meta class for the mediafile model."""
|
||||
ordering = ['title']
|
||||
permissions = (
|
||||
('can_see', ugettext_noop('Can see the list of files')),
|
||||
('can_upload', ugettext_noop('Can upload files')),
|
||||
('can_manage', ugettext_noop('Can manage files')),)
|
||||
|
||||
def __unicode__(self):
|
||||
"""Method for representation."""
|
||||
return self.title
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Method to read filetype and then save to the database."""
|
||||
if self.mediafile:
|
||||
self.filetype = mimetypes.guess_type(self.mediafile.path)[0] or ugettext_noop('unknown')
|
||||
else:
|
||||
self.filetype = ugettext_noop('unknown')
|
||||
return super(Mediafile, self).save(*args, **kwargs)
|
||||
|
||||
@models.permalink
|
||||
def get_absolute_url(self, link='update'):
|
||||
"""Returns the URL to a mediafile. The link can be 'update' or 'delete'."""
|
||||
if link == 'update' or link == 'edit': # 'edit' ist only used until utils/views.py is fixed
|
||||
return ('mediafile_update', [str(self.id)])
|
||||
if link == 'delete':
|
||||
return ('mediafile_delete', [str(self.id)])
|
||||
|
||||
def get_filesize(self):
|
||||
"""Transforms Bytes to Kilobytes or Megabytes. Returns the size as string."""
|
||||
size = self.mediafile.size
|
||||
if size < 1024:
|
||||
return '< 1 kB'
|
||||
if size >= 1024 * 1024:
|
||||
mB = size / 1024 / 1024
|
||||
return '%d MB' % mB
|
||||
else:
|
||||
kB = size / 1024
|
||||
return '%d kB' % kB
|
||||
# TODO: Read http://stackoverflow.com/a/1094933 and think about it.
|
12
openslides/mediafile/static/styles/mediafile.css
Normal file
12
openslides/mediafile/static/styles/mediafile.css
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* OpenSlides mediafile style
|
||||
*
|
||||
* :copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
* :license: GNU GPL, see LICENSE for more details.
|
||||
*/
|
||||
|
||||
/** Navigation icons (mapping to glyphicons-halflings) **/
|
||||
|
||||
.icon-mediafile {
|
||||
background-position: -96px -24px;
|
||||
}
|
37
openslides/mediafile/templates/mediafile/mediafile_form.html
Normal file
37
openslides/mediafile/templates/mediafile/mediafile_form.html
Normal file
@ -0,0 +1,37 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}
|
||||
{{ block.super }} –
|
||||
{% if mediafile %}
|
||||
{% trans "Edit media" %}
|
||||
{% else %}
|
||||
{% trans "New media" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% if mediafile %}
|
||||
{% trans "Edit media" %}
|
||||
{% else %}
|
||||
{% trans "New media" %}
|
||||
{% endif %}
|
||||
<small class="pull-right">
|
||||
<a href="{% url 'mediafile_list' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to overview" %}</a>
|
||||
</small>
|
||||
</h1>
|
||||
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
|
||||
{% include "form.html" %}
|
||||
<p>
|
||||
{% if perms.mediafile.can_manage %}
|
||||
{% include "formbuttons_saveapply.html" %}
|
||||
{% else %}
|
||||
{% include "formbuttons_save.html" %}
|
||||
{% endif %}
|
||||
<a href="{% url 'mediafile_list' %}" class="btn">{% trans 'Cancel' %}</a>
|
||||
</p>
|
||||
<small>* {% trans "required" %}</small>
|
||||
</form>
|
||||
{% endblock %}
|
49
openslides/mediafile/templates/mediafile/mediafile_list.html
Normal file
49
openslides/mediafile/templates/mediafile/mediafile_list.html
Normal file
@ -0,0 +1,49 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load tags %}
|
||||
|
||||
{% block title %}{{ block.super }} – {% trans 'Media' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans 'Media' %}
|
||||
<small class="pull-right">
|
||||
{% if perms.mediafile.can_upload %}
|
||||
<a href="{% url 'mediafile_create' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'New media' %}"><i class="icon-plus"></i> {% trans "New" %}</a>
|
||||
{% endif %}
|
||||
</small>
|
||||
</h1>
|
||||
<table class="table table-striped table-bordered">
|
||||
<tr>
|
||||
<th>{% trans 'Title' %}</th>
|
||||
<th>{% trans 'Type' %}</th>
|
||||
<th>{% trans 'Size' %}</th>
|
||||
<th>{% trans 'Upload time' %}</th>
|
||||
<th>{% trans 'Uploader' %}</th>
|
||||
{% if perms.mediafile.can_manage %}
|
||||
<th class="mini_width">{% trans "Actions" %}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% for mediafile in mediafile_list %}
|
||||
<tr>
|
||||
<td><a href="{{ mediafile.mediafile.url }}">{{ mediafile }}</a></td>
|
||||
<td>{{ mediafile.filetype }}</td>
|
||||
<td>{{ mediafile.get_filesize }}</td>
|
||||
<td>{{ mediafile.timestamp }}</td>
|
||||
<td>{{ mediafile.uploader }}</td>
|
||||
{% if perms.mediafile.can_manage %}
|
||||
<td>
|
||||
<span style="width: 1px; white-space: nowrap;">
|
||||
<a href="{% model_url mediafile 'update' %}" title="{% trans 'Edit' %}" class="btn btn-mini"><i class="icon-pencil"></i></a>
|
||||
<a href="{% model_url mediafile 'delete' %}" title="{% trans 'Delete' %}" class="btn btn-mini"><i class="icon-remove"></i></a>
|
||||
</span>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6"><i>{% trans 'No media available.' %}</i></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
40
openslides/mediafile/urls.py
Normal file
40
openslides/mediafile/urls.py
Normal file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
openslides.mediafile.urls
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
URL patterns for the mediafile app.
|
||||
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from django.conf.urls import url, patterns
|
||||
|
||||
from .models import Mediafile
|
||||
from .views import (MediafileListView, MediafileCreateView,
|
||||
MediafileUpdateView, MediafileDeleteView)
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$',
|
||||
MediafileListView.as_view(),
|
||||
name='mediafile_list',
|
||||
),
|
||||
|
||||
url(r'^new/$',
|
||||
MediafileCreateView.as_view(),
|
||||
name='mediafile_create',
|
||||
),
|
||||
|
||||
url(r'^(?P<pk>\d+)/edit/$',
|
||||
MediafileUpdateView.as_view(),
|
||||
name='mediafile_update',
|
||||
),
|
||||
|
||||
url(r'^(?P<pk>\d+)/del/$',
|
||||
MediafileDeleteView.as_view(),
|
||||
name='mediafile_delete',
|
||||
),
|
||||
)
|
96
openslides/mediafile/views.py
Normal file
96
openslides/mediafile/views.py
Normal file
@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
openslides.mediafile.views
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Views for the mediafile app.
|
||||
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.views import ListView, CreateView, UpdateView, DeleteView
|
||||
|
||||
from .models import Mediafile
|
||||
from .forms import MediafileNormalUserCreateForm, MediafileUpdateForm
|
||||
|
||||
|
||||
class MediafileListView(ListView):
|
||||
"""View to see a table of all uploaded files."""
|
||||
model = Mediafile
|
||||
|
||||
def has_permission(self, request, *args, **kwargs):
|
||||
return (request.user.has_perm('mediafile.can_see') or
|
||||
request.user.has_perm('mediafile.can_upload') or
|
||||
request.user.has_perm('mediafile.can_manage'))
|
||||
|
||||
|
||||
class MediafileCreateView(CreateView):
|
||||
"""View to upload a new file
|
||||
|
||||
A manager can also set the uploader, else the request user is set as uploader.
|
||||
|
||||
"""
|
||||
model = Mediafile
|
||||
permission_required = 'mediafile.can_upload'
|
||||
success_url_name = 'mediafile_list'
|
||||
|
||||
def get_form(self, form_class):
|
||||
form_kwargs = self.get_form_kwargs()
|
||||
if self.request.method == 'GET':
|
||||
form_kwargs['initial'].update({'uploader': self.request.user.person_id}) # TODO: Check this.
|
||||
if not self.request.user.has_perm('mediafile.can_manage'):
|
||||
# Return our own ModelForm
|
||||
return MediafileNormalUserCreateForm(**form_kwargs)
|
||||
else:
|
||||
# Return a ModelForm created by Django.
|
||||
return form_class(**form_kwargs)
|
||||
|
||||
def manipulate_object(self, *args, **kwargs):
|
||||
if not self.request.user.has_perm('mediafile.can_manage'):
|
||||
self.object.uploader = self.request.user
|
||||
return super(MediafileCreateView, self).manipulate_object(*args, **kwargs)
|
||||
|
||||
|
||||
class MediafileUpdateView(UpdateView):
|
||||
"""View to edit the entry of an uploaded file."""
|
||||
model = Mediafile
|
||||
permission_required = 'mediafile.can_manage'
|
||||
form_class = MediafileUpdateForm
|
||||
success_url_name = 'mediafile_list'
|
||||
|
||||
def get_form_kwargs(self, *args, **kwargs):
|
||||
form_kwargs = super(MediafileUpdateView, self).get_form_kwargs(*args, **kwargs)
|
||||
form_kwargs['initial'].update({'uploader': self.object.uploader.person_id})
|
||||
return form_kwargs
|
||||
|
||||
|
||||
class MediafileDeleteView(DeleteView):
|
||||
"""View to delete the entry of an uploaded file and the file itself."""
|
||||
model = Mediafile
|
||||
permission_required = 'mediafile.can_manage'
|
||||
success_url_name = 'mediafile_list'
|
||||
|
||||
def case_yes(self, *args, **kwargs):
|
||||
"""Deletes the file in the filesystem, if user clicks "Yes"."""
|
||||
self.object.mediafile.delete()
|
||||
return super(MediafileDeleteView, self).case_yes(*args, **kwargs)
|
||||
|
||||
|
||||
def register_tab(request):
|
||||
"""Inserts a new Tab to the views for files."""
|
||||
selected = request.path.startswith('/mediafile/')
|
||||
return Tab(
|
||||
title=_('Media'),
|
||||
app='mediafile', # TODO: Rename this to icon='mediafile' later
|
||||
stylefile='styles/mediafile.css',
|
||||
url=reverse('mediafile_list'),
|
||||
permission=(request.user.has_perm('mediafile.can_see') or
|
||||
request.user.has_perm('mediafile.can_upload') or
|
||||
request.user.has_perm('mediafile.can_manage')),
|
||||
selected=selected)
|
@ -6,14 +6,13 @@
|
||||
|
||||
Useful functions for the participant app.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from random import choice
|
||||
import csv
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.db import transaction
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
@ -22,11 +21,6 @@ from openslides.utils import csv_ext
|
||||
from openslides.participant.models import User, Group
|
||||
|
||||
|
||||
DEFAULT_PERMS = ['can_see_agenda', 'can_see_projector',
|
||||
'can_see_motion', 'can_see_assignment',
|
||||
'can_see_dashboard']
|
||||
|
||||
|
||||
def gen_password():
|
||||
"""
|
||||
generates a random passwort.
|
||||
|
@ -26,7 +26,7 @@ from openslides.projector.api import register_slidemodel
|
||||
from openslides.projector.projector import SlideMixin
|
||||
|
||||
|
||||
class User(DjangoUser, PersonMixin, Person, SlideMixin):
|
||||
class User(PersonMixin, DjangoUser, Person, SlideMixin):
|
||||
prefix = 'user' # This is for the slides
|
||||
person_prefix = 'user'
|
||||
GENDER_CHOICES = (
|
||||
|
@ -42,19 +42,23 @@ def create_builtin_groups(sender, **kwargs):
|
||||
ct_participant = ContentType.objects.get(app_label='participant', model='user')
|
||||
perm_6 = Permission.objects.get(content_type=ct_participant, codename='can_see_participant')
|
||||
|
||||
ct_mediafile = ContentType.objects.get(app_label='mediafile', model='mediafile')
|
||||
perm_6a = Permission.objects.get(content_type=ct_mediafile, codename='can_see')
|
||||
|
||||
group_anonymous = Group.objects.create(name=ugettext_noop('Anonymous'))
|
||||
group_anonymous.permissions.add(perm_1, perm_2, perm_3, perm_4, perm_5, perm_6)
|
||||
group_anonymous.permissions.add(perm_1, perm_2, perm_3, perm_4, perm_5, perm_6, perm_6a)
|
||||
group_registered = Group.objects.create(name=ugettext_noop('Registered'))
|
||||
group_registered.permissions.add(perm_1, perm_2, perm_3, perm_4, perm_5, perm_6)
|
||||
group_registered.permissions.add(perm_1, perm_2, perm_3, perm_4, perm_5, perm_6, perm_6a)
|
||||
|
||||
# Delegates
|
||||
perm_7 = Permission.objects.get(content_type=ct_motion, codename='can_create_motion')
|
||||
perm_8 = Permission.objects.get(content_type=ct_motion, codename='can_support_motion')
|
||||
perm_9 = Permission.objects.get(content_type=ct_assignment, codename='can_nominate_other')
|
||||
perm_10 = Permission.objects.get(content_type=ct_assignment, codename='can_nominate_self')
|
||||
perm_10a = Permission.objects.get(content_type=ct_mediafile, codename='can_upload')
|
||||
|
||||
group_delegates = Group.objects.create(name=ugettext_noop('Delegates'))
|
||||
group_delegates.permissions.add(perm_7, perm_8, perm_9, perm_10)
|
||||
group_delegates.permissions.add(perm_7, perm_8, perm_9, perm_10, perm_10a)
|
||||
|
||||
# Staff
|
||||
perm_11 = Permission.objects.get(content_type=ct_agenda, codename='can_manage_agenda')
|
||||
@ -62,9 +66,10 @@ def create_builtin_groups(sender, **kwargs):
|
||||
perm_13 = Permission.objects.get(content_type=ct_assignment, codename='can_manage_assignment')
|
||||
perm_14 = Permission.objects.get(content_type=ct_participant, codename='can_manage_participant')
|
||||
perm_15 = Permission.objects.get(content_type=ct_projector, codename='can_manage_projector')
|
||||
perm_15a = Permission.objects.get(content_type=ct_mediafile, codename='can_manage')
|
||||
|
||||
ct_config = ContentType.objects.get(app_label='config', model='configstore')
|
||||
perm_16 = Permission.objects.get(content_type=ct_config, codename='can_manage_config')
|
||||
|
||||
group_staff = Group.objects.create(name=ugettext_noop('Staff'))
|
||||
group_staff.permissions.add(perm_7, perm_9, perm_10, perm_11, perm_12, perm_13, perm_14, perm_15, perm_16)
|
||||
group_staff.permissions.add(perm_7, perm_9, perm_10, perm_11, perm_12, perm_13, perm_14, perm_15, perm_15a, perm_16)
|
||||
|
@ -12,13 +12,14 @@
|
||||
<title>{% block title %}{% get_config 'event_name' %}{% endblock %}</title>
|
||||
|
||||
<!-- styles -->
|
||||
<link href="{% static 'styles/bootstrap.min.css' %}" type="text/css" rel="stylesheet">
|
||||
<link href="{% static 'styles/bootstrap-responsive.min.css' %}" type="text/css" rel="stylesheet">
|
||||
<link href="{% static 'styles/base.css' %}" type="text/css" rel="stylesheet">
|
||||
<link href="{% static 'styles/bootstrap.min.css' %}" type="text/css" rel="stylesheet" />
|
||||
<link href="{% static 'styles/bootstrap-responsive.min.css' %}" type="text/css" rel="stylesheet" />
|
||||
<link href="{% static 'styles/base.css' %}" type="text/css" rel="stylesheet" />
|
||||
<link href="{% static 'img/favicon.png' %}" type="image/png" rel="shortcut icon" />
|
||||
|
||||
{% block header %}
|
||||
{% endblock %}
|
||||
{% for stylefile in extra_stylefiles %}
|
||||
<link href="{% static stylefile %}" type="text/css" rel="stylesheet" />
|
||||
{% endfor %}
|
||||
{% block header %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navbar -->
|
||||
@ -26,7 +27,7 @@
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div id="header">
|
||||
<a href="/" class="logo" title="{% trans 'Home' %}"><img src="{% static 'img/logo.png' %}"></a>
|
||||
<a href="/" class="logo" title="{% trans 'Home' %}"><img src="{% static 'img/logo.png' %}" /></a>
|
||||
<!--<a class="title" href="#">{% get_config 'event_name' %}</a>-->
|
||||
{% block loginbutton %}
|
||||
<div class="btn-group pull-right">
|
||||
@ -65,7 +66,7 @@
|
||||
{% if tab.permission %}
|
||||
<li{% if tab.selected %} class="active"{% endif %}>
|
||||
<a href="{{ tab.url }}">
|
||||
<span class="ico"><i class="icon-{{tab.app}}"></i></span>
|
||||
<span class="ico"><i class="icon-{{ tab.app }}"></i></span>
|
||||
<span class="text">{{ tab.title }}</span>
|
||||
</a>
|
||||
</li>
|
||||
@ -94,7 +95,7 @@
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<hr />
|
||||
<footer>
|
||||
<small>
|
||||
© Copyright 2011-2013 | Powered by <a href="http://openslides.org" target="_blank">OpenSlides</a>
|
||||
@ -111,7 +112,6 @@
|
||||
<script src="{% static 'javascript/bootstrap.min.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'javascript/utils.js' %}" type="text/javascript"></script>
|
||||
<script src="{% url 'django.views.i18n.javascript_catalog' %}" type="text/javascript"></script>
|
||||
{% block javascript %}
|
||||
{% endblock %}
|
||||
{% block javascript %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Global URL list for OpenSlides.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
@ -16,6 +16,7 @@ from django.utils.importlib import import_module
|
||||
|
||||
from openslides.utils.views import RedirectView
|
||||
|
||||
|
||||
handler500 = 'openslides.utils.views.server_error'
|
||||
|
||||
urlpatterns = patterns('',
|
||||
@ -26,18 +27,13 @@ urlpatterns = patterns('',
|
||||
(r'^motion/', include('openslides.motion.urls')),
|
||||
(r'^assignment/', include('openslides.assignment.urls')),
|
||||
(r'^participant/', include('openslides.participant.urls')),
|
||||
(r'^mediafile/', include('openslides.mediafile.urls')),
|
||||
(r'^config/', include('openslides.config.urls')),
|
||||
(r'^projector/', include('openslides.projector.urls')),
|
||||
(r'^i18n/', include('django.conf.urls.i18n')),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('django.contrib.staticfiles.views',
|
||||
url(r'^static/(?P<path>.*)$', 'serve', {'insecure': True}),
|
||||
)
|
||||
|
||||
js_info_dict = {
|
||||
'packages': [],
|
||||
}
|
||||
js_info_dict = {'packages': [],}
|
||||
|
||||
for plugin in settings.INSTALLED_PLUGINS:
|
||||
try:
|
||||
@ -50,7 +46,6 @@ for plugin in settings.INSTALLED_PLUGINS:
|
||||
% plugin_name)))
|
||||
js_info_dict['packages'].append(plugin_name)
|
||||
|
||||
|
||||
urlpatterns += patterns('',
|
||||
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
|
||||
|
||||
|
@ -21,7 +21,7 @@ class PersonChoices(object):
|
||||
|
||||
def __iter__(self):
|
||||
if self.field.empty_label is not None:
|
||||
yield (u"", self.field.empty_label)
|
||||
yield (u'', self.field.empty_label)
|
||||
for person in sorted(Persons(), key=lambda person: person.sort_name):
|
||||
yield (person.person_id, person)
|
||||
|
||||
@ -51,6 +51,8 @@ class PersonFormField(forms.fields.ChoiceField):
|
||||
choices = property(_get_choices, forms.fields.ChoiceField._set_choices)
|
||||
|
||||
def to_python(self, value):
|
||||
if value == u'':
|
||||
return u''
|
||||
return get_person(value)
|
||||
|
||||
def valid_value(self, value):
|
||||
|
@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
openslides.template
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
openslides.utils.template
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Useful template functions for OpenSlides.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
@ -15,13 +15,13 @@ from django.template.loader_tags import BlockNode, ExtendsNode
|
||||
|
||||
|
||||
class Tab(object):
|
||||
def __init__(self, title='', app='', url='', permission=True, selected=False):
|
||||
self.selected = False
|
||||
def __init__(self, title='', app='', stylefile='', url='', permission=True, selected=False):
|
||||
self.title = title
|
||||
self.app = app
|
||||
self.stylefile = stylefile
|
||||
self.url = url
|
||||
self.permission = permission
|
||||
self.selected = selected
|
||||
self.url = url
|
||||
|
||||
|
||||
## All following function are only needed to render a block from a template
|
||||
|
@ -21,7 +21,7 @@ from django.core.handlers.wsgi import WSGIHandler as Django_WSGIHandler
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class StaticFileHandler(StaticFileHandler):
|
||||
class DjangoStaticFileHandler(StaticFileHandler):
|
||||
"""Handels static data by using the django finders."""
|
||||
|
||||
def initialize(self):
|
||||
@ -33,14 +33,15 @@ class StaticFileHandler(StaticFileHandler):
|
||||
from django.contrib.staticfiles import finders
|
||||
normalized_path = posixpath.normpath(unquote(path)).lstrip('/')
|
||||
absolute_path = finders.find(normalized_path)
|
||||
return super(StaticFileHandler, self).get(absolute_path, include_body)
|
||||
return super(DjangoStaticFileHandler, self).get(absolute_path, include_body)
|
||||
|
||||
|
||||
def run_tornado(addr, port):
|
||||
parse_command_line()
|
||||
app = WSGIContainer(Django_WSGIHandler())
|
||||
tornado_app = Application([
|
||||
(r"%s(.*)" % settings.STATIC_URL, StaticFileHandler),
|
||||
(r"%s(.*)" % settings.STATIC_URL, DjangoStaticFileHandler),
|
||||
(r'%s(.*)' % settings.MEDIA_URL, StaticFileHandler, {'path': settings.MEDIA_ROOT}),
|
||||
('.*', FallbackHandler, dict(fallback=app))])
|
||||
|
||||
server = HTTPServer(tornado_app)
|
||||
|
@ -385,14 +385,24 @@ def server_error(request, template_name='500.html'):
|
||||
|
||||
@receiver(template_manipulation, dispatch_uid="send_register_tab")
|
||||
def send_register_tab(sender, request, context, **kwargs):
|
||||
"""
|
||||
Receiver to the template_manipulation signal. Collects from the file
|
||||
views.py in all apps the tabs setup by the function register_tab.
|
||||
Inserts the tab objects and also the extra_stylefiles to the context.
|
||||
"""
|
||||
tabs = []
|
||||
extra_stylefiles = []
|
||||
for app in settings.INSTALLED_APPS:
|
||||
try:
|
||||
mod = import_module(app + '.views')
|
||||
tabs.append(mod.register_tab(request))
|
||||
tab = mod.register_tab(request)
|
||||
tabs.append(tab)
|
||||
if tab.stylefile:
|
||||
extra_stylefiles.append(tab.stylefile)
|
||||
except (ImportError, AttributeError):
|
||||
continue
|
||||
|
||||
context.update({
|
||||
'tabs': tabs,
|
||||
'extra_stylefiles': extra_stylefiles,
|
||||
})
|
||||
|
0
tests/mediafile/__init__.py
Normal file
0
tests/mediafile/__init__.py
Normal file
204
tests/mediafile/tests.py
Normal file
204
tests/mediafile/tests.py
Normal file
@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
openslides.mediafile.tests
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Unit test for the mediafile app.
|
||||
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from django.test.client import Client
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
from openslides.utils.test import TestCase
|
||||
from openslides.mediafile.models import Mediafile
|
||||
from openslides.participant.models import User
|
||||
|
||||
|
||||
class MediafileTest(TestCase):
|
||||
"""
|
||||
Unit test for the mediafile model.
|
||||
"""
|
||||
def setUp(self):
|
||||
# Setup the three permissions
|
||||
ct = ContentType.objects.get(app_label='mediafile', model='mediafile')
|
||||
perm_1 = Permission.objects.get(content_type=ct, codename='can_see')
|
||||
perm_2 = Permission.objects.get(content_type=ct, codename='can_upload')
|
||||
perm_3 = Permission.objects.get(content_type=ct, codename='can_manage')
|
||||
|
||||
# Setup three different users
|
||||
self.manager = User.objects.create(username='mediafile_test_manager')
|
||||
self.manager.reset_password('default')
|
||||
self.manager.user_permissions.add(perm_1, perm_2, perm_3)
|
||||
self.vip_user = User.objects.create(username='mediafile_test_vip_user')
|
||||
self.vip_user.reset_password('default')
|
||||
self.vip_user.user_permissions.add(perm_1, perm_2)
|
||||
self.normal_user = User.objects.create(username='mediafile_test_normal_user')
|
||||
self.normal_user.reset_password('default')
|
||||
|
||||
# Setup a mediafile object
|
||||
self.tmp_dir = os.path.realpath(os.path.dirname(__file__))
|
||||
settings.MEDIA_ROOT = self.tmp_dir
|
||||
tmpfile_no, mediafile_path = tempfile.mkstemp(prefix='tmp_openslides_test', dir=self.tmp_dir)
|
||||
self.object = Mediafile.objects.create(title='Title File 1', mediafile=mediafile_path, uploader=self.vip_user)
|
||||
os.close(tmpfile_no)
|
||||
|
||||
def tearDown(self):
|
||||
self.object.mediafile.delete()
|
||||
|
||||
def test_unicode(self):
|
||||
self.assertEqual(self.object.__unicode__(), 'Title File 1')
|
||||
|
||||
def test_absolute_url(self):
|
||||
self.assertEqual(self.object.get_absolute_url(), '/mediafile/1/edit/')
|
||||
self.assertEqual(self.object.get_absolute_url('edit'), '/mediafile/1/edit/')
|
||||
self.assertEqual(self.object.get_absolute_url('update'), '/mediafile/1/edit/')
|
||||
self.assertEqual(self.object.get_absolute_url(link='delete'), '/mediafile/1/del/')
|
||||
|
||||
def login_clients(self):
|
||||
"""
|
||||
Helper function to login all three test users.
|
||||
"""
|
||||
client_manager = Client()
|
||||
client_manager.login(username='mediafile_test_manager', password='default')
|
||||
client_vip_user = Client()
|
||||
client_vip_user.login(username='mediafile_test_vip_user', password='default')
|
||||
client_normal_user = Client()
|
||||
client_normal_user.login(username='mediafile_test_normal_user', password='default')
|
||||
return {'client_manager': client_manager,
|
||||
'client_vip_user': client_vip_user,
|
||||
'client_normal_user': client_normal_user}
|
||||
|
||||
def test_see_mediafilelist(self):
|
||||
for client in self.login_clients().itervalues():
|
||||
response = client.get('/mediafile/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
bad_client = Client()
|
||||
response = bad_client.get('/mediafile/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_upload_mediafile_get_request(self):
|
||||
clients = self.login_clients()
|
||||
response = clients['client_manager'].get('/mediafile/new/')
|
||||
self.assertContains(response, '---------', status_code=200)
|
||||
self.assertContains(response, '<option value="user:1" selected="selected">mediafile_test_manager</option>', status_code=200)
|
||||
response = clients['client_vip_user'].get('/mediafile/new/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = clients['client_normal_user'].get('/mediafile/new/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
bad_client = Client()
|
||||
response = bad_client.get('/mediafile/new/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_upload_mediafile_post_request(self):
|
||||
# Test first user
|
||||
client_1 = self.login_clients()['client_manager']
|
||||
new_file_1 = SimpleUploadedFile(name='new_test_file.txt', content='test content hello manager')
|
||||
response_1 = client_1.post('/mediafile/new/',
|
||||
{'title': 'new_test_file_title_1',
|
||||
'mediafile': new_file_1,
|
||||
'uploader': self.manager.person_id})
|
||||
self.assertEqual(response_1.status_code, 302)
|
||||
object_1 = Mediafile.objects.latest('timestamp')
|
||||
self.assertEqual(object_1.mediafile.url, '/media/file/new_test_file.txt')
|
||||
path_1 = object_1.mediafile.path
|
||||
object_1.mediafile.delete()
|
||||
self.assertFalse(os.path.exists(path_1))
|
||||
|
||||
# Test second user
|
||||
client_2 = self.login_clients()['client_vip_user']
|
||||
new_file_2 = SimpleUploadedFile(name='new_test_file.txt', content='test content hello vip_user')
|
||||
response_2 = client_2.post('/mediafile/new/',
|
||||
{'title': 'new_test_file_title_2',
|
||||
'mediafile': new_file_2})
|
||||
self.assertEqual(response_2.status_code, 302)
|
||||
object_2 = Mediafile.objects.latest('timestamp')
|
||||
self.assertEqual(object_2.mediafile.url, '/media/file/new_test_file.txt')
|
||||
path_2 = object_2.mediafile.path
|
||||
object_2.mediafile.delete()
|
||||
self.assertFalse(os.path.exists(path_2))
|
||||
|
||||
# Test third user
|
||||
client_3 = self.login_clients()['client_normal_user']
|
||||
new_file_3 = SimpleUploadedFile(name='new_test_file.txt', content='test content hello vip_user')
|
||||
response_3 = client_3.post('/mediafile/new/',
|
||||
{'title': 'new_test_file_title_2',
|
||||
'mediafile': new_file_3})
|
||||
self.assertEqual(response_3.status_code, 403)
|
||||
|
||||
def test_edit_mediafile_get_request(self):
|
||||
clients = self.login_clients()
|
||||
response = clients['client_manager'].get('/mediafile/1/edit/')
|
||||
self.assertContains(response, '---------', status_code=200)
|
||||
self.assertContains(response, '<option value="user:2" selected="selected">mediafile_test_vip_user</option>', status_code=200)
|
||||
response = clients['client_vip_user'].get('/mediafile/1/edit/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
response = clients['client_normal_user'].get('/mediafile/1/edit/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
bad_client = Client()
|
||||
response = bad_client.get('/mediafile/1/edit/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_edit_mediafile_post_request(self):
|
||||
# Test only one user
|
||||
tmpfile_no, mediafile_2_path = tempfile.mkstemp(prefix='tmp_openslides_test', dir=self.tmp_dir)
|
||||
os.close(tmpfile_no)
|
||||
object_2 = Mediafile.objects.create(title='Title File 2', mediafile=mediafile_2_path, uploader=self.vip_user)
|
||||
client_1 = self.login_clients()['client_manager']
|
||||
new_file_1 = SimpleUploadedFile(name='new_test_file.txt', content='test content hello manager')
|
||||
response_1 = client_1.post('/mediafile/2/edit/',
|
||||
{'title': 'new_test_file_title_1',
|
||||
'mediafile': new_file_1,
|
||||
'uploader': self.manager.person_id})
|
||||
self.assertEqual(response_1.status_code, 302)
|
||||
object_2 = Mediafile.objects.get(pk=2)
|
||||
self.assertEqual(object_2.mediafile.url, '/media/file/new_test_file.txt')
|
||||
self.assertEqual(object_2.uploader, self.manager)
|
||||
path_2 = object_2.mediafile.path
|
||||
object_2.mediafile.delete()
|
||||
self.assertFalse(os.path.exists(path_2))
|
||||
|
||||
def test_delete_mediafile_get_request(self):
|
||||
clients = self.login_clients()
|
||||
response = clients['client_manager'].get('/mediafile/1/del/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
response = clients['client_vip_user'].get('/mediafile/1/del/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
response = clients['client_normal_user'].get('/mediafile/1/del/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
bad_client = Client()
|
||||
response = bad_client.get('/mediafile/2/del/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_delete_mediafile_post_request(self):
|
||||
tmpfile_no, mediafile_3_path = tempfile.mkstemp(prefix='tmp_openslides_test', dir=self.tmp_dir)
|
||||
os.close(tmpfile_no)
|
||||
object_3 = Mediafile.objects.create(title='Title File 3', mediafile=mediafile_3_path)
|
||||
client_1 = self.login_clients()['client_manager']
|
||||
response_1 = client_1.post('/mediafile/2/del/', {'yes': 'foo'})
|
||||
self.assertEqual(response_1.status_code, 302)
|
||||
self.assertFalse(os.path.exists(object_3.mediafile.path))
|
||||
|
||||
def test_filesize(self):
|
||||
tmpfile_no, mediafile_4_path = tempfile.mkstemp(prefix='tmp_openslides_test', dir=self.tmp_dir)
|
||||
os.close(tmpfile_no)
|
||||
object_4 = Mediafile.objects.create(title='Title File 4', mediafile=mediafile_4_path)
|
||||
self.assertEqual(object_4.get_filesize(), '< 1 kB')
|
||||
with open(object_4.mediafile.path, 'wb') as bigfile:
|
||||
bigfile.seek(2047)
|
||||
bigfile.write('0')
|
||||
self.assertEqual(object_4.get_filesize(), '2 kB')
|
||||
with open(object_4.mediafile.path, 'wb') as bigfile:
|
||||
bigfile.seek(1048575)
|
||||
bigfile.write('0')
|
||||
self.assertEqual(object_4.get_filesize(), '1 MB')
|
||||
object_4.mediafile.delete()
|
Loading…
Reference in New Issue
Block a user