# HG changeset patch # User Brian Neal # Date 1269738467 0 # Node ID 500e5875a30697d318c117c5596e5b70d118c951 # Parent aef00df9116510808db715368cb4ecaaaf183eb9 Implementing #61: adding a forum topic subscription feature. diff -r aef00df91165 -r 500e5875a306 gpp/core/functions.py --- a/gpp/core/functions.py Sun Mar 21 20:33:33 2010 +0000 +++ b/gpp/core/functions.py Sun Mar 28 01:07:47 2010 +0000 @@ -5,6 +5,8 @@ from django.contrib.sites.models import Site from django.conf import settings +import mailer + def send_mail(subject, message, from_email, recipient_list, fail_silently = False, auth_user = None, auth_password = None): diff -r aef00df91165 -r 500e5875a306 gpp/forums/admin.py --- a/gpp/forums/admin.py Sun Mar 21 20:33:33 2010 +0000 +++ b/gpp/forums/admin.py Sun Mar 28 01:07:47 2010 +0000 @@ -27,7 +27,7 @@ class TopicAdmin(admin.ModelAdmin): list_display = ('name', 'forum', 'creation_date', 'update_date', 'user', 'sticky', 'locked', 'post_count') - raw_id_fields = ('user', 'last_post', ) + raw_id_fields = ('user', 'last_post', 'subscribers') search_fields = ('name', ) date_hierarchy = 'creation_date' list_filter = ('creation_date', 'update_date', ) diff -r aef00df91165 -r 500e5875a306 gpp/forums/models.py --- a/gpp/forums/models.py Sun Mar 21 20:33:33 2010 +0000 +++ b/gpp/forums/models.py Sun Mar 28 01:07:47 2010 +0000 @@ -172,6 +172,8 @@ view_count = models.IntegerField(blank=True, default=0) sticky = models.BooleanField(blank=True, default=False) locked = models.BooleanField(blank=True, default=False) + subscribers = models.ManyToManyField(User, related_name='subscriptions', + verbose_name='subscribers', blank=True) # denormalized fields to reduce database hits post_count = models.IntegerField(blank=True, default=0) diff -r aef00df91165 -r 500e5875a306 gpp/forums/signals.py --- a/gpp/forums/signals.py Sun Mar 21 20:33:33 2010 +0000 +++ b/gpp/forums/signals.py Sun Mar 28 01:07:47 2010 +0000 @@ -3,8 +3,9 @@ """ from django.db.models.signals import post_save from django.db.models.signals import post_delete -from models import Topic -from models import Post + +from forums.models import Topic, Post +from forums.subscriptions import notify_topic_subscribers def on_topic_save(sender, **kwargs): @@ -32,6 +33,9 @@ post.topic.forum.post_count_update() post.topic.forum.save() + # send out any email notifications + notify_topic_subscribers(post) + def on_post_delete(sender, **kwargs): post = kwargs['instance'] diff -r aef00df91165 -r 500e5875a306 gpp/forums/subscriptions.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/subscriptions.py Sun Mar 28 01:07:47 2010 +0000 @@ -0,0 +1,114 @@ +"""This module handles the subscriptions of users to forum topics.""" +from django.conf import settings +from django.contrib.auth.decorators import login_required +from django.contrib.sites.models import Site +from django.core.paginator import InvalidPage +from django.core.urlresolvers import reverse +from django.http import HttpResponseRedirect +from django.http import Http404 +from django.template.loader import render_to_string +from django.shortcuts import get_object_or_404 +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.views.decorators.http import require_POST + +from forums.models import Topic +from core.functions import send_mail +from core.paginator import DiggPaginator + + +def notify_topic_subscribers(post): + """The argument post is a newly created post. Send out an email + notification to all subscribers of the post's parent Topic.""" + + topic = post.topic + recipients = topic.subscribers.exclude( + id=post.user.id).values_list('email', flat=True) + + if recipients: + site = Site.objects.get_current() + subject = "[%s] Topic Reply: %s" % (site.name, topic.name) + url_prefix = "http://%s" % site.domain + post_url = url_prefix + post.get_absolute_url() + unsubscribe_url = url_prefix + reverse("forums-manage_subscriptions") + msg = render_to_string("forums/topic_notify_email.txt", { + 'poster': post.user.username, + 'topic_name': topic.name, + 'message': post.body, + 'post_url': post_url, + 'unsubscribe_url': unsubscribe_url, + }) + for recipient in recipients: + send_mail(subject, msg, settings.DEFAULT_FROM_EMAIL, [recipient]) + + +@login_required +@require_POST +def subscribe_topic(request, topic_id): + """Subscribe the user to the requested topic.""" + topic = get_object_or_404(Topic.objects.select_related(), id=topic_id) + if topic.forum.category.can_access(request.user): + topic.subscribers.add(request.user) + return HttpResponseRedirect( + reverse("forums-subscription_status", args=[topic.id])) + raise Http404 + + +@login_required +@require_POST +def unsubscribe_topic(request, topic_id): + """Unsubscribe the user to the requested topic.""" + topic = get_object_or_404(Topic, id=topic_id) + topic.subscribers.remove(request.user) + return HttpResponseRedirect( + reverse("forums-subscription_status", args=[topic.id])) + + +@login_required +def subscription_status(request, topic_id): + """Display the subscription status for the given topic.""" + topic = get_object_or_404(Topic.objects.select_related(), id=topic_id) + is_subscribed = request.user in topic.subscribers.all() + return render_to_response('forums/subscription_status.html', { + 'topic': topic, + 'is_subscribed': is_subscribed, + }, + context_instance=RequestContext(request)) + + +@login_required +def manage_subscriptions(request): + """Display a user's topic subscriptions, and allow them to be deleted.""" + + user = request.user + if request.method == "POST": + if request.POST.get('delete_all'): + user.subscriptions.clear() + else: + delete_ids = request.POST.getlist('delete_ids') + try: + delete_ids = [int(id) for id in delete_ids] + except ValueError: + raise Http404 + for topic in user.subscriptions.filter(id__in=delete_ids): + user.subscriptions.remove(topic) + + page_num = request.POST.get('page', 1) + else: + page_num = request.GET.get('page', 1) + + topics = user.subscriptions.select_related().order_by('-creation_date') + paginator = DiggPaginator(topics, 20, body=5, tail=2, margin=3, padding=2) + try: + page_num = int(page_num) + except ValueError: + page_num = 1 + try: + page = paginator.page(page_num) + except InvalidPage: + raise Http404 + + return render_to_response('forums/manage_subscriptions.html', { + 'page': page, + }, + context_instance=RequestContext(request)) diff -r aef00df91165 -r 500e5875a306 gpp/forums/urls.py --- a/gpp/forums/urls.py Sun Mar 21 20:33:33 2010 +0000 +++ b/gpp/forums/urls.py Sun Mar 28 01:07:47 2010 +0000 @@ -28,3 +28,9 @@ url(r'^unread/$', 'unread_topics', name='forums-unread_topics'), ) +urlpatterns += patterns('forums.subscriptions', + url(r'^subscribe/(\d+)/$', 'subscribe_topic', name='forums-subscribe_topic'), + url(r'^subscriptions/$', 'manage_subscriptions', name='forums-manage_subscriptions'), + url(r'^subscriptions/(\d+)/$', 'subscription_status', name='forums-subscription_status'), + url(r'^unsubscribe/(\d+)/$', 'unsubscribe_topic', name='forums-unsubscribe_topic'), +) diff -r aef00df91165 -r 500e5875a306 gpp/forums/views.py --- a/gpp/forums/views.py Sun Mar 21 20:33:33 2010 +0000 +++ b/gpp/forums/views.py Sun Mar 28 01:07:47 2010 +0000 @@ -193,6 +193,9 @@ can_reply = request.user.is_authenticated() and ( not topic.locked or can_moderate) + is_subscribed = request.user.is_authenticated() and ( + topic in request.user.subscriptions.all()) + return render_to_response('forums/topic.html', { 'forum': topic.forum, 'topic': topic, @@ -202,6 +205,7 @@ 'can_moderate': can_moderate, 'can_reply': can_reply, 'form': NewPostForm(initial={'topic_id': topic.id}), + 'is_subscribed': is_subscribed, }, context_instance=RequestContext(request)) @@ -435,6 +439,9 @@ topic.forum.last_post_pre_delete() topic.forum.save() + # delete subscriptions to this topic + topic.subscribers.clear() + # It should be safe to just delete the topic now. This will # automatically delete all posts in the topic. topic.delete() diff -r aef00df91165 -r 500e5875a306 gpp/settings.py --- a/gpp/settings.py Sun Mar 21 20:33:33 2010 +0000 +++ b/gpp/settings.py Sun Mar 28 01:07:47 2010 +0000 @@ -59,12 +59,19 @@ SECRET_KEY = local_settings.SECRET_KEY # List of Loader classes that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - ('django.template.loaders.cached.Loader', ( + +if DEBUG: + TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', - )), -) + ) +else: + TEMPLATE_LOADERS = ( + ('django.template.loaders.cached.Loader', ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + )), + ) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', diff -r aef00df91165 -r 500e5875a306 gpp/templates/forums/manage_subscriptions.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/templates/forums/manage_subscriptions.html Sun Mar 28 01:07:47 2010 +0000 @@ -0,0 +1,39 @@ +{% extends 'base.html' %} +{% block title %}Forums: Topic Subscriptions{% endblock %} +{% block content %} +

