bgneal@425: """
bgneal@425: Views for the messages application.
bgneal@425: 
bgneal@425: """
bgneal@425: import datetime
bgneal@425: 
bgneal@425: from django.contrib.auth.decorators import login_required
bgneal@806: from django.views.decorators.http import require_POST
bgneal@436: from django.contrib import messages as django_messages
bgneal@801: from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
bgneal@425: from django.core.urlresolvers import reverse
bgneal@425: from django.shortcuts import get_object_or_404
bgneal@805: from django.shortcuts import render, redirect
bgneal@818: from django.db.models import Q
bgneal@818: from django.template.loader import render_to_string
bgneal@818: from django.contrib.sites.models import Site
bgneal@818: from django.conf import settings
bgneal@425: 
bgneal@810: from messages.models import Flag, Message, Options
bgneal@810: from messages.forms import OptionsForm, ComposeForm, ReportForm
bgneal@566: from messages.utils import reply_subject
bgneal@436: from messages import MSG_BOX_LIMIT
bgneal@818: from core.functions import email_admins, quote_message, send_mail
bgneal@425: 
bgneal@425: 
bgneal@436: MSGS_PER_PAGE = 20      # message pagination value
bgneal@818: PM_EMAIL_MAX = 100      # max messages we will send via email
bgneal@425: 
bgneal@813: REPORT_SUBJECT = 'A user has flagged a private message'
bgneal@813: REPORT_MSG = """Hello,
bgneal@813: 
bgneal@813: A user has flagged a private message for review.
bgneal@813: """
bgneal@813: 
bgneal@425: 
bgneal@436: def _quota_check(box_name, count, request):
bgneal@436:     """
bgneal@436:     Checks the message box count against MSG_BOX_LIMIT.
bgneal@436:     Emits a message to the user if the quota is exceeded.
bgneal@441: 
bgneal@568:     Returns the percent used as an integer between 0-100.
bgneal@568: 
bgneal@436:     """
bgneal@436:     if count >= MSG_BOX_LIMIT:
bgneal@436:         django_messages.warning(request,
bgneal@436:             "Your %s is full. Please delete some messages." % box_name)
bgneal@436: 
bgneal@568:     return 100 * count / MSG_BOX_LIMIT
bgneal@568: 
bgneal@436: 
bgneal@801: def _get_page(request, qs):
bgneal@801:     """Paginates the given queryset and returns a page object"""
bgneal@801:     paginator = Paginator(qs, MSGS_PER_PAGE)
bgneal@801:     try:
bgneal@801:         page = paginator.page(request.GET.get('page', '1'))
bgneal@801:     except PageNotAnInteger:
bgneal@801:         page = paginator.page(1)
bgneal@801:     except EmptyPage:
bgneal@801:         page = paginator.page(paginator.num_pages)
bgneal@801:     return page
bgneal@801: 
bgneal@801: 
bgneal@425: @login_required
bgneal@801: def inbox(request):
bgneal@425: 
bgneal@801:     msg_list = Message.objects.inbox(request.user)
bgneal@801:     msg_count = msg_list.count()
bgneal@801:     pct_used = _quota_check('inbox', msg_count, request)
bgneal@801: 
bgneal@801:     page = _get_page(request, msg_list)
bgneal@801: 
bgneal@801:     return render(request, 'messages/inbox.html', {
bgneal@801:         'tab': 'inbox',
bgneal@801:         'page': page,
bgneal@801:         'inbox_pct': pct_used,
bgneal@801:         'outbox_pct': None,
bgneal@801:         })
bgneal@801: 
bgneal@801: 
bgneal@801: @login_required
bgneal@801: def outbox(request):
bgneal@801: 
bgneal@801:     msg_list = Message.objects.outbox(request.user)
bgneal@801:     msg_count = msg_list.count()
bgneal@801:     pct_used = _quota_check('outbox', msg_count, request)
bgneal@801: 
bgneal@801:     page = _get_page(request, msg_list)
bgneal@801: 
bgneal@801:     return render(request, 'messages/outbox.html', {
bgneal@801:         'tab': 'outbox',
bgneal@801:         'page': page,
bgneal@801:         'inbox_pct': None,
bgneal@801:         'outbox_pct': pct_used,
bgneal@425:         })
bgneal@425: 
bgneal@425: 
bgneal@428: @login_required
bgneal@802: def trash(request):
bgneal@802: 
bgneal@802:     msg_list = Message.objects.trash(request.user)
bgneal@802: 
bgneal@802:     page = _get_page(request, msg_list)
bgneal@802: 
bgneal@802:     return render(request, 'messages/trash.html', {
bgneal@802:         'tab': 'trash',
bgneal@802:         'page': page,
bgneal@802:         'inbox_pct': None,
bgneal@802:         'outbox_pct': None,
bgneal@802:         })
bgneal@802: 
bgneal@802: 
bgneal@802: @login_required
bgneal@803: def options(request):
bgneal@803:     """
bgneal@803:     This view handles the displaying and changing of private message options.
bgneal@803: 
bgneal@803:     """
bgneal@803:     if request.method == 'POST':
bgneal@803:         options = Options.objects.for_user(request.user)
bgneal@803:         form = OptionsForm(request.POST, instance=options, prefix='opts')
bgneal@803:         if form.is_valid():
bgneal@803:             form.save()
bgneal@803:             django_messages.success(request, 'Options saved.')
bgneal@812:             return redirect('messages-options')
bgneal@803:     else:
bgneal@803:         options = Options.objects.for_user(request.user)
bgneal@803:         form = OptionsForm(instance=options, prefix='opts')
bgneal@803: 
bgneal@803:     return render(request, 'messages/options.html', {
bgneal@803:         'tab': 'options',
bgneal@803:         'form': form,
bgneal@803:         })
bgneal@803: 
bgneal@803: 
bgneal@803: @login_required
bgneal@804: def compose(request):
bgneal@428:     """
bgneal@804:     Process or prepare the compose form to create a new private message.
bgneal@428: 
bgneal@428:     """
bgneal@804:     if request.method == 'POST':
bgneal@804:         compose_form = ComposeForm(request.user, request.POST)
bgneal@804:         if compose_form.is_valid():
bgneal@804:             compose_form.save()
bgneal@804:             django_messages.success(request, 'Message sent.')
bgneal@804:             compose_form = ComposeForm(request.user)
bgneal@804:     else:
bgneal@804:         receiver = request.GET.get('to')
bgneal@804:         if receiver:
bgneal@804:             form_data = {'receiver': receiver}
bgneal@804:             compose_form = ComposeForm(request.user, initial=form_data)
bgneal@804:         else:
bgneal@804:             compose_form = ComposeForm(request.user)
bgneal@804: 
bgneal@804:         _quota_check('outbox', Message.objects.outbox(request.user).count(), request)
bgneal@804: 
bgneal@804:     return render(request, 'messages/compose.html', {
bgneal@804:         'tab': 'compose',
bgneal@804:         'compose_form': compose_form,
bgneal@428:         })
bgneal@428: 
bgneal@428: 
bgneal@804: @login_required
bgneal@804: def view(request, msg_id):
bgneal@425:     """
bgneal@805:     This view function displays a private message for reading to the user. If
bgneal@805:     the user is a recipient of the message, a reply can be composed and sent.
bgneal@425: 
bgneal@425:     """
bgneal@812:     msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
bgneal@812: 
bgneal@805:     if request.method == 'POST':
bgneal@805:         form = ComposeForm(request.user, request.POST)
bgneal@805:         if form.is_valid():
bgneal@805:             form.save()
bgneal@805:             django_messages.success(request, 'Reply sent.')
bgneal@805:             return redirect('messages-inbox')
bgneal@805:     else:
bgneal@805:         if msg.sender != request.user and msg.receiver != request.user:
bgneal@805:             django_messages.error(request,
bgneal@805:                     "You don't have permission to read that message.")
bgneal@805:             return redirect('messages-inbox')
bgneal@425: 
bgneal@805:         initial_data = {
bgneal@805:             'receiver': msg.sender.username,
bgneal@805:             'subject': reply_subject(msg.subject),
bgneal@805:             'message': quote_message(msg.sender.username, msg.message),
bgneal@805:             'parent_id': msg.pk,
bgneal@805:         }
bgneal@425: 
bgneal@805:         if msg.receiver == request.user:
bgneal@805:             if msg.read_date is None:
bgneal@805:                 msg.read_date = datetime.datetime.now()
bgneal@805:                 msg.save()
bgneal@805:         else:
bgneal@805:             initial_data['receiver'] = msg.receiver.username
bgneal@425: 
bgneal@805:         form = ComposeForm(request.user, initial=initial_data)
bgneal@425: 
bgneal@810:     try:
bgneal@810:         msg_flag = msg.flag
bgneal@810:     except Flag.DoesNotExist:
bgneal@810:         msg_flag = None
bgneal@810: 
bgneal@805:     return render(request, 'messages/view_message.html', {
bgneal@805:         'msg': msg,
bgneal@805:         'form': form,
bgneal@810:         'msg_flag': msg_flag,
bgneal@805:         })
bgneal@425: 
bgneal@425: 
bgneal@818: @login_required
bgneal@818: @require_POST
bgneal@818: def bulk(request):
bgneal@818:     """
bgneal@818:     Perform bulk action on a list of PMs.
bgneal@818: 
bgneal@818:     """
bgneal@818:     pm_ids = request.POST.getlist('pm_ids')
bgneal@818: 
bgneal@818:     # Figure out what action to perform
bgneal@818:     action = None
bgneal@818:     action_val = request.POST.get('action', '')
bgneal@818:     if action_val.startswith('Delete'):
bgneal@818:         action = _delete_pms
bgneal@818:     elif action_val.startswith('Email'):
bgneal@818:         action = _email_pms
bgneal@818: 
bgneal@818:     if pm_ids and action:
bgneal@818:         action(request, pm_ids)
bgneal@818: 
bgneal@818:     # Figure out where to redirect to
bgneal@818:     src = request.POST.get('src', 'inbox')
bgneal@818:     try:
bgneal@818:         page = int(request.POST.get('page', '1'))
bgneal@818:     except ValueError:
bgneal@818:         page = 1
bgneal@818: 
bgneal@818:     view_name = 'messages-inbox' if src == 'inbox' else 'messages-outbox'
bgneal@818:     url = reverse(view_name) + '?page={}'.format(page)
bgneal@818:     return redirect(url)
bgneal@818: 
bgneal@818: 
bgneal@818: def _delete_pms(request, pm_ids):
bgneal@425:     """
bgneal@806:     Process the request to delete the list of PM ids by user.
bgneal@425: 
bgneal@807:     Returns the number of PM's deleted.
bgneal@807: 
bgneal@425:     """
bgneal@818:     user = request.user
bgneal@806:     msgs = Message.objects.filter(id__in=pm_ids)
bgneal@806:     now = datetime.datetime.now()
bgneal@425: 
bgneal@807:     count = 0
bgneal@425:     for msg in msgs:
bgneal@425:         if msg.sender == user:
bgneal@806:             if msg.receiver_delete_date is not None or msg.read_date is None:
bgneal@425:                 # Both parties deleted the message or receiver hasn't read it
bgneal@425:                 # yet, we can delete it now
bgneal@425:                 msg.delete()
bgneal@425:             else:
bgneal@806:                 # receiver still has PM in their inbox
bgneal@806:                 msg.sender_delete_date = now
bgneal@425:                 msg.save()
bgneal@807:             count += 1
bgneal@425:         elif msg.receiver == user:
bgneal@425:             if msg.sender_delete_date is not None:
bgneal@425:                 # both parties deleted the message, we can delete it now
bgneal@425:                 msg.delete()
bgneal@425:             else:
bgneal@806:                 # sender still has PM in their inbox
bgneal@806:                 msg.receiver_delete_date = now
bgneal@425:                 msg.save()
bgneal@807:             count += 1
bgneal@807: 
bgneal@818:     msg = '{} message{} deleted.'.format(count, '' if count == 1 else 's')
bgneal@818:     if count > 0:
bgneal@807:         django_messages.success(request, msg)
bgneal@818:     else:
bgneal@818:         django_messages.error(request, msg)
bgneal@806: 
bgneal@806: 
bgneal@807: def _undelete_pms(user, msg_ids):
bgneal@425:     """
bgneal@425:     Attempts to "undelete" the messages given by the msg_ids list.
bgneal@425:     This will only succeed if the user is either the sender or receiver.
bgneal@425: 
bgneal@807:     Returns the number of PM's undeleted.
bgneal@807: 
bgneal@425:     """
bgneal@425:     msgs = Message.objects.filter(id__in=msg_ids)
bgneal@807:     count = 0
bgneal@425:     for msg in msgs:
bgneal@425:         if msg.sender == user:
bgneal@425:             msg.sender_delete_date = None
bgneal@425:             msg.save()
bgneal@807:             count += 1
bgneal@425:         elif msg.receiver == user:
bgneal@425:             msg.receiver_delete_date = None
bgneal@425:             msg.save()
bgneal@807:             count += 1
bgneal@807:     return count
bgneal@425: 
bgneal@425: 
bgneal@807: @login_required
bgneal@807: @require_POST
bgneal@807: def undelete(request):
bgneal@425:     """
bgneal@807:     Undeletes the requested PM's. The user must be either a sender or receiver for
bgneal@807:     this to work.
bgneal@425: 
bgneal@425:     """
bgneal@807:     pm_ids = request.POST.getlist('pm_ids')
bgneal@807:     if pm_ids:
bgneal@807:         count = _undelete_pms(request.user, pm_ids)
bgneal@807:         msg = '{} message{} undeleted.'.format(count, '' if count == 1 else 's')
bgneal@807:         django_messages.success(request, msg)
bgneal@425: 
bgneal@807:     # Figure out where to redirect to
bgneal@807:     try:
bgneal@807:         page = int(request.POST.get('page', '1'))
bgneal@807:     except ValueError:
bgneal@807:         page = 1
bgneal@425: 
bgneal@807:     url = reverse('messages-trash') + '?page={}'.format(page)
bgneal@807:     return redirect(url)
bgneal@810: 
bgneal@810: 
bgneal@810: @login_required
bgneal@810: def report(request, msg_id):
bgneal@810:     """This view is for reporting a PM as spam or abuse.
bgneal@810: 
bgneal@810:     """
bgneal@810:     msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
bgneal@810:     if msg.receiver != request.user:
bgneal@810:         django_messages.error(request, "You can't report this message.")
bgneal@810:         return redirect('messages-inbox')
bgneal@810:     try:
bgneal@810:         msg.flag
bgneal@810:     except Flag.DoesNotExist:
bgneal@810:         pass
bgneal@810:     else:
bgneal@810:         django_messages.error(request, "This message has already been reported.")
bgneal@810:         return redirect('messages-inbox')
bgneal@810: 
bgneal@810:     if request.method == 'POST':
bgneal@810:         form = ReportForm(request.POST)
bgneal@810:         if form.is_valid():
bgneal@810:             flag = form.save(commit=False)
bgneal@810:             flag.message = msg
bgneal@810:             flag.save()
bgneal@813:             email_admins(REPORT_SUBJECT, REPORT_MSG)
bgneal@810:             django_messages.success(request,
bgneal@810:                     'Message reported. An admin will be notified. Thank you.')
bgneal@810:             return redirect('messages-inbox')
bgneal@810:     else:
bgneal@810:         form = ReportForm()
bgneal@810: 
bgneal@810:     return render(request, 'messages/report_message.html', {
bgneal@810:         'msg': msg,
bgneal@810:         'form': form,
bgneal@810:         })
bgneal@818: 
bgneal@818: 
bgneal@818: def _email_pms(request, msg_ids):
bgneal@818:     """
bgneal@818:     Attempts to email the messages given by the msg_ids list to the user.
bgneal@818:     This will only succeed if the user is either the sender or receiver.
bgneal@818: 
bgneal@818:     Returns the number of PM's sent.
bgneal@818: 
bgneal@818:     """
bgneal@818:     # Does the user have an email address?
bgneal@818:     user = request.user
bgneal@818:     email_addr = user.email
bgneal@818:     if not email_addr:
bgneal@818:         return 0
bgneal@818: 
bgneal@818:     msgs = Message.objects.filter(
bgneal@818:             Q(sender=user) | Q(receiver=user),
bgneal@818:             id__in=msg_ids).order_by('pk')[:PM_EMAIL_MAX]
bgneal@818: 
bgneal@818:     count = len(msgs)
bgneal@818:     if count == 0:
bgneal@818:         return 0
bgneal@818: 
bgneal@818:     site = Site.objects.get_current()
bgneal@818:     admin_email = settings.ADMINS[0][1]
bgneal@818: 
bgneal@818:     subject = 'Private messages from {} - {}'.format(site.domain,
bgneal@818:             msgs[0].subject)
bgneal@818:     if count > 1:
bgneal@818:         subject += ' (et al.)'
bgneal@818:     from_email = '{}@{}'.format(settings.GPP_NO_REPLY_EMAIL, site.domain)
bgneal@818:     msg_body = render_to_string('messages/pm_email.txt', {
bgneal@818:                     'site': site,
bgneal@818:                     'user': user,
bgneal@818:                     'msgs': msgs,
bgneal@818:                     'admin_email': admin_email,
bgneal@818:                 })
bgneal@818:     send_mail(subject, msg_body, from_email, [email_addr], defer=False)
bgneal@818: 
bgneal@818:     msg = '{} message{} sent to email.'.format(count, '' if count == 1 else 's')
bgneal@818:     if count > 0:
bgneal@818:         django_messages.success(request, msg)
bgneal@818:     else:
bgneal@818:         django_messages.error(request, msg)