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))
|