Forums: Topic Subscriptions

+ +

+ SurfGuitar101 Forum Index » Topic Subscriptions +

+

The forum topics you are currently subscribed to are listed below.

+{% include 'forums/pagination.html' %} +
+ + + + + + + + + + {% for topic in page.object_list %} + + + + + + {% empty %} + + {% endfor %} + +
ForumTopicSelect
{{ topic.forum.name }}{{ topic.name }}
No Topic Subscriptions
+{% include 'forums/pagination.html' %} +{% if page.object_list %} + + • + +{% endif %} +
+{% endblock %} diff -r aef00df91165 -r 500e5875a306 gpp/templates/forums/subscription_status.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/templates/forums/subscription_status.html Sun Mar 28 01:07:47 2010 +0000 @@ -0,0 +1,24 @@ +{% extends 'base.html' %} +{% block title %}Forums: {% if is_subscribed %}S{% else %}Uns{% endif %}ubscribed to Topic{% endblock %} +{% block content %} +

Forums: {% if is_subscribed %}S{% else %}Uns{% endif %}ubscribed to Topic

+ +

+ SurfGuitar101 Forum Index » + {{ topic.forum.name }} » + {{ topic.name }} +

+

+{% if is_subscribed %} +You are now subscribed to the forum topic {{ topic.name }}. +You will receive an email notification whenever a new reply is posted. +{% else %} +You have successfully unsubscribed to the forum topic {{ topic.name }}. +You will no longer receive emails when new replies are posted. +{% endif %} +

