annotate messages/views.py @ 1133:62cfdddd584a

Convert private messages to V3_DESIGN.
author Brian Neal <bgneal@gmail.com>
date Tue, 04 Oct 2016 20:26:34 -0500
parents 79a71b9d0a2a
children
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@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")