Commit 832d7903 by benjaoming

Loads of changes in permission system. Many aspects are now configurable.

parent 495d70ee
...@@ -16,7 +16,8 @@ def get_notifications(request, latest_id=None, is_viewed=False, max_results=10): ...@@ -16,7 +16,8 @@ def get_notifications(request, latest_id=None, is_viewed=False, max_results=10):
if not latest_id is None: if not latest_id is None:
notifications = notifications.filter(latest_id__gt=latest_id) notifications = notifications.filter(latest_id__gt=latest_id)
notifications = notifications.prefetch_related('subscription')
notifications = notifications[:max_results] notifications = notifications[:max_results]
from django.contrib.humanize.templatetags.humanize import naturaltime from django.contrib.humanize.templatetags.humanize import naturaltime
......
...@@ -28,15 +28,26 @@ LOG_IPS_USERS = getattr(django_settings, 'WIKI_LOG_IPS_USERS', False) ...@@ -28,15 +28,26 @@ LOG_IPS_USERS = getattr(django_settings, 'WIKI_LOG_IPS_USERS', False)
# PERMISSIONS AND ACCOUNT HANDLING # # PERMISSIONS AND ACCOUNT HANDLING #
#################################### ####################################
# NB! None of these callables need to handle anonymous users as they are treated
# in separate settings...
# A function returning True/False if a user has permission to assign # A function returning True/False if a user has permission to assign
# permissions on an article # permissions on an article
# Relevance: changing owner and group membership # Relevance: changing owner and group membership
CAN_ASSIGN = getattr(django_settings, 'WIKI_CAN_ASSIGN', lambda article, user: user.has_perm('wiki.assign')) CAN_ASSIGN = getattr(django_settings, 'WIKI_CAN_ASSIGN', lambda article, user: user.has_perm('wiki.assign'))
# A function returning True/False if the owner of an article has permission to change
# the group to a user's own groups
# Relevance: changing group membership
CAN_ASSIGN_OWNER = getattr(django_settings, 'WIKI_ASSIGN_OWNER', lambda article, user: False)
# A function returning True/False if a user has permission to change # A function returning True/False if a user has permission to change
# read/write access for groups and others # read/write access for groups and others
CAN_CHANGE_PERMISSIONS = getattr(django_settings, 'WIKI_CAN_CHANGE_PERMISSIONS', lambda article, user: article.owner == user or user.has_perm('wiki.assign')) CAN_CHANGE_PERMISSIONS = getattr(django_settings, 'WIKI_CAN_CHANGE_PERMISSIONS', lambda article, user: article.owner == user or user.has_perm('wiki.assign'))
# Specifies if a user has access to soft deletion of articles
CAN_DELETE = getattr(django_settings, 'WIKI_CAN_DELETE', lambda article, user: article.can_write(user=user))
# A function returning True/False if a user has permission to change # A function returning True/False if a user has permission to change
# moderate, ie. lock articles and permanently delete content. # moderate, ie. lock articles and permanently delete content.
CAN_MODERATE = getattr(django_settings, 'WIKI_CAN_MODERATE', lambda article, user: user.has_perm('wiki.moderate')) CAN_MODERATE = getattr(django_settings, 'WIKI_CAN_MODERATE', lambda article, user: user.has_perm('wiki.moderate'))
......
from wiki.conf import settings
# Article settings.
def can_assign(article, user):
return not user.is_anonymous() and settings.CAN_ASSIGN(article, user)
def can_assign_owner(article, user):
return not user.is_anonymous() and settings.CAN_ASSIGN_OWNER(article, user)
def can_change_permissions(article, user):
return not user.is_anonymous() and settings.CAN_CHANGE_PERMISSIONS(article, user)
def can_delete(article, user):
return not user.is_anonymous() and settings.CAN_DELETE(article, user)
def can_moderate(article, user):
return not user.is_anonymous() and settings.CAN_MODERATE(article, user)
def can_admin(article, user):
return not user.is_anonymous() and settings.CAN_ADMIN(article, user)
...@@ -20,7 +20,17 @@ def json_view(func): ...@@ -20,7 +20,17 @@ def json_view(func):
return response return response
return wrap return wrap
def get_article(func=None, can_read=True, can_write=False, deleted_contents=False, not_locked=False): def response_forbidden(request, article, urlpath):
if request.user.is_anonymous():
return redirect(django_settings.LOGIN_URL)
else:
c = RequestContext(request, {'article': article,
'urlpath' : urlpath})
return HttpResponseForbidden(render_to_string("wiki/permission_denied.html", context_instance=c))
def get_article(func=None, can_read=True, can_write=False,
deleted_contents=False, not_locked=False,
can_delete=False, can_moderate=False):
"""View decorator for processing standard url keyword args: Intercepts the """View decorator for processing standard url keyword args: Intercepts the
keyword args path or article_id and looks up an article, calling the decorated keyword args path or article_id and looks up an article, calling the decorated
func with this ID. func with this ID.
...@@ -88,20 +98,6 @@ def get_article(func=None, can_read=True, can_write=False, deleted_contents=Fals ...@@ -88,20 +98,6 @@ def get_article(func=None, can_read=True, can_write=False, deleted_contents=Fals
else: else:
raise TypeError('You should specify either article_id or path') raise TypeError('You should specify either article_id or path')
if can_read and not article.can_read(user=request.user):
if request.user.is_anonymous():
return redirect(django_settings.LOGIN_URL)
else:
c = RequestContext(request, {'urlpath' : urlpath})
return HttpResponseForbidden(render_to_string("wiki/permission_denied.html", context_instance=c))
if can_write and not article.can_write(user=request.user):
if request.user.is_anonymous():
return redirect(django_settings.LOGIN_URL)
else:
c = RequestContext(request, {'urlpath' : urlpath})
return HttpResponseForbidden(render_to_string("wiki/permission_denied.html", context_instance=c))
# If the article has been deleted, show a special page. # If the article has been deleted, show a special page.
if not deleted_contents and article.current_revision and article.current_revision.deleted: if not deleted_contents and article.current_revision and article.current_revision.deleted:
if urlpath: if urlpath:
...@@ -109,11 +105,20 @@ def get_article(func=None, can_read=True, can_write=False, deleted_contents=Fals ...@@ -109,11 +105,20 @@ def get_article(func=None, can_read=True, can_write=False, deleted_contents=Fals
else: else:
return redirect('wiki:deleted', article_id=article.id) return redirect('wiki:deleted', article_id=article.id)
# The article is locked and this request is invalid because the view specified
# not to be requested for locked contents
if article.current_revision.locked and not_locked: if article.current_revision.locked and not_locked:
c = RequestContext(request, {'urlpath' : urlpath}) return response_forbidden(request, article, urlpath)
return HttpResponseForbidden(render_to_string("wiki/permission_denied.html", context_instance=c))
if can_read and not article.can_read(user=request.user):
return response_forbidden(request, article, urlpath)
if can_write and not article.can_write(user=request.user):
return response_forbidden(request, article, urlpath)
if can_delete and not article.can_delete(request.user):
return response_forbidden(request, article, urlpath)
if can_moderate and not article.can_moderate(request.user):
return response_forbidden(request, article, urlpath)
kwargs['urlpath'] = urlpath kwargs['urlpath'] = urlpath
...@@ -124,5 +129,6 @@ def get_article(func=None, can_read=True, can_write=False, deleted_contents=Fals ...@@ -124,5 +129,6 @@ def get_article(func=None, can_read=True, can_write=False, deleted_contents=Fals
else: else:
return lambda func: get_article(func, can_read=can_read, can_write=can_write, return lambda func: get_article(func, can_read=can_read, can_write=can_write,
deleted_contents=deleted_contents, deleted_contents=deleted_contents,
not_locked=not_locked) not_locked=not_locked,can_delete=can_delete,
can_moderate=can_moderate)
...@@ -14,6 +14,7 @@ from wiki.core.diff import simple_merge ...@@ -14,6 +14,7 @@ from wiki.core.diff import simple_merge
from django.forms.widgets import HiddenInput from django.forms.widgets import HiddenInput
from wiki.core.plugins.base import PluginSettingsFormMixin from wiki.core.plugins.base import PluginSettingsFormMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from wiki.core import permissions
class SpamProtectionMixin(): class SpamProtectionMixin():
...@@ -24,7 +25,10 @@ class SpamProtectionMixin(): ...@@ -24,7 +25,10 @@ class SpamProtectionMixin():
current_revision can be any object inheriting from models.BaseRevisionMixin current_revision can be any object inheriting from models.BaseRevisionMixin
""" """
ipaddress = request.META.get('REMOTE_ADDR', None) ipaddress = request.META.get('REMOTE_ADDR', None)
if not ipaddress == "127.0.0.1":
raise forms.ValidationError(_('Only localhost... muahahaha'))
# TODO: Finish this stuff and integrate it in forms....
class CreateRootForm(forms.Form): class CreateRootForm(forms.Form):
...@@ -269,25 +273,25 @@ class PermissionsForm(PluginSettingsFormMixin, forms.ModelForm): ...@@ -269,25 +273,25 @@ class PermissionsForm(PluginSettingsFormMixin, forms.ModelForm):
kwargs['instance'] = article kwargs['instance'] = article
kwargs['initial'] = {'locked': article.current_revision.locked} kwargs['initial'] = {'locked': article.current_revision.locked}
super(PermissionsForm, self).__init__(*args, **kwargs) super(PermissionsForm, self).__init__(*args, **kwargs)
self.can_change_groups = True
self.can_change_groups = False
self.can_assign = False self.can_assign = False
if request.user.has_perm("wiki.assign"):
if permissions.can_assign(article, request.user):
self.can_assign = True self.can_assign = True
self.fields['group'].queryset = models.Group.objects.all() self.fields['group'].queryset = models.Group.objects.all()
elif permissions.can_assign_owner(article, request.user):
self.fields['group'].queryset = models.Group.objects.filter(user=request.user)
self.can_change_groups = True
else: else:
self.fields['group'].widget = forms.HiddenInput()
self.fields['group_read'].widget = forms.HiddenInput()
self.fields['group_write'].widget = forms.HiddenInput()
if not self.can_assign:
self.fields['owner_username'].widget = forms.HiddenInput() self.fields['owner_username'].widget = forms.HiddenInput()
self.fields['recursive'].widget = forms.HiddenInput() self.fields['recursive'].widget = forms.HiddenInput()
self.fields['locked'].widget = forms.HiddenInput() self.fields['locked'].widget = forms.HiddenInput()
groups = models.Group.objects.filter(user=request.user)
self.fields['group'].queryset = groups
# Sanity: If somehow the article belongs to a group that the
# owner is not a member of, don't let the owner make any decisions
# for group permissions.
if article.group and not request.user in article.group.user_set.all():
self.can_change_groups = False
self.fields['group'].widget = forms.HiddenInput()
self.fields['group_read'].widget = forms.HiddenInput()
self.fields['group_write'].widget = forms.HiddenInput()
self.fields['owner_username'].initial = article.owner.username if article.owner else "" self.fields['owner_username'].initial = article.owner.username if article.owner else ""
......
...@@ -103,7 +103,7 @@ class ArticleManager(models.Manager): ...@@ -103,7 +103,7 @@ class ArticleManager(models.Manager):
class ArticleFkManager(models.Manager): class ArticleFkManager(models.Manager):
def get_empty_query_set(self): def get_empty_query_set(self):
return ArticleFkEmptyQuerySet() return ArticleFkEmptyQuerySet(model=self.model)
def get_query_set(self): def get_query_set(self):
return ArticleFkQuerySet(self.model, using=self._db) return ArticleFkQuerySet(self.model, using=self._db)
def active(self): def active(self):
......
...@@ -7,7 +7,7 @@ from django.utils.safestring import mark_safe ...@@ -7,7 +7,7 @@ from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from wiki.conf import settings from wiki.conf import settings
from wiki.core import article_markdown from wiki.core import article_markdown, permissions
from wiki.core.plugins import registry as plugin_registry from wiki.core.plugins import registry as plugin_registry
from wiki import managers from wiki import managers
from mptt.models import MPTTModel from mptt.models import MPTTModel
...@@ -39,36 +39,57 @@ class Article(models.Model): ...@@ -39,36 +39,57 @@ class Article(models.Model):
other_read = models.BooleanField(default=True, verbose_name=_(u'others read access')) other_read = models.BooleanField(default=True, verbose_name=_(u'others read access'))
other_write = models.BooleanField(default=True, verbose_name=_(u'others write access')) other_write = models.BooleanField(default=True, verbose_name=_(u'others write access'))
def can_read(self, user=None, group=None): # TODO: Do not use kwargs, it can lead to dangerous situations with bad
is_other = (user and not user.is_anonymous() ) or settings.ANONYMOUS # permission checking patterns. Also, since there are no other keywords,
if is_other and self.other_read: # it doesn't make much sense.
def can_read(self, user=None):
# Deny reading access to deleted articles if user has no delete access
if self.current_revision and self.current_revision.deleted and not self.can_delete(user):
return False
# Check access for other users...
if user.is_anonymous() and not settings.ANONYMOUS:
return False
elif self.other_read:
return True return True
elif user.is_anonymous():
return False
if user == self.owner: if user == self.owner:
return True return True
if self.group_read: if self.group_read:
if self.group and group == self.group: if self.group and user.groups.filter(id=self.group.id):
return True
if self.group and user and user.groups.filter(id=self.group.id):
return True return True
if user and user.has_perm('wiki_moderator'): if self.can_moderate(user):
return True return True
return False return False
def can_write(self, user=None, group=None): def can_write(self, user=None):
is_other = (user and not user.is_anonymous() ) or settings.ANONYMOUS_WRITE # Deny writing access to deleted articles if user has no delete access
if is_other and self.other_write: if self.current_revision and self.current_revision.deleted and not self.can_delete(user):
return False
# Check access for other users...
if user.is_anonymous() and not settings.ANONYMOUS_WRITE:
return False
elif self.other_write:
return True return True
elif user.is_anonymous():
return False
if user == self.owner: if user == self.owner:
return True return True
if self.group_write: if self.group_write:
if self.group and group == self.group:
return True
if self.group and user and user.groups.filter(id=self.group.id): if self.group and user and user.groups.filter(id=self.group.id):
return True return True
if user and user.has_perm('wiki_moderator'): if self.can_moderate(user):
return True return True
return False return False
def can_delete(self, user):
return permissions.can_delete(self, user)
def can_moderate(self, user):
return permissions.can_moderate(self, user)
def can_assign(self, user):
return permissions.can_assign(self, user)
def descendant_objects(self): def descendant_objects(self):
"""NB! This generator is expensive, so use it with care!!""" """NB! This generator is expensive, so use it with care!!"""
for obj in self.articleforobject_set.filter(is_mptt=True): for obj in self.articleforobject_set.filter(is_mptt=True):
...@@ -148,7 +169,7 @@ class Article(models.Model): ...@@ -148,7 +169,7 @@ class Article(models.Model):
class Meta: class Meta:
app_label = settings.APP_LABEL app_label = settings.APP_LABEL
permissions = ( permissions = (
("moderator", "Can edit all articles and lock/unlock/restore"), ("moderate", "Can edit all articles and lock/unlock/restore"),
("assign", "Can change ownership of any article"), ("assign", "Can change ownership of any article"),
("grant", "Can assign permissions to other users"), ("grant", "Can assign permissions to other users"),
) )
......
...@@ -42,6 +42,16 @@ class ArticlePlugin(models.Model): ...@@ -42,6 +42,16 @@ class ArticlePlugin(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
# Permission methods - you should override these, if they don't fit your logic.
def can_read(self, **kwargs):
return self.article.can_read(**kwargs)
def can_write(self, **kwargs):
return self.article.can_write(**kwargs)
def can_delete(self, user):
return self.article.can_delete(user)
def can_moderate(self, user):
return self.article.can_moderate(user)
def purge(self): def purge(self):
"""Remove related contents completely, ie. media files.""" """Remove related contents completely, ie. media files."""
pass pass
...@@ -52,8 +62,15 @@ class ArticlePlugin(models.Model): ...@@ -52,8 +62,15 @@ class ArticlePlugin(models.Model):
class ReusablePlugin(ArticlePlugin): class ReusablePlugin(ArticlePlugin):
"""Extend from this model if you have a plugin that may be related to many """Extend from this model if you have a plugin that may be related to many
articles. Please note that the ArticlePlugin.article ForeignKey STAYS! This articles. Please note that the ArticlePlugin.article ForeignKey STAYS! This
is in order to maintain an explicit set of permissions. If you do not like this, is in order to maintain an explicit set of permissions.
you can override can_read and can_write."""
In general, it's quite complicated to maintain plugin content that's shared
between different articles. The best way to go is to avoid this. For inspiration,
look at wiki.plugins.attachments
You might have to override the permission methods (can_read, can_write etc.)
if you have certain needs for logic in your reusable plugin.
"""
# The article on which the plugin was originally created. # The article on which the plugin was originally created.
# Used to apply permissions. # Used to apply permissions.
ArticlePlugin.article.on_delete=models.SET_NULL ArticlePlugin.article.on_delete=models.SET_NULL
...@@ -64,17 +81,17 @@ class ReusablePlugin(ArticlePlugin): ...@@ -64,17 +81,17 @@ class ReusablePlugin(ArticlePlugin):
articles = models.ManyToManyField(Article, related_name='shared_plugins_set') articles = models.ManyToManyField(Article, related_name='shared_plugins_set')
# Permission methods - you may override these, if they don't fit your logic. # Since the article relation may be None, we have to check for this
# before handling permissions....
def can_read(self, **kwargs): def can_read(self, **kwargs):
if self.article: return self.article.can_read(**kwargs) if self.article else False
return self.article.can_read(**kwargs)
return False
def can_write(self, **kwargs): def can_write(self, **kwargs):
if self.article: return self.article.can_write(**kwargs) if self.article else False
return self.article.can_write(**kwargs) def can_delete(self, user):
return False return self.article.can_delete(user) if self.article else False
def can_moderate(self, user):
return self.article.can_moderate(user) if self.article else False
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
# Automatically make the original article the first one in the added set # Automatically make the original article the first one in the added set
...@@ -151,12 +168,6 @@ class RevisionPlugin(ArticlePlugin): ...@@ -151,12 +168,6 @@ class RevisionPlugin(ArticlePlugin):
'If you need to do a roll-back, simply change the value of this field.'), 'If you need to do a roll-back, simply change the value of this field.'),
) )
# Permissions... overwrite if necessary
def can_read(self, **kwargs):
return self.article.can_read(**kwargs)
def can_write(self, **kwargs):
return self.article.can_write(**kwargs)
def add_revision(self, new_revision, save=True): def add_revision(self, new_revision, save=True):
""" """
Sets the properties of a revision and ensures its the current Sets the properties of a revision and ensures its the current
......
...@@ -26,7 +26,7 @@ class AttachmentPreprocessor(markdown.preprocessors.Preprocessor): ...@@ -26,7 +26,7 @@ class AttachmentPreprocessor(markdown.preprocessors.Preprocessor):
attachment_id = m.group('id').strip() attachment_id = m.group('id').strip()
try: try:
attachment = models.Attachment.objects.get(articles=self.markdown.article, attachment = models.Attachment.objects.get(articles=self.markdown.article,
id=attachment_id) id=attachment_id, current_revision__deleted=False)
url = reverse('wiki:attachments_download', kwargs={'article_id': self.markdown.article.id, url = reverse('wiki:attachments_download', kwargs={'article_id': self.markdown.article.id,
'attachment_id':attachment.id,}) 'attachment_id':attachment.id,})
line = line.replace(m.group(1), u"""<span class="attachment"><a href="%s" title="%s">%s</a>""" % line = line.replace(m.group(1), u"""<span class="attachment"><a href="%s" title="%s">%s</a>""" %
......
...@@ -28,7 +28,10 @@ class Attachment(ReusablePlugin): ...@@ -28,7 +28,10 @@ class Attachment(ReusablePlugin):
if not settings.ANONYMOUS and (not user or user.is_anonymous()): if not settings.ANONYMOUS and (not user or user.is_anonymous()):
return False return False
return ReusablePlugin.can_write(self, **kwargs) return ReusablePlugin.can_write(self, **kwargs)
def can_delete(self, user):
return self.can_write(user=user)
class Meta: class Meta:
verbose_name = _(u'attachment') verbose_name = _(u'attachment')
verbose_name_plural = _(u'attachments') verbose_name_plural = _(u'attachments')
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
{% if revision.deleted %}<span class="badge badge-important">{% trans "deleted" %}</span>{% endif %} {% if revision.deleted %}<span class="badge badge-important">{% trans "deleted" %}</span>{% endif %}
</td> </td>
<td> <td>
{% if revision.user %}{{ revision.user }}{% else %}{% if user|is_moderator %}{{ revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %} {% include "wiki/includes/revision_info.html" with revision=attachment.current_revision hidedate=1 hidenumber=1 %}
</td> </td>
<td>{{ revision.description|default:_("<em>No description</em>")|safe }}</td> <td>{{ revision.description|default:_("<em>No description</em>")|safe }}</td>
<td>{{ revision.get_filename }}</td> <td>{{ revision.get_filename }}</td>
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
{% block wiki_contents_tab %} {% block wiki_contents_tab %}
<div class="row-fluid"> <div class="row-fluid">
<div class="span7"> <div class="span8">
<p class="lead">{% trans "The following files are available for this article. Copy the markdown tag to directly refer to a file from the article text." %}</p> <p class="lead">{% trans "The following files are available for this article. Copy the markdown tag to directly refer to a file from the article text." %}</p>
{% for attachment in attachments %} {% for attachment in attachments %}
<table class="table table-bordered table-striped" style="width: 100%;"> <table class="table table-bordered table-striped" style="width: 100%;">
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
<th>{% trans "Markdown tag" %}</th> <th>{% trans "Markdown tag" %}</th>
<th>{% trans "Uploaded by" %}</th> <th>{% trans "Uploaded by" %}</th>
<th>{% trans "Size" %}</th> <th>{% trans "Size" %}</th>
<td style="text-align: right;" rowspan="2"> <td style="text-align: right; white-space: nowrap;" rowspan="2">
{% if attachment|can_write:user %} {% if attachment|can_write:user %}
<p> <p>
{% if not attachment.current_revision.deleted %} {% if not attachment.current_revision.deleted %}
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
<tr> <tr>
<td><code>[attachment:{{ attachment.id }}]</code></td> <td><code>[attachment:{{ attachment.id }}]</code></td>
<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 %} {% include "wiki/includes/revision_info.html" with revision=attachment.current_revision hidedate=1 hidenumber=1 %}
</td> </td>
<td>{{ attachment.current_revision.get_size|filesizeformat }}</td> <td>{{ attachment.current_revision.get_size|filesizeformat }}</td>
</tr> </tr>
...@@ -69,7 +69,7 @@ ...@@ -69,7 +69,7 @@
{% endfor %} {% endfor %}
</div> </div>
{% if article|can_write:user %} {% if article|can_write:user %}
<div class="span5" style="min-width: 330px;"> <div class="span4" style="min-width: 330px;">
<div class="accordion" id="accordion_upload"> <div class="accordion" id="accordion_upload">
<div class="accordion-group"> <div class="accordion-group">
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
{% if attachment.current_revision.deleted %}<span class="badge badge-important">{% trans "deleted" %}</span>{% endif %} {% if attachment.current_revision.deleted %}<span class="badge badge-important">{% trans "deleted" %}</span>{% endif %}
</td> </td>
<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 %} {% include "wiki/includes/revision_info.html" with revision=attachment.current_revision hidedate=1 hidenumber=1 %}
</td> </td>
<td>{{ attachment.current_revision.file.size|filesizeformat }}</td> <td>{{ attachment.current_revision.file.size|filesizeformat }}</td>
<td style="text-align: right"> <td style="text-align: right">
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.conf import settings as django_settings
from django.contrib import messages from django.contrib import messages
from django.db import transaction from django.db import transaction
from django.db.models import Q from django.db.models import Q
...@@ -11,9 +10,8 @@ from django.views.generic.base import TemplateView, View ...@@ -11,9 +10,8 @@ from django.views.generic.base import TemplateView, View
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django.views.generic.list import ListView from django.views.generic.list import ListView
from wiki.conf import settings as wiki_settings
from wiki.core.http import send_file from wiki.core.http import send_file
from wiki.decorators import get_article from wiki.decorators import get_article, response_forbidden
from wiki.plugins.attachments import models, settings, forms from wiki.plugins.attachments import models, settings, forms
from wiki.views.mixins import ArticleMixin from wiki.views.mixins import ArticleMixin
...@@ -25,7 +23,7 @@ class AttachmentView(ArticleMixin, FormView): ...@@ -25,7 +23,7 @@ class AttachmentView(ArticleMixin, FormView):
@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):
if request.user.has_perm('wiki.moderator'): if article.can_moderate(request.user):
self.attachments = models.Attachment.objects.filter(articles=article).order_by('current_revision__deleted', 'original_filename') self.attachments = models.Attachment.objects.filter(articles=article).order_by('current_revision__deleted', 'original_filename')
else: else:
self.attachments = models.Attachment.objects.active().filter(articles=article) self.attachments = models.Attachment.objects.active().filter(articles=article)
...@@ -36,8 +34,9 @@ class AttachmentView(ArticleMixin, FormView): ...@@ -36,8 +34,9 @@ class AttachmentView(ArticleMixin, FormView):
# WARNING! The below decorator silences other exceptions that may occur! # WARNING! The below decorator silences other exceptions that may occur!
@transaction.commit_manually @transaction.commit_manually
def form_valid(self, form): def form_valid(self, form):
if self.request.user.is_anonymous() and not settings.ANONYMOUS: if (self.request.user.is_anonymous() and not settings.ANONYMOUS or
return redirect(django_settings.LOGIN_URL) not self.article.can_write(self.request.user)):
return response_forbidden(self.request, self.article, self.urlpath)
try: try:
attachment_revision = form.save(commit=False) attachment_revision = form.save(commit=False)
...@@ -74,7 +73,7 @@ class AttachmentHistoryView(ArticleMixin, TemplateView): ...@@ -74,7 +73,7 @@ class AttachmentHistoryView(ArticleMixin, TemplateView):
@method_decorator(get_article(can_read=True)) @method_decorator(get_article(can_read=True))
def dispatch(self, request, article, attachment_id, *args, **kwargs): def dispatch(self, request, article, attachment_id, *args, **kwargs):
if request.user.has_perm('wiki.moderator'): if article.can_moderate(request.user):
self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article) self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article)
else: else:
self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article) self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article)
...@@ -92,11 +91,14 @@ class AttachmentReplaceView(ArticleMixin, FormView): ...@@ -92,11 +91,14 @@ class AttachmentReplaceView(ArticleMixin, FormView):
form_class = forms.AttachmentForm form_class = forms.AttachmentForm
template_name="wiki/plugins/attachments/replace.html" template_name="wiki/plugins/attachments/replace.html"
@method_decorator(get_article(can_read=True)) @method_decorator(get_article(can_write=True))
def dispatch(self, request, article, attachment_id, *args, **kwargs): def dispatch(self, request, article, attachment_id, *args, **kwargs):
self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article) if self.request.user.is_anonymous() and not settings.ANONYMOUS:
if not self.attachment.can_write(user=request.user): return response_forbidden(request, article, kwargs.get('urlpath', None))
return redirect(wiki_settings.LOGIN_URL) if article.can_moderate(request.user):
self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article)
else:
self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article)
return super(AttachmentReplaceView, self).dispatch(request, article, *args, **kwargs) return super(AttachmentReplaceView, self).dispatch(request, article, *args, **kwargs)
def form_valid(self, form): def form_valid(self, form):
...@@ -138,7 +140,10 @@ class AttachmentDownloadView(ArticleMixin, View): ...@@ -138,7 +140,10 @@ class AttachmentDownloadView(ArticleMixin, View):
@method_decorator(get_article(can_read=True)) @method_decorator(get_article(can_read=True))
def dispatch(self, request, article, attachment_id, *args, **kwargs): def dispatch(self, request, article, attachment_id, *args, **kwargs):
self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article) if article.can_moderate(request.user):
self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article)
else:
self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article)
revision_id = kwargs.get('revision_id', None) revision_id = kwargs.get('revision_id', None)
if revision_id: if revision_id:
self.revision = get_object_or_404(models.AttachmentRevision, id=revision_id, attachment__articles=article) self.revision = get_object_or_404(models.AttachmentRevision, id=revision_id, attachment__articles=article)
...@@ -162,7 +167,7 @@ class AttachmentChangeRevisionView(ArticleMixin, View): ...@@ -162,7 +167,7 @@ class AttachmentChangeRevisionView(ArticleMixin, View):
@method_decorator(get_article(can_write=True)) @method_decorator(get_article(can_write=True))
def dispatch(self, request, article, attachment_id, revision_id, *args, **kwargs): def dispatch(self, request, article, attachment_id, revision_id, *args, **kwargs):
if request.user.has_perm('wiki.moderator'): if article.can_moderate(request.user):
self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article) self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article)
else: else:
self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article) self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article)
...@@ -188,8 +193,9 @@ class AttachmentAddView(ArticleMixin, View): ...@@ -188,8 +193,9 @@ class AttachmentAddView(ArticleMixin, View):
return super(AttachmentAddView, self).dispatch(request, article, *args, **kwargs) return super(AttachmentAddView, self).dispatch(request, article, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.attachment.articles.add(self.article) if self.attachment.articles.filter(id=self.article.id):
self.attachment.save() self.attachment.articles.add(self.article)
self.attachment.save()
messages.success(self.request, _(u'Added a reference to "%(att)s" from "%(art)s".') % messages.success(self.request, _(u'Added a reference to "%(att)s" from "%(art)s".') %
{'att': self.attachment.original_filename, {'att': self.attachment.original_filename,
'art': self.article.current_revision.title}) 'art': self.article.current_revision.title})
...@@ -203,10 +209,9 @@ class AttachmentDeleteView(ArticleMixin, FormView): ...@@ -203,10 +209,9 @@ class AttachmentDeleteView(ArticleMixin, FormView):
@method_decorator(get_article(can_write=True)) @method_decorator(get_article(can_write=True))
def dispatch(self, request, article, attachment_id, *args, **kwargs): def dispatch(self, request, article, attachment_id, *args, **kwargs):
if request.user.has_perm("wiki.moderator"): self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article)
self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article) if not self.attachment.can_delete(request.user):
else: return response_forbidden(request, article, kwargs.get('urlpath', None))
self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article)
return super(AttachmentDeleteView, self).dispatch(request, article, *args, **kwargs) return super(AttachmentDeleteView, self).dispatch(request, article, *args, **kwargs)
def form_valid(self, form): def form_valid(self, form):
...@@ -254,7 +259,6 @@ class AttachmentSearchView(ArticleMixin, ListView): ...@@ -254,7 +259,6 @@ class AttachmentSearchView(ArticleMixin, ListView):
qs = qs.filter(Q(original_filename__contains=self.query) | qs = qs.filter(Q(original_filename__contains=self.query) |
Q(current_revision__description__contains=self.query) | Q(current_revision__description__contains=self.query) |
Q(article__current_revision__title__contains=self.query)) Q(article__current_revision__title__contains=self.query))
qs = qs.exclude(articles=self.article)
return qs return qs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
......
...@@ -33,6 +33,9 @@ class Image(RevisionPlugin): ...@@ -33,6 +33,9 @@ class Image(RevisionPlugin):
return False return False
return RevisionPlugin.can_write(self, **kwargs) return RevisionPlugin.can_write(self, **kwargs)
def can_delete(self, user):
return self.can_write(user=user)
class Meta: class Meta:
verbose_name = _(u'image') verbose_name = _(u'image')
verbose_name_plural = _(u'images') verbose_name_plural = _(u'images')
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
{% trans "Remove image" %} {% trans "Remove image" %}
</a> </a>
{% endif %} {% endif %}
{% if user|is_moderator %} {% if article|can_moderate:user %}
<br /> <br />
<a href="{% url 'wiki:images_purge' path=urlpath.path article_id=article.id image_id=image.id %}"> <a href="{% url 'wiki:images_purge' path=urlpath.path article_id=article.id image_id=image.id %}">
<span class="icon-trash"></span> <span class="icon-trash"></span>
......
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
...@@ -27,8 +26,8 @@ class ImageView(ArticleMixin, ListView): ...@@ -27,8 +26,8 @@ class ImageView(ArticleMixin, ListView):
return super(ImageView, self).dispatch(request, article, *args, **kwargs) return super(ImageView, self).dispatch(request, article, *args, **kwargs)
def get_queryset(self): def get_queryset(self):
if (self.request.user.has_perm('wiki.moderator') or if (self.article.can_moderate(self.request.user) or
self.article.owner == self.request.user): self.article.can_delete(self.request.user)):
images = models.Image.objects.filter(article=self.article) images = models.Image.objects.filter(article=self.article)
else: else:
images = models.Image.objects.filter(article=self.article, images = models.Image.objects.filter(article=self.article,
...@@ -76,8 +75,7 @@ class PurgeView(ArticleMixin, FormView): ...@@ -76,8 +75,7 @@ class PurgeView(ArticleMixin, FormView):
permanent = False permanent = False
form_class = forms.PurgeForm form_class = forms.PurgeForm
@method_decorator(permission_required('wiki.moderator', login_url=wiki_settings.LOGIN_URL)) @method_decorator(get_article(can_write=True, can_moderate=True))
@method_decorator(get_article(can_write=True))
def dispatch(self, request, article, *args, **kwargs): def dispatch(self, request, article, *args, **kwargs):
self.image = get_object_or_404(models.Image, article=article, self.image = get_object_or_404(models.Image, article=article,
id=kwargs.get('image_id', None)) id=kwargs.get('image_id', None))
......
...@@ -4,11 +4,13 @@ from django.utils.decorators import method_decorator ...@@ -4,11 +4,13 @@ from django.utils.decorators import method_decorator
class QueryUrlPath(View): class QueryUrlPath(View):
# TODO: get_article does not actually support JSON responses
@method_decorator(json_view) @method_decorator(json_view)
@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):
max_num = kwargs.pop('max_num', 20) max_num = kwargs.pop('max_num', 20)
# TODO: Move this import when # TODO: Move this import when circularity issue is resolved
# https://github.com/benjaoming/django-wiki/issues/23
from wiki import models from wiki import models
query = request.GET.get('query', None) query = request.GET.get('query', None)
......
* Create signal for new articles and automatically subscribe users creating the article to notifications?
...@@ -22,11 +22,13 @@ ...@@ -22,11 +22,13 @@
#id_title {font-size: 20px; height: 30px; padding: 6px; width: 98%;} #id_title {font-size: 20px; height: 30px; padding: 6px; width: 98%;}
#id_summary {width: 98%; padding: 6px;} #id_summary {width: 98%; padding: 6px;}
.table { font-size: 90%;}
#article_edit_form label {max-width: 100px;} #article_edit_form label {max-width: 100px;}
#article_edit_form .controls {margin-left: 120px;} #article_edit_form .controls {margin-left: 120px;}
.form-horizontal label { font-size: 16px; font-weight: normal; color: #777;} .form-horizontal label { font-size: 16px; font-weight: normal; color: #777;}
.settings-form label {min-width: 250px; font-size: inherit; font-weight: normal;} .settings-form label {min-width: 250px; font-size: inherit; font-weight: normal;}
.settings-form .controls {margin-left: 270px;} .settings-form .controls {margin-left: 270px;}
.settings-form select {} .settings-form select {}
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<div class="row-fluid"> <div class="row-fluid">
{% if not article.current_revision.locked or user|is_moderator %} {% if not article.current_revision.locked or article|can_delete:user %}
<div class="span6"> <div class="span6">
<div class="well"> <div class="well">
<h2>{% trans "Restore" %}</h2> <h2>{% trans "Restore" %}</h2>
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
</div> </div>
{% endif %} {% endif %}
{% if user|is_moderator %} {% if article|can_moderate:user %}
<div class="span6"> <div class="span6">
<div class="well"> <div class="well">
<h2>{% trans "Purge deletion" %}</h2> <h2>{% trans "Purge deletion" %}</h2>
......
...@@ -34,12 +34,14 @@ ...@@ -34,12 +34,14 @@
<a class="btn btn-large btn-primary" onclick="document.getElementById('article_edit_form').target=''; document.getElementById('article_edit_form').action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'; $('#article_edit_form').submit();" href="#"> <a class="btn btn-large btn-primary" onclick="document.getElementById('article_edit_form').target=''; document.getElementById('article_edit_form').action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'; $('#article_edit_form').submit();" href="#">
<span class="icon-ok"></span> <span class="icon-ok"></span>
{% trans "Save changes" %} {% trans "Save changes" %}
</button> </a>
{% if article|can_delete:user %}
<a href="{% url 'wiki:delete' path=urlpath.path article_id=article.id %}" class="pull-right btn"> <a href="{% url 'wiki:delete' path=urlpath.path article_id=article.id %}" class="pull-right btn">
<span class="icon-trash"></span> <span class="icon-trash"></span>
{% trans "Delete article" %} {% trans "Delete article" %}
</a> </a>
{% endif %}
</div> </div>
<div class="modal hide fade" id="previewModal" style="width: 80%; min-height: 500px; margin-left: -40%;"> <div class="modal hide fade" id="previewModal" style="width: 80%; min-height: 500px; margin-left: -40%;">
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
{% load wiki_tags i18n %} {% load wiki_tags i18n %}
{{ revision.created }} (#{{ revision.revision_number }}) {% trans "by" %} {% if revision.user %}{{ revision.user }}{% else %}{% if user|is_moderator %}{{ revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %} {% if not hidedate %}{{ revision.created }}{% endif %} {% if not hidenumber %}(#{{ revision.revision_number }}) {% trans "by" %}{% endif %} {% if revision.user %}{{ revision.user }}{% else %}{% if article|can_moderate:user %}{{ revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
{% if revision == current_revision %} {% if revision == current_revision %}
<strong>*</strong> <strong>*</strong>
{% endif %} {% endif %}
......
...@@ -63,9 +63,20 @@ def can_read(obj, user): ...@@ -63,9 +63,20 @@ def can_read(obj, user):
@register.filter @register.filter
def can_write(obj, user): def can_write(obj, user):
"""Articles and plugins have a can_write method...""" """Articles and plugins have a can_write method..."""
return obj.can_write(**{'user': user}) return obj.can_write(user=user)
@register.filter
def can_delete(obj, user):
"""Articles and plugins have a can_delete method..."""
return obj.can_delete(user)
@register.filter
def can_moderate(obj, user):
"""Articles and plugins have a can_moderate method..."""
return obj.can_moderate(user)
@register.filter @register.filter
def is_moderator(user): def is_moderator(user):
"""Tells if a user is a moderator""" """Tells if a user is a moderator"""
return user.has_perm('wiki.moderator') return user.has_perm('wiki.moderate')
...@@ -21,8 +21,7 @@ from django.core.urlresolvers import reverse ...@@ -21,8 +21,7 @@ from django.core.urlresolvers import reverse
from django.db import transaction from django.db import transaction
from wiki.core.exceptions import NoRootURL from wiki.core.exceptions import NoRootURL
from django_notify.decorators import disable_notify from django_notify.decorators import disable_notify
from django.http import HttpResponseForbidden from wiki.core import permissions
from django.template.loader import render_to_string
class ArticleView(ArticleMixin, TemplateView): class ArticleView(ArticleMixin, TemplateView):
...@@ -83,14 +82,13 @@ class Create(FormView, ArticleMixin): ...@@ -83,14 +82,13 @@ class Create(FormView, ArticleMixin):
'other_read': self.article.other_read, 'other_read': self.article.other_read,
'other_write': self.article.other_write, 'other_write': self.article.other_write,
}) })
# TODO: Subscribe user to new article and send notifications that user was subscribed.
messages.success(self.request, _(u"New article '%s' created.") % self.newpath.article.current_revision.title) messages.success(self.request, _(u"New article '%s' created.") % self.newpath.article.current_revision.title)
transaction.commit() transaction.commit()
# TODO: Handle individual exceptions better and give good feedback. # TODO: Handle individual exceptions better and give good feedback.
except Exception, e: except Exception, e:
transaction.rollback() transaction.rollback()
if self.request.user.has_perm('wiki.moderator'): if self.request.user.is_superuser():
messages.error(self.request, _(u"There was an error creating this article: %s") % str(e)) messages.error(self.request, _(u"There was an error creating this article: %s") % str(e))
else: else:
messages.error(self.request, _(u"There was an error creating this article.")) messages.error(self.request, _(u"There was an error creating this article."))
...@@ -117,7 +115,7 @@ class Delete(FormView, ArticleMixin): ...@@ -117,7 +115,7 @@ class Delete(FormView, ArticleMixin):
form_class = forms.DeleteForm form_class = forms.DeleteForm
template_name="wiki/delete.html" template_name="wiki/delete.html"
@method_decorator(get_article(can_write=True, not_locked=True)) @method_decorator(get_article(can_write=True, not_locked=True, can_delete=True))
def dispatch(self, request, article, *args, **kwargs): def dispatch(self, request, article, *args, **kwargs):
return self.dispatch1(request, article, *args, **kwargs) return self.dispatch1(request, article, *args, **kwargs)
...@@ -147,7 +145,7 @@ class Delete(FormView, ArticleMixin): ...@@ -147,7 +145,7 @@ class Delete(FormView, ArticleMixin):
def get_form(self, form_class): def get_form(self, form_class):
form = super(Delete, self).get_form(form_class) form = super(Delete, self).get_form(form_class)
if self.request.user.has_perm('wiki.moderator'): if self.article.can_delete(self.request.user):
form.fields['purge'].widget = forms.forms.CheckboxInput() form.fields['purge'].widget = forms.forms.CheckboxInput()
return form return form
...@@ -179,7 +177,8 @@ class Delete(FormView, ArticleMixin): ...@@ -179,7 +177,8 @@ class Delete(FormView, ArticleMixin):
cd = form.cleaned_data cd = form.cleaned_data
cannot_delete_children = False cannot_delete_children = False
if self.children_slice and not self.request.user.has_perm('wiki.moderator'): can_moderate = self.article.can_moderate(self.request.user)
if self.children_slice and not can_moderate:
cannot_delete_children = True cannot_delete_children = True
if self.cannot_delete_root or cannot_delete_children: if self.cannot_delete_root or cannot_delete_children:
...@@ -189,7 +188,7 @@ class Delete(FormView, ArticleMixin): ...@@ -189,7 +188,7 @@ class Delete(FormView, ArticleMixin):
# First, remove children # First, remove children
self.delete_children(purge=cd['purge']) self.delete_children(purge=cd['purge'])
if self.request.user.has_perm('wiki.moderator') and cd['purge']: if can_moderate and cd['purge']:
self.article.delete() self.article.delete()
messages.success(self.request, _(u'This article together with all its contents are now completely gone! Thanks!')) messages.success(self.request, _(u'This article together with all its contents are now completely gone! Thanks!'))
else: else:
...@@ -206,7 +205,7 @@ class Delete(FormView, ArticleMixin): ...@@ -206,7 +205,7 @@ class Delete(FormView, ArticleMixin):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
cannot_delete_children = False cannot_delete_children = False
if self.children_slice and not self.request.user.has_perm('wiki.moderator'): if self.children_slice and not self.article.can_moderate(self.request.user):
cannot_delete_children = True cannot_delete_children = True
kwargs['delete_form'] = kwargs.pop('form', None) kwargs['delete_form'] = kwargs.pop('form', None)
...@@ -336,7 +335,8 @@ class Deleted(Delete): ...@@ -336,7 +335,8 @@ class Deleted(Delete):
# Restore # Restore
if (request.GET.get('restore', False) and if (request.GET.get('restore', False) and
(not article.current_revision.locked or request.user.has_perm('wiki.moderator'))): (not article.current_revision.locked and article.can_delete(request.user)) or
article.can_moderate(request.user)):
self.delete_children(restore=True) self.delete_children(restore=True)
revision = models.ArticleRevision() revision = models.ArticleRevision()
revision.inherit_predecessor(self.article) revision.inherit_predecessor(self.article)
...@@ -409,9 +409,13 @@ class Dir(ListView, ArticleMixin): ...@@ -409,9 +409,13 @@ class Dir(ListView, ArticleMixin):
model = models.URLPath model = models.URLPath
paginate_by = 30 paginate_by = 30
@method_decorator(get_article(can_read=True))
def dispatch(self, request, article, *args, **kwargs):
return super(Dir, self).dispatch(request, article, *args, **kwargs)
def get_queryset(self): def get_queryset(self):
children = self.urlpath.get_children().can_read(self.request.user).select_related_common().order_by('article__current_revision__title') children = self.urlpath.get_children().can_read(self.request.user).select_related_common().order_by('article__current_revision__title')
if not self.request.user.has_perm('wiki.moderator'): if not self.article.can_moderate(self.request.user):
children = children.active() children = children.active()
return children return children
...@@ -431,14 +435,6 @@ class Dir(ListView, ArticleMixin): ...@@ -431,14 +435,6 @@ class Dir(ListView, ArticleMixin):
return kwargs return kwargs
def get_template_names(self):
#WHY IS THIS CALLED???????
return [self.__class__.template_name]
@method_decorator(get_article(can_read=True))
def dispatch(self, request, article, *args, **kwargs):
return super(Dir, self).dispatch(request, article, *args, **kwargs)
class Plugin(View): class Plugin(View):
...@@ -464,8 +460,7 @@ class Settings(ArticleMixin, TemplateView): ...@@ -464,8 +460,7 @@ class Settings(ArticleMixin, TemplateView):
Return all settings forms that can be filled in Return all settings forms that can be filled in
""" """
settings_forms = [F for F in plugin_registry.get_settings_forms()] settings_forms = [F for F in plugin_registry.get_settings_forms()]
if (self.request.user.has_perm('wiki.assign') or if permissions.can_change_permissions(self.article, self.request.user):
self.article.owner == self.request.user):
settings_forms.append(self.permission_form_class) settings_forms.append(self.permission_form_class)
settings_forms.sort(key=lambda form: form.settings_order) settings_forms.sort(key=lambda form: form.settings_order)
for i in range(len(settings_forms)): for i in range(len(settings_forms)):
...@@ -627,12 +622,14 @@ def root_create(request): ...@@ -627,12 +622,14 @@ def root_create(request):
try: try:
root = models.URLPath.root() root = models.URLPath.root()
if not root.article: if not root.article:
# TODO: This is too dangerous... let's say there is no root.article and we end up here,
# then it might cascade to delete a lot of things on an existing installation.... / benjaoming
root.delete() root.delete()
raise NoRootURL raise NoRootURL
return redirect('wiki:get', path=root.path) return redirect('wiki:get', path=root.path)
except NoRootURL: except NoRootURL:
pass pass
if not request.user.has_perm('wiki.add_article'): if not request.user.is_superuser():
return redirect(settings.LOGIN_URL + "?next=" + reverse("wiki:root_create")) return redirect(settings.LOGIN_URL + "?next=" + reverse("wiki:root_create"))
if request.method == 'POST': if request.method == 'POST':
create_form = forms.CreateRootForm(request.POST) create_form = forms.CreateRootForm(request.POST)
......
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