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
|
*.pyc
|
||||||
*.swp
|
*.swp
|
||||||
*~
|
*~
|
||||||
|
|
||||||
|
# Virtual Environment
|
||||||
|
.venv/*
|
||||||
|
|
||||||
|
# Development settings and database
|
||||||
settings.py
|
settings.py
|
||||||
database.sqlite
|
database.sqlite
|
||||||
!tests/settings.py
|
!tests/settings.py
|
||||||
@ -14,6 +19,6 @@ dist/*
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
versiontools*
|
versiontools*
|
||||||
|
|
||||||
# Unit test / coverage reports
|
# Unit test and coverage reports
|
||||||
.coverage
|
.coverage
|
||||||
htmlcov
|
htmlcov
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
openslides.openslides_settings
|
openslides.global_settings
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
OpenSlides default 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.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ import sys
|
|||||||
|
|
||||||
from openslides.main import fs2unicode
|
from openslides.main import fs2unicode
|
||||||
|
|
||||||
|
|
||||||
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
|
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = (
|
AUTHENTICATION_BACKENDS = (
|
||||||
@ -34,7 +35,6 @@ LANGUAGES = (
|
|||||||
('fr', ugettext('French')),
|
('fr', ugettext('French')),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# If you set this to False, Django will make some optimizations so as not
|
# If you set this to False, Django will make some optimizations so as not
|
||||||
# to load the internationalization machinery.
|
# to load the internationalization machinery.
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
@ -47,18 +47,14 @@ LOCALE_PATHS = (
|
|||||||
fs2unicode(os.path.join(SITE_ROOT, 'locale')),
|
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
|
# 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).
|
# trailing slash if there is a path component (optional in other cases).
|
||||||
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
# 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``
|
# Absolute path to the directory that holds static media from ``collectstatic``
|
||||||
# Example: "/home/media/static.lawrence.com/"
|
# 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
|
# 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).
|
# trailing slash if there is a path component (optional in other cases).
|
||||||
@ -119,6 +115,7 @@ INSTALLED_APPS = (
|
|||||||
'openslides.motion',
|
'openslides.motion',
|
||||||
'openslides.assignment',
|
'openslides.assignment',
|
||||||
'openslides.participant',
|
'openslides.participant',
|
||||||
|
'openslides.mediafile',
|
||||||
'openslides.config',
|
'openslides.config',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Main script to start and set up OpenSlides.
|
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.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -64,6 +64,11 @@ INSTALLED_PLUGINS = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
INSTALLED_APPS += 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
|
KEY_LENGTH = 30
|
||||||
@ -195,14 +200,17 @@ def create_settings(settings_path, database_path=None):
|
|||||||
if database_path is _portable_db_path:
|
if database_path is _portable_db_path:
|
||||||
database_path = get_portable_db_path()
|
database_path = get_portable_db_path()
|
||||||
dbpath_value = 'openslides.main.get_portable_db_path()'
|
dbpath_value = 'openslides.main.get_portable_db_path()'
|
||||||
|
media_root_path_value = 'openslides.main.get_portable_media_root_path()'
|
||||||
else:
|
else:
|
||||||
if database_path is None:
|
if database_path is None:
|
||||||
database_path = get_user_data_path('openslides', 'database.sqlite')
|
database_path = get_user_data_path('openslides', 'database.sqlite')
|
||||||
dbpath_value = repr(fs2unicode(database_path))
|
dbpath_value = repr(fs2unicode(database_path))
|
||||||
|
media_root_path_value = repr(fs2unicode(get_user_data_path('openslides', 'media', '')))
|
||||||
|
|
||||||
settings_content = CONFIG_TEMPLATE % dict(
|
settings_content = CONFIG_TEMPLATE % dict(
|
||||||
default_key=base64.b64encode(os.urandom(KEY_LENGTH)),
|
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):
|
if not os.path.exists(settings_module):
|
||||||
os.makedirs(settings_module)
|
os.makedirs(settings_module)
|
||||||
@ -370,6 +378,10 @@ def get_portable_db_path():
|
|||||||
return get_portable_path('openslides', 'database.sqlite')
|
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):
|
def win32_get_app_data_path(*args):
|
||||||
shell32 = ctypes.WinDLL("shell32.dll")
|
shell32 = ctypes.WinDLL("shell32.dll")
|
||||||
SHGetFolderPath = shell32.SHGetFolderPathW
|
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.
|
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.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from random import choice
|
from random import choice
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
from django.contrib.auth.models import Permission
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
@ -22,11 +21,6 @@ from openslides.utils import csv_ext
|
|||||||
from openslides.participant.models import User, Group
|
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():
|
def gen_password():
|
||||||
"""
|
"""
|
||||||
generates a random passwort.
|
generates a random passwort.
|
||||||
|
@ -26,7 +26,7 @@ from openslides.projector.api import register_slidemodel
|
|||||||
from openslides.projector.projector import SlideMixin
|
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
|
prefix = 'user' # This is for the slides
|
||||||
person_prefix = 'user'
|
person_prefix = 'user'
|
||||||
GENDER_CHOICES = (
|
GENDER_CHOICES = (
|
||||||
|
@ -42,19 +42,23 @@ def create_builtin_groups(sender, **kwargs):
|
|||||||
ct_participant = ContentType.objects.get(app_label='participant', model='user')
|
ct_participant = ContentType.objects.get(app_label='participant', model='user')
|
||||||
perm_6 = Permission.objects.get(content_type=ct_participant, codename='can_see_participant')
|
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 = 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 = 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
|
# Delegates
|
||||||
perm_7 = Permission.objects.get(content_type=ct_motion, codename='can_create_motion')
|
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_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_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_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 = 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
|
# Staff
|
||||||
perm_11 = Permission.objects.get(content_type=ct_agenda, codename='can_manage_agenda')
|
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_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_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_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')
|
ct_config = ContentType.objects.get(app_label='config', model='configstore')
|
||||||
perm_16 = Permission.objects.get(content_type=ct_config, codename='can_manage_config')
|
perm_16 = Permission.objects.get(content_type=ct_config, codename='can_manage_config')
|
||||||
|
|
||||||
group_staff = Group.objects.create(name=ugettext_noop('Staff'))
|
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>
|
<title>{% block title %}{% get_config 'event_name' %}{% endblock %}</title>
|
||||||
|
|
||||||
<!-- styles -->
|
<!-- styles -->
|
||||||
<link href="{% static 'styles/bootstrap.min.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/bootstrap-responsive.min.css' %}" type="text/css" rel="stylesheet" />
|
||||||
<link href="{% static 'styles/base.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" />
|
<link href="{% static 'img/favicon.png' %}" type="image/png" rel="shortcut icon" />
|
||||||
|
{% for stylefile in extra_stylefiles %}
|
||||||
{% block header %}
|
<link href="{% static stylefile %}" type="text/css" rel="stylesheet" />
|
||||||
{% endblock %}
|
{% endfor %}
|
||||||
|
{% block header %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Navbar -->
|
<!-- Navbar -->
|
||||||
@ -26,7 +27,7 @@
|
|||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div id="header">
|
<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>-->
|
<!--<a class="title" href="#">{% get_config 'event_name' %}</a>-->
|
||||||
{% block loginbutton %}
|
{% block loginbutton %}
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
@ -65,7 +66,7 @@
|
|||||||
{% if tab.permission %}
|
{% if tab.permission %}
|
||||||
<li{% if tab.selected %} class="active"{% endif %}>
|
<li{% if tab.selected %} class="active"{% endif %}>
|
||||||
<a href="{{ tab.url }}">
|
<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>
|
<span class="text">{{ tab.title }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -94,7 +95,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr />
|
||||||
<footer>
|
<footer>
|
||||||
<small>
|
<small>
|
||||||
© Copyright 2011-2013 | Powered by <a href="http://openslides.org" target="_blank">OpenSlides</a>
|
© 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/bootstrap.min.js' %}" type="text/javascript"></script>
|
||||||
<script src="{% static 'javascript/utils.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>
|
<script src="{% url 'django.views.i18n.javascript_catalog' %}" type="text/javascript"></script>
|
||||||
{% block javascript %}
|
{% block javascript %}{% endblock %}
|
||||||
{% endblock %}
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Global URL list for OpenSlides.
|
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.
|
: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
|
from openslides.utils.views import RedirectView
|
||||||
|
|
||||||
|
|
||||||
handler500 = 'openslides.utils.views.server_error'
|
handler500 = 'openslides.utils.views.server_error'
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
@ -26,18 +27,13 @@ urlpatterns = patterns('',
|
|||||||
(r'^motion/', include('openslides.motion.urls')),
|
(r'^motion/', include('openslides.motion.urls')),
|
||||||
(r'^assignment/', include('openslides.assignment.urls')),
|
(r'^assignment/', include('openslides.assignment.urls')),
|
||||||
(r'^participant/', include('openslides.participant.urls')),
|
(r'^participant/', include('openslides.participant.urls')),
|
||||||
|
(r'^mediafile/', include('openslides.mediafile.urls')),
|
||||||
(r'^config/', include('openslides.config.urls')),
|
(r'^config/', include('openslides.config.urls')),
|
||||||
(r'^projector/', include('openslides.projector.urls')),
|
(r'^projector/', include('openslides.projector.urls')),
|
||||||
(r'^i18n/', include('django.conf.urls.i18n')),
|
(r'^i18n/', include('django.conf.urls.i18n')),
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns += patterns('django.contrib.staticfiles.views',
|
js_info_dict = {'packages': [],}
|
||||||
url(r'^static/(?P<path>.*)$', 'serve', {'insecure': True}),
|
|
||||||
)
|
|
||||||
|
|
||||||
js_info_dict = {
|
|
||||||
'packages': [],
|
|
||||||
}
|
|
||||||
|
|
||||||
for plugin in settings.INSTALLED_PLUGINS:
|
for plugin in settings.INSTALLED_PLUGINS:
|
||||||
try:
|
try:
|
||||||
@ -50,7 +46,6 @@ for plugin in settings.INSTALLED_PLUGINS:
|
|||||||
% plugin_name)))
|
% plugin_name)))
|
||||||
js_info_dict['packages'].append(plugin_name)
|
js_info_dict['packages'].append(plugin_name)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns += patterns('',
|
urlpatterns += patterns('',
|
||||||
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
|
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class PersonChoices(object):
|
|||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
if self.field.empty_label is not None:
|
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):
|
for person in sorted(Persons(), key=lambda person: person.sort_name):
|
||||||
yield (person.person_id, person)
|
yield (person.person_id, person)
|
||||||
|
|
||||||
@ -51,6 +51,8 @@ class PersonFormField(forms.fields.ChoiceField):
|
|||||||
choices = property(_get_choices, forms.fields.ChoiceField._set_choices)
|
choices = property(_get_choices, forms.fields.ChoiceField._set_choices)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
|
if value == u'':
|
||||||
|
return u''
|
||||||
return get_person(value)
|
return get_person(value)
|
||||||
|
|
||||||
def valid_value(self, value):
|
def valid_value(self, value):
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
openslides.template
|
openslides.utils.template
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Useful template functions for OpenSlides.
|
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.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -15,13 +15,13 @@ from django.template.loader_tags import BlockNode, ExtendsNode
|
|||||||
|
|
||||||
|
|
||||||
class Tab(object):
|
class Tab(object):
|
||||||
def __init__(self, title='', app='', url='', permission=True, selected=False):
|
def __init__(self, title='', app='', stylefile='', url='', permission=True, selected=False):
|
||||||
self.selected = False
|
|
||||||
self.title = title
|
self.title = title
|
||||||
self.app = app
|
self.app = app
|
||||||
|
self.stylefile = stylefile
|
||||||
|
self.url = url
|
||||||
self.permission = permission
|
self.permission = permission
|
||||||
self.selected = selected
|
self.selected = selected
|
||||||
self.url = url
|
|
||||||
|
|
||||||
|
|
||||||
## All following function are only needed to render a block from a template
|
## 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
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
class StaticFileHandler(StaticFileHandler):
|
class DjangoStaticFileHandler(StaticFileHandler):
|
||||||
"""Handels static data by using the django finders."""
|
"""Handels static data by using the django finders."""
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
@ -33,14 +33,15 @@ class StaticFileHandler(StaticFileHandler):
|
|||||||
from django.contrib.staticfiles import finders
|
from django.contrib.staticfiles import finders
|
||||||
normalized_path = posixpath.normpath(unquote(path)).lstrip('/')
|
normalized_path = posixpath.normpath(unquote(path)).lstrip('/')
|
||||||
absolute_path = finders.find(normalized_path)
|
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):
|
def run_tornado(addr, port):
|
||||||
parse_command_line()
|
parse_command_line()
|
||||||
app = WSGIContainer(Django_WSGIHandler())
|
app = WSGIContainer(Django_WSGIHandler())
|
||||||
tornado_app = Application([
|
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))])
|
('.*', FallbackHandler, dict(fallback=app))])
|
||||||
|
|
||||||
server = HTTPServer(tornado_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")
|
@receiver(template_manipulation, dispatch_uid="send_register_tab")
|
||||||
def send_register_tab(sender, request, context, **kwargs):
|
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 = []
|
tabs = []
|
||||||
|
extra_stylefiles = []
|
||||||
for app in settings.INSTALLED_APPS:
|
for app in settings.INSTALLED_APPS:
|
||||||
try:
|
try:
|
||||||
mod = import_module(app + '.views')
|
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):
|
except (ImportError, AttributeError):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'tabs': tabs,
|
'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