+

+To manage all your forum topic subscriptions, please visit your +subscriptions page. +

+{% endblock %} diff -r aef00df91165 -r 500e5875a306 gpp/templates/forums/topic.html --- a/gpp/templates/forums/topic.html Sun Mar 21 20:33:33 2010 +0000 +++ b/gpp/templates/forums/topic.html Sun Mar 28 01:07:47 2010 +0000 @@ -57,5 +57,28 @@ {% show_form "Reply to Topic" form "Submit Reply" 1 MEDIA_URL %} {% endif %} + +{% if user.is_authenticated %} +
+
+ Subscription Options +

+ {% if is_subscribed %} + Email + You are currently subscribed to this topic and will receive an email when new replies are posted. + + {% else %} + Email + Would you like to receive an email when someone replies to this topic? + + {% endif %} +

+

+ To manage all your forum topic subscriptions, please visit your + subscriptions page. +

+
+
+{% endif %} {% endblock %} diff -r aef00df91165 -r 500e5875a306 gpp/templates/forums/topic_notify_email.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/templates/forums/topic_notify_email.txt Sun Mar 28 01:07:47 2010 +0000 @@ -0,0 +1,27 @@ +Hello, + +You are receiving this message because you have subscribed to one or +more forum topics at SurfGuitar101.com. + +This is a heads up that {{ poster }} replied to a forum topic you have +subscribed to: + +--- +RE: {{ topic_name }} + +{{ message }} +--- + +To view this post on the site, please visit: +{{ post_url }} + +If you would like to stop receiving these email notifications, you may +manage your topic subscriptions at: +{{ unsubscribe_url }} + +Surf's up! +-The staff at SurfGuitar101.com + +P.S. This is an automated message from SurfGuitar101.com. Please do not +reply to this email unless you wish to report a problem to the staff. +Thanks! diff -r aef00df91165 -r 500e5875a306 media/icons/email_add.png Binary file media/icons/email_add.png has changed diff -r aef00df91165 -r 500e5875a306 media/icons/email_delete.png Binary file media/icons/email_delete.png has changed