Skip to content

Commit

Permalink
#410: thread events
Browse files Browse the repository at this point in the history
  • Loading branch information
rafalp committed Oct 19, 2014
1 parent e765dd4 commit e582887
Show file tree
Hide file tree
Showing 7 changed files with 302 additions and 7 deletions.
26 changes: 23 additions & 3 deletions misago/static/misago/css/misago/events.less
Expand Up @@ -46,21 +46,41 @@
text-decoration: underline;
}
}

form {
display: inline-block;
margin: 0px;
margin-left: @line-height-computed / 2;
padding: 0px;
.opacity(0.2);
transition-duration: 100ms;
position: relative;
}

&:hover, &:active {
form {
.opacity(1);
}
}
}

.divider {
padding-left: @line-height-computed / 4;
color: @post-panel-border;

abbr {
div {
display: inline-block;
margin-left: @line-height-computed / 2;
position: relative;
left: 1px;
bottom: 2px;

font-size: @font-size-small;

&:hover {
color: @text-muted;
abbr {
&:hover {
color: @text-muted;
}
}
}
}
Expand Down
31 changes: 28 additions & 3 deletions misago/templates/misago/thread/events.html
Expand Up @@ -3,9 +3,14 @@
{% for event in post.events %}
<li class="divider">
<span class="fa fa-arrow-down fa-fw"></span>
<abbr class="tooltip-top dynamic time-ago-compact" title="{% blocktrans with date=event.occured_on %}This event occured on {{ date }}.{% endblocktrans %}" data-timestamp="{{ event.occured_on|date:"c" }}">
{{ event.occured_on|date }}
</abbr>
<div>
<abbr class="tooltip-top dynamic time-ago" title="{% blocktrans with date=event.occured_on %}This event occured on {{ date }}.{% endblocktrans %}" data-timestamp="{{ event.occured_on|date:"c" }}">
{{ event.occured_on|date }}
</abbr>
{% if event.is_hidden %}
<span class="text-warning">{% trans "Hidden" %}</span>
{% endif %}
</div>
</li>
<li class="event">
<span class="fa-stack">
Expand All @@ -18,6 +23,26 @@
{% else %}
<em>{% trans "Event message is invalid." %}</em>
{% endif %}

{% if forum.acl.can_hide_events %}
<form action="{% url 'misago:edit_event' event_id=event.id %}" class="event-form" method="post">
{% csrf_token %}
{% if event.is_hidden %}
<button type="submit" class="btn btn-default btn-flat btn-sm event-toggle">
{% trans "Show" %}
</button>
{% else %}
<button type="submit" class="btn btn-default btn-flat btn-sm event-toggle">
{% trans "Hide" %}
</button>
{% endif %}
{% if forum.acl.can_hide_events == 2 %}
<button type="submit" class="btn btn-danger btn-flat btn-sm event-delete">
{% trans "Delete" %}
</button>
{% endif %}
</form>
{% endif %}
</li>
{% endfor %}
</ul>
52 changes: 52 additions & 0 deletions misago/templates/misago/thread/events_js.html
@@ -0,0 +1,52 @@
{% load i18n %}
<script lang="JavaScript">
$(function() {

$('.event-form').each(function() {
var $form = $(this);
var $list = $form.parents('.post-events');
var $li = $form.parent();
var $divider = $li.prev();
var action = $form.attr('action');

$form.find('.event-toggle').click(function() {
var $btn = $(this);
var data = $form.serialize() + "&action=toggle";
$.post(action, data, function(data) {
if (data.is_hidden) {
$btn.text("{% trans "Show" %}");
$label = $('<span class="text-warning">{% trans "Hidden" %}</span>');
$label.hide();
$divider.find('div').append($label)
$label.fadeIn();
} else {
$btn.text("{% trans "Hide" %}");
$divider.find('.text-warning').fadeOut(function() {$(this).remove()});
}
});
return false;
});

$form.find('.event-delete').click(function() {
var $btn = $(this);
var data = $form.serialize() + "&action=delete";

var decision = confirm("{% trans "Are you sure you want to delete this event?" %}");
if (decision) {
$.post(action, data, function(data) {
if (data.is_deleted) {
if ($list.find('.event:visible').length == 1) {
$list.slideUp();
} else {
$li.slideUp();
$divider.slideUp();
}
}
});
}
return false;
});
})

});
</script>
138 changes: 138 additions & 0 deletions misago/threads/tests/test_events_view.py
@@ -0,0 +1,138 @@
from django.core.urlresolvers import reverse

from misago.acl.testutils import override_acl
from misago.forums.models import Forum
from misago.users.testutils import AuthenticatedUserTestCase

from misago.threads.models import Thread, Event
from misago.threads.testutils import post_thread, reply_thread


class EventsViewTestCase(AuthenticatedUserTestCase):
ajax_header = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}

def setUp(self):
super(EventsViewTestCase, self).setUp()

self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
self.forum.labels = []

self.thread = post_thread(self.forum)

def override_acl(self, new_acl):
new_acl.update({
'can_see': True,
'can_browse': True,
'can_see_all_threads': True,
'can_see_own_threads': False,
'can_pin_threads': True
})

forums_acl = self.user.acl
forums_acl['visible_forums'].append(self.forum.pk)
forums_acl['forums'][self.forum.pk] = new_acl
override_acl(self.user, forums_acl)

def test_hide_event(self):
"""its possible to hide event"""
self.override_acl({'can_hide_events': 0})
response = self.client.post(self.thread.get_absolute_url(),
data={'thread_action': 'pin'})
self.assertEqual(response.status_code, 302)

event = self.thread.event_set.all()[0]
self.override_acl({'can_hide_events': 0})
response = self.client.post(
reverse('misago:edit_event', kwargs={'event_id': event.id}),
data={'action': 'toggle'},
**self.ajax_header)
self.assertEqual(response.status_code, 403)

self.override_acl({'can_hide_events': 1})
response = self.client.post(
reverse('misago:edit_event', kwargs={'event_id': event.id}),
data={'action': 'toggle'},
**self.ajax_header)
self.assertEqual(response.status_code, 200)

event = Event.objects.get(id=event.id)
self.assertTrue(event.is_hidden)

def test_show_event(self):
"""its possible to unhide event"""
self.override_acl({'can_hide_events': 0})
response = self.client.post(self.thread.get_absolute_url(),
data={'thread_action': 'pin'})
self.assertEqual(response.status_code, 302)

event = self.thread.event_set.all()[0]
event.is_hidden = True
event.save()

self.override_acl({'can_hide_events': 0})
response = self.client.post(
reverse('misago:edit_event', kwargs={'event_id': event.id}),
data={'action': 'toggle'},
**self.ajax_header)
self.assertEqual(response.status_code, 403)

self.override_acl({'can_hide_events': 1})
response = self.client.post(
reverse('misago:edit_event', kwargs={'event_id': event.id}),
data={'action': 'toggle'},
**self.ajax_header)
self.assertEqual(response.status_code, 200)

event = Event.objects.get(id=event.id)
self.assertFalse(event.is_hidden)

def test_delete_event(self):
"""its possible to delete event"""
self.override_acl({'can_hide_events': 0})
response = self.client.post(self.thread.get_absolute_url(),
data={'thread_action': 'pin'})
self.assertEqual(response.status_code, 302)

self.override_acl({'can_hide_events': 0})
response = self.client.post(self.thread.get_absolute_url(),
data={'thread_action': 'unpin'})
self.assertEqual(response.status_code, 302)

event = self.thread.event_set.all()[0]

self.override_acl({'can_hide_events': 0})
response = self.client.post(
reverse('misago:edit_event', kwargs={'event_id': event.id}),
data={'action': 'delete'},
**self.ajax_header)
self.assertEqual(response.status_code, 403)

self.override_acl({'can_hide_events': 1})
response = self.client.post(
reverse('misago:edit_event', kwargs={'event_id': event.id}),
data={'action': 'delete'},
**self.ajax_header)
self.assertEqual(response.status_code, 403)

self.override_acl({'can_hide_events': 2})
response = self.client.post(
reverse('misago:edit_event', kwargs={'event_id': event.id}),
data={'action': 'delete'},
**self.ajax_header)
self.assertEqual(response.status_code, 200)

thread = Thread.objects.get(id=self.thread.id)
self.assertTrue(thread.has_events)
self.assertTrue(thread.event_set.exists())

event = self.thread.event_set.all()[0]
self.override_acl({'can_hide_events': 2})
response = self.client.post(
reverse('misago:edit_event', kwargs={'event_id': event.id}),
data={'action': 'delete'},
**self.ajax_header)
self.assertEqual(response.status_code, 200)

thread = Thread.objects.get(id=self.thread.id)
self.assertFalse(thread.has_events)
self.assertFalse(thread.event_set.exists())
1 change: 0 additions & 1 deletion misago/threads/tests/test_thread_view.py
@@ -1,6 +1,5 @@
from django.core.urlresolvers import reverse

from misago.acl import add_acl
from misago.acl.testutils import override_acl
from misago.forums.models import Forum
from misago.users.testutils import AuthenticatedUserTestCase
Expand Down
7 changes: 7 additions & 0 deletions misago/threads/urls.py
Expand Up @@ -44,3 +44,10 @@
url(r'^unread-threads/sort-(?P<sort>[\w-]+)(?P<page>\d+)/$', UnreadThreadsView.as_view(), name='unread_threads'),
url(r'^unread-threads/clear/$', clear_unread_threads, name='clear_unread_threads'),
)


