annotate messages/views.py @ 818:cf486a8e8b43

Added the ability to export private messages to email.
author Brian Neal <bgneal@gmail.com>
date Sat, 13 Sep 2014 16:19:46 -0500
parents eca0c17ff9c8
children 79a71b9d0a2a
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@801 77 })
bgneal@801 78
bgneal@801 79
bgneal@801 80 @login_required
bgneal@801 81 def outbox(request):
bgneal@801 82
bgneal@801 83 msg_list = Message.objects.outbox(request.user)
bgneal@801 84 msg_count = msg_list.count()
bgneal@801 85 pct_used = _quota_check('outbox', msg_count, request)
bgneal@801 86
bgneal@801 87 page = _get_page(request, msg_list)
bgneal@801 88
bgneal@801 89 return render(request, 'messages/outbox.html', {
bgneal@801 90 'tab': 'outbox',
bgneal@801 91 'page': page,
bgneal@801 92 'inbox_pct': None,
bgneal@801 93 'outbox_pct': pct_used,
bgneal@425 94 })
bgneal@425 95
bgneal@425 96
bgneal@428 97 @login_required
bgneal@802 98 def trash(request):
bgneal@802 99
bgneal@802 100 msg_list = Message.objects.trash(request.user)
bgneal@802 101
bgneal@802 102 page = _get_page(request, msg_list)
bgneal@802 103
bgneal@802 104 return render(request, 'messages/trash.html', {
bgneal@802 105 'tab': 'trash',
bgneal@802 106 'page': page,
bgneal@802 107 'inbox_pct': None,
bgneal@802 108 'outbox_pct': None,
bgneal@802 109 })
bgneal@802 110
bgneal@802 111
bgneal@802 112 @login_required
bgneal@803 113 def options(request):
bgneal@803 114 """
bgneal@803 115 This view handles the displaying and changing of private message options.
bgneal@803 116
bgneal@803 117 """
bgneal@803 118 if request.method == 'POST':
bgneal@803 119 options = Options.objects.for_user(request.user)
bgneal@803 120 form = OptionsForm(request.POST, instance=options, prefix='opts')
bgneal@803 121 if form.is_valid():
bgneal@803 122 form.save()
bgneal@803 123 django_messages.success(request, 'Options saved.')
bgneal@812 124 return redirect('messages-options')
bgneal@803 125 else:
bgneal@803 126 options = Options.objects.for_user(request.user)
bgneal@803 127 form = OptionsForm(instance=options, prefix='opts')
bgneal@803 128
bgneal@803 129 return render(request, 'messages/options.html', {
bgneal@803 130 'tab': 'options',
bgneal@803 131 'form': form,
bgneal@803 132 })
bgneal@803 133
bgneal@803 134
bgneal@803 135 @login_required
bgneal@804 136 def compose(request):
bgneal@428 137 """
bgneal@804 138 Process or prepare the compose form to create a new private message.
bgneal@428 139
bgneal@428 140 """
bgneal@804 141 if request.method == 'POST':
bgneal@804 142 compose_form = ComposeForm(request.user, request.POST)
bgneal@804 143 if compose_form.is_valid():
bgneal@804 144 compose_form.save()
bgneal@804 145 django_messages.success(request, 'Message sent.')
bgneal@804 146 compose_form = ComposeForm(request.user)
bgneal@804 147 else:
bgneal@804 148 receiver = request.GET.get('to')
bgneal@804 149 if receiver:
bgneal@804 150 form_data = {'receiver': receiver}
bgneal@804 151 compose_form = ComposeForm(request.user, initial=form_data)
bgneal@804 152 else:
bgneal@804 153 compose_form = ComposeForm(request.user)
bgneal@804 154
bgneal@804 155 _quota_check('outbox', Message.objects.outbox(request.user).count(), request)
bgneal@804 156
bgneal@804 157 return render(request, 'messages/compose.html', {
bgneal@804 158 'tab': 'compose',
bgneal@804 159 'compose_form': compose_form,
bgneal@428 160 })
bgneal@428 161
bgneal@428 162
bgneal@804 163 @login_required
bgneal@804 164 def view(request, msg_id):
bgneal@425 165 """
bgneal@805 166 This view function displays a private message for reading to the user. If
bgneal@805 167 the user is a recipient of the message, a reply can be composed and sent.
bgneal@425 168
bgneal@425 169 """
bgneal@812 170 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
bgneal@812 171
bgneal@805 172 if request.method == 'POST':
bgneal@805 173 form = ComposeForm(request.user, request.POST)
bgneal@805 174 if form.is_valid():
bgneal@805 175 form.save()
bgneal@805 176 django_messages.success(request, 'Reply sent.')
bgneal@805 177 return redirect('messages-inbox')
bgneal@805 178 else:
bgneal@805 179 if msg.sender != request.user and msg.receiver != request.user:
bgneal@805 180 django_messages.error(request,
bgneal@805 181 "You don't have permission to read that message.")
bgneal@805 182 return redirect('messages-inbox')
bgneal@425 183
bgneal@805 184 initial_data = {
bgneal@805 185 'receiver': msg.sender.username,
bgneal@805 186 'subject': reply_subject(msg.subject),
bgneal@805 187 'message': quote_message(msg.sender.username, msg.message),
bgneal@805 188 'parent_id': msg.pk,
bgneal@805 189 }
bgneal@425 190
bgneal@805 191 if msg.receiver == request.user:
bgneal@805 192 if msg.read_date is None:
bgneal@805 193 msg.read_date = datetime.datetime.now()
bgneal@805 194 msg.save()
bgneal@805 195 else:
bgneal@805 196 initial_data['receiver'] = msg.receiver.username
bgneal@425 197
bgneal@805 198 form = ComposeForm(request.user, initial=initial_data)
bgneal@425 199
bgneal@810 200 try:
bgneal@810 201 msg_flag = msg.flag
bgneal@810 202 except Flag.DoesNotExist:
bgneal@810 203 msg_flag = None
bgneal@810 204
bgneal@805 205 return render(request, 'messages/view_message.html', {
bgneal@805 206 'msg': msg,
bgneal@805 207 'form': form,
bgneal@810 208 'msg_flag': msg_flag,
bgneal@805 209 })
bgneal@425 210
bgneal@425 211
bgneal@818 212 @login_required
bgneal@818 213 @require_POST
bgneal@818 214 def bulk(request):
bgneal@818 215 """
bgneal@818 216 Perform bulk action on a list of PMs.
bgneal@818 217
bgneal@818 218 """
bgneal@818 219 pm_ids = request.POST.getlist('pm_ids')
bgneal@818 220
bgneal@818 221 # Figure out what action to perform
bgneal@818 222 action = None
bgneal@818 223 action_val = request.POST.get('action', '')
bgneal@818 224 if action_val.startswith('Delete'):
bgneal@818 225 action = _delete_pms
bgneal@818 226 elif action_val.startswith('Email'):
bgneal@818 227 action = _email_pms
bgneal@818 228
bgneal@818 229 if pm_ids and action:
bgneal@818 230 action(request, pm_ids)
bgneal@818 231
bgneal@818 232 # Figure out where to redirect to
bgneal@818 233 src = request.POST.get('src', 'inbox')
bgneal@818 234 try:
bgneal@818 235 page = int(request.POST.get('page', '1'))
bgneal@818 236 except ValueError:
bgneal@818 237 page = 1
bgneal@818 238
bgneal@818 239 view_name = 'messages-inbox' if src == 'inbox' else 'messages-outbox'
bgneal@818 240 url = reverse(view_name) + '?page={}'.format(page)
bgneal@818 241 return redirect(url)
bgneal@818 242
bgneal@818 243
bgneal@818 244 def _delete_pms(request, pm_ids):
bgneal@425 245 """
bgneal@806 246 Process the request to delete the list of PM ids by user.
bgneal@425 247
bgneal@807 248 Returns the number of PM's deleted.
bgneal@807 249
bgneal@425 250 """
bgneal@818 251 user = request.user
bgneal@806 252 msgs = Message.objects.filter(id__in=pm_ids)
bgneal@806 253 now = datetime.datetime.now()
bgneal@425 254
bgneal@807 255 count = 0
bgneal@425 256 for msg in msgs:
bgneal@425 257 if msg.sender == user:
bgneal@806 258 if msg.receiver_delete_date is not None or msg.read_date is None:
bgneal@425 259 # Both parties deleted the message or receiver hasn't read it
bgneal@425 260 # yet, we can delete it now
bgneal@425 261 msg.delete()
bgneal@425 262 else:
bgneal@806 263 # receiver still has PM in their inbox
bgneal@806 264 msg.sender_delete_date = now
bgneal@425 265 msg.save()
bgneal@807 266 count += 1
bgneal@425 267 elif msg.receiver == user:
bgneal@425 268 if msg.sender_delete_date is not None:
bgneal@425 269 # both parties deleted the message, we can delete it now
bgneal@425 270 msg.delete()
bgneal@425 271 else:
bgneal@806 272 # sender still has PM in their inbox
bgneal@806 273 msg.receiver_delete_date = now
bgneal@425 274 msg.save()
bgneal@807 275 count += 1
bgneal@807 276
bgneal@818 277 msg = '{} message{} deleted.'.format(count, '' if count == 1 else 's')
bgneal@818 278 if count > 0:
bgneal@807 279 django_messages.success(request, msg)
bgneal@818 280 else:
bgneal@818 281 django_messages.error(request, msg)
bgneal@806 282
bgneal@806 283
bgneal@807 284 def _undelete_pms(user, msg_ids):
bgneal@425 285 """
bgneal@425 286 Attempts to "undelete" the messages given by the msg_ids list.
bgneal@425 287 This will only succeed if the user is either the sender or receiver.
bgneal@425 288
bgneal@807 289 Returns the number of PM's undeleted.
bgneal@807 290
bgneal@425 291 """
bgneal@425 292 msgs = Message.objects.filter(id__in=msg_ids)
bgneal@807 293 count = 0
bgneal@425 294 for msg in msgs:
bgneal@425 295 if msg.sender == user:
bgneal@425 296 msg.sender_delete_date = None
bgneal@425 297 msg.save()
bgneal@807 298 count += 1
bgneal@425 299 elif msg.receiver == user:
bgneal@425 300 msg.receiver_delete_date = None
bgneal@425 301 msg.save()
bgneal@807 302 count += 1
bgneal@807 303 return count
bgneal@425 304
bgneal@425 305
bgneal@807 306 @login_required
bgneal@807 307 @require_POST
bgneal@807 308 def undelete(request):
bgneal@425 309 """
bgneal@807 310 Undeletes the requested PM's. The user must be either a sender or receiver for
bgneal@807 311 this to work.
bgneal@425 312
bgneal@425 313 """
bgneal@807 314 pm_ids = request.POST.getlist('pm_ids')
bgneal@807 315 if pm_ids:
bgneal@807 316 count = _undelete_pms(request.user, pm_ids)
bgneal@807 317 msg = '{} message{} undeleted.'.format(count, '' if count == 1 else 's')
bgneal@807 318 django_messages.success(request, msg)
bgneal@425 319
bgneal@807 320 # Figure out where to redirect to
bgneal@807 321 try:
bgneal@807 322 page = int(request.POST.get('page', '1'))
bgneal@807 323 except ValueError:
bgneal@807 324 page = 1
bgneal@425 325
bgneal@807 326 url = reverse('messages-trash') + '?page={}'.format(page)
bgneal@807 327 return redirect(url)
bgneal@810 328
bgneal@810 329
bgneal@810 330 @login_required
bgneal@810 331 def report(request, msg_id):
bgneal@810 332 """This view is for reporting a PM as spam or abuse.
bgneal@810 333
bgneal@810 334 """
bgneal@810 335 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
bgneal@810 336 if msg.receiver != request.user:
bgneal@810 337 django_messages.error(request, "You can't report this message.")
bgneal@810 338 return redirect('messages-inbox')
bgneal@810 339 try:
bgneal@810 340 msg.flag
bgneal@810 341 except Flag.DoesNotExist:
bgneal@810 342 pass
bgneal@810 343 else:
bgneal@810 344 django_messages.error(request, "This message has already been reported.")
bgneal@810 345 return redirect('messages-inbox')
bgneal@810 346
bgneal@810 347 if request.method == 'POST':
bgneal@810 348 form = ReportForm(request.POST)
bgneal@810 349 if form.is_valid():
bgneal@810 350 flag = form.save(commit=False)
bgneal@810 351 flag.message = msg
bgneal@810 352 flag.save()
bgneal@813 353 email_admins(REPORT_SUBJECT, REPORT_MSG)
bgneal@810 354 django_messages.success(request,
bgneal@810 355 'Message reported. An admin will be notified. Thank you.')
bgneal@810 356 return redirect('messages-inbox')
bgneal@810 357 else:
bgneal@810 358 form = ReportForm()
bgneal@810 359
bgneal@810 360 return render(request, 'messages/report_message.html', {
bgneal@810 361 'msg': msg,
bgneal@810 362 'form': form,
bgneal@810 363 })
bgneal@818 364
bgneal@818 365
bgneal@818 366 def _email_pms(request, msg_ids):
bgneal@818 367 """
bgneal@818 368 Attempts to email the messages given by the msg_ids list to the user.
bgneal@818 369 This will only succeed if the user is either the sender or receiver.
bgneal@818 370
bgneal@818 371 Returns the number of PM's sent.
bgneal@818 372
bgneal@818 373 """
bgneal@818 374 # Does the user have an email address?
bgneal@818 375 user = request.user
bgneal@818 376 email_addr = user.email
bgneal@818 377 if not email_addr:
bgneal@818 378 return 0
bgneal@818 379
bgneal@818 380 msgs = Message.objects.filter(
bgneal@818 381 Q(sender=user) | Q(receiver=user),
bgneal@818 382 id__in=msg_ids).order_by('pk')[:PM_EMAIL_MAX]
bgneal@818 383
bgneal@818 384 count = len(msgs)
bgneal@818 385 if count == 0:
bgneal@818 386 return 0
bgneal@818 387
bgneal@818 388 site = Site.objects.get_current()
bgneal@818 389 admin_email = settings.ADMINS[0][1]
bgneal@818 390
bgneal@818 391 subject = 'Private messages from {} - {}'.format(site.domain,
bgneal@818 392 msgs[0].subject)
bgneal@818 393 if count > 1:
bgneal@818 394 subject += ' (et al.)'
bgneal@818 395 from_email = '{}@{}'.format(settings.GPP_NO_REPLY_EMAIL, site.domain)
bgneal@818 396 msg_body = render_to_string('messages/pm_email.txt', {
bgneal@818 397 'site': site,
bgneal@818 398 'user': user,
bgneal@818 399 'msgs': msgs,
bgneal@818 400 'admin_email': admin_email,
bgneal@818 401 })
bgneal@818 402 send_mail(subject, msg_body, from_email, [email_addr], defer=False)
bgneal@818 403
bgneal@818 404 msg = '{} message{} sent to email.'.format(count, '' if count == 1 else 's')
bgneal@818 405 if count > 0:
bgneal@818 406 django_messages.success(request, msg)
bgneal@818 407 else:
bgneal@818 408 django_messages.error(request, msg)