Mercurial > public > sg101
changeset 181:500e5875a306
Implementing #61: adding a forum topic subscription feature.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sun, 28 Mar 2010 01:07:47 +0000 |
parents | aef00df91165 |
children | 5c889b587416 |
files | gpp/core/functions.py gpp/forums/admin.py gpp/forums/models.py gpp/forums/signals.py gpp/forums/subscriptions.py gpp/forums/urls.py gpp/forums/views.py gpp/settings.py gpp/templates/forums/manage_subscriptions.html gpp/templates/forums/subscription_status.html gpp/templates/forums/topic.html gpp/templates/forums/topic_notify_email.txt media/icons/email_add.png media/icons/email_delete.png |
diffstat | 14 files changed, 262 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- 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):
--- 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', )
--- 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)
--- 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']
--- /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))
--- 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'), +)
--- 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()
--- 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',
--- /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 %} +<h2>Forums: Topic Subscriptions</h2> + +<h3> + <a href="{% url forums-index %}">SurfGuitar101 Forum Index</a> » Topic Subscriptions +</h3> +<p>The forum topics you are currently subscribed to are listed below.</p> +{% include 'forums/pagination.html' %} +<form action="." method="post"> +<table class="forum-topic-table"> + <thead> + <tr> + <th>Forum</th> + <th>Topic</th> + <th>Select</th> + </tr> + </thead> + <tbody> + {% for topic in page.object_list %} + <tr> + <td><a href="{{ topic.forum.get_absolute_url }}">{{ topic.forum.name }}</a></td> + <td><a href="{{ topic.get_absolute_url }}">{{ topic.name }}</a></td> + <td><input type="checkbox" name="delete_ids" value="{{ topic.id }}" /></td> + </tr> + {% empty %} + <tr><td colspan="3"><em>No Topic Subscriptions</em></td></tr> + {% endfor %} + </tbody> +</table> +{% include 'forums/pagination.html' %} +{% if page.object_list %} +<input type="hidden" name="page" value="{{ page.number }}" /> +<input type="submit" name="delete_selected" value="Delete Selected" /> • +<input type="submit" name="delete_all" value="Delete All" /> +{% endif %} +</form> +{% endblock %}
--- /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 %} +<h2>Forums: {% if is_subscribed %}S{% else %}Uns{% endif %}ubscribed to Topic</h2> + +<h3> + <a href="{% url forums-index %}">SurfGuitar101 Forum Index</a> » + <a href="{% url forums-forum_index slug=topic.forum.slug %}">{{ topic.forum.name }}</a> » + <a href="{% url forums-topic_index id=topic.id %}">{{ topic.name }}</a> +</h3> +<p> +{% if is_subscribed %} +You are now subscribed to the forum topic <a href="{{ topic.get_absolute_url }}">{{ topic.name }}</a>. +You will receive an email notification whenever a new reply is posted. +{% else %} +You have successfully unsubscribed to the forum topic <a href="{{ topic.get_absolute_url }}">{{ topic.name }}</a>. +You will no longer receive emails when new replies are posted. +{% endif %} +</p> +<p> +To manage all your forum topic subscriptions, please visit your +<a href="{% url forums-manage_subscriptions %}">subscriptions page</a>. +</p> +{% endblock %}
--- 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 @@ <a name="forum-reply-form"></a> {% show_form "Reply to Topic" form "Submit Reply" 1 MEDIA_URL %} {% endif %} + +{% if user.is_authenticated %} +<form action={% if is_subscribed %}"{% url forums-unsubscribe_topic topic.id %}"{% else %}"{% url forums-subscribe_topic topic.id %}"{% endif %} method="post"> +<fieldset> + <legend>Subscription Options</legend> + <p> + {% if is_subscribed %} + <img src="{{ MEDIA_URL }}icons/email_delete.png" alt="Email" /> + You are currently subscribed to this topic and will receive an email when new replies are posted. + <input type="submit" value="Unsubscribe Me" /> + {% else %} + <img src="{{ MEDIA_URL }}icons/email_add.png" alt="Email" /> + Would you like to receive an email when someone replies to this topic? + <input type="submit" value="Subscribe via Email" /> + {% endif %} + </p> + <p> + To manage all your forum topic subscriptions, please visit your + <a href="{% url forums-manage_subscriptions %}">subscriptions page</a>. + </p> +</fieldset> +</form> +{% endif %} </div> {% endblock %}
--- /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!