bgneal@425
|
1 """
|
bgneal@425
|
2 Views for the messages application.
|
bgneal@425
|
3
|
bgneal@425
|
4 """
|
bgneal@425
|
5 import datetime
|
bgneal@679
|
6 import json
|
bgneal@425
|
7
|
bgneal@425
|
8 from django.contrib.auth.decorators import login_required
|
bgneal@806
|
9 from django.views.decorators.http import require_POST
|
bgneal@428
|
10 from django.contrib.auth.models import User
|
bgneal@436
|
11 from django.contrib import messages as django_messages
|
bgneal@801
|
12 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
bgneal@425
|
13 from django.core.urlresolvers import reverse
|
bgneal@425
|
14 from django.http import HttpResponse
|
bgneal@425
|
15 from django.http import HttpResponseForbidden
|
bgneal@425
|
16 from django.http import HttpResponseNotAllowed
|
bgneal@425
|
17 from django.shortcuts import get_object_or_404
|
bgneal@805
|
18 from django.shortcuts import render, redirect
|
bgneal@425
|
19
|
bgneal@425
|
20 from messages.models import Message, Options
|
bgneal@425
|
21 from messages.forms import OptionsForm, ComposeForm
|
bgneal@566
|
22 from messages.utils import reply_subject
|
bgneal@436
|
23 from messages import MSG_BOX_LIMIT
|
bgneal@566
|
24 from core.functions import quote_message
|
bgneal@425
|
25
|
bgneal@425
|
26
|
bgneal@436
|
27 MSGS_PER_PAGE = 20 # message pagination value
|
bgneal@425
|
28
|
bgneal@436
|
29 # This must match the jQuery UI tab control
|
bgneal@425
|
30 TAB_INDICES = {
|
bgneal@425
|
31 'inbox': 0,
|
bgneal@425
|
32 'compose': 1,
|
bgneal@425
|
33 'outbox': 2,
|
bgneal@425
|
34 'trash': 3,
|
bgneal@425
|
35 'options': 4,
|
bgneal@425
|
36 }
|
bgneal@425
|
37
|
bgneal@425
|
38
|
bgneal@436
|
39 def _quota_check(box_name, count, request):
|
bgneal@436
|
40 """
|
bgneal@436
|
41 Checks the message box count against MSG_BOX_LIMIT.
|
bgneal@436
|
42 Emits a message to the user if the quota is exceeded.
|
bgneal@441
|
43
|
bgneal@568
|
44 Returns the percent used as an integer between 0-100.
|
bgneal@568
|
45
|
bgneal@436
|
46 """
|
bgneal@436
|
47 if count >= MSG_BOX_LIMIT:
|
bgneal@436
|
48 django_messages.warning(request,
|
bgneal@436
|
49 "Your %s is full. Please delete some messages." % box_name)
|
bgneal@436
|
50
|
bgneal@568
|
51 return 100 * count / MSG_BOX_LIMIT
|
bgneal@568
|
52
|
bgneal@436
|
53
|
bgneal@801
|
54 def _get_page(request, qs):
|
bgneal@801
|
55 """Paginates the given queryset and returns a page object"""
|
bgneal@801
|
56 paginator = Paginator(qs, MSGS_PER_PAGE)
|
bgneal@801
|
57 try:
|
bgneal@801
|
58 page = paginator.page(request.GET.get('page', '1'))
|
bgneal@801
|
59 except PageNotAnInteger:
|
bgneal@801
|
60 page = paginator.page(1)
|
bgneal@801
|
61 except EmptyPage:
|
bgneal@801
|
62 page = paginator.page(paginator.num_pages)
|
bgneal@801
|
63 return page
|
bgneal@801
|
64
|
bgneal@801
|
65
|
bgneal@425
|
66 @login_required
|
bgneal@801
|
67 def inbox(request):
|
bgneal@425
|
68
|
bgneal@801
|
69 msg_list = Message.objects.inbox(request.user)
|
bgneal@801
|
70 msg_count = msg_list.count()
|
bgneal@801
|
71 pct_used = _quota_check('inbox', msg_count, request)
|
bgneal@801
|
72
|
bgneal@801
|
73 page = _get_page(request, msg_list)
|
bgneal@801
|
74
|
bgneal@801
|
75 return render(request, 'messages/inbox.html', {
|
bgneal@801
|
76 'tab': 'inbox',
|
bgneal@801
|
77 'page': page,
|
bgneal@801
|
78 'inbox_pct': pct_used,
|
bgneal@801
|
79 'outbox_pct': None,
|
bgneal@801
|
80 })
|
bgneal@801
|
81
|
bgneal@801
|
82
|
bgneal@801
|
83 @login_required
|
bgneal@801
|
84 def outbox(request):
|
bgneal@801
|
85
|
bgneal@801
|
86 msg_list = Message.objects.outbox(request.user)
|
bgneal@801
|
87 msg_count = msg_list.count()
|
bgneal@801
|
88 pct_used = _quota_check('outbox', msg_count, request)
|
bgneal@801
|
89
|
bgneal@801
|
90 page = _get_page(request, msg_list)
|
bgneal@801
|
91
|
bgneal@801
|
92 return render(request, 'messages/outbox.html', {
|
bgneal@801
|
93 'tab': 'outbox',
|
bgneal@801
|
94 'page': page,
|
bgneal@801
|
95 'inbox_pct': None,
|
bgneal@801
|
96 'outbox_pct': pct_used,
|
bgneal@425
|
97 })
|
bgneal@425
|
98
|
bgneal@425
|
99
|
bgneal@428
|
100 @login_required
|
bgneal@802
|
101 def trash(request):
|
bgneal@802
|
102
|
bgneal@802
|
103 msg_list = Message.objects.trash(request.user)
|
bgneal@802
|
104
|
bgneal@802
|
105 page = _get_page(request, msg_list)
|
bgneal@802
|
106
|
bgneal@802
|
107 return render(request, 'messages/trash.html', {
|
bgneal@802
|
108 'tab': 'trash',
|
bgneal@802
|
109 'page': page,
|
bgneal@802
|
110 'inbox_pct': None,
|
bgneal@802
|
111 'outbox_pct': None,
|
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@803
|
127 else:
|
bgneal@803
|
128 options = Options.objects.for_user(request.user)
|
bgneal@803
|
129 form = OptionsForm(instance=options, prefix='opts')
|
bgneal@803
|
130
|
bgneal@803
|
131 return render(request, 'messages/options.html', {
|
bgneal@803
|
132 'tab': 'options',
|
bgneal@803
|
133 'form': form,
|
bgneal@803
|
134 })
|
bgneal@803
|
135
|
bgneal@803
|
136
|
bgneal@803
|
137 @login_required
|
bgneal@804
|
138 def compose(request):
|
bgneal@428
|
139 """
|
bgneal@804
|
140 Process or prepare the compose form to create a new private message.
|
bgneal@428
|
141
|
bgneal@428
|
142 """
|
bgneal@804
|
143 if request.method == 'POST':
|
bgneal@804
|
144 compose_form = ComposeForm(request.user, request.POST)
|
bgneal@804
|
145 if compose_form.is_valid():
|
bgneal@804
|
146 compose_form.save()
|
bgneal@804
|
147 django_messages.success(request, 'Message sent.')
|
bgneal@804
|
148 compose_form = ComposeForm(request.user)
|
bgneal@804
|
149 else:
|
bgneal@804
|
150 receiver = request.GET.get('to')
|
bgneal@804
|
151 if receiver:
|
bgneal@804
|
152 form_data = {'receiver': receiver}
|
bgneal@804
|
153 compose_form = ComposeForm(request.user, initial=form_data)
|
bgneal@804
|
154 else:
|
bgneal@804
|
155 compose_form = ComposeForm(request.user)
|
bgneal@804
|
156
|
bgneal@804
|
157 _quota_check('outbox', Message.objects.outbox(request.user).count(), request)
|
bgneal@804
|
158
|
bgneal@804
|
159 return render(request, 'messages/compose.html', {
|
bgneal@804
|
160 'tab': 'compose',
|
bgneal@804
|
161 'compose_form': compose_form,
|
bgneal@428
|
162 })
|
bgneal@428
|
163
|
bgneal@428
|
164
|
bgneal@804
|
165 @login_required
|
bgneal@804
|
166 def view(request, msg_id):
|
bgneal@425
|
167 """
|
bgneal@805
|
168 This view function displays a private message for reading to the user. If
|
bgneal@805
|
169 the user is a recipient of the message, a reply can be composed and sent.
|
bgneal@425
|
170
|
bgneal@425
|
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 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
|
bgneal@805
|
180 if msg.sender != request.user and msg.receiver != request.user:
|
bgneal@805
|
181 django_messages.error(request,
|
bgneal@805
|
182 "You don't have permission to read that message.")
|
bgneal@805
|
183 return redirect('messages-inbox')
|
bgneal@425
|
184
|
bgneal@805
|
185 initial_data = {
|
bgneal@805
|
186 'receiver': msg.sender.username,
|
bgneal@805
|
187 'subject': reply_subject(msg.subject),
|
bgneal@805
|
188 'message': quote_message(msg.sender.username, msg.message),
|
bgneal@805
|
189 'parent_id': msg.pk,
|
bgneal@805
|
190 }
|
bgneal@425
|
191
|
bgneal@805
|
192 if msg.receiver == request.user:
|
bgneal@805
|
193 if msg.read_date is None:
|
bgneal@805
|
194 msg.read_date = datetime.datetime.now()
|
bgneal@805
|
195 msg.save()
|
bgneal@805
|
196 else:
|
bgneal@805
|
197 initial_data['receiver'] = msg.receiver.username
|
bgneal@425
|
198
|
bgneal@805
|
199 form = ComposeForm(request.user, initial=initial_data)
|
bgneal@425
|
200
|
bgneal@805
|
201 return render(request, 'messages/view_message.html', {
|
bgneal@805
|
202 'msg': msg,
|
bgneal@805
|
203 'form': form,
|
bgneal@805
|
204 })
|
bgneal@425
|
205
|
bgneal@425
|
206
|
bgneal@806
|
207 def _delete_pms(user, pm_ids):
|
bgneal@425
|
208 """
|
bgneal@806
|
209 Process the request to delete the list of PM ids by user.
|
bgneal@425
|
210
|
bgneal@425
|
211 """
|
bgneal@806
|
212 msgs = Message.objects.filter(id__in=pm_ids)
|
bgneal@806
|
213 now = datetime.datetime.now()
|
bgneal@425
|
214
|
bgneal@425
|
215 for msg in msgs:
|
bgneal@425
|
216 if msg.sender == user:
|
bgneal@806
|
217 if msg.receiver_delete_date is not None or msg.read_date is None:
|
bgneal@425
|
218 # Both parties deleted the message or receiver hasn't read it
|
bgneal@425
|
219 # yet, we can delete it now
|
bgneal@425
|
220 msg.delete()
|
bgneal@425
|
221 else:
|
bgneal@806
|
222 # receiver still has PM in their inbox
|
bgneal@806
|
223 msg.sender_delete_date = now
|
bgneal@425
|
224 msg.save()
|
bgneal@425
|
225 elif msg.receiver == user:
|
bgneal@425
|
226 if msg.sender_delete_date is not None:
|
bgneal@425
|
227 # both parties deleted the message, we can delete it now
|
bgneal@425
|
228 msg.delete()
|
bgneal@425
|
229 else:
|
bgneal@806
|
230 # sender still has PM in their inbox
|
bgneal@806
|
231 msg.receiver_delete_date = now
|
bgneal@425
|
232 msg.save()
|
bgneal@425
|
233
|
bgneal@425
|
234
|
bgneal@806
|
235 @login_required
|
bgneal@806
|
236 @require_POST
|
bgneal@806
|
237 def delete(request):
|
bgneal@806
|
238 """
|
bgneal@806
|
239 Deletes the requested PM's. The user must be either a sender or receiver for
|
bgneal@806
|
240 this to work.
|
bgneal@806
|
241
|
bgneal@806
|
242 """
|
bgneal@806
|
243 pm_ids = request.POST.getlist('pm_ids')
|
bgneal@806
|
244 if pm_ids:
|
bgneal@806
|
245 _delete_pms(request.user, pm_ids)
|
bgneal@806
|
246
|
bgneal@806
|
247 # Figure out where to redirect to
|
bgneal@806
|
248 src = request.POST.get('src', 'inbox')
|
bgneal@806
|
249 try:
|
bgneal@806
|
250 page = int(request.POST.get('page', '1'))
|
bgneal@806
|
251 except ValueError:
|
bgneal@806
|
252 page = 1
|
bgneal@806
|
253
|
bgneal@806
|
254 view_name = 'messages-inbox' if src == inbox else 'messages-outbox'
|
bgneal@806
|
255 url = reverse(view_name) + '?page={}'.format(page)
|
bgneal@806
|
256 return redirect(url)
|
bgneal@806
|
257
|
bgneal@806
|
258
|
bgneal@425
|
259 def _undelete_msgs(user, msg_ids):
|
bgneal@425
|
260 """
|
bgneal@425
|
261 Attempts to "undelete" the messages given by the msg_ids list.
|
bgneal@425
|
262 This will only succeed if the user is either the sender or receiver.
|
bgneal@425
|
263
|
bgneal@425
|
264 """
|
bgneal@425
|
265 msg_ids = _only_integers(msg_ids)
|
bgneal@425
|
266 msgs = Message.objects.filter(id__in=msg_ids)
|
bgneal@425
|
267 for msg in msgs:
|
bgneal@425
|
268 if msg.sender == user:
|
bgneal@425
|
269 msg.sender_delete_date = None
|
bgneal@425
|
270 msg.save()
|
bgneal@425
|
271 elif msg.receiver == user:
|
bgneal@425
|
272 msg.receiver_delete_date = None
|
bgneal@425
|
273 msg.save()
|
bgneal@425
|
274
|
bgneal@425
|
275
|
bgneal@425
|
276 def bulk(request):
|
bgneal@425
|
277 """
|
bgneal@425
|
278 This view processes messages in bulk. Arrays of message ids are expected in
|
bgneal@425
|
279 the POST query dict: inbox_ids and outbox_ids will be deleted; trash_ids will
|
bgneal@425
|
280 be undeleted.
|
bgneal@425
|
281
|
bgneal@425
|
282 """
|
bgneal@425
|
283 if not request.user.is_authenticated():
|
bgneal@425
|
284 return HttpResponseForbidden()
|
bgneal@425
|
285 if request.method != 'POST':
|
bgneal@425
|
286 return HttpResponseNotAllowed(['POST'])
|
bgneal@425
|
287
|
bgneal@425
|
288 delete_ids = []
|
bgneal@425
|
289 if 'inbox_ids' in request.POST:
|
bgneal@425
|
290 delete_ids.extend(request.POST.getlist('inbox_ids'))
|
bgneal@425
|
291 if 'outbox_ids' in request.POST:
|
bgneal@425
|
292 delete_ids.extend(request.POST.getlist('outbox_ids'))
|
bgneal@425
|
293
|
bgneal@425
|
294 if len(delete_ids):
|
bgneal@425
|
295 _delete_msgs(request.user, delete_ids)
|
bgneal@425
|
296
|
bgneal@425
|
297 if 'trash_ids' in request.POST:
|
bgneal@425
|
298 _undelete_msgs(request.user, request.POST.getlist('trash_ids'))
|
bgneal@425
|
299
|
bgneal@425
|
300 return HttpResponse('');
|