annotate messages/views.py @ 811:56b30c79f10e

Private messages refactor: start adding tests.
author Brian Neal <bgneal@gmail.com>
date Sun, 07 Sep 2014 13:12:19 -0500
parents 4a4fa174a0ec
children 42436d674ba8
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@803 113 else:
bgneal@803 114 options = Options.objects.for_user(request.user)
bgneal@803 115 form = OptionsForm(instance=options, prefix='opts')
bgneal@803 116
bgneal@803 117 return render(request, 'messages/options.html', {
bgneal@803 118 'tab': 'options',
bgneal@803 119 'form': form,
bgneal@803 120 })
bgneal@803 121
bgneal@803 122
bgneal@803 123 @login_required
bgneal@804 124 def compose(request):
bgneal@428 125 """
bgneal@804 126 Process or prepare the compose form to create a new private message.
bgneal@428 127
bgneal@428 128 """
bgneal@804 129 if request.method == 'POST':
bgneal@804 130 compose_form = ComposeForm(request.user, request.POST)
bgneal@804 131 if compose_form.is_valid():
bgneal@804 132 compose_form.save()
bgneal@804 133 django_messages.success(request, 'Message sent.')
bgneal@804 134 compose_form = ComposeForm(request.user)
bgneal@804 135 else:
bgneal@804 136 receiver = request.GET.get('to')
bgneal@804 137 if receiver:
bgneal@804 138 form_data = {'receiver': receiver}
bgneal@804 139 compose_form = ComposeForm(request.user, initial=form_data)
bgneal@804 140 else:
bgneal@804 141 compose_form = ComposeForm(request.user)
bgneal@804 142
bgneal@804 143 _quota_check('outbox', Message.objects.outbox(request.user).count(), request)
bgneal@804 144
bgneal@804 145 return render(request, 'messages/compose.html', {
bgneal@804 146 'tab': 'compose',
bgneal@804 147 'compose_form': compose_form,
bgneal@428 148 })
bgneal@428 149
bgneal@428 150
bgneal@804 151 @login_required
bgneal@804 152 def view(request, msg_id):
bgneal@425 153 """
bgneal@805 154 This view function displays a private message for reading to the user. If
bgneal@805 155 the user is a recipient of the message, a reply can be composed and sent.
bgneal@425 156
bgneal@425 157 """
bgneal@805 158 if request.method == 'POST':
bgneal@805 159 form = ComposeForm(request.user, request.POST)
bgneal@805 160 if form.is_valid():
bgneal@805 161 form.save()
bgneal@805 162 django_messages.success(request, 'Reply sent.')
bgneal@805 163 return redirect('messages-inbox')
bgneal@805 164 else:
bgneal@805 165 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
bgneal@805 166 if msg.sender != request.user and msg.receiver != request.user:
bgneal@805 167 django_messages.error(request,
bgneal@805 168 "You don't have permission to read that message.")
bgneal@805 169 return redirect('messages-inbox')
bgneal@425 170
bgneal@805 171 initial_data = {
bgneal@805 172 'receiver': msg.sender.username,
bgneal@805 173 'subject': reply_subject(msg.subject),
bgneal@805 174 'message': quote_message(msg.sender.username, msg.message),
bgneal@805 175 'parent_id': msg.pk,
bgneal@805 176 }
bgneal@425 177
bgneal@805 178 if msg.receiver == request.user:
bgneal@805 179 if msg.read_date is None:
bgneal@805 180 msg.read_date = datetime.datetime.now()
bgneal@805 181 msg.save()
bgneal@805 182 else:
bgneal@805 183 initial_data['receiver'] = msg.receiver.username
bgneal@425 184
bgneal@805 185 form = ComposeForm(request.user, initial=initial_data)
bgneal@425 186
bgneal@810 187 try:
bgneal@810 188 msg_flag = msg.flag
bgneal@810 189 except Flag.DoesNotExist:
bgneal@810 190 msg_flag = None
bgneal@810 191
bgneal@805 192 return render(request, 'messages/view_message.html', {
bgneal@805 193 'msg': msg,
bgneal@805 194 'form': form,
bgneal@810 195 'msg_flag': msg_flag,
bgneal@805 196 })
bgneal@425 197
bgneal@425 198
bgneal@806 199 def _delete_pms(user, pm_ids):
bgneal@425 200 """
bgneal@806 201 Process the request to delete the list of PM ids by user.
bgneal@425 202
bgneal@807 203 Returns the number of PM's deleted.
bgneal@807 204
bgneal@425 205 """
bgneal@806 206 msgs = Message.objects.filter(id__in=pm_ids)
bgneal@806 207 now = datetime.datetime.now()
bgneal@425 208
bgneal@807 209 count = 0
bgneal@425 210 for msg in msgs:
bgneal@425 211 if msg.sender == user:
bgneal@806 212 if msg.receiver_delete_date is not None or msg.read_date is None:
bgneal@425 213 # Both parties deleted the message or receiver hasn't read it
bgneal@425 214 # yet, we can delete it now
bgneal@425 215 msg.delete()
bgneal@425 216 else:
bgneal@806 217 # receiver still has PM in their inbox
bgneal@806 218 msg.sender_delete_date = now
bgneal@425 219 msg.save()
bgneal@807 220 count += 1
bgneal@425 221 elif msg.receiver == user:
bgneal@425 222 if msg.sender_delete_date is not None:
bgneal@425 223 # both parties deleted the message, we can delete it now
bgneal@425 224 msg.delete()
bgneal@425 225 else:
bgneal@806 226 # sender still has PM in their inbox
bgneal@806 227 msg.receiver_delete_date = now
bgneal@425 228 msg.save()
bgneal@807 229 count += 1
bgneal@807 230
bgneal@807 231 return count
bgneal@425 232
bgneal@425 233
bgneal@806 234 @login_required
bgneal@806 235 @require_POST
bgneal@806 236 def delete(request):
bgneal@806 237 """
bgneal@806 238 Deletes the requested PM's. The user must be either a sender or receiver for
bgneal@806 239 this to work.
bgneal@806 240
bgneal@806 241 """
bgneal@806 242 pm_ids = request.POST.getlist('pm_ids')
bgneal@806 243 if pm_ids:
bgneal@807 244 count = _delete_pms(request.user, pm_ids)
bgneal@807 245 msg = '{} message{} deleted.'.format(count, '' if count == 1 else 's')
bgneal@807 246 django_messages.success(request, msg)
bgneal@806 247
bgneal@806 248 # Figure out where to redirect to
bgneal@806 249 src = request.POST.get('src', 'inbox')
bgneal@806 250 try:
bgneal@806 251 page = int(request.POST.get('page', '1'))
bgneal@806 252 except ValueError:
bgneal@806 253 page = 1
bgneal@806 254
bgneal@807 255 view_name = 'messages-inbox' if src == 'inbox' else 'messages-outbox'
bgneal@806 256 url = reverse(view_name) + '?page={}'.format(page)
bgneal@806 257 return redirect(url)
bgneal@806 258
bgneal@806 259
bgneal@807 260 def _undelete_pms(user, msg_ids):
bgneal@425 261 """
bgneal@425 262 Attempts to "undelete" the messages given by the msg_ids list.
bgneal@425 263 This will only succeed if the user is either the sender or receiver.
bgneal@425 264
bgneal@807 265 Returns the number of PM's undeleted.
bgneal@807 266
bgneal@425 267 """
bgneal@425 268 msgs = Message.objects.filter(id__in=msg_ids)
bgneal@807 269 count = 0
bgneal@425 270 for msg in msgs:
bgneal@425 271 if msg.sender == user:
bgneal@425 272 msg.sender_delete_date = None
bgneal@425 273 msg.save()
bgneal@807 274 count += 1
bgneal@425 275 elif msg.receiver == user:
bgneal@425 276 msg.receiver_delete_date = None
bgneal@425 277 msg.save()
bgneal@807 278 count += 1
bgneal@807 279 return count
bgneal@425 280
bgneal@425 281
bgneal@807 282 @login_required
bgneal@807 283 @require_POST
bgneal@807 284 def undelete(request):
bgneal@425 285 """
bgneal@807 286 Undeletes the requested PM's. The user must be either a sender or receiver for
bgneal@807 287 this to work.
bgneal@425 288
bgneal@425 289 """
bgneal@807 290 pm_ids = request.POST.getlist('pm_ids')
bgneal@807 291 if pm_ids:
bgneal@807 292 count = _undelete_pms(request.user, pm_ids)
bgneal@807 293 msg = '{} message{} undeleted.'.format(count, '' if count == 1 else 's')
bgneal@807 294 django_messages.success(request, msg)
bgneal@425 295
bgneal@807 296 # Figure out where to redirect to
bgneal@807 297 try:
bgneal@807 298 page = int(request.POST.get('page', '1'))
bgneal@807 299 except ValueError:
bgneal@807 300 page = 1
bgneal@425 301
bgneal@807 302 url = reverse('messages-trash') + '?page={}'.format(page)
bgneal@807 303 return redirect(url)
bgneal@810 304
bgneal@810 305
bgneal@810 306 @login_required
bgneal@810 307 def report(request, msg_id):
bgneal@810 308 """This view is for reporting a PM as spam or abuse.
bgneal@810 309
bgneal@810 310 """
bgneal@810 311 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
bgneal@810 312 if msg.receiver != request.user:
bgneal@810 313 django_messages.error(request, "You can't report this message.")
bgneal@810 314 return redirect('messages-inbox')
bgneal@810 315 try:
bgneal@810 316 msg.flag
bgneal@810 317 except Flag.DoesNotExist:
bgneal@810 318 pass
bgneal@810 319 else:
bgneal@810 320 django_messages.error(request, "This message has already been reported.")
bgneal@810 321 return redirect('messages-inbox')
bgneal@810 322
bgneal@810 323 if request.method == 'POST':
bgneal@810 324 form = ReportForm(request.POST)
bgneal@810 325 if form.is_valid():
bgneal@810 326 flag = form.save(commit=False)
bgneal@810 327 flag.message = msg
bgneal@810 328 flag.save()
bgneal@810 329 django_messages.success(request,
bgneal@810 330 'Message reported. An admin will be notified. Thank you.')
bgneal@810 331 return redirect('messages-inbox')
bgneal@810 332 else:
bgneal@810 333 form = ReportForm()
bgneal@810 334
bgneal@810 335 return render(request, 'messages/report_message.html', {
bgneal@810 336 'msg': msg,
bgneal@810 337 'form': form,
bgneal@810 338 })