annotate messages/views.py @ 813:eca0c17ff9c8

Private message refactor: reports should email admins. Added tests for reporting messages and ensuring emails are sent when messages are sent (when user options allow it).
author Brian Neal <bgneal@gmail.com>
date Mon, 08 Sep 2014 20:50:37 -0500
parents 42436d674ba8
children cf486a8e8b43
rev   line source
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@425 14
bgneal@810 15 from messages.models import Flag, Message, Options
bgneal@810 16 from messages.forms import OptionsForm, ComposeForm, ReportForm
bgneal@566 17 from messages.utils import reply_subject
bgneal@436 18 from messages import MSG_BOX_LIMIT
bgneal@813 19 from core.functions import email_admins, quote_message
bgneal@425 20
bgneal@425 21
bgneal@436 22 MSGS_PER_PAGE = 20 # message pagination value
bgneal@425 23
bgneal@813 24 REPORT_SUBJECT = 'A user has flagged a private message'
bgneal@813 25 REPORT_MSG = """Hello,
bgneal@813 26
bgneal@813 27 A user has flagged a private message for review.
bgneal@813 28 """
bgneal@813 29
bgneal@425 30
bgneal@436 31 def _quota_check(box_name, count, request):
bgneal@436 32 """
bgneal@436 33 Checks the message box count against MSG_BOX_LIMIT.
bgneal@436 34 Emits a message to the user if the quota is exceeded.
bgneal@441 35
bgneal@568 36 Returns the percent used as an integer between 0-100.
bgneal@568 37
bgneal@436 38 """
bgneal@436 39 if count >= MSG_BOX_LIMIT:
bgneal@436 40 django_messages.warning(request,
bgneal@436 41 "Your %s is full. Please delete some messages." % box_name)
bgneal@436 42
bgneal@568 43 return 100 * count / MSG_BOX_LIMIT
bgneal@568 44
bgneal@436 45
bgneal@801 46 def _get_page(request, qs):
bgneal@801 47 """Paginates the given queryset and returns a page object"""
bgneal@801 48 paginator = Paginator(qs, MSGS_PER_PAGE)
bgneal@801 49 try:
bgneal@801 50 page = paginator.page(request.GET.get('page', '1'))
bgneal@801 51 except PageNotAnInteger:
bgneal@801 52 page = paginator.page(1)
bgneal@801 53 except EmptyPage:
bgneal@801 54 page = paginator.page(paginator.num_pages)
bgneal@801 55 return page
bgneal@801 56
bgneal@801 57
bgneal@425 58 @login_required
bgneal@801 59 def inbox(request):
bgneal@425 60
bgneal@801 61 msg_list = Message.objects.inbox(request.user)
bgneal@801 62 msg_count = msg_list.count()
bgneal@801 63 pct_used = _quota_check('inbox', msg_count, request)
bgneal@801 64
bgneal@801 65 page = _get_page(request, msg_list)
bgneal@801 66
bgneal@801 67 return render(request, 'messages/inbox.html', {
bgneal@801 68 'tab': 'inbox',
bgneal@801 69 'page': page,
bgneal@801 70 'inbox_pct': pct_used,
bgneal@801 71 'outbox_pct': None,
bgneal@801 72 })
bgneal@801 73
bgneal@801 74
bgneal@801 75 @login_required
bgneal@801 76 def outbox(request):
bgneal@801 77
bgneal@801 78 msg_list = Message.objects.outbox(request.user)
bgneal@801 79 msg_count = msg_list.count()
bgneal@801 80 pct_used = _quota_check('outbox', msg_count, request)
bgneal@801 81
bgneal@801 82 page = _get_page(request, msg_list)
bgneal@801 83
bgneal@801 84 return render(request, 'messages/outbox.html', {
bgneal@801 85 'tab': 'outbox',
bgneal@801 86 'page': page,
bgneal@801 87 'inbox_pct': None,
bgneal@801 88 'outbox_pct': pct_used,
bgneal@425 89 })
bgneal@425 90
bgneal@425 91
bgneal@428 92 @login_required
bgneal@802 93 def trash(request):
bgneal@802 94
bgneal@802 95 msg_list = Message.objects.trash(request.user)
bgneal@802 96
bgneal@802 97 page = _get_page(request, msg_list)
bgneal@802 98
bgneal@802 99 return render(request, 'messages/trash.html', {
bgneal@802 100 'tab': 'trash',
bgneal@802 101 'page': page,
bgneal@802 102 'inbox_pct': None,
bgneal@802 103 'outbox_pct': None,
bgneal@802 104 })
bgneal@802 105
bgneal@802 106
bgneal@802 107 @login_required
bgneal@803 108 def options(request):
bgneal@803 109 """
bgneal@803 110 This view handles the displaying and changing of private message options.
bgneal@803 111
bgneal@803 112 """
bgneal@803 113 if request.method == 'POST':
bgneal@803 114 options = Options.objects.for_user(request.user)
bgneal@803 115 form = OptionsForm(request.POST, instance=options, prefix='opts')
bgneal@803 116 if form.is_valid():
bgneal@803 117 form.save()
bgneal@803 118 django_messages.success(request, 'Options saved.')
bgneal@812 119 return redirect('messages-options')
bgneal@803 120 else:
bgneal@803 121 options = Options.objects.for_user(request.user)
bgneal@803 122 form = OptionsForm(instance=options, prefix='opts')
bgneal@803 123
bgneal@803 124 return render(request, 'messages/options.html', {
bgneal@803 125 'tab': 'options',
bgneal@803 126 'form': form,
bgneal@803 127 })
bgneal@803 128
bgneal@803 129
bgneal@803 130 @login_required
bgneal@804 131 def compose(request):
bgneal@428 132 """
bgneal@804 133 Process or prepare the compose form to create a new private message.
bgneal@428 134
bgneal@428 135 """
bgneal@804 136 if request.method == 'POST':
bgneal@804 137 compose_form = ComposeForm(request.user, request.POST)
bgneal@804 138 if compose_form.is_valid():
bgneal@804 139 compose_form.save()
bgneal@804 140 django_messages.success(request, 'Message sent.')
bgneal@804 141 compose_form = ComposeForm(request.user)
bgneal@804 142 else:
bgneal@804 143 receiver = request.GET.get('to')
bgneal@804 144 if receiver:
bgneal@804 145 form_data = {'receiver': receiver}
bgneal@804 146 compose_form = ComposeForm(request.user, initial=form_data)
bgneal@804 147 else:
bgneal@804 148 compose_form = ComposeForm(request.user)
bgneal@804 149
bgneal@804 150 _quota_check('outbox', Message.objects.outbox(request.user).count(), request)
bgneal@804 151
bgneal@804 152 return render(request, 'messages/compose.html', {
bgneal@804 153 'tab': 'compose',
bgneal@804 154 'compose_form': compose_form,
bgneal@428 155 })
bgneal@428 156
bgneal@428 157
bgneal@804 158 @login_required
bgneal@804 159 def view(request, msg_id):
bgneal@425 160 """
bgneal@805 161 This view function displays a private message for reading to the user. If
bgneal@805 162 the user is a recipient of the message, a reply can be composed and sent.
bgneal@425 163
bgneal@425 164 """
bgneal@812 165 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
bgneal@812 166
bgneal@805 167 if request.method == 'POST':
bgneal@805 168 form = ComposeForm(request.user, request.POST)
bgneal@805 169 if form.is_valid():
bgneal@805 170 form.save()
bgneal@805 171 django_messages.success(request, 'Reply sent.')
bgneal@805 172 return redirect('messages-inbox')
bgneal@805 173 else:
bgneal@805 174 if msg.sender != request.user and msg.receiver != request.user:
bgneal@805 175 django_messages.error(request,
bgneal@805 176 "You don't have permission to read that message.")
bgneal@805 177 return redirect('messages-inbox')
bgneal@425 178
bgneal@805 179 initial_data = {
bgneal@805 180 'receiver': msg.sender.username,
bgneal@805 181 'subject': reply_subject(msg.subject),
bgneal@805 182 'message': quote_message(msg.sender.username, msg.message),
bgneal@805 183 'parent_id': msg.pk,
bgneal@805 184 }
bgneal@425 185
bgneal@805 186 if msg.receiver == request.user:
bgneal@805 187 if msg.read_date is None:
bgneal@805 188 msg.read_date = datetime.datetime.now()
bgneal@805 189 msg.save()
bgneal@805 190 else:
bgneal@805 191 initial_data['receiver'] = msg.receiver.username
bgneal@425 192
bgneal@805 193 form = ComposeForm(request.user, initial=initial_data)
bgneal@425 194
bgneal@810 195 try:
bgneal@810 196 msg_flag = msg.flag
bgneal@810 197 except Flag.DoesNotExist:
bgneal@810 198 msg_flag = None
bgneal@810 199
bgneal@805 200 return render(request, 'messages/view_message.html', {
bgneal@805 201 'msg': msg,
bgneal@805 202 'form': form,
bgneal@810 203 'msg_flag': msg_flag,
bgneal@805 204 })
bgneal@425 205
bgneal@425 206
bgneal@806 207 def _delete_pms(user, pm_ids):
bgneal@425 208 """
bgneal@806 209 Process the request to delete the list of PM ids by user.
bgneal@425 210
bgneal@807 211 Returns the number of PM's deleted.
bgneal@807 212
bgneal@425 213 """
bgneal@806 214 msgs = Message.objects.filter(id__in=pm_ids)
bgneal@806 215 now = datetime.datetime.now()
bgneal@425 216
bgneal@807 217 count = 0
bgneal@425 218 for msg in msgs:
bgneal@425 219 if msg.sender == user:
bgneal@806 220 if msg.receiver_delete_date is not None or msg.read_date is None:
bgneal@425 221 # Both parties deleted the message or receiver hasn't read it
bgneal@425 222 # yet, we can delete it now
bgneal@425 223 msg.delete()
bgneal@425 224 else:
bgneal@806 225 # receiver still has PM in their inbox
bgneal@806 226 msg.sender_delete_date = now
bgneal@425 227 msg.save()
bgneal@807 228 count += 1
bgneal@425 229 elif msg.receiver == user:
bgneal@425 230 if msg.sender_delete_date is not None:
bgneal@425 231 # both parties deleted the message, we can delete it now
bgneal@425 232 msg.delete()
bgneal@425 233 else:
bgneal@806 234 # sender still has PM in their inbox
bgneal@806 235 msg.receiver_delete_date = now
bgneal@425 236 msg.save()
bgneal@807 237 count += 1
bgneal@807 238
bgneal@807 239 return count
bgneal@425 240
bgneal@425 241
bgneal@806 242 @login_required
bgneal@806 243 @require_POST
bgneal@806 244 def delete(request):
bgneal@806 245 """
bgneal@806 246 Deletes the requested PM's. The user must be either a sender or receiver for
bgneal@806 247 this to work.
bgneal@806 248
bgneal@806 249 """
bgneal@806 250 pm_ids = request.POST.getlist('pm_ids')
bgneal@806 251 if pm_ids:
bgneal@807 252 count = _delete_pms(request.user, pm_ids)
bgneal@807 253 msg = '{} message{} deleted.'.format(count, '' if count == 1 else 's')
bgneal@807 254 django_messages.success(request, msg)
bgneal@806 255
bgneal@806 256 # Figure out where to redirect to
bgneal@806 257 src = request.POST.get('src', 'inbox')
bgneal@806 258 try:
bgneal@806 259 page = int(request.POST.get('page', '1'))
bgneal@806 260 except ValueError:
bgneal@806 261 page = 1
bgneal@806 262
bgneal@807 263 view_name = 'messages-inbox' if src == 'inbox' else 'messages-outbox'
bgneal@806 264 url = reverse(view_name) + '?page={}'.format(page)
bgneal@806 265 return redirect(url)
bgneal@806 266
bgneal@806 267
bgneal@807 268 def _undelete_pms(user, msg_ids):
bgneal@425 269 """
bgneal@425 270 Attempts to "undelete" the messages given by the msg_ids list.
bgneal@425 271 This will only succeed if the user is either the sender or receiver.
bgneal@425 272
bgneal@807 273 Returns the number of PM's undeleted.
bgneal@807 274
bgneal@425 275 """
bgneal@425 276 msgs = Message.objects.filter(id__in=msg_ids)
bgneal@807 277 count = 0
bgneal@425 278 for msg in msgs:
bgneal@425 279 if msg.sender == user:
bgneal@425 280 msg.sender_delete_date = None
bgneal@425 281 msg.save()
bgneal@807 282 count += 1
bgneal@425 283 elif msg.receiver == user:
bgneal@425 284 msg.receiver_delete_date = None
bgneal@425 285 msg.save()
bgneal@807 286 count += 1
bgneal@807 287 return count
bgneal@425 288
bgneal@425 289
bgneal@807 290 @login_required
bgneal@807 291 @require_POST
bgneal@807 292 def undelete(request):
bgneal@425 293 """
bgneal@807 294 Undeletes the requested PM's. The user must be either a sender or receiver for
bgneal@807 295 this to work.
bgneal@425 296
bgneal@425 297 """
bgneal@807 298 pm_ids = request.POST.getlist('pm_ids')
bgneal@807 299 if pm_ids:
bgneal@807 300 count = _undelete_pms(request.user, pm_ids)
bgneal@807 301 msg = '{} message{} undeleted.'.format(count, '' if count == 1 else 's')
bgneal@807 302 django_messages.success(request, msg)
bgneal@425 303
bgneal@807 304 # Figure out where to redirect to
bgneal@807 305 try:
bgneal@807 306 page = int(request.POST.get('page', '1'))
bgneal@807 307 except ValueError:
bgneal@807 308 page = 1
bgneal@425 309
bgneal@807 310 url = reverse('messages-trash') + '?page={}'.format(page)
bgneal@807 311 return redirect(url)
bgneal@810 312
bgneal@810 313
bgneal@810 314 @login_required
bgneal@810 315 def report(request, msg_id):
bgneal@810 316 """This view is for reporting a PM as spam or abuse.
bgneal@810 317
bgneal@810 318 """
bgneal@810 319 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
bgneal@810 320 if msg.receiver != request.user:
bgneal@810 321 django_messages.error(request, "You can't report this message.")
bgneal@810 322 return redirect('messages-inbox')
bgneal@810 323 try:
bgneal@810 324 msg.flag
bgneal@810 325 except Flag.DoesNotExist:
bgneal@810 326 pass
bgneal@810 327 else:
bgneal@810 328 django_messages.error(request, "This message has already been reported.")
bgneal@810 329 return redirect('messages-inbox')
bgneal@810 330
bgneal@810 331 if request.method == 'POST':
bgneal@810 332 form = ReportForm(request.POST)
bgneal@810 333 if form.is_valid():
bgneal@810 334 flag = form.save(commit=False)
bgneal@810 335 flag.message = msg
bgneal@810 336 flag.save()
bgneal@813 337 email_admins(REPORT_SUBJECT, REPORT_MSG)
bgneal@810 338 django_messages.success(request,
bgneal@810 339 'Message reported. An admin will be notified. Thank you.')
bgneal@810 340 return redirect('messages-inbox')
bgneal@810 341 else:
bgneal@810 342 form = ReportForm()
bgneal@810 343
bgneal@810 344 return render(request, 'messages/report_message.html', {
bgneal@810 345 'msg': msg,
bgneal@810 346 'form': form,
bgneal@810 347 })