OpenSlides/openslides/core/views.py
2015-03-02 19:03:19 +01:00

364 lines
12 KiB
Python

import re
from django.conf import settings
from django.contrib import messages
from django.contrib.staticfiles import finders
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import get_resolver, reverse
from django.db import IntegrityError
from django.http import HttpResponse
from django.shortcuts import redirect, render_to_response
from django.template import RequestContext
from django.utils.importlib import import_module
from django.utils.translation import ugettext as _
from haystack.views import SearchView as _SearchView
from openslides import __version__ as openslides_version
from openslides.config.api import config
from openslides.utils import views as utils_views
from openslides.utils.plugins import (
get_plugin_description,
get_plugin_verbose_name,
get_plugin_version,
)
from openslides.utils.rest_api import ModelViewSet
from openslides.utils.signals import template_manipulation
from openslides.utils.widgets import Widget
from .exceptions import TagException
from .forms import SelectWidgetsForm
from .models import CustomSlide, Tag
from .serializers import CustomSlideSerializer, TagSerializer
class IndexView(utils_views.CSRFMixin, utils_views.View):
"""
The primary view for OpenSlides using AngularJS.
The default base template is 'openslides/core/static/templates/index.html'.
You can override it by simply adding a custom 'templates/index.html' file
to the custom staticfiles directory. See STATICFILES_DIRS in settings.py.
"""
def get(self, *args, **kwargs):
with open(finders.find('templates/index.html')) as f:
content = f.read()
return HttpResponse(content)
class DashboardView(utils_views.AjaxMixin, utils_views.TemplateView):
"""
Overview over all possible slides, the overlays and a live view: the
Dashboard of OpenSlides. This main view uses the widget api to collect all
widgets from all apps. See openslides.utils.widgets.Widget for more details.
"""
required_permission = 'core.can_see_dashboard'
template_name = 'core/dashboard.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
widgets = []
for widget in Widget.get_all(self.request):
if widget.is_active():
widgets.append(widget)
context['extra_stylefiles'].extend(widget.get_stylesheets())
context['extra_javascript'].extend(widget.get_javascript_files())
context['widgets'] = widgets
return context
class SelectWidgetsView(utils_views.TemplateView):
"""
Shows a form to select which widgets should be displayed on the own
dashboard. The setting is saved in the session.
"""
# TODO: Use another base view class here, e. g. a FormView
required_permission = 'core.can_see_dashboard'
template_name = 'core/select_widgets.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
widgets = Widget.get_all(self.request)
for widget in widgets:
initial = {'widget': widget.is_active()}
prefix = widget.name
if self.request.method == 'POST':
widget.form = SelectWidgetsForm(
self.request.POST,
prefix=prefix,
initial=initial)
else:
widget.form = SelectWidgetsForm(prefix=prefix, initial=initial)
context['widgets'] = widgets
return context
def post(self, request, *args, **kwargs):
"""
Activates or deactivates the widgets in a post request.
"""
context = self.get_context_data(**kwargs)
session_widgets = self.request.session.get('widgets', {})
for widget in context['widgets']:
if widget.form.is_valid():
session_widgets[widget.name] = widget.form.cleaned_data['widget']
else:
messages.error(request, _('There are errors in the form.'))
break
else:
self.request.session['widgets'] = session_widgets
return redirect(reverse('core_dashboard'))
class VersionView(utils_views.TemplateView):
"""
Shows version infos.
"""
template_name = 'core/version.html'
def get_context_data(self, **kwargs):
"""
Adds version strings to the context.
"""
context = super().get_context_data(**kwargs)
context['modules'] = [{'verbose_name': 'OpenSlides',
'description': '',
'version': openslides_version}]
# Versions of plugins.
for plugin in settings.INSTALLED_PLUGINS:
context['modules'].append({'verbose_name': get_plugin_verbose_name(plugin),
'description': get_plugin_description(plugin),
'version': get_plugin_version(plugin)})
return context
class SearchView(_SearchView):
"""
Shows search result page.
"""
template = 'core/search.html'
def __call__(self, request):
if not request.user.is_authenticated() and not config['system_enable_anonymous']:
raise PermissionDenied
return super().__call__(request)
def extra_context(self):
"""
Adds extra context variables to set navigation and search filter.
Returns a context dictionary.
"""
context = {}
template_manipulation.send(
sender=self.__class__, request=self.request, context=context)
context['models'] = self.get_indexed_searchmodels()
context['get_values'] = self.request.GET.getlist('models')
return context
def get_indexed_searchmodels(self):
"""
Iterate over all INSTALLED_APPS and return a list of models which are
indexed by haystack/whoosh for using in customized model search filter
in search template search.html. Each list entry contains a verbose name
of the model and a special form field value for haystack (app_name.model_name),
e.g. ['Agenda', 'agenda.item'].
"""
models = []
# TODO: cache this query!
for app in settings.INSTALLED_APPS:
try:
module = import_module(app + '.search_indexes')
except ImportError:
pass
else:
models.append([module.Index.modelfilter_name, module.Index.modelfilter_value])
return models
class ErrorView(utils_views.View):
"""
View for Http 403, 404 and 500 error pages.
"""
status_code = None
def dispatch(self, request, *args, **kwargs):
http_error_strings = {
403: {'name': _('Forbidden'),
'description': _('Sorry, you have no permission to see this page.'),
'status_code': '403'},
404: {'name': _('Not Found'),
'description': _('Sorry, the requested page could not be found.'),
'status_code': '404'},
500: {'name': _('Internal Server Error'),
'description': _('Sorry, there was an unknown error. Please contact the event manager.'),
'status_code': '500'}}
context = {}
context['http_error'] = http_error_strings[self.status_code]
template_manipulation.send(sender=self.__class__, request=request, context=context)
response = render_to_response(
'core/error.html',
context_instance=RequestContext(request, context))
response.status_code = self.status_code
return response
class CustomSlideViewMixin(object):
"""
Mixin for for CustomSlide Views.
"""
fields = ('title', 'text', 'weight',)
required_permission = 'core.can_manage_projector'
template_name = 'core/customslide_update.html'
model = CustomSlide
success_url_name = 'core_dashboard'
url_name_args = []
class CustomSlideCreateView(CustomSlideViewMixin, utils_views.CreateView):
"""
Create a custom slide.
"""
pass
class CustomSlideUpdateView(CustomSlideViewMixin, utils_views.UpdateView):
"""
Update a custom slide.
"""
pass
class CustomSlideDeleteView(CustomSlideViewMixin, utils_views.DeleteView):
"""
Delete a custom slide.
"""
pass
class CustomSlideViewSet(ModelViewSet):
"""
API endpoint to list, retrieve, create, update and destroy custom slides.
"""
queryset = CustomSlide.objects.all()
serializer_class = CustomSlideSerializer
def check_permissions(self, request):
"""
Calls self.permission_denied() if the requesting user has not the
permission to manage projector.
"""
if not request.user.has_perm('core.can_manage_projector'):
self.permission_denied(request)
class TagListView(utils_views.AjaxMixin, utils_views.ListView):
"""
View to list and manipulate tags.
Shows all tags when requested via a GET-request. Manipulates tags with
POST-requests.
"""
model = Tag
required_permission = 'core.can_manage_tags'
def post(self, *args, **kwargs):
return self.ajax_get(*args, **kwargs)
def ajax_get(self, request, *args, **kwargs):
name, value = request.POST['name'], request.POST.get('value', None)
# Create a new tag
if name == 'new':
try:
tag = Tag.objects.create(name=value)
except IntegrityError:
# The name of the tag is already taken. It must be unique.
self.error = 'Tag name is already taken'
else:
self.pk = tag.pk
self.action = 'created'
# Update an existing tag
elif name.startswith('edit-tag-'):
try:
self.get_tag_queryset(name, 9).update(name=value)
except TagException as error:
self.error = str(error)
except IntegrityError:
self.error = 'Tag name is already taken'
except Tag.DoesNotExist:
self.error = 'Tag does not exist'
else:
self.action = 'updated'
# Delete a tag
elif name.startswith('delete-tag-'):
try:
self.get_tag_queryset(name, 11).delete()
except TagException as error:
self.error = str(error)
except Tag.DoesNotExist:
self.error = 'Tag does not exist'
else:
self.action = 'deleted'
return super().ajax_get(request, *args, **kwargs)
def get_tag_queryset(self, name, place_in_str):
"""
Get a django-tag-queryset from a string.
'name' is the string in which the pk is (at the end).
'place_in_str' is the place where to look for the pk. It has to be an int.
Returns a Tag QuerySet or raises TagException.
Also sets self.pk to the pk inside the name.
"""
try:
self.pk = int(name[place_in_str:])
except ValueError:
raise TagException('Invalid name in request')
return Tag.objects.filter(pk=self.pk)
def get_ajax_context(self, **context):
return super().get_ajax_context(
pk=getattr(self, 'pk', None),
action=getattr(self, 'action', None),
error=getattr(self, 'error', None),
**context)
class TagViewSet(ModelViewSet):
"""
API endpoint to list, retrieve, create, update and destroy tags.
"""
queryset = Tag.objects.all()
serializer_class = TagSerializer
def check_permissions(self, request):
"""
Calls self.permission_denied() if the requesting user has not the
permission to manage tags and it is a create, update or detroy request.
"""
if (self.action in ('create', 'update', 'destroy') and
not request.user.has_perm('core.can_manage_tags')):
self.permission_denied(request)
class UrlPatternsView(utils_views.APIView):
"""
Returns a dictonary with all url patterns as json.
"""
URL_KWARGS_REGEX = re.compile(r'%\((\w*)\)s')
http_method_names = ['get']
def get_context_data(self, **context):
result = {}
url_dict = get_resolver(None).reverse_dict
for pattern_name in filter(lambda key: isinstance(key, str), url_dict.keys()):
url = url_dict[pattern_name][0][0][0]
result[pattern_name] = self.URL_KWARGS_REGEX.sub(r':\1', url)
return result