Commit 8408f01f by benjaoming

More class-based views. Mixin class for Article-related views handling…

More class-based views. Mixin class for Article-related views handling permissions etc. More complex plugin structure for easy creation of plugins with very easy integration in the article tab menu etc.
parent fa4af858
Not implemented - will be ASAP Not implemented - will be ASAP
============================== ==============================
* Permission system in settings tab * Permission system in settings tab **Done**
* Notification system * Notification system **Almost done**
* Simple user account handling: login/register etc. * Simple user account handling: login/register etc.
* Implement notifications, revision log messages and user messages thoroughly
* Attachment plugin **In the making**
* Image plugin
* Example plugin
Ideas Ideas
===== =====
...@@ -19,5 +23,6 @@ Management script ...@@ -19,5 +23,6 @@ Management script
================= =================
* Cleanup deleted Image's image files * Cleanup deleted Image's image files
* Cleanup revisions * Cleanup attachments
* Cleanup revisions + plugin revisions
...@@ -24,4 +24,4 @@ def register(PluginClass): ...@@ -24,4 +24,4 @@ def register(PluginClass):
form_module = import_module(modulename) form_module = import_module(modulename)
settings_form = getattr(form_module, klassname) settings_form = getattr(form_module, klassname)
_settings_forms.append(settings_form) _settings_forms.append(settings_form)
\ No newline at end of file
...@@ -2,7 +2,6 @@ from django.utils import simplejson as json ...@@ -2,7 +2,6 @@ from django.utils import simplejson as json
from django.http import HttpResponse, HttpResponseForbidden,\ from django.http import HttpResponse, HttpResponseForbidden,\
HttpResponseNotFound HttpResponseNotFound
import models
from wiki.core.exceptions import NoRootURL from wiki.core.exceptions import NoRootURL
from django.shortcuts import redirect, get_object_or_404 from django.shortcuts import redirect, get_object_or_404
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
...@@ -21,10 +20,11 @@ def get_article(func=None, can_read=True, can_write=False): ...@@ -21,10 +20,11 @@ def get_article(func=None, can_read=True, can_write=False):
calling the decorated func with this ID.""" calling the decorated func with this ID."""
def the_func(request, *args, **kwargs): def the_func(request, *args, **kwargs):
import models
path = kwargs.pop('path', None) path = kwargs.pop('path', None)
article_id = kwargs.pop('article_id', None) article_id = kwargs.pop('article_id', None)
urlpath = None urlpath = None
if not path is None: if not path is None:
try: try:
......
...@@ -3,7 +3,7 @@ from django import forms ...@@ -3,7 +3,7 @@ from django import forms
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from itertools import chain from itertools import chain
import editors from editors import editor
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from wiki import models from wiki import models
from django.forms.util import flatatt from django.forms.util import flatatt
...@@ -16,14 +16,14 @@ class CreateRoot(forms.Form): ...@@ -16,14 +16,14 @@ class CreateRoot(forms.Form):
title = forms.CharField(label=_(u'Title'), help_text=_(u'Initial title of the article. May be overridden with revision titles.')) title = forms.CharField(label=_(u'Title'), help_text=_(u'Initial title of the article. May be overridden with revision titles.'))
content = forms.CharField(label=_(u'Type in some contents'), content = forms.CharField(label=_(u'Type in some contents'),
help_text=_(u'This is just the initial contents of your article. After creating it, you can use more complex features like adding plugins, meta data, related articles etc...'), help_text=_(u'This is just the initial contents of your article. After creating it, you can use more complex features like adding plugins, meta data, related articles etc...'),
required=False, widget=editors.editor.get_widget()) required=False, widget=editor.get_widget())
class EditForm(forms.Form): class EditForm(forms.Form):
title = forms.CharField(label=_(u'Title'),) title = forms.CharField(label=_(u'Title'),)
content = forms.CharField(label=_(u'Contents'), content = forms.CharField(label=_(u'Contents'),
required=False, widget=editors.editor.get_widget()) required=False, widget=editor.get_widget())
summary = forms.CharField(label=_(u'Summary'), help_text=_(u'Give a short reason for your edit, which will be stated in the revision log.'), summary = forms.CharField(label=_(u'Summary'), help_text=_(u'Give a short reason for your edit, which will be stated in the revision log.'),
required=False) required=False)
...@@ -178,7 +178,7 @@ class CreateForm(forms.Form): ...@@ -178,7 +178,7 @@ class CreateForm(forms.Form):
title = forms.CharField(label=_(u'Title'),) title = forms.CharField(label=_(u'Title'),)
slug = forms.SlugField(label=_(u'Slug'), help_text=_(u"This will be the address where your article can be found. Use only alphanumeric characters and '-' or '_'."),) slug = forms.SlugField(label=_(u'Slug'), help_text=_(u"This will be the address where your article can be found. Use only alphanumeric characters and '-' or '_'."),)
content = forms.CharField(label=_(u'Contents'), content = forms.CharField(label=_(u'Contents'),
required=False, widget=editors.editor.get_widget()) required=False, widget=editor.get_widget())
summary = forms.CharField(label=_(u'Summary'), help_text=_(u"Write a brief message for the article's history log."), summary = forms.CharField(label=_(u'Summary'), help_text=_(u"Write a brief message for the article's history log."),
required=False) required=False)
......
{% extends "wiki/base.html" %}
{% load wiki_tags i18n %}
{% load url from future %}
{% block pagetitle %}{% trans "Settings" %}: {% article_for_object urlpath as article %}{{ article.current_revision.title }}{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents %}
{% article_for_object urlpath as article %}
{% if article %}
<div class="tabbable tabs-top" style="margin-top: 40px;">
<ul class="nav nav-tabs">
{% with "attachments" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li>
<h1 style="margin-top: -10px;">
{{ article.current_revision.title }}
</h1>
</li>
</ul>
<div class="tab-content">
Upload form coming up! List of attachments coming here and in the article text + a special markdown tag for including a link to each attachment in the article text.
</div>
</div>
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
</div>
{% else %}
{% trans "An article for this path does not exist." %}
{% endif %}
{% endblock %}
from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateView
from django.utils.decorators import method_decorator
from wiki.core import plugins_registry
from wiki.views.mixins import ArticleMixin
from wiki.decorators import get_article
class AttachmentView(ArticleMixin, TemplateView):
template_name="wiki/plugins/attachments/tab.html"
@method_decorator(get_article(can_read=True))
def dispatch(self, request, article, *args, **kwargs):
return super(AttachmentView, self).dispatch(request, article, *args, **kwargs)
class AttachmentPlugin(plugins_registry.BasePlugin):
#settings_form = 'wiki.plugins.notifications.forms.SubscriptionForm'
slug = 'attachments'
article_tab = (_(u'Attachments'), "icon-file")
article_view = AttachmentView().dispatch
article_template_append = 'wiki/plugins/attachments/append.html'
def __init__(self):
#print "I WAS LOADED!"
pass
plugins_registry.register(AttachmentPlugin)
...@@ -19,14 +19,14 @@ class ArticleSubscription(wiki_models.pluginbase.ArticlePlugin, Subscription): ...@@ -19,14 +19,14 @@ class ArticleSubscription(wiki_models.pluginbase.ArticlePlugin, Subscription):
'type': self.notification_type.label}) 'type': self.notification_type.label})
def post_article_save(instance, **kwargs): def post_article_save(instance, **kwargs):
if kwargs.get('created', False): if kwargs.get('created', True):
urlpath = wiki_models.URLPath.objects.filter(articles=instance) urlpath = wiki_models.URLPath.objects.filter(articles=instance)
if urlpath: if urlpath:
url = reverse('wiki:get_url', urlpath.path) url = reverse('wiki:get_url', urlpath.path)
else: else:
url = None url = None
notify(_(u'New article created: %s') % instance.title, settings.ARTICLE_CREATE, notify(_(u'New article created: %s') % instance.title, settings.ARTICLE_CREATE,
target_object=instance.id, url=url) target_object=instance, url=url)
def post_article_revision_save(instance, **kwargs): def post_article_revision_save(instance, **kwargs):
if kwargs.get('created', False): if kwargs.get('created', False):
......
{% load i18n wiki_tags %}{% load url from future %} {% load i18n wiki_tags %}{% load url from future %}
{% for plugin in plugins %}
{% if plugin.article_tab %}
<li class="pull-right{% if selected == plugin.slug %} active{% endif %}">
<a href="{% url 'wiki:plugin_url' urlpath.path plugin.slug %}">
<span class="{{ plugin.article_tab.1 }}"></span>
{{ plugin.article_tab.0 }}
</a>
</li>
{% endif %}
{% endfor %}
<li class="pull-right{% if selected == "settings" %} active{% endif %}"> <li class="pull-right{% if selected == "settings" %} active{% endif %}">
{% if not user.is_anonymous %} {% if not user.is_anonymous %}
<a href="{% url 'wiki:settings_url' urlpath.path %}"> <a href="{% url 'wiki:settings_url' urlpath.path %}">
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.conf.urls.defaults import patterns, url from django.conf.urls.defaults import patterns, url
import views from wiki.views import article
urlpatterns = patterns('', urlpatterns = patterns('',
url('^$', 'wiki.views.root', name='root', kwargs={'path': ''}), url('^$', article.ArticleView.as_view(), name='root', kwargs={'path': ''}),
url('^create-root/$', 'wiki.views.root_create', name='root_create'), url('^create-root/$', 'wiki.views.article.root_create', name='root_create'),
url('^_revision/diff/(\d+)/$', 'wiki.views.diff', name='diff'), url('^_revision/diff/(\d+)/$', 'wiki.views.article.diff', name='diff'),
# This one doesn't work because it don't know where to redirect after... # This one doesn't work because it don't know where to redirect after...
url('^_revision/change/(?P<article_id>\d+)/(?P<revision_id>\d+)/$', 'wiki.views.change_revision', name='change_revision'), url('^_revision/change/(?P<article_id>\d+)/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision'),
url('^_revision/preview/(?P<article_id>\d+)/$', 'wiki.views.preview', name='preview_revision'), url('^_revision/preview/(?P<article_id>\d+)/$', 'wiki.views.article.preview', name='preview_revision'),
url('^_revision/merge/(?P<article_id>\d+)/(?P<revision_id>\d+)/preview/$', 'wiki.views.merge', name='merge_revision_preview', kwargs={'preview': True}), url('^_revision/merge/(?P<article_id>\d+)/(?P<revision_id>\d+)/preview/$', 'wiki.views.article.merge', name='merge_revision_preview', kwargs={'preview': True}),
url('^(?P<path>.+/|)_create/$', views.Create.as_view(), name='create_url'), url('^(?P<path>.+/|)_create/$', article.Create.as_view(), name='create_url'),
url('^(?P<path>.+/|)_edit/$', views.Edit.as_view(), name='edit_url'), url('^(?P<path>.+/|)_edit/$', article.Edit.as_view(), name='edit_url'),
url('^(?P<path>.+/|)_preview/$', 'wiki.views.preview', name='preview_url'), url('^(?P<path>.+/|)_preview/$', 'wiki.views.article.preview', name='preview_url'),
url('^(?P<path>.+/|)_history/$', views.History.as_view(), name='history_url'), url('^(?P<path>.+/|)_history/$', article.History.as_view(), name='history_url'),
url('^(?P<path>.+/|)_settings/$', views.Settings.as_view(), name='settings_url'), url('^(?P<path>.+/|)_settings/$', article.Settings.as_view(), name='settings_url'),
url('^(?P<path>.+/|)_revision/change/(?P<revision_id>\d+)/$', 'wiki.views.change_revision', name='change_revision_url'), url('^(?P<path>.+/|)_revision/change/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision_url'),
url('^(?P<path>.+/|)_revision/merge/(?P<revision_id>\d+)/$', 'wiki.views.merge', name='merge_revision_url'), url('^(?P<path>.+/|)_revision/merge/(?P<revision_id>\d+)/$', 'wiki.views.article.merge', name='merge_revision_url'),
url('^(?P<path>.+/|)$', 'wiki.views.get_url', name='get_url'), url('^(?P<path>.+/|)_plugin/(?P<slug>\w+)/$', article.Plugin.as_view(), name='plugin_url'),
url('^(?P<path>.+/|)$', article.ArticleView.as_view(), name='get_url'),
) )
def get_pattern(app_name="wiki", namespace="wiki"): def get_pattern(app_name="wiki", namespace="wiki"):
......
...@@ -8,6 +8,9 @@ from wiki import models ...@@ -8,6 +8,9 @@ from wiki import models
from wiki import forms from wiki import forms
from wiki import editors from wiki import editors
from wiki.conf import settings from wiki.conf import settings
from wiki.core import plugins_registry
from mixins import ArticleMixin
from django.contrib import messages from django.contrib import messages
from django.views.generic.list import ListView from django.views.generic.list import ListView
...@@ -17,9 +20,8 @@ from django.utils.decorators import method_decorator ...@@ -17,9 +20,8 @@ from django.utils.decorators import method_decorator
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from wiki.decorators import get_article from wiki.decorators import get_article
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView, View
from wiki.core import plugins_registry
from wiki.core.diff import simple_merge from wiki.core.diff import simple_merge
@get_article(can_read=True) @get_article(can_read=True)
...@@ -49,23 +51,14 @@ def preview(request, article, urlpath=None, template_file="wiki/preview_inline.h ...@@ -49,23 +51,14 @@ def preview(request, article, urlpath=None, template_file="wiki/preview_inline.h
'content': content}) 'content': content})
return render_to_response(template_file, c) return render_to_response(template_file, c)
@get_article(can_read=True) class Edit(FormView, ArticleMixin):
def root(request, article, template_file="wiki/article.html", urlpath=None):
c = RequestContext(request, {'urlpath': urlpath,
'article': article,})
return render_to_response(template_file, c)
class Edit(FormView):
form_class = forms.EditForm form_class = forms.EditForm
template_name="wiki/edit.html" template_name="wiki/edit.html"
@method_decorator(get_article(can_write=True)) @method_decorator(get_article(can_write=True))
def dispatch(self, request, article, *args, **kwargs): def dispatch(self, request, article, *args, **kwargs):
self.urlpath = kwargs.pop('urlpath', None) return super(Edit, self).dispatch(request, article, *args, **kwargs)
self.article = article
return super(Edit, self).dispatch(request, *args, **kwargs)
def get_form(self, form_class): def get_form(self, form_class):
""" """
...@@ -98,23 +91,19 @@ class Edit(FormView): ...@@ -98,23 +91,19 @@ class Edit(FormView):
return return
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['urlpath'] = self.urlpath
kwargs['article'] = self.article
kwargs['edit_form'] = kwargs.pop('form', None) kwargs['edit_form'] = kwargs.pop('form', None)
kwargs['editor'] = editors.editor kwargs['editor'] = editors.editor
return super(Edit, self).get_context_data(**kwargs) return super(Edit, self).get_context_data(**kwargs)
class Create(FormView): class Create(FormView, ArticleMixin):
form_class = forms.CreateForm form_class = forms.CreateForm
template_name="wiki/create.html" template_name="wiki/create.html"
@method_decorator(get_article(can_write=True)) @method_decorator(get_article(can_write=True))
def dispatch(self, request, article, *args, **kwargs): def dispatch(self, request, article, *args, **kwargs):
self.urlpath = kwargs.pop('urlpath', None) return super(Create, self).dispatch(request, article, *args, **kwargs)
self.article = article
return super(Create, self).dispatch(request, *args, **kwargs)
def get_form(self, form_class): def get_form(self, form_class):
""" """
...@@ -155,16 +144,22 @@ class Create(FormView): ...@@ -155,16 +144,22 @@ class Create(FormView):
kwargs['editor'] = editors.editor kwargs['editor'] = editors.editor
return super(Create, self).get_context_data(**kwargs) return super(Create, self).get_context_data(**kwargs)
class Settings(TemplateView): class Plugin(View):
def dispatch(self, request, path=None, slug=None, **kwargs):
kwargs['path'] = path
for plugin in plugins_registry._cache.values():
if getattr(plugin, 'slug', None) == slug:
return plugin.article_view(request, **kwargs)
class Settings(ArticleMixin, TemplateView):
permission_form_class = forms.PermissionsForm permission_form_class = forms.PermissionsForm
template_name="wiki/settings.html" template_name="wiki/settings.html"
@method_decorator(get_article(can_read=True)) @method_decorator(get_article(can_read=True))
def dispatch(self, request, article, *args, **kwargs): def dispatch(self, request, article, *args, **kwargs):
self.urlpath = kwargs.pop('urlpath', None) return super(Settings, self).dispatch(request, article, *args, **kwargs)
self.article = article
return super(Settings, self).dispatch(request, *args, **kwargs)
def get_form_classes(self,): def get_form_classes(self,):
""" """
...@@ -205,12 +200,10 @@ class Settings(TemplateView): ...@@ -205,12 +200,10 @@ class Settings(TemplateView):
return redirect('wiki:settings_url', self.urlpath.path) return redirect('wiki:settings_url', self.urlpath.path)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['urlpath'] = self.urlpath
kwargs['article'] = self.article
kwargs['forms'] = self.forms kwargs['forms'] = self.forms
return kwargs return super(Settings, self).get_context_data(**kwargs)
class History(ListView): class History(ListView, ArticleMixin):
template_name="wiki/history.html" template_name="wiki/history.html"
allow_empty = True allow_empty = True
...@@ -221,15 +214,16 @@ class History(ListView): ...@@ -221,15 +214,16 @@ class History(ListView):
return models.ArticleRevision.objects.filter(article=self.article).order_by('-created') return models.ArticleRevision.objects.filter(article=self.article).order_by('-created')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['urlpath'] = self.urlpath # Is this a bit of a hack? Use better inheritance?
kwargs['article'] = self.article kwargs_article = ArticleMixin.get_context_data(self, **kwargs)
return super(History, self).get_context_data(**kwargs) kwargs_listview = ListView.get_context_data(self, **kwargs)
kwargs.update(kwargs_article)
kwargs.update(kwargs_listview)
return kwargs
@method_decorator(get_article(can_read=True)) @method_decorator(get_article(can_read=True))
def dispatch(self, request, article, *args, **kwargs): def dispatch(self, request, article, *args, **kwargs):
self.urlpath = kwargs.pop('urlpath', None) return super(History, self).dispatch(request, article, *args, **kwargs)
self.article = article
return super(History, self).dispatch(request, *args, **kwargs)
@get_article(can_write=True) @get_article(can_write=True)
def change_revision(request, article, revision_id=None, urlpath=None): def change_revision(request, article, revision_id=None, urlpath=None):
...@@ -258,13 +252,14 @@ def root_create(request): ...@@ -258,13 +252,14 @@ def root_create(request):
'editor': editors.editor,}) 'editor': editors.editor,})
return render_to_response("wiki/article/create_root.html", c) return render_to_response("wiki/article/create_root.html", c)
@get_article(can_read=True) class ArticleView(ArticleMixin, TemplateView, ):
def get_url(request, article, template_file="wiki/article.html", urlpath=None):
c = RequestContext(request, {'urlpath': urlpath,
'article': article,})
return render_to_response(template_file, c)
template_name="wiki/article.html"
@method_decorator(get_article(can_read=True))
def dispatch(self, request, article, *args, **kwargs):
return super(ArticleView, self).dispatch(request, article, *args, **kwargs)
@json_view @json_view
def diff(request, revision_id, other_revision_id=None): def diff(request, revision_id, other_revision_id=None):
......
from django.views.generic.base import TemplateResponseMixin
from wiki.core import plugins_registry
class ArticleMixin(TemplateResponseMixin):
def dispatch(self, request, article, *args, **kwargs):
self.urlpath = kwargs.pop('urlpath', None)
self.article = article
return super(ArticleMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
kwargs['urlpath'] = self.urlpath
kwargs['article'] = self.article
kwargs['plugins'] = plugins_registry._cache.values()
return kwargs
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment