Skip to content

Commit

Permalink
Implemented forum-wide watches. (bug 583273)
Browse files Browse the repository at this point in the history
  • Loading branch information
rlr committed Aug 11, 2010
1 parent 5536aca commit 806cd53
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 46 deletions.
4 changes: 0 additions & 4 deletions apps/forums/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from sumo.urlresolvers import reverse
from sumo.models import ModelBase
from sumo.utils import wiki_to_html
from forums.tasks import build_notification
from notifications.tasks import delete_watches
import forums

Expand Down Expand Up @@ -126,9 +125,6 @@ def save(self, *args, **kwargs):
self.thread.forum.last_post = self
self.thread.forum.save()

# Send notifications to thread watchers.
build_notification.delay(self)

def delete(self, *args, **kwargs):
"""Override delete method to update parent thread info."""

Expand Down
31 changes: 28 additions & 3 deletions apps/forums/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@


@task
def build_notification(post):
ct = ContentType.objects.get_for_model(post.thread)
def build_reply_notification(post):
thread_ct = ContentType.objects.get_for_model(post.thread)
forum_ct = ContentType.objects.get_for_model(post.thread.forum)

subject = _('Reply to: %s') % post.thread.title
t = loader.get_template('forums/email/new_post.ltxt')
Expand All @@ -21,4 +22,28 @@ def build_notification(post):
content = t.render(Context(c))
exclude = (post.author.email,)

send_notification.delay(ct.id, post.thread.id, subject, content, exclude)
# Send to thread watchers
send_notification.delay(thread_ct.id, post.thread.id, subject,
content, exclude, 'reply')
# And forum watchers
send_notification.delay(forum_ct.id, post.thread.forum.id, subject,
content, exclude, 'post')


@task
def build_thread_notification(post):
forum_ct = ContentType.objects.get_for_model(post.thread.forum)

subject = _('New thread in %s forum: %s') % (post.thread.forum.name,
post.thread.title)
t = loader.get_template('forums/email/new_thread.ltxt')
c = {'post': post.content, 'author': post.author.username,
'host': Site.objects.get_current().domain,
'thread_title': post.thread.title,
'post_url': post.thread.get_absolute_url()}
content = t.render(Context(c))
exclude = (post.thread.creator.email,)

# Send to forum watchers
send_notification.delay(forum_ct.id, post.thread.forum.id, subject,
content, exclude, 'post')
17 changes: 17 additions & 0 deletions apps/forums/templates/forums/email/new_thread.ltxt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% load i18n %}
{# L10n: This is an email. Whitespace matters! #}
{% blocktrans %}New thread: {{ thread_title }}

User {{ author }} has posted a new thread in a forum you're watching.
Here is the thread:
{% endblocktrans %}
========
{% autoescape off %}
{{ post }}
{% endautoescape %}
========
{% blocktrans %}
To view this post on the site, click the following link, or
paste it into your browser's location bar:
{% endblocktrans %}
https://{{ host }}{{ post_url }}
4 changes: 3 additions & 1 deletion apps/forums/templates/forums/posts.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ <h2>{{ thread.title }}</h2>
</div>
<div class="thread-actions">
{% if user.is_authenticated() %}
<form action="{{ url('forums.watch', forum_slug=forum.slug, thread_id=thread.id) }}" method="post">
<form action="{{ url('forums.watch_thread', forum_slug=forum.slug, thread_id=thread.id) }}" method="post">
{{ csrf() }}
{% if is_watching(thread) %}
{% set watch = _('Stop watching') %}
<input type="hidden" name="watch" value="no" />
{% else %}
{% set watch = _('Watch this thread') %}
<input type="hidden" name="watch" value="yes" />
{% endif %}
<input type="image" alt="{{ watch }}" title="{{ watch }}" src="{{ MEDIA_URL }}img/forums/watch.png"/>
</form>
Expand Down
22 changes: 19 additions & 3 deletions apps/forums/templates/forums/threads.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,25 @@

{% block content %}
<h2>{{ forum.name }}</h2>

<a id="new-thread" href="{{ url('forums.new_thread', forum_slug=forum.slug) }}">{{ _('Post a new thread') }}</a>

<div class="badges">
{% if is_watching(forum) %}<span class="watching">{{ _('Watching') }}</span>{% endif %}
</div>
<div class="forum-actions">
{% if user.is_authenticated() %}
<form action="{{ url('forums.watch_forum', forum_slug=forum.slug) }}" method="post">
{{ csrf() }}
{% if is_watching(forum) %}
{% set watch = _('Stop watching') %}
<input type="hidden" name="watch" value="no" />
{% else %}
{% set watch = _('Watch this forum') %}
<input type="hidden" name="watch" value="yes" />
{% endif %}
<input type="image" alt="{{ watch }}" title="{{ watch }}" src="{{ MEDIA_URL }}img/forums/watch.png"/>
</form>
{% endif %}
<a id="new-thread" href="{{ url('forums.new_thread', forum_slug=forum.slug) }}">{{ _('Post a new thread') }}</a>
</div>
<ol class="threads-columns {% if not desc_toggle %}desc{% endif %}">
<li class="type"><a href="{{ url('forums.threads', forum_slug=forum.slug) }}">{{ _('Type') }}</a></li>
<li class="title">{{ _('Title') }}</a></li>
Expand Down
117 changes: 97 additions & 20 deletions apps/forums/tests/test_notifications.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.contrib.auth.models import User

import mock
from nose.tools import eq_

from forums.tasks import build_notification
from forums.tasks import build_reply_notification, build_thread_notification
import notifications.tasks
from . import ForumTestCase
from forums.models import Post, Thread
from forums.models import Post, Thread, Forum
from forums.tests import post


EMAIL_CONTENT = (
Expand Down Expand Up @@ -46,6 +47,42 @@
paste it into your browser's location bar:
https://testserver/en-US/forums/test-forum/2#post-%s
""",
u"""
New thread: Sticky Thread
User jsocol has posted a new thread in a forum you're watching.
Here is the thread:
========
This is a sticky thread
========
To view this post on the site, click the following link, or
paste it into your browser's location bar:
https://testserver/en-US/forums/test-forum/2
""",
u"""
New thread: Awesome Thread
User jsocol has posted a new thread in a forum you're watching.
Here is the thread:
========
With awesome content!
========
To view this post on the site, click the following link, or
paste it into your browser's location bar:
https://testserver/en-US/forums/test-forum/%s
""",)


Expand All @@ -55,34 +92,74 @@ class NotificationTestCase(ForumTestCase):
def setUp(self):
super(NotificationTestCase, self).setUp()

self.ct = ContentType.objects.get_for_model(Thread).pk
self.thread_ct = ContentType.objects.get_for_model(Thread).pk
self.forum_ct = ContentType.objects.get_for_model(Forum).pk

@mock.patch_object(notifications.tasks.send_notification, 'delay')
@mock.patch_object(Site.objects, 'get_current')
def test_notification(self, get_current, delay):
def test_reply_notification(self, get_current, delay):
get_current.return_value.domain = 'testserver'

post = Post.objects.get(pk=4)
build_notification(post)
p = Post.objects.get(pk=4)
build_reply_notification(p)

delay.assert_called_with(
self.ct, post.thread.id,
u'Reply to: Sticky Thread',
EMAIL_CONTENT[0],
(u'user1@nowhere',))
# delay() is called twice. Verify the args.
eq_(((self.thread_ct, p.thread.id,
u'Reply to: Sticky Thread', EMAIL_CONTENT[0],
(u'user1@nowhere',), 'reply'), {}), delay.call_args_list[0])
eq_(((self.forum_ct, p.thread.forum.id,
u'Reply to: Sticky Thread', EMAIL_CONTENT[0],
(u'user1@nowhere',), 'post'), {}), delay.call_args_list[1])

@mock.patch_object(notifications.tasks.send_notification, 'delay')
@mock.patch_object(Site.objects, 'get_current')
def test_notification_on_save(self, get_current, delay):
def test_notification_on_reply(self, get_current, delay):
get_current.return_value.domain = 'testserver'

self.client.login(username='jsocol', password='testpass')

t = Thread.objects.get(pk=2)
f = t.forum
post(self.client, 'forums.reply', {'content': 'a post'},
args=[f.slug, t.id])
t = Thread.objects.get(pk=2)
user = User.objects.get(pk=118533)
p = t.post_set.create(author=user, content='a post')
p.save()
p = t.last_post

# delay() is called twice. Verify the args.
eq_(((self.thread_ct, t.pk,
u'Reply to: Sticky Thread', EMAIL_CONTENT[1] % p.pk,
(u'user118533@nowhere',), 'reply'), {}), delay.call_args_list[0])
eq_(((self.forum_ct, t.forum.id,
u'Reply to: Sticky Thread', EMAIL_CONTENT[1] % p.pk,
(u'user118533@nowhere',), 'post'), {}), delay.call_args_list[1])

@mock.patch_object(notifications.tasks.send_notification, 'delay')
@mock.patch_object(Site.objects, 'get_current')
def test_post_notification(self, get_current, delay):
get_current.return_value.domain = 'testserver'

post = Post.objects.get(pk=3)
build_thread_notification(post)

delay.assert_called_with(
self.forum_ct, post.thread.forum.id,
u'New thread in Test forum forum: Sticky Thread',
EMAIL_CONTENT[2], (u'user118533@nowhere',), 'post')

@mock.patch_object(notifications.tasks.send_notification, 'delay')
@mock.patch_object(Site.objects, 'get_current')
def test_notification_on_thread_post(self, get_current, delay):
get_current.return_value.domain = 'testserver'

f = Forum.objects.filter()[0]
self.client.login(username='jsocol', password='testpass')
post(self.client, 'forums.new_thread',
{'title': 'Awesome Thread', 'content': 'With awesome content!'},
args=[f.slug])
f = Forum.objects.get(pk=f.pk)
t = f.last_post.thread

delay.assert_called_with(
self.ct, t.pk,
u'Reply to: Sticky Thread',
EMAIL_CONTENT[1] % p.pk,
(u'user118533@nowhere',))
self.forum_ct, f.id,
u'New thread in Test forum forum: Awesome Thread',
EMAIL_CONTENT[3] % t.pk, (u'user118533@nowhere',), 'post')
22 changes: 22 additions & 0 deletions apps/forums/tests/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from forums.models import Forum, Thread, Post
from forums.tests import ForumTestCase, get, post
from notifications import check_watch


class PostsTemplateTestCase(ForumTestCase):
Expand Down Expand Up @@ -188,6 +189,27 @@ def test_edit_thread_moderator(self):
edited_t = Thread.uncached.get(pk=2)
eq_('new title', edited_t.title)

def test_watch_GET_405(self):
"""Watch forum with HTTP GET results in 405."""
self.client.login(username='rrosario', password='testpass')
f = Forum.objects.filter()[0]
response = get(self.client, 'forums.watch_forum', args=[f.id])
eq_(405, response.status_code)

def test_watch_forum(self):
"""Watch then unwatch a forum."""
self.client.login(username='rrosario', password='testpass')
f = Forum.objects.filter()[0]
post(self.client, 'forums.watch_forum', {'watch': 'yes'},
args=[f.slug])
assert check_watch(Forum, f.id, 'user118577@nowhere',
'post'), 'Watch was not created'

post(self.client, 'forums.watch_forum', {'watch': 'no'},
args=[f.slug])
assert not check_watch(Forum, f.id, 'user118577@nowhere',
'post'), 'Watch was not created'


class ForumsTemplateTestCase(ForumTestCase):

Expand Down
4 changes: 3 additions & 1 deletion apps/forums/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@
url(r'^/(?P<forum_slug>[\w\-]+)/(?P<thread_id>\d+)/(?P<post_id>\d+)/delete$',
'delete_post', name='forums.delete_post'),
url(r'^/(?P<forum_slug>[\w\-]+)/(?P<thread_id>\d+)/watch',
'watch_thread', name='forums.watch'),
'watch_thread', name='forums.watch_thread'),
url(r'^/(?P<forum_slug>[\w\-]+)/watch',
'watch_forum', name='forums.watch_forum'),
)
Loading

0 comments on commit 806cd53

Please sign in to comment.