Commit efe01f76 by benjaoming

Improving managers. Adding select_related (untested) on get_article decorator.…

Improving managers. Adding select_related (untested) on get_article decorator. Adding search function for attachments to add other article's attachments. Adding better permission handling through managers.
parent 69880593
...@@ -26,11 +26,25 @@ def get_article(func=None, can_read=True, can_write=False): ...@@ -26,11 +26,25 @@ def get_article(func=None, can_read=True, can_write=False):
path = kwargs.pop('path', None) path = kwargs.pop('path', None)
article_id = kwargs.pop('article_id', None) article_id = kwargs.pop('article_id', None)
if can_read:
articles = models.Article.objects.can_read(request.user)
if can_write:
articles = models.Article.objects.can_write(request.user)
# TODO: Is this the way to do it?
articles = articles.select_related()
urlpath = None urlpath = None
if not path is None: if article_id:
article = get_object_or_404(articles, id=article_id)
try:
urlpath = models.URLPath.objects.get(articles=article)
except models.URLPath.DoesNotExist, models.URLPath.MultipleObjectsReturned:
urlpath = None
else:
try: try:
urlpath = models.URLPath.get_by_path(path) urlpath = models.URLPath.get_by_path(path, select_related=True)
except NoRootURL: except NoRootURL:
return redirect('wiki:root_create') return redirect('wiki:root_create')
except models.URLPath.DoesNotExist: except models.URLPath.DoesNotExist:
...@@ -40,13 +54,10 @@ def get_article(func=None, can_read=True, can_write=False): ...@@ -40,13 +54,10 @@ def get_article(func=None, can_read=True, can_write=False):
parent = models.URLPath.get_by_path(path) parent = models.URLPath.get_by_path(path)
return redirect(reverse("wiki:create_url", args=(parent.path,)) + "?slug=%s" % pathlist[-1]) return redirect(reverse("wiki:create_url", args=(parent.path,)) + "?slug=%s" % pathlist[-1])
except models.URLPath.DoesNotExist: except models.URLPath.DoesNotExist:
return HttpResponseNotFound("This article was not found. This page should look nicer.") # TODO: Make a nice page
article = urlpath.article return HttpResponseNotFound("This article was not found, and neither was the parent. This page should look nicer.")
elif article_id: # TODO: If the article is not found but it exists, there is a permission error!
article = get_object_or_404(models.Article, id=article_id) article = get_object_or_404(articles, id=urlpath.article.id)
if not article.can_write(request.user):
raise HttpResponseForbidden()
kwargs['urlpath'] = urlpath kwargs['urlpath'] = urlpath
......
from django.db import models from django.db import models
from django.db.models import Q
# First, define the Manager subclass. class PermissionArticleManagerMixin(object):
class ActiveObjectsManager(models.Manager):
def can_read(self, user):
"""Filter objects so only the ones with a user's reading access
are included"""
if user.has_perm('wiki.moderator'):
return self.get_query_set()
q = self.get_query_set().filter(Q(other_read=True) |
Q(owner=user) |
(Q(group__user=user) & Q(group_read=True))
)
return q
def can_write(self, user):
"""Filter objects so only the ones with a user's writing access
are included"""
if user.has_perm('wiki.moderator'):
return self.get_query_set()
q = self.get_query_set().filter(Q(other_write=True) |
Q(owner=user) |
(Q(group__user=user) & Q(group_write=True))
)
return q
class PermissionArticleManager(PermissionArticleManagerMixin, models.Manager):
pass
class PermissionArticleFkManagerMixin(object):
"""A manager like the above but for objects that have a ForeignKey to
an article object and inherits its permissions."""
def can_read(self, user):
"""Filter objects so only the ones with a user's reading access
are included"""
if user.has_perm('wiki.moderator'):
return self.get_query_set()
q = self.get_query_set().filter(Q(article__other_read=True) |
Q(article__owner=user) |
(Q(article__group__user=user) & Q(article__group_read=True))
)
return q
def can_write(self, user):
"""Filter objects so only the ones with a user's writing access
are included"""
if user.has_perm('wiki.moderator'):
return self.get_query_set()
q = self.get_query_set().filter(Q(article__other_write=True) |
Q(article__owner=user) |
(Q(article__group__user=user) & Q(article__group_write=True))
)
return q
class PermissionArticleFkManager(models.Manager, PermissionArticleFkManagerMixin):
pass
class ActiveObjectsManager(models.Manager, PermissionArticleManagerMixin):
"""A manager for objects that have a ForeignKey named 'current_revision'
(ie. a BaseRevision inheritor)."""
def get_query_set(self): def get_query_set(self):
return super(ActiveObjectsManager, self).get_query_set().filter(current_revision__deleted=False) return super(ActiveObjectsManager, self).get_query_set().filter(current_revision__deleted=False)
class ActiveObjectsFkManager(ActiveObjectsManager, PermissionArticleFkManagerMixin):
pass
...@@ -9,9 +9,18 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -9,9 +9,18 @@ from django.utils.translation import ugettext_lazy as _
from markdown import markdown from markdown import markdown
from wiki.conf import settings from wiki.conf import settings
from wiki import managers
class Article(models.Model): class Article(models.Model):
objects = managers.PermissionArticleManager()
active_objects = managers.ActiveObjectsManager()
@classmethod
def objects_can_read(cls, user, objects):
if user.has_perm("wiki.moderator"):
return objects
title = models.CharField(max_length=512, verbose_name=_(u'title'), title = models.CharField(max_length=512, verbose_name=_(u'title'),
null=False, blank=False, help_text=_(u'Initial title of the article. ' null=False, blank=False, help_text=_(u'Initial title of the article. '
'May be overridden with revision titles.')) 'May be overridden with revision titles.'))
...@@ -238,11 +247,11 @@ class ArticleRevision(BaseRevision): ...@@ -238,11 +247,11 @@ class ArticleRevision(BaseRevision):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if (not self.id and if (not self.id and
not self.previous_revision and not self.previous_revision and
self.attachment and self.article and
self.attachment.current_revision and self.article.current_revision and
self.attachment.current_revision != self): self.article.current_revision != self):
self.previous_revision = self.attachment.current_revision self.previous_revision = self.article.current_revision
if not self.revision_number: if not self.revision_number:
try: try:
......
...@@ -76,7 +76,7 @@ class URLPath(MPTTModel): ...@@ -76,7 +76,7 @@ class URLPath(MPTTModel):
super(URLPath, self).clean(*args, **kwargs) super(URLPath, self).clean(*args, **kwargs)
@classmethod @classmethod
def get_by_path(cls, path): def get_by_path(cls, path, select_related=False):
""" """
Strategy: Don't handle all kinds of weird cases. Be strict. Strategy: Don't handle all kinds of weird cases. Be strict.
Accepts paths both starting with and without '/' Accepts paths both starting with and without '/'
...@@ -98,6 +98,13 @@ class URLPath(MPTTModel): ...@@ -98,6 +98,13 @@ class URLPath(MPTTModel):
parent = parent.get_children().get(slug__iexact=slug) parent = parent.get_children().get(slug__iexact=slug)
level += 1 level += 1
# TODO: Is this the way to do it?
if select_related:
parent.get_children_mptt = parent.get_children
parent.get_ancestors_mptt = parent.get_children
parent.get_children = lambda *a: parent.get_children_mptt().select_related()
parent.get_ancestors = lambda *a: parent.get_ancestors_mptt().select_related()
return parent return parent
def get_absolute_url(self): def get_absolute_url(self):
......
...@@ -26,5 +26,4 @@ class DeleteForm(forms.Form): ...@@ -26,5 +26,4 @@ class DeleteForm(forms.Form):
class SearchForm(forms.Form): class SearchForm(forms.Form):
query = forms.CharField(label=_(u'Query'), query = forms.CharField(label="", widget=forms.TextInput(attrs={'class': 'search-query'}),)
help_text=_(u'You may search file names and descriptions.'))
...@@ -13,10 +13,9 @@ class IllegalFileExtension(Exception): ...@@ -13,10 +13,9 @@ class IllegalFileExtension(Exception):
pass pass
class Attachment(ReusablePlugin): class Attachment(ReusablePlugin):
objects = models.Manager() objects = managers.PermissionArticleFkManager()
active_objects = managers.ActiveObjectsManager() active_objects = managers.ActiveObjectsFkManager()
current_revision = models.OneToOneField('AttachmentRevision', current_revision = models.OneToOneField('AttachmentRevision',
verbose_name=_(u'current revision'), verbose_name=_(u'current revision'),
...@@ -25,7 +24,7 @@ class Attachment(ReusablePlugin): ...@@ -25,7 +24,7 @@ class Attachment(ReusablePlugin):
) )
original_filename = models.CharField(max_length=256, verbose_name=_(u'original filename'), blank=True, null=True) original_filename = models.CharField(max_length=256, verbose_name=_(u'original filename'), blank=True, null=True)
class Meta: class Meta:
verbose_name = _(u'attachment') verbose_name = _(u'attachment')
verbose_name_plural = _(u'attachments') verbose_name_plural = _(u'attachments')
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
<h3> <h3>
<a href="{% url 'wiki:attachments_download' path=urlpath.path attachment_id=attachment.id %}">{{ attachment.current_revision.get_filename }}</a> <a href="{% url 'wiki:attachments_download' path=urlpath.path attachment_id=attachment.id %}">{{ attachment.current_revision.get_filename }}</a>
<span class="badge">{{ attachment.current_revision.created|naturaltime }}</span> <span class="badge">{{ attachment.current_revision.created|naturaltime }}</span>
<span class="badge badge-info">{{ attachment.current_revision.get_size|filesizeformat }}</span>
{% if attachment.current_revision.deleted %} {% if attachment.current_revision.deleted %}
<span class="badge badge-important">{% trans "deleted" %}</span> <span class="badge badge-important">{% trans "deleted" %}</span>
{% endif %} {% endif %}
...@@ -46,45 +45,43 @@ ...@@ -46,45 +45,43 @@
<tr> <tr>
<th>{% trans "Markdown tag" %}</th> <th>{% trans "Markdown tag" %}</th>
<th>{% trans "Uploaded by" %}</th> <th>{% trans "Uploaded by" %}</th>
<th style="text-align: right;">{% trans "Actions" %}</th> <th>{% trans "Size" %}</th>
</tr> <td style="text-align: right;" rowspan="2">
<tr>
<td><code>[attachment:{{ attachment.id }}]</code></td>
<td>
{% if attachment.current_revision.user %}{{ attachment.current_revision.user }}{% else %}{% if user|is_moderator %}{{ attachment.current_revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
</td>
<td style="text-align: right;">
{% if attachment|can_write:user %} {% if attachment|can_write:user %}
<p>
{% if not attachment.current_revision.deleted %} {% if not attachment.current_revision.deleted %}
<a href="{% url 'wiki:attachments_replace' path=urlpath.path attachment_id=attachment.id %}" class="btn"><span class="icon-upload"></span> {% trans "Replace" %}</a> <a href="{% url 'wiki:attachments_replace' path=urlpath.path attachment_id=attachment.id %}" class="btn">{% trans "Replace" %}</a>
{% if attachment.article = article %} {% if attachment.article = article %}
<a href="{% url 'wiki:attachments_delete' path=urlpath.path attachment_id=attachment.id %}" class="btn"><span class="icon-remove"></span> {% trans "Delete" %}</a> <a href="{% url 'wiki:attachments_delete' path=urlpath.path attachment_id=attachment.id %}" class="btn">{% trans "Delete" %}</a>
{% else %} {% else %}
<a href="{% url 'wiki:attachments_delete' path=urlpath.path attachment_id=attachment.id %}" class="btn"><span class="icon-minus-sign"></span> {% trans "Detach" %}</a> <a href="{% url 'wiki:attachments_delete' path=urlpath.path attachment_id=attachment.id %}" class="btn">{% trans "Detach" %}</a>
{% endif %} {% endif %}
{% else %} {% else %}
{% if attachment.current_revision.previous_revision.id %} {% if attachment.current_revision.previous_revision.id %}
<form method="POST" action="{% url 'wiki:attachments_revision_change' path=urlpath.path attachment_id=attachment.id revision_id=attachment.current_revision.previous_revision.id %}"> <form method="POST" action="{% url 'wiki:attachments_revision_change' path=urlpath.path attachment_id=attachment.id revision_id=attachment.current_revision.previous_revision.id %}">
{% csrf_token %} {% csrf_token %}
<button class="btn"> <button class="btn">
<span class="icon-flag"></span>
{% trans "Restore" %} {% trans "Restore" %}
</button> </button>
</form> </form>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% else %} </p>
<em>{% trans "none" %}</em>
{% endif %} {% endif %}
<p>
<a href="{% url 'wiki:attachments_history' path=urlpath.path attachment_id=attachment.id %}">
<span class="icon-time"></span>
{% trans "File history" %} ({{ attachment.attachmentrevision_set.all.count }} {% trans "revisions" %})
</a>
</p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="4"> <td><code>[attachment:{{ attachment.id }}]</code></td>
<a href="{% url 'wiki:attachments_history' path=urlpath.path attachment_id=attachment.id %}"> <td>
<span class="icon-time"></span> {% if attachment.current_revision.user %}{{ attachment.current_revision.user }}{% else %}{% if user|is_moderator %}{{ attachment.current_revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
{% trans "View file history" %} ({{ attachment.attachmentrevision_set.all.count }} {% trans "revisions" %})
</a>
</td> </td>
<td>{{ attachment.current_revision.get_size|filesizeformat }}</td>
</tr> </tr>
</table> </table>
{% empty %} {% empty %}
...@@ -96,7 +93,7 @@ ...@@ -96,7 +93,7 @@
<h2>{% trans "Add from upload" %}</h2> <h2>{% trans "Add from upload" %}</h2>
<form method="POST" class="form-vertical" id="attachment_form" enctype="multipart/form-data"> <form method="POST" class="form-vertical" id="attachment_form" enctype="multipart/form-data">
{% wiki_form form %} {% wiki_form form %}
<button type="submit" name="save" value="1" class="btn btn-primary"> <button type="submit" name="save" value="1" class="btn">
<span class="icon-upload"></span> <span class="icon-upload"></span>
{% trans "Upload file" %} {% trans "Upload file" %}
</button> </button>
...@@ -105,6 +102,13 @@ ...@@ -105,6 +102,13 @@
<div class="well"> <div class="well">
<h2>{% trans "Add from existing attachments..." %}</h2> <h2>{% trans "Add from existing attachments..." %}</h2>
<p>{% trans "You can reuse files from other articles. Please note that these files are subject to updates on other articles." %}</p> <p>{% trans "You can reuse files from other articles. Please note that these files are subject to updates on other articles." %}</p>
<form method="GET" action="{% url 'wiki:attachments_search' path=urlpath.path %}" class="form-search">
{{ search_form.query }}
<button class="btn">
<span class="icon-search"></span>
{% trans "Search files and articles" %}
</button>
</form>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
<ul> <ul>
{% for a in attachment.articles.all %}<li>{{ a.current_revision.title }}</li>{% endfor %} {% for a in attachment.articles.all %}<li>{{ a.current_revision.title }}</li>{% endfor %}
</ul> </ul>
<hr />
{% else %} {% else %}
<p class="lead"> <p class="lead">
{% blocktrans with attachment.original_filename as filename %} {% blocktrans with attachment.original_filename as filename %}
......
{% extends "wiki/base.html" %}
{% load wiki_tags i18n humanize %}
{% load url from future %}
{% block pagetitle %}{% trans "Add file to" %} "{{ 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">
<h2>{% trans "Add existing attachment to" %} {{ article.current_revision.title }}</h2>
<form method="GET" action="{% url 'wiki:attachments_search' path=urlpath.path %}" class="form-search">
{{ search_form.query }}
<button class="btn">
<span class="icon-search"></span>
{% trans "Search files and articles" %}
</button>
</form>
{% if attachments %}
<table class="table table-striped table-bordered">
<tr>
<th>{% trans "File" %}</th>
<th>{% trans "Main article" %}</th>
<th>{% trans "Date" %}</th>
<th>{% trans "Uploaded by" %}</th>
<th>{% trans "Size" %}</th>
<th style="text-align: right">{% trans "Action" %}</th>
</tr>
{% for attachment in attachments %}
<tr>
<td>
<h4>{{ attachment.original_filename }}</h4>
{{ attachment.current_revision.description|default:_("<em>No description</em>")|safe }}
</td>
<td>
<strong>{{ attachment.article.current_revision.title }}</strong>
</td>
<td>
{{ attachment.current_revision.created }}
{% if attachment.current_revision.deleted %}<span class="badge badge-important">{% trans "deleted" %}</span>{% endif %}
</td>
<td>
{% if attachment.current_revision.user %}{{ attachment.current_revision.user }}{% else %}{% if user|is_moderator %}{{ attachment.current_revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
</td>
<td>{{ attachment.current_revision.file.size|filesizeformat }}</td>
<td style="text-align: right">
<form method="POST" action="{% url 'wiki:attachments_add' path=urlpath.path attachment_id=attachment.id %}">
{% csrf_token %}
<a href="{% url 'wiki:attachments_download' path=urlpath.path attachment_id=attachment.id %}" class="btn">
<span class="icon-download"></span>
{% trans "Download" %}
</a>
<button class="btn btn-primary">
<span class="icon-plus"></span>
{% trans "Add to article" %}
</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<p><em>{% trans "Your search did not return any results" %}</em></p>
{% endif %}
{% with query as appended_value and "query" as appended_key %}
{% include "wiki/includes/pagination.html" %}
{% endwith %}
<p>
<a href="{% url 'wiki:attachments_index' path=urlpath.path %}"><span class="icon-arrow-left"></span> {% trans "Go back" %}</a>
</p>
</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 %}
...@@ -3,6 +3,7 @@ from django.shortcuts import redirect, get_object_or_404 ...@@ -3,6 +3,7 @@ from django.shortcuts import redirect, get_object_or_404
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django.db.models import Q
from wiki.views.mixins import ArticleMixin from wiki.views.mixins import ArticleMixin
from wiki.decorators import get_article from wiki.decorators import get_article
...@@ -13,6 +14,7 @@ from django.views.generic.base import TemplateView, View ...@@ -13,6 +14,7 @@ from django.views.generic.base import TemplateView, View
from wiki.core.http import send_file from wiki.core.http import send_file
from django.http import Http404 from django.http import Http404
from django.db import transaction from django.db import transaction
from django.views.generic.list import ListView
class AttachmentView(ArticleMixin, FormView): class AttachmentView(ArticleMixin, FormView):
...@@ -57,6 +59,7 @@ class AttachmentView(ArticleMixin, FormView): ...@@ -57,6 +59,7 @@ class AttachmentView(ArticleMixin, FormView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['attachments'] = self.attachments kwargs['attachments'] = self.attachments
kwargs['search_form'] = forms.SearchForm()
return super(AttachmentView, self).get_context_data(**kwargs) return super(AttachmentView, self).get_context_data(**kwargs)
...@@ -77,6 +80,7 @@ class AttachmentHistoryView(ArticleMixin, TemplateView): ...@@ -77,6 +80,7 @@ class AttachmentHistoryView(ArticleMixin, TemplateView):
kwargs['revisions'] = self.attachment.attachmentrevision_set.all().order_by('-revision_number') kwargs['revisions'] = self.attachment.attachmentrevision_set.all().order_by('-revision_number')
return super(AttachmentHistoryView, self).get_context_data(**kwargs) return super(AttachmentHistoryView, self).get_context_data(**kwargs)
class AttachmentReplaceView(ArticleMixin, FormView): class AttachmentReplaceView(ArticleMixin, FormView):
form_class = forms.AttachmentForm form_class = forms.AttachmentForm
...@@ -160,6 +164,26 @@ class AttachmentChangeRevisionView(ArticleMixin, View): ...@@ -160,6 +164,26 @@ class AttachmentChangeRevisionView(ArticleMixin, View):
pass pass
class AttachmentAddView(ArticleMixin, View):
@method_decorator(get_article(can_write=True))
def dispatch(self, request, article, attachment_id, *args, **kwargs):
self.attachment = get_object_or_404(models.Attachment.active_objects.can_write(request.user), id=attachment_id)
return super(AttachmentAddView, self).dispatch(request, article, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.attachment.articles.add(self.article)
self.attachment.save()
messages.success(self.request, _(u'Added a reference to "%(att)s" from "%(art)s".') %
{'att': self.attachment.original_filename,
'art': self.article.current_revision.title})
if self.urlpath:
return redirect("wiki:attachments_index", path=self.urlpath.path)
# TODO: What if this hasn't got a urlpath.
else:
pass
class AttachmentDeleteView(ArticleMixin, FormView): class AttachmentDeleteView(ArticleMixin, FormView):
form_class = forms.DeleteForm form_class = forms.DeleteForm
...@@ -197,3 +221,37 @@ class AttachmentDeleteView(ArticleMixin, FormView): ...@@ -197,3 +221,37 @@ class AttachmentDeleteView(ArticleMixin, FormView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['attachment'] = self.attachment kwargs['attachment'] = self.attachment
return super(AttachmentDeleteView, self).get_context_data(**kwargs) return super(AttachmentDeleteView, self).get_context_data(**kwargs)
class AttachmentSearchView(ArticleMixin, ListView):
template_name="wiki/plugins/attachments/search.html"
allow_empty = True
context_object_name = 'attachments'
paginate_by = 10
@method_decorator(get_article(can_write=True))
def dispatch(self, request, article, *args, **kwargs):
return super(AttachmentSearchView, self).dispatch(request, article, *args, **kwargs)
def get_queryset(self):
self.query = self.request.GET.get('query', None)
if not self.query:
qs = models.Attachment.active_objects.get_empty_query_set()
else:
qs = models.Attachment.active_objects.can_read(self.request.user)
qs = qs.filter(Q(original_filename__contains=self.query) |
Q(current_revision__description__contains=self.query) |
Q(article__current_revision__title__contains=self.query))
qs = qs.exclude(articles=self.article)
return qs
def get_context_data(self, **kwargs):
# Is this a bit of a hack? Use better inheritance?
kwargs_article = ArticleMixin.get_context_data(self, **kwargs)
kwargs_listview = ListView.get_context_data(self, **kwargs)
kwargs['search_form'] = forms.SearchForm(self.request.GET)
kwargs['query'] = self.query
kwargs.update(kwargs_article)
kwargs.update(kwargs_listview)
return kwargs
...@@ -13,6 +13,8 @@ class AttachmentPlugin(plugins_registry.BasePlugin): ...@@ -13,6 +13,8 @@ class AttachmentPlugin(plugins_registry.BasePlugin):
slug = settings.SLUG slug = settings.SLUG
urlpatterns = patterns('', urlpatterns = patterns('',
url('^$', views.AttachmentView.as_view(), name='attachments_index'), url('^$', views.AttachmentView.as_view(), name='attachments_index'),
url('^search/$', views.AttachmentSearchView.as_view(), name='attachments_search'),
url('^add/(?P<attachment_id>\d+)/$', views.AttachmentAddView.as_view(), name='attachments_add'),
url('^replace/(?P<attachment_id>\d+)/$', views.AttachmentReplaceView.as_view(), name='attachments_replace'), url('^replace/(?P<attachment_id>\d+)/$', views.AttachmentReplaceView.as_view(), name='attachments_replace'),
url('^history/(?P<attachment_id>\d+)/$', views.AttachmentHistoryView.as_view(), name='attachments_history'), url('^history/(?P<attachment_id>\d+)/$', views.AttachmentHistoryView.as_view(), name='attachments_history'),
url('^download/(?P<attachment_id>\d+)/$', views.AttachmentDownloadView.as_view(), name='attachments_download'), url('^download/(?P<attachment_id>\d+)/$', views.AttachmentDownloadView.as_view(), name='attachments_download'),
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
<div class="pagination"> <div class="pagination">
<ul> <ul>
{% for pc in paginator.page_range %} {% for pc in paginator.page_range %}
<li class="{% if pc == page_obj.number %} active{% endif %}"><a href="?page={{ pc }}">{{ pc }}</a></li> <li class="{% if pc == page_obj.number %} active{% endif %}"><a href="?page={{ pc }}{% if appended_key %}&{{ appended_key }}={{ appended_value }}{% endif %}">{{ pc }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div>
{% endif %} {% endif %}
...@@ -17,6 +17,7 @@ from wiki.core import plugins_registry ...@@ -17,6 +17,7 @@ from wiki.core import plugins_registry
from wiki.core.diff import simple_merge from wiki.core.diff import simple_merge
from wiki.decorators import get_article, json_view from wiki.decorators import get_article, json_view
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction
class ArticleView(ArticleMixin, TemplateView, ): class ArticleView(ArticleMixin, TemplateView, ):
...@@ -48,6 +49,7 @@ class Create(FormView, ArticleMixin): ...@@ -48,6 +49,7 @@ class Create(FormView, ArticleMixin):
form.fields['slug'].widget = forms.TextInputPrepend(prepend='/'+self.urlpath.path) form.fields['slug'].widget = forms.TextInputPrepend(prepend='/'+self.urlpath.path)
return form return form
@transaction.commit_manually
def form_valid(self, form): def form_valid(self, form):
user=None user=None
ip_address = None ip_address = None
...@@ -57,14 +59,27 @@ class Create(FormView, ArticleMixin): ...@@ -57,14 +59,27 @@ class Create(FormView, ArticleMixin):
ip_address = self.request.META.get('REMOTE_ADDR', None) ip_address = self.request.META.get('REMOTE_ADDR', None)
elif settings.LOG_IPS_ANONYMOUS: elif settings.LOG_IPS_ANONYMOUS:
ip_address = self.request.META.get('REMOTE_ADDR', None) ip_address = self.request.META.get('REMOTE_ADDR', None)
self.newpath = models.URLPath.create_article(self.urlpath, try:
form.cleaned_data['slug'], self.newpath = models.URLPath.create_article(self.urlpath,
title=form.cleaned_data['title'], form.cleaned_data['slug'],
content=form.cleaned_data['content'], title=form.cleaned_data['title'],
user_message=form.cleaned_data['summary'], content=form.cleaned_data['content'],
user=user, user_message=form.cleaned_data['summary'],
ip_address=ip_address) user=user,
messages.success(self.request, _(u"New article '%s' created.") % self.newpath.article.title) ip_address=ip_address)
messages.success(self.request, _(u"New article '%s' created.") % self.newpath.article.title)
# TODO: Handle individual exceptions better and give good feedback.
except Exception, e:
transaction.rollback()
if self.request.user.has_perm('wiki.moderator'):
messages.error(self.request, _(u"There was an error creating this article: %s") % str(e))
else:
messages.error(self.request, _(u"There was an error creating this article."))
transaction.commit()
return redirect('wiki:get_url', '')
transaction.commit()
return self.get_success_url() return self.get_success_url()
def get_success_url(self): def get_success_url(self):
......
...@@ -9,7 +9,7 @@ class ArticleMixin(TemplateResponseMixin): ...@@ -9,7 +9,7 @@ class ArticleMixin(TemplateResponseMixin):
def dispatch(self, request, article, *args, **kwargs): def dispatch(self, request, article, *args, **kwargs):
self.urlpath = kwargs.pop('urlpath', None) self.urlpath = kwargs.pop('urlpath', None)
self.article = article self.article = article
return super(ArticleMixin, self).dispatch(request, *args, **kwargs) return super(ArticleMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **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