annotate gpp/forums/views/subscriptions.py @ 318:c550933ff5b6

Fix a bug where you'd get an error when trying to delete a forum thread (topic does not exist). Apparently when you call topic.delete() the posts would get deleted, but the signal handler for each one would run, and it would try to update the topic's post count or something, but the topic was gone? Reworked the code a bit and explicitly delete the posts first. I also added a sync() call on the parent forum since post counts were not getting adjusted.
author Brian Neal <bgneal@gmail.com>
date Sat, 05 Feb 2011 21:46:52 +0000
parents ee451ad46af1
children b2b37cdd020a
rev   line source
bgneal@232 1 """This module handles the subscriptions of users to forum topics."""
bgneal@301 2 import datetime
bgneal@301 3
bgneal@232 4 from django.conf import settings
bgneal@232 5 from django.contrib.auth.decorators import login_required
bgneal@232 6 from django.contrib.sites.models import Site
bgneal@232 7 from django.core.paginator import InvalidPage
bgneal@232 8 from django.core.urlresolvers import reverse
bgneal@232 9 from django.http import HttpResponseRedirect
bgneal@232 10 from django.http import Http404
bgneal@232 11 from django.template.loader import render_to_string
bgneal@232 12 from django.shortcuts import get_object_or_404
bgneal@232 13 from django.shortcuts import render_to_response
bgneal@232 14 from django.template import RequestContext
bgneal@232 15 from django.views.decorators.http import require_POST
bgneal@232 16
bgneal@301 17 from forums.models import Topic, TopicLastVisit, Subscription
bgneal@232 18 from core.functions import send_mail
bgneal@232 19 from core.paginator import DiggPaginator
bgneal@232 20
bgneal@232 21
bgneal@301 22 # This constant is the minimum amount of time a user will be
bgneal@301 23 # notified of new posts in a topic. Currently it is set to 1 day.
bgneal@301 24 # Thus a user will be notified at most once per day of new posts.
bgneal@301 25 TOPIC_NOTIFY_DELAY = datetime.timedelta(days=1)
bgneal@301 26
bgneal@301 27
bgneal@232 28 def notify_topic_subscribers(post):
bgneal@301 29 """
bgneal@301 30 The argument post is a newly created post. Send out an email
bgneal@301 31 notification to all subscribers of the post's parent Topic.
bgneal@301 32 The emails are throttled such that unless the user visits the topic,
bgneal@301 33 only 1 email will be sent per TOPIC_NOTIFY_DELAY period.
bgneal@301 34 Visiting the topic will reset this delay.
bgneal@301 35
bgneal@301 36 """
bgneal@301 37 #TODO: consider moving this function of the HTTP request/response cycle.
bgneal@232 38
bgneal@232 39 topic = post.topic
bgneal@301 40 subscriptions = Subscription.objects.filter(topic=post.topic).exclude(
bgneal@301 41 user=post.user).select_related()
bgneal@301 42
bgneal@301 43 if not subscriptions:
bgneal@301 44 return
bgneal@301 45
bgneal@301 46 subscriber_ids = [sub.user.id for sub in subscriptions]
bgneal@301 47 tlvs = dict(TopicLastVisit.objects.filter(topic=topic,
bgneal@301 48 user__in=subscriber_ids).values_list('user', 'last_visit'))
bgneal@301 49
bgneal@301 50 recipients = []
bgneal@301 51 for sub in subscriptions:
bgneal@301 52 if (sub.notify_date is None or
bgneal@301 53 post.creation_date - sub.notify_date > TOPIC_NOTIFY_DELAY or
bgneal@301 54 tlvs.get(sub.user.id, datetime.datetime.min) > sub.notify_date):
bgneal@301 55
bgneal@301 56 recipients.append(sub.user.email)
bgneal@301 57 sub.notify_date = post.creation_date
bgneal@301 58 sub.save()
bgneal@232 59
bgneal@232 60 if recipients:
bgneal@232 61 site = Site.objects.get_current()
bgneal@232 62 subject = "[%s] Topic Reply: %s" % (site.name, topic.name)
bgneal@232 63 url_prefix = "http://%s" % site.domain
bgneal@232 64 post_url = url_prefix + post.get_absolute_url()
bgneal@232 65 unsubscribe_url = url_prefix + reverse("forums-manage_subscriptions")
bgneal@232 66 msg = render_to_string("forums/topic_notify_email.txt", {
bgneal@232 67 'poster': post.user.username,
bgneal@232 68 'topic_name': topic.name,
bgneal@232 69 'message': post.body,
bgneal@232 70 'post_url': post_url,
bgneal@232 71 'unsubscribe_url': unsubscribe_url,
bgneal@232 72 })
bgneal@232 73 for recipient in recipients:
bgneal@232 74 send_mail(subject, msg, settings.DEFAULT_FROM_EMAIL, [recipient])
bgneal@232 75
bgneal@232 76
bgneal@232 77 @login_required
bgneal@232 78 @require_POST
bgneal@232 79 def subscribe_topic(request, topic_id):
bgneal@232 80 """Subscribe the user to the requested topic."""
bgneal@232 81 topic = get_object_or_404(Topic.objects.select_related(), id=topic_id)
bgneal@232 82 if topic.forum.category.can_access(request.user):
bgneal@301 83 sub = Subscription(topic=topic, user=request.user)
bgneal@301 84 sub.save()
bgneal@232 85 return HttpResponseRedirect(
bgneal@232 86 reverse("forums-subscription_status", args=[topic.id]))
bgneal@232 87 raise Http404 # TODO return HttpResponseForbidden instead
bgneal@232 88
bgneal@232 89
bgneal@232 90 @login_required
bgneal@232 91 @require_POST
bgneal@232 92 def unsubscribe_topic(request, topic_id):
bgneal@232 93 """Unsubscribe the user to the requested topic."""
bgneal@232 94 topic = get_object_or_404(Topic, id=topic_id)
bgneal@301 95 try:
bgneal@301 96 sub = Subscription.objects.get(topic=topic, user=request.user)
bgneal@301 97 except Subscriptions.DoesNotExist:
bgneal@301 98 pass
bgneal@301 99 else:
bgneal@301 100 sub.delete()
bgneal@232 101 return HttpResponseRedirect(
bgneal@232 102 reverse("forums-subscription_status", args=[topic.id]))
bgneal@232 103
bgneal@232 104
bgneal@232 105 @login_required
bgneal@232 106 def subscription_status(request, topic_id):
bgneal@232 107 """Display the subscription status for the given topic."""
bgneal@232 108 topic = get_object_or_404(Topic.objects.select_related(), id=topic_id)
bgneal@232 109 is_subscribed = request.user in topic.subscribers.all()
bgneal@232 110 return render_to_response('forums/subscription_status.html', {
bgneal@232 111 'topic': topic,
bgneal@232 112 'is_subscribed': is_subscribed,
bgneal@232 113 },
bgneal@232 114 context_instance=RequestContext(request))
bgneal@232 115
bgneal@232 116
bgneal@232 117 @login_required
bgneal@232 118 def manage_subscriptions(request):
bgneal@232 119 """Display a user's topic subscriptions, and allow them to be deleted."""
bgneal@232 120
bgneal@232 121 user = request.user
bgneal@232 122 if request.method == "POST":
bgneal@232 123 if request.POST.get('delete_all'):
bgneal@232 124 user.subscriptions.clear()
bgneal@232 125 else:
bgneal@232 126 delete_ids = request.POST.getlist('delete_ids')
bgneal@232 127 try:
bgneal@232 128 delete_ids = [int(id) for id in delete_ids]
bgneal@232 129 except ValueError:
bgneal@232 130 raise Http404
bgneal@232 131 for topic in user.subscriptions.filter(id__in=delete_ids):
bgneal@232 132 user.subscriptions.remove(topic)
bgneal@232 133
bgneal@232 134 page_num = request.POST.get('page', 1)
bgneal@232 135 else:
bgneal@232 136 page_num = request.GET.get('page', 1)
bgneal@232 137
bgneal@232 138 topics = user.subscriptions.select_related().order_by('-update_date')
bgneal@232 139 paginator = DiggPaginator(topics, 20, body=5, tail=2, margin=3, padding=2)
bgneal@232 140 try:
bgneal@232 141 page_num = int(page_num)
bgneal@232 142 except ValueError:
bgneal@232 143 page_num = 1
bgneal@232 144 try:
bgneal@232 145 page = paginator.page(page_num)
bgneal@232 146 except InvalidPage:
bgneal@232 147 raise Http404
bgneal@232 148
bgneal@232 149 return render_to_response('forums/manage_topics.html', {
bgneal@232 150 'page_title': 'Topic Subscriptions',
bgneal@232 151 'description': 'The forum topics you are currently subscribed to are listed below.',
bgneal@232 152 'page': page,
bgneal@232 153 },
bgneal@232 154 context_instance=RequestContext(request))