# events moderation
from misago.threads.views.events import EventsView
urlpatterns += patterns('',
url(r'^edit-event/(?P<event_id>\d+)/$', EventsView.as_view(), name='edit_event'),
)
54 changes: 54 additions & 0 deletions misago/threads/views/events.py
@@ -0,0 +1,54 @@
from django.core.exceptions import PermissionDenied
from django.db.transaction import atomic
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext as _

from misago.core.decorators import ajax_only, require_POST
from misago.users.decorators import deny_guests

from misago.threads.models import Event
from misago.threads.views.generic.base import ViewBase


class EventsView(ViewBase):
def dispatch(self, request, event_id):
def toggle_event(request, event):
event.is_hidden = not event.is_hidden
event.save(update_fields=['is_hidden'])
return JsonResponse({'is_hidden': event.is_hidden})

def delete_event(request, event):
event.delete();

event.thread.has_events = event.thread.event_set.exists()
event.thread.save(update_fields=['has_events'])

return JsonResponse({'is_deleted': True})

@ajax_only
@require_POST
@deny_guests
@atomic
def real_view(request, event_id):
queryset = Event.objects.select_for_update()
queryset = queryset.select_related('forum', 'thread')
event = get_object_or_404(queryset, id=event_id)

forum = event.forum
thread = event.thread

self.check_forum_permissions(request, forum)
self.check_thread_permissions(request, thread)

if request.POST.get('action') == 'toggle':
if not forum.acl.get('can_hide_events'):
raise PermissionDenied(_("You can't hide events."))
return toggle_event(request, event)
elif request.POST.get('action') == 'delete':
if forum.acl.get('can_hide_events') != 2:
raise PermissionDenied(_("You can't delete events."))
return delete_event(request, event)
else:
raise PermissionDenied(_("Invalid action requested."))
return real_view(request, event_id)

0 comments on commit e582887

Please sign in to comment.