bgneal@425
|
1 """
|
bgneal@425
|
2 Views for the messages application.
|
bgneal@425
|
3
|
bgneal@425
|
4 """
|
bgneal@425
|
5 import datetime
|
bgneal@425
|
6
|
bgneal@425
|
7 from django.contrib.auth.decorators import login_required
|
bgneal@806
|
8 from django.views.decorators.http import require_POST
|
bgneal@436
|
9 from django.contrib import messages as django_messages
|
bgneal@801
|
10 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
bgneal@425
|
11 from django.core.urlresolvers import reverse
|
bgneal@425
|
12 from django.shortcuts import get_object_or_404
|
bgneal@805
|
13 from django.shortcuts import render, redirect
|
bgneal@818
|
14 from django.db.models import Q
|
bgneal@818
|
15 from django.template.loader import render_to_string
|
bgneal@818
|
16 from django.contrib.sites.models import Site
|
bgneal@818
|
17 from django.conf import settings
|
bgneal@425
|
18
|
bgneal@810
|
19 from messages.models import Flag, Message, Options
|
bgneal@810
|
20 from messages.forms import OptionsForm, ComposeForm, ReportForm
|
bgneal@566
|
21 from messages.utils import reply_subject
|
bgneal@436
|
22 from messages import MSG_BOX_LIMIT
|
bgneal@818
|
23 from core.functions import email_admins, quote_message, send_mail
|
bgneal@425
|
24
|
bgneal@425
|
25
|
bgneal@436
|
26 MSGS_PER_PAGE = 20 # message pagination value
|
bgneal@818
|
27 PM_EMAIL_MAX = 100 # max messages we will send via email
|
bgneal@425
|
28
|
bgneal@813
|
29 REPORT_SUBJECT = 'A user has flagged a private message'
|
bgneal@813
|
30 REPORT_MSG = """Hello,
|
bgneal@813
|
31
|
bgneal@813
|
32 A user has flagged a private message for review.
|
bgneal@813
|
33 """
|
bgneal@813
|
34
|
bgneal@425
|
35
|
bgneal@436
|
36 def _quota_check(box_name, count, request):
|
bgneal@436
|
37 """
|
bgneal@436
|
38 Checks the message box count against MSG_BOX_LIMIT.
|
bgneal@436
|
39 Emits a message to the user if the quota is exceeded.
|
bgneal@441
|
40
|
bgneal@568
|
41 Returns the percent used as an integer between 0-100.
|
bgneal@568
|
42
|
bgneal@436
|
43 """
|
bgneal@436
|
44 if count >= MSG_BOX_LIMIT:
|
bgneal@436
|
45 django_messages.warning(request,
|
bgneal@436
|
46 "Your %s is full. Please delete some messages." % box_name)
|
bgneal@436
|
47
|
bgneal@568
|
48 return 100 * count / MSG_BOX_LIMIT
|
bgneal@568
|
49
|
bgneal@436
|
50
|
bgneal@801
|
51 def _get_page(request, qs):
|
bgneal@801
|
52 """Paginates the given queryset and returns a page object"""
|
bgneal@801
|
53 paginator = Paginator(qs, MSGS_PER_PAGE)
|
bgneal@801
|
54 try:
|
bgneal@801
|
55 page = paginator.page(request.GET.get('page', '1'))
|
bgneal@801
|
56 except PageNotAnInteger:
|
bgneal@801
|
57 page = paginator.page(1)
|
bgneal@801
|
58 except EmptyPage:
|
bgneal@801
|
59 page = paginator.page(paginator.num_pages)
|
bgneal@801
|
60 return page
|
bgneal@801
|
61
|
bgneal@801
|
62
|
bgneal@425
|
63 @login_required
|
bgneal@801
|
64 def inbox(request):
|
bgneal@425
|
65
|
bgneal@801
|
66 msg_list = Message.objects.inbox(request.user)
|
bgneal@801
|
67 msg_count = msg_list.count()
|
bgneal@801
|
68 pct_used = _quota_check('inbox', msg_count, request)
|
bgneal@801
|
69
|
bgneal@801
|
70 page = _get_page(request, msg_list)
|
bgneal@801
|
71
|
bgneal@801
|
72 return render(request, 'messages/inbox.html', {
|
bgneal@801
|
73 'tab': 'inbox',
|
bgneal@801
|
74 'page': page,
|
bgneal@801
|
75 'inbox_pct': pct_used,
|
bgneal@801
|
76 'outbox_pct': None,
|
bgneal@1133
|
77 'V3_DESIGN': True,
|
bgneal@801
|
78 })
|
bgneal@801
|
79
|
bgneal@801
|
80
|
bgneal@801
|
81 @login_required
|
bgneal@801
|
82 def outbox(request):
|
bgneal@801
|
83
|
bgneal@801
|
84 msg_list = Message.objects.outbox(request.user)
|
bgneal@801
|
85 msg_count = msg_list.count()
|
bgneal@801
|
86 pct_used = _quota_check('outbox', msg_count, request)
|
bgneal@801
|
87
|
bgneal@801
|
88 page = _get_page(request, msg_list)
|
bgneal@801
|
89
|
bgneal@801
|
90 return render(request, 'messages/outbox.html', {
|
bgneal@801
|
91 'tab': 'outbox',
|
bgneal@801
|
92 'page': page,
|
bgneal@801
|
93 'inbox_pct': None,
|
bgneal@801
|
94 'outbox_pct': pct_used,
|
bgneal@1133
|
95 'V3_DESIGN': True,
|
bgneal@425
|
96 })
|
bgneal@425
|
97
|
bgneal@425
|
98
|
bgneal@428
|
99 @login_required
|
bgneal@802
|
100 def trash(request):
|
bgneal@802
|
101
|
bgneal@802
|
102 msg_list = Message.objects.trash(request.user)
|
bgneal@802
|
103
|
bgneal@802
|
104 page = _get_page(request, msg_list)
|
bgneal@802
|
105
|
bgneal@802
|
106 return render(request, 'messages/trash.html', {
|
bgneal@802
|
107 'tab': 'trash',
|
bgneal@802
|
108 'page': page,
|
bgneal@802
|
109 'inbox_pct': None,
|
bgneal@802
|
110 'outbox_pct': None,
|
bgneal@1133
|
111 'V3_DESIGN': True,
|
bgneal@802
|
112 })
|
bgneal@802
|
113
|
bgneal@802
|
114
|
bgneal@802
|
115 @login_required
|
bgneal@803
|
116 def options(request):
|
bgneal@803
|
117 """
|
bgneal@803
|
118 This view handles the displaying and changing of private message options.
|
bgneal@803
|
119
|
bgneal@803
|
120 """
|
bgneal@803
|
121 if request.method == 'POST':
|
bgneal@803
|
122 options = Options.objects.for_user(request.user)
|
bgneal@803
|
123 form = OptionsForm(request.POST, instance=options, prefix='opts')
|
bgneal@803
|
124 if form.is_valid():
|
bgneal@803
|
125 form.save()
|
bgneal@803
|
126 django_messages.success(request, 'Options saved.')
|
bgneal@812
|
127 return redirect('messages-options')
|
bgneal@803
|
128 else:
|
bgneal@803
|
129 options = Options.objects.for_user(request.user)
|
bgneal@803
|
130 form = OptionsForm(instance=options, prefix='opts')
|
bgneal@803
|
131
|
bgneal@803
|
132 return render(request, 'messages/options.html', {
|
bgneal@803
|
133 'tab': 'options',
|
bgneal@803
|
134 'form': form,
|
bgneal@1133
|
135 'V3_DESIGN': True,
|
bgneal@803
|
136 })
|
bgneal@803
|
137
|
bgneal@803
|
138
|
bgneal@803
|
139 @login_required
|
bgneal@804
|
140 def compose(request):
|
bgneal@428
|
141 """
|
bgneal@804
|
142 Process or prepare the compose form to create a new private message.
|
bgneal@428
|
143
|
bgneal@428
|
144 """
|
bgneal@804
|
145 if request.method == 'POST':
|
bgneal@804
|
146 compose_form = ComposeForm(request.user, request.POST)
|
bgneal@804
|
147 if compose_form.is_valid():
|
bgneal@804
|
148 compose_form.save()
|
bgneal@804
|
149 django_messages.success(request, 'Message sent.')
|
bgneal@804
|
150 compose_form = ComposeForm(request.user)
|
bgneal@804
|
151 else:
|
bgneal@804
|
152 receiver = request.GET.get('to')
|
bgneal@804
|
153 if receiver:
|
bgneal@804
|
154 form_data = {'receiver': receiver}
|
bgneal@804
|
155 compose_form = ComposeForm(request.user, initial=form_data)
|
bgneal@804
|
156 else:
|
bgneal@804
|
157 compose_form = ComposeForm(request.user)
|
bgneal@804
|
158
|
bgneal@804
|
159 _quota_check('outbox', Message.objects.outbox(request.user).count(), request)
|
bgneal@804
|
160
|
bgneal@804
|
161 return render(request, 'messages/compose.html', {
|
bgneal@804
|
162 'tab': 'compose',
|
bgneal@804
|
163 'compose_form': compose_form,
|
bgneal@1133
|
164 'V3_DESIGN': True,
|
bgneal@428
|
165 })
|
bgneal@428
|
166
|
bgneal@428
|
167
|
bgneal@804
|
168 @login_required
|
bgneal@804
|
169 def view(request, msg_id):
|
bgneal@425
|
170 """
|
bgneal@805
|
171 This view function displays a private message for reading to the user. If
|
bgneal@805
|
172 the user is a recipient of the message, a reply can be composed and sent.
|
bgneal@425
|
173
|
bgneal@425
|
174 """
|
bgneal@812
|
175 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
|
bgneal@812
|
176
|
bgneal@805
|
177 if request.method == 'POST':
|
bgneal@805
|
178 form = ComposeForm(request.user, request.POST)
|
bgneal@805
|
179 if form.is_valid():
|
bgneal@805
|
180 form.save()
|
bgneal@805
|
181 django_messages.success(request, 'Reply sent.')
|
bgneal@805
|
182 return redirect('messages-inbox')
|
bgneal@805
|
183 else:
|
bgneal@805
|
184 if msg.sender != request.user and msg.receiver != request.user:
|
bgneal@805
|
185 django_messages.error(request,
|
bgneal@1133
|
186 "You don't have permission to read that message.",
|
bgneal@1133
|
187 extra_tags="alert")
|
bgneal@1133
|
188
|
bgneal@805
|
189 return redirect('messages-inbox')
|
bgneal@425
|
190
|
bgneal@805
|
191 initial_data = {
|
bgneal@805
|
192 'receiver': msg.sender.username,
|
bgneal@805
|
193 'subject': reply_subject(msg.subject),
|
bgneal@805
|
194 'message': quote_message(msg.sender.username, msg.message),
|
bgneal@805
|
195 'parent_id': msg.pk,
|
bgneal@805
|
196 }
|
bgneal@425
|
197
|
bgneal@805
|
198 if msg.receiver == request.user:
|
bgneal@805
|
199 if msg.read_date is None:
|
bgneal@805
|
200 msg.read_date = datetime.datetime.now()
|
bgneal@805
|
201 msg.save()
|
bgneal@805
|
202 else:
|
bgneal@805
|
203 initial_data['receiver'] = msg.receiver.username
|
bgneal@425
|
204
|
bgneal@805
|
205 form = ComposeForm(request.user, initial=initial_data)
|
bgneal@425
|
206
|
bgneal@810
|
207 try:
|
bgneal@810
|
208 msg_flag = msg.flag
|
bgneal@810
|
209 except Flag.DoesNotExist:
|
bgneal@810
|
210 msg_flag = None
|
bgneal@810
|
211
|
bgneal@805
|
212 return render(request, 'messages/view_message.html', {
|
bgneal@805
|
213 'msg': msg,
|
bgneal@805
|
214 'form': form,
|
bgneal@810
|
215 'msg_flag': msg_flag,
|
bgneal@1133
|
216 'V3_DESIGN': True,
|
bgneal@805
|
217 })
|
bgneal@425
|
218
|
bgneal@425
|
219
|
bgneal@818
|
220 @login_required
|
bgneal@818
|
221 @require_POST
|
bgneal@818
|
222 def bulk(request):
|
bgneal@818
|
223 """
|
bgneal@818
|
224 Perform bulk action on a list of PMs.
|
bgneal@818
|
225
|
bgneal@818
|
226 """
|
bgneal@818
|
227 pm_ids = request.POST.getlist('pm_ids')
|
bgneal@818
|
228
|
bgneal@818
|
229 # Figure out what action to perform
|
bgneal@818
|
230 action = None
|
bgneal@818
|
231 action_val = request.POST.get('action', '')
|
bgneal@818
|
232 if action_val.startswith('Delete'):
|
bgneal@818
|
233 action = _delete_pms
|
bgneal@818
|
234 elif action_val.startswith('Email'):
|
bgneal@818
|
235 action = _email_pms
|
bgneal@818
|
236
|
bgneal@818
|
237 if pm_ids and action:
|
bgneal@818
|
238 action(request, pm_ids)
|
bgneal@818
|
239
|
bgneal@818
|
240 # Figure out where to redirect to
|
bgneal@818
|
241 src = request.POST.get('src', 'inbox')
|
bgneal@818
|
242 try:
|
bgneal@818
|
243 page = int(request.POST.get('page', '1'))
|
bgneal@818
|
244 except ValueError:
|
bgneal@818
|
245 page = 1
|
bgneal@818
|
246
|
bgneal@818
|
247 view_name = 'messages-inbox' if src == 'inbox' else 'messages-outbox'
|
bgneal@818
|
248 url = reverse(view_name) + '?page={}'.format(page)
|
bgneal@818
|
249 return redirect(url)
|
bgneal@818
|
250
|
bgneal@818
|
251
|
bgneal@818
|
252 def _delete_pms(request, pm_ids):
|
bgneal@425
|
253 """
|
bgneal@806
|
254 Process the request to delete the list of PM ids by user.
|
bgneal@425
|
255
|
bgneal@807
|
256 Returns the number of PM's deleted.
|
bgneal@807
|
257
|
bgneal@425
|
258 """
|
bgneal@818
|
259 user = request.user
|
bgneal@806
|
260 msgs = Message.objects.filter(id__in=pm_ids)
|
bgneal@806
|
261 now = datetime.datetime.now()
|
bgneal@425
|
262
|
bgneal@807
|
263 count = 0
|
bgneal@425
|
264 for msg in msgs:
|
bgneal@425
|
265 if msg.sender == user:
|
bgneal@806
|
266 if msg.receiver_delete_date is not None or msg.read_date is None:
|
bgneal@425
|
267 # Both parties deleted the message or receiver hasn't read it
|
bgneal@425
|
268 # yet, we can delete it now
|
bgneal@425
|
269 msg.delete()
|
bgneal@425
|
270 else:
|
bgneal@806
|
271 # receiver still has PM in their inbox
|
bgneal@806
|
272 msg.sender_delete_date = now
|
bgneal@425
|
273 msg.save()
|
bgneal@807
|
274 count += 1
|
bgneal@425
|
275 elif msg.receiver == user:
|
bgneal@425
|
276 if msg.sender_delete_date is not None:
|
bgneal@425
|
277 # both parties deleted the message, we can delete it now
|
bgneal@425
|
278 msg.delete()
|
bgneal@425
|
279 else:
|
bgneal@806
|
280 # sender still has PM in their inbox
|
bgneal@806
|
281 msg.receiver_delete_date = now
|
bgneal@425
|
282 msg.save()
|
bgneal@807
|
283 count += 1
|
bgneal@807
|
284
|
bgneal@818
|
285 msg = '{} message{} deleted.'.format(count, '' if count == 1 else 's')
|
bgneal@818
|
286 if count > 0:
|
bgneal@807
|
287 django_messages.success(request, msg)
|
bgneal@818
|
288 else:
|
bgneal@1133
|
289 django_messages.error(request, msg, extra_tags="alert")
|
bgneal@806
|
290
|
bgneal@806
|
291
|
bgneal@807
|
292 def _undelete_pms(user, msg_ids):
|
bgneal@425
|
293 """
|
bgneal@425
|
294 Attempts to "undelete" the messages given by the msg_ids list.
|
bgneal@425
|
295 This will only succeed if the user is either the sender or receiver.
|
bgneal@425
|
296
|
bgneal@807
|
297 Returns the number of PM's undeleted.
|
bgneal@807
|
298
|
bgneal@425
|
299 """
|
bgneal@425
|
300 msgs = Message.objects.filter(id__in=msg_ids)
|
bgneal@807
|
301 count = 0
|
bgneal@425
|
302 for msg in msgs:
|
bgneal@425
|
303 if msg.sender == user:
|
bgneal@425
|
304 msg.sender_delete_date = None
|
bgneal@425
|
305 msg.save()
|
bgneal@807
|
306 count += 1
|
bgneal@425
|
307 elif msg.receiver == user:
|
bgneal@425
|
308 msg.receiver_delete_date = None
|
bgneal@425
|
309 msg.save()
|
bgneal@807
|
310 count += 1
|
bgneal@807
|
311 return count
|
bgneal@425
|
312
|
bgneal@425
|
313
|
bgneal@807
|
314 @login_required
|
bgneal@807
|
315 @require_POST
|
bgneal@807
|
316 def undelete(request):
|
bgneal@425
|
317 """
|
bgneal@807
|
318 Undeletes the requested PM's. The user must be either a sender or receiver for
|
bgneal@807
|
319 this to work.
|
bgneal@425
|
320
|
bgneal@425
|
321 """
|
bgneal@807
|
322 pm_ids = request.POST.getlist('pm_ids')
|
bgneal@807
|
323 if pm_ids:
|
bgneal@807
|
324 count = _undelete_pms(request.user, pm_ids)
|
bgneal@807
|
325 msg = '{} message{} undeleted.'.format(count, '' if count == 1 else 's')
|
bgneal@807
|
326 django_messages.success(request, msg)
|
bgneal@425
|
327
|
bgneal@807
|
328 # Figure out where to redirect to
|
bgneal@807
|
329 try:
|
bgneal@807
|
330 page = int(request.POST.get('page', '1'))
|
bgneal@807
|
331 except ValueError:
|
bgneal@807
|
332 page = 1
|
bgneal@425
|
333
|
bgneal@807
|
334 url = reverse('messages-trash') + '?page={}'.format(page)
|
bgneal@807
|
335 return redirect(url)
|
bgneal@810
|
336
|
bgneal@810
|
337
|
bgneal@810
|
338 @login_required
|
bgneal@810
|
339 def report(request, msg_id):
|
bgneal@810
|
340 """This view is for reporting a PM as spam or abuse.
|
bgneal@810
|
341
|
bgneal@810
|
342 """
|
bgneal@810
|
343 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
|
bgneal@810
|
344 if msg.receiver != request.user:
|
bgneal@1133
|
345 django_messages.error(request, "You can't report this message.",
|
bgneal@1133
|
346 extra_tags="alert")
|
bgneal@810
|
347 return redirect('messages-inbox')
|
bgneal@810
|
348 try:
|
bgneal@810
|
349 msg.flag
|
bgneal@810
|
350 except Flag.DoesNotExist:
|
bgneal@810
|
351 pass
|
bgneal@810
|
352 else:
|
bgneal@1133
|
353 django_messages.error(request, "This message has already been reported.",
|
bgneal@1133
|
354 extra_tags="alert")
|
bgneal@810
|
355 return redirect('messages-inbox')
|
bgneal@810
|
356
|
bgneal@810
|
357 if request.method == 'POST':
|
bgneal@810
|
358 form = ReportForm(request.POST)
|
bgneal@810
|
359 if form.is_valid():
|
bgneal@810
|
360 flag = form.save(commit=False)
|
bgneal@810
|
361 flag.message = msg
|
bgneal@810
|
362 flag.save()
|
bgneal@813
|
363 email_admins(REPORT_SUBJECT, REPORT_MSG)
|
bgneal@810
|
364 django_messages.success(request,
|
bgneal@810
|
365 'Message reported. An admin will be notified. Thank you.')
|
bgneal@810
|
366 return redirect('messages-inbox')
|
bgneal@810
|
367 else:
|
bgneal@810
|
368 form = ReportForm()
|
bgneal@810
|
369
|
bgneal@810
|
370 return render(request, 'messages/report_message.html', {
|
bgneal@810
|
371 'msg': msg,
|
bgneal@810
|
372 'form': form,
|
bgneal@1133
|
373 'V3_DESIGN': True,
|
bgneal@810
|
374 })
|
bgneal@818
|
375
|
bgneal@818
|
376
|
bgneal@818
|
377 def _email_pms(request, msg_ids):
|
bgneal@818
|
378 """
|
bgneal@818
|
379 Attempts to email the messages given by the msg_ids list to the user.
|
bgneal@818
|
380 This will only succeed if the user is either the sender or receiver.
|
bgneal@818
|
381
|
bgneal@818
|
382 Returns the number of PM's sent.
|
bgneal@818
|
383
|
bgneal@818
|
384 """
|
bgneal@818
|
385 # Does the user have an email address?
|
bgneal@818
|
386 user = request.user
|
bgneal@818
|
387 email_addr = user.email
|
bgneal@818
|
388 if not email_addr:
|
bgneal@818
|
389 return 0
|
bgneal@818
|
390
|
bgneal@818
|
391 msgs = Message.objects.filter(
|
bgneal@818
|
392 Q(sender=user) | Q(receiver=user),
|
bgneal@818
|
393 id__in=msg_ids).order_by('pk')[:PM_EMAIL_MAX]
|
bgneal@818
|
394
|
bgneal@818
|
395 count = len(msgs)
|
bgneal@818
|
396 if count == 0:
|
bgneal@818
|
397 return 0
|
bgneal@818
|
398
|
bgneal@818
|
399 site = Site.objects.get_current()
|
bgneal@818
|
400 admin_email = settings.ADMINS[0][1]
|
bgneal@818
|
401
|
bgneal@818
|
402 subject = 'Private messages from {} - {}'.format(site.domain,
|
bgneal@818
|
403 msgs[0].subject)
|
bgneal@818
|
404 if count > 1:
|
bgneal@818
|
405 subject += ' (et al.)'
|
bgneal@818
|
406 from_email = '{}@{}'.format(settings.GPP_NO_REPLY_EMAIL, site.domain)
|
bgneal@818
|
407 msg_body = render_to_string('messages/pm_email.txt', {
|
bgneal@818
|
408 'site': site,
|
bgneal@818
|
409 'user': user,
|
bgneal@818
|
410 'msgs': msgs,
|
bgneal@818
|
411 'admin_email': admin_email,
|
bgneal@818
|
412 })
|
bgneal@892
|
413 send_mail(subject, msg_body, from_email, [email_addr])
|
bgneal@818
|
414
|
bgneal@818
|
415 msg = '{} message{} sent to email.'.format(count, '' if count == 1 else 's')
|
bgneal@818
|
416 if count > 0:
|
bgneal@818
|
417 django_messages.success(request, msg)
|
bgneal@818
|
418 else:
|
bgneal@1133
|
419 django_messages.error(request, msg, extra_tags="alert")
|