Skip to content
This repository has been archived by the owner on Aug 26, 2022. It is now read-only.

Commit

Permalink
Add jinja filter for has_perm_or_owns for use in templates, clean up …
Browse files Browse the repository at this point in the history
…thread forms, deny edit on locked threads.
  • Loading branch information
Paul Craciunoiu committed Jun 2, 2010
1 parent 061eae3 commit 44b81d8
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 19 deletions.
2 changes: 1 addition & 1 deletion apps/forums/fixtures/posts.json
Expand Up @@ -22,7 +22,7 @@
"is_sticky": false,
"forum": 1,
"created": "2010-05-04 14:09:33",
"is_locked": false,
"is_locked": true,
"creator": 118533,
"replies": 0,
"title": "A thread with a very very long title that should get truncated in the breadcrumbs.",
Expand Down
3 changes: 1 addition & 2 deletions apps/forums/templates/edit_thread.html
Expand Up @@ -21,8 +21,7 @@ <h2>{{ _('Edit thread "{t}"')|f(t=thread.title) }}</h2>
{{ form.title|safe }}
</div>

<div class="form-widget">
<label for="submit">{{ _('Edit') }}</label>
<div class="form-widget submit">
<a href="{{ url('forums.posts', forum.slug, thread.id) }}">{{ _('Cancel') }}</a>
<input type="submit" value="{{ _('Update thread') }}" />
</div>
Expand Down
8 changes: 4 additions & 4 deletions apps/forums/templates/new_thread.html
Expand Up @@ -11,7 +11,7 @@
{% block content %}
<h2>{{ _('Create a new thread') }}</h2>

<form action="{{ url('forums.new_thread', forum_slug=forum.slug) }}" method="post" class="new-thread">
<form action="{{ url('forums.new_thread', forum.slug) }}" method="post" class="new-thread">
{{ csrf() }}
{{ errorlist(form) }}

Expand All @@ -25,9 +25,9 @@ <h2>{{ _('Create a new thread') }}</h2>
</div>
{% endfor %}

<div class="form-widget">
<label for="submit">Post</label>
<input type="submit" value="Post" />
<div class="form-widget submit">
<a href="{{ url('forums.threads', forum.slug) }}">{{ _('Cancel') }}</a>
<input type="submit" value="{{ _('Post') }}" />
</div>
</form>
{% endblock %}
3 changes: 2 additions & 1 deletion apps/forums/templates/posts.html
Expand Up @@ -17,7 +17,8 @@ <h2>{{ thread.title }}</h2>
</div>
{% endif %}
<div class="thread-actions">
{% if has_perm('forums_forum.thread_edit_forum', forum) %}
{% if not thread.is_locked and
has_perm_or_owns('forums_forum.thread_edit_forum', thread, forum) %}
<a href="{{ url('forums.edit_thread', forum_slug=forum.slug, thread_id=thread.id) }}"><img src="{{ MEDIA_URL }}img/forums/edit.png" alt="{{ _('Edit') }}" title="{{ _('Edit') }}"/></a>
{% endif %}
{% if has_perm('forums_forum.thread_delete_forum', forum) %}
Expand Down
32 changes: 23 additions & 9 deletions apps/forums/tests/test_permissions.py
Expand Up @@ -4,9 +4,9 @@
from django.test import TestCase
from django.contrib.auth.models import User

from sumo.helpers import has_perm
from sumo.helpers import has_perm, has_perm_or_owns
from sumo.urlresolvers import reverse
from sumo.utils import has_perm_or_owns
from sumo import utils
from forums.models import Forum, Thread


Expand All @@ -32,6 +32,21 @@ def test_has_perm_thread_edit(self):
self.forum_2)
eq_(allowed, False)

def test_has_perm_or_owns_thread_edit(self):
"""
User in ForumsModerator group can edit thread in forum_1, but not in
forum_2.
"""
me = User.objects.get(pk=118533)
my_t = Thread.objects.filter(creator=me)[0]
other_t = Thread.objects.exclude(creator=me)[0]
self.context['request'].user = me
perm = 'forums_forum.thread_edit_forum'
allowed = has_perm_or_owns(self.context, perm, my_t, self.forum_1)
eq_(allowed, True)
allowed = has_perm_or_owns(self.context, perm, other_t, self.forum_1)
eq_(allowed, False)

def test_has_perm_thread_delete(self):
"""
User in ForumsModerator group can delete thread in forum_1, but not in
Expand Down Expand Up @@ -122,14 +137,13 @@ def test_admin_perm_thread(self):
forum)
eq_(allowed, True)

def test_has_perm_or_owns_sanity(self):
def test_util_has_perm_or_owns_sanity(self):
"""Sanity check for has_perm_or_owns."""
me = User.objects.get(pk=118533)
my_thread = Thread.objects.filter(creator=me)[0]
other_thread = Thread.objects.exclude(creator=me)[0]
allowed = has_perm_or_owns(me, 'forums_forum.thread_edit_forum',
my_thread, self.forum_1)
my_t = Thread.objects.filter(creator=me)[0]
other_t = Thread.objects.exclude(creator=me)[0]
perm = 'forums_forum.thread_edit_forum'
allowed = utils.has_perm_or_owns(me, perm, my_t, self.forum_1)
eq_(allowed, True)
allowed = has_perm_or_owns(me, 'forums_forum.thread_edit_forum',
other_thread, self.forum_1)
allowed = utils.has_perm_or_owns(me, perm, other_t, self.forum_1)
eq_(allowed, False)
8 changes: 8 additions & 0 deletions apps/forums/tests/test_templates.py
Expand Up @@ -105,6 +105,14 @@ def test_edit_thread_403(self):
args=[self.forum.slug, self.thread.id])
eq_(403, response.status_code)

def test_edit_locked_thread_403(self):
"""Editing a locked thread returns 403."""
jsocol = User.objects.get(username='jsocol')
t = self.forum.thread_set.filter(creator=jsocol, is_locked=True)[0]
response = get(self.client, 'forums.edit_thread',
args=[self.forum.slug, t.id])
eq_(403, response.status_code)

def test_delete_thread_403(self):
"""Deleting a thread without permissions returns 403."""
response = get(self.client, 'forums.delete_thread',
Expand Down
8 changes: 7 additions & 1 deletion apps/forums/views.py
@@ -1,6 +1,7 @@
import logging

from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.views.decorators.http import require_POST
Expand Down Expand Up @@ -196,6 +197,9 @@ def edit_thread(request, forum_slug, thread_id):
forum = get_object_or_404(Forum, slug=forum_slug)
thread = get_object_or_404(Thread, pk=thread_id, forum=forum)

if thread.is_locked:
raise PermissionDenied

if request.method == 'GET':
form = EditThreadForm(instance=thread)
return jingo.render(request, 'edit_thread.html',
Expand All @@ -204,6 +208,8 @@ def edit_thread(request, forum_slug, thread_id):
form = EditThreadForm(request.POST)

if form.is_valid():
log.warning('User %s is editing thread with id=%s' %
(request.user, thread.id))
thread.title = form.cleaned_data['title']
thread.save()

Expand All @@ -229,7 +235,7 @@ def delete_thread(request, forum_slug, thread_id):
{'forum': forum, 'thread': thread})

# Handle confirm delete form POST
log.warning("User %s is deleting thread with id=%s" %
log.warning('User %s is deleting thread with id=%s' %
(request.user, thread.id))
thread.delete()

Expand Down
14 changes: 14 additions & 0 deletions apps/sumo/helpers.py
Expand Up @@ -15,6 +15,7 @@

from sumo.urlresolvers import reverse
from sumo.utils import urlencode
from sumo import utils


class DateTimeFormatError(Exception):
Expand Down Expand Up @@ -202,3 +203,16 @@ def has_perm(context, perm, obj):
"""
check = authority.get_check(context['request'].user, perm)
return check(obj)


@register.function
@jinja2.contextfunction
def has_perm_or_owns(context, perm, obj, perm_obj, field_name='creator'):
"""
Check if the user has a permission or owns the object.
Ownership is determined by comparing perm_obj.field_name to the user in
context.
"""
return utils.has_perm_or_owns(context['request'].user, perm, obj,
perm_obj, field_name)
10 changes: 9 additions & 1 deletion media/css/forums.css
Expand Up @@ -324,7 +324,7 @@ form.new-thread div.form-widget,
form.edit-thread div.form-widget {
border-top: 1px dotted #c0c0c0;
clear: left;
padding: 10px;
padding: 1em;
}

form.new-thread div.form-widget label,
Expand All @@ -334,6 +334,14 @@ form.edit-thread div.form-widget label {
float: left;
}

form div.form-widget.submit {
padding-left: 11em;
}

form.new-thread div.form-widget div.forum-editor-tools {
margin: 0 0 0 10em;
}

/* Delete Thread/Post Confirmation */
div.thread-to-delete label,
div.post-to-delete label {
Expand Down

0 comments on commit 44b81d8

Please sign in to comment.