import os import django_comments as comments from csp.decorators import csp_update from django.conf import settings from django.contrib.auth.mixins import PermissionRequiredMixin from django.http import HttpResponse, Http404 from django.utils.translation import ugettext as _ from django.views import generic from . import models, forms class WYSIWYGEditorMixin(PermissionRequiredMixin): """ A view to update the Content-Security-Policy for the WYSIWYG editor. Since it is only used in edit forms, it extends the PermissionRequiredMixin. """ @csp_update(SCRIPT_SRC="'unsafe-eval'") def dispatch(self, request, *args, **kwargs): """Add "unsafe-eval" to the Content-Secuirty-Policy HTTP Headers for the WYSIWYG Editor. :param request: the HTTP Request Object :param args: args for the super dispatch :param kwargs: kwargs for the super dispatch :return: django HTTPResponse object """ return super(WYSIWYGEditorMixin, self).dispatch( request, *args, **kwargs) class ArticleArchiveMixin(object): """Mixin to add common context data to Views of the news archive.""" category = None def get_category(self, queryset): """ Filter the queryset by category if one has been specified in the URL :param queryset: an model.Article.objects Queryset :return: an model.Article.objects Queryset filterd by category """ category_slug = self.kwargs.get('category') if not category_slug: self.category = None return queryset try: self.category = models.Category.objects.get(slug=category_slug) except models.Category.DoesNotExist: raise Http404(_("This Category does not exist.")) return queryset.filter(category=self.category) def get_context_data(self, **kwargs): """ Adds the categories and the active category to the template context. :return: an django.template.context """ context = super(ArticleArchiveMixin, self).get_context_data(**kwargs) context['categories'] = models.Category.objects.all() context['active_category'] = self.category return context class ArticleArchiveIndex(ArticleArchiveMixin, generic.ArchiveIndexView): """ Displays the latest news and the filters to browse the archives. """ queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) date_field = 'date_created' paginate_by = 5 context_object_name = 'article_list' allow_empty = True def get_queryset(self): """ Filter the Queryset by category. :return: models.Article.objects queryset """ return self.get_category( super(ArticleArchiveIndex, self).get_queryset() ) class ArticleYearArchive(ArticleArchiveMixin, generic.YearArchiveView): """ Displays the Articles filterd by a specific year """ queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) date_field = 'date_created' paginate_by = 5 year_format = '%Y' make_object_list = True allow_empty = True def get_queryset(self): """ Filter the Queryset by category. :return: models.Article.objects queryset """ return self.get_category( super(ArticleYearArchive, self).get_queryset() ) class ArticleMonthArchive(ArticleArchiveMixin, generic.MonthArchiveView): """ Displays the Articles filterd by a specific month """ queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) date_field = 'date_created' month_format = '%m' paginate_by = 5 make_object_list = True allow_empty = True def get_queryset(self): """ Filter the Queryset by category. :return: models.Article.objects queryset """ return self.get_category( super(ArticleMonthArchive, self).get_queryset() ) class ArticleDetail(generic.DetailView): """ Render the news Article, but only if it got published. """ queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) class ArticleForm(WYSIWYGEditorMixin, generic.UpdateView): """ View to add or edit an Article """ model = models.Article form_class = forms.ArticleForm permission_required = 'content.change_article' def get_context_data(self, **kwargs): """Set the title variable to create/or edit article.""" context = super(ArticleForm, self).get_context_data(**kwargs) context['title'] = _("Edit Article") if self.kwargs.get('pk') else \ _("Create Article") return context def get_object(self, queryset=None): """Get the Article or create a new one if no id has been provided. :param queryset: Get the single item from this filtered queryset. :return: """ queryset = queryset or self.get_queryset() if self.kwargs.get('pk'): return queryset.get(pk=self.kwargs['pk']) return models.Article(author=self.request.user) class PageAddForm(WYSIWYGEditorMixin, generic.CreateView): """ Renders an Form to create a new page for users with conforming permissions.""" form_class = forms.PageForm template_name = 'content/page_form.html' permission_required = 'content.add_page' def get_initial(self): """Try to get the path of the parent page as initial data.""" path = os.path.splitext(self.kwargs['path'])[0] if path.startswith('/'): path = path[1:] if path.endswith('/'): path = path[:-1] parent = models.Page.objects.get(path=path) return {'parent': parent} class PageEditForm(WYSIWYGEditorMixin, generic.UpdateView): """Renders an Form to edit a page for users with conforming permissions.""" form_class = forms.PageForm model = models.Page permission_required = 'content.change_page' def get_object(self, queryset=None): """ Get the path from the URL and fetch the corresponding page. First get the path wihout fileextentsion leading or trailing slashes, then search in the database if such a page exists. :param queryset: Get the single item from this filtered queryset. :return: """ path = os.path.splitext(self.kwargs['path'])[0] queryset = queryset or self.get_queryset() if path.startswith('/'): path = path[1:] if path.endswith('/'): path = path[:-1] return queryset.get(path=path) class PageHtml(generic.DetailView): """Display static HTML content from the database.""" model = models.Page def get_object(self, queryset=None): """Get the page content from the db that equals the given URL. :param queryset: Get the single item from this filtered queryset. :return: """ queryset = queryset or self.get_queryset() try: return queryset.get(path=self.kwargs['path'], content_type=models.HTML_PAGE) except models.Page.DoesNotExist: raise Http404( _("No Page found matching the Path %s") % self.request.path ) def get_template_names(self): """Each static page can define its own template, so return the template name that has been stored in the dataset. :return: filename of the template to render. """ return self.object.template class PagePdf(generic.DeleteView): """Deliver an static PDF File under this given URL.""" model = models.Page def get_object(self, queryset=None): """Get the PDF page from the db that equals the given URL. :param queryset: Get the single item from this filtered queryset. :return: models.Page object or raise a 404 if not found. """ queryset = queryset or self.get_queryset() try: return queryset.get(path=self.kwargs['path'], content_type=models.PDF_PAGE) except models.Page.DoesNotExist: raise Http404( _("No PDF Document found matching the Path %s") % self.request.path ) def render_to_response(self, context, **response_kwargs): """Stream the PDF File to the client and set the right content headers. :param context: useless only for compatility :param response_kwargs: will be added to the HttpResponse kwargs. :return: an HTTPResponse with PDF Content or an Http404 exception """ try: with open(self.object.pdf_file.path, 'rb') as pdf_file: response = HttpResponse( content=pdf_file.read(), content_type='application/pdf', **response_kwargs ) return response except: raise Http404('File not Found %s.pdf' % self.kwargs['path']) class StartPage(generic.TemplateView): """The Frontpage, a page with the latest infos and some static content.""" template_name = 'index.html' def get_context_data(self): """ Adds recent ariticles and recent comments to the context. :return: array() with the context data """ page = models.Page.objects.get(slug='index') recent_comment_list = comments.get_model().objects.filter( site__pk=settings.SITE_ID, is_public=True, is_removed=False, ) recent_comment_list = recent_comment_list.order_by('-submit_date')[:10] context = { 'title': page.title, 'content': page.content, 'recent_article_list': models.Article.objects.published()[:3], 'recent_comment_list': recent_comment_list, } return context queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED)