annotate messages/views.py @ 812:42436d674ba8

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