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@1133: 'V3_DESIGN': True, 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@1133: 'V3_DESIGN': True, 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@1133: 'V3_DESIGN': True, 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@1133: 'V3_DESIGN': True, 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@1133: 'V3_DESIGN': True, 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@1133: "You don't have permission to read that message.", bgneal@1133: extra_tags="alert") bgneal@1133: 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@1133: 'V3_DESIGN': True, 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@1133: django_messages.error(request, msg, extra_tags="alert") 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@1133: django_messages.error(request, "You can't report this message.", bgneal@1133: extra_tags="alert") 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@1133: django_messages.error(request, "This message has already been reported.", bgneal@1133: extra_tags="alert") 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@1133: 'V3_DESIGN': True, 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@892: send_mail(subject, msg_body, from_email, [email_addr]) 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@1133: django_messages.error(request, msg, extra_tags="alert")