comparison messages/views.py @ 581:ee87ea74d46b

For Django 1.4, rearranged project structure for new manage.py.
author Brian Neal <bgneal@gmail.com>
date Sat, 05 May 2012 17:10:48 -0500
parents gpp/messages/views.py@7cec8d6f9581
children 89b240fe9297
comparison
equal deleted inserted replaced
580:c525f3e0b5d0 581:ee87ea74d46b
1 """
2 Views for the messages application.
3
4 """
5 import datetime
6
7 from django.contrib.auth.decorators import login_required
8 from django.contrib.auth.models import User
9 from django.contrib import messages as django_messages
10 from django.core.paginator import Paginator, EmptyPage, InvalidPage
11 from django.core.urlresolvers import reverse
12 from django.http import HttpResponse
13 from django.http import HttpResponseForbidden
14 from django.http import HttpResponseNotAllowed
15 from django.shortcuts import get_object_or_404
16 from django.shortcuts import render
17 import django.utils.simplejson as json
18
19 from messages.models import Message, Options
20 from messages.forms import OptionsForm, ComposeForm
21 from messages.utils import reply_subject
22 from messages import MSG_BOX_LIMIT
23 from core.functions import quote_message
24
25
26 MSGS_PER_PAGE = 20 # message pagination value
27
28 # This must match the jQuery UI tab control
29 TAB_INDICES = {
30 'inbox': 0,
31 'compose': 1,
32 'outbox': 2,
33 'trash': 3,
34 'options': 4,
35 }
36
37
38 def _get_page(request):
39 try:
40 n = int(request.GET.get('page', '1'))
41 except ValueError:
42 n = 1
43 return n
44
45
46 def _quota_check(box_name, count, request):
47 """
48 Checks the message box count against MSG_BOX_LIMIT.
49 Emits a message to the user if the quota is exceeded.
50
51 Returns the percent used as an integer between 0-100.
52
53 """
54 if count >= MSG_BOX_LIMIT:
55 django_messages.warning(request,
56 "Your %s is full. Please delete some messages." % box_name)
57
58 return 100 * count / MSG_BOX_LIMIT
59
60
61 @login_required
62 def index(request, tab=None):
63 """
64 This function displays the base tabbed private messages view.
65
66 """
67 tab_index = TAB_INDICES[tab] if tab else 0
68 return render(request, 'messages/tabbed_base.html', {
69 'tab': tab_index,
70 'unread_count': Message.objects.unread_count(request.user),
71 })
72
73
74 @login_required
75 def compose_to(request, receiver):
76 """
77 This function displays the base tabbed private messages view,
78 and configures it to display the compose PM tab for the given
79 receiver.
80
81 """
82 user = get_object_or_404(User, username=receiver)
83 tab_index = TAB_INDICES['compose']
84 return render(request, 'messages/tabbed_base.html', {
85 'tab': tab_index,
86 'receiver': receiver,
87 'unread_count': Message.objects.unread_count(request.user),
88 })
89
90
91 def inbox(request):
92 """
93 Returns the inbox for the user.
94
95 """
96 if not request.user.is_authenticated():
97 return HttpResponseForbidden()
98
99 msg_list = Message.objects.inbox(request.user)
100 msg_count = msg_list.count()
101 pct_used = _quota_check('inbox', msg_count, request)
102
103 paginator = Paginator(msg_list, MSGS_PER_PAGE)
104 try:
105 msgs = paginator.page(_get_page(request))
106 except (EmptyPage, InvalidPage):
107 msgs = paginator.page(paginator.num_pages)
108
109 return render(request, 'messages/inbox_tab.html', {
110 'msgs': msgs,
111 'url': reverse('messages-inbox'),
112 'pct_used': pct_used,
113 })
114
115
116 def outbox(request):
117 """
118 Returns the outbox for the user.
119
120 """
121 if not request.user.is_authenticated():
122 return HttpResponseForbidden()
123
124 msg_list = Message.objects.outbox(request.user)
125 msg_count = msg_list.count()
126 pct_used = _quota_check('outbox', msg_count, request)
127
128 paginator = Paginator(msg_list, MSGS_PER_PAGE)
129 try:
130 msgs = paginator.page(_get_page(request))
131 except (EmptyPage, InvalidPage):
132 msgs = paginator.page(paginator.num_pages)
133
134 return render(request, 'messages/outbox_tab.html', {
135 'msgs': msgs,
136 'url': reverse('messages-outbox'),
137 'pct_used': pct_used,
138 })
139
140
141 def trash(request):
142 """
143 Returns the trash for the user.
144
145 """
146 if not request.user.is_authenticated():
147 return HttpResponseForbidden()
148
149 msg_list = Message.objects.trash(request.user)
150 paginator = Paginator(msg_list, MSGS_PER_PAGE)
151 try:
152 msgs = paginator.page(_get_page(request))
153 except (EmptyPage, InvalidPage):
154 msgs = paginator.page(paginator.num_pages)
155
156 return render(request, 'messages/trash_tab.html', {
157 'msgs': msgs,
158 'url': reverse('messages-trash'),
159 })
160
161
162 def message(request):
163 """
164 This view function retrieves a message and returns it as a JSON object.
165
166 """
167 if not request.user.is_authenticated():
168 return HttpResponseForbidden()
169 if request.method != 'POST':
170 return HttpResponseNotAllowed(['POST'])
171
172 msg_id = request.POST.get('msg_id')
173 msg = get_object_or_404(Message.objects.select_related(), pk=msg_id)
174 if msg.sender != request.user and msg.receiver != request.user:
175 return HttpResponseForbidden()
176
177 if msg.receiver == request.user and msg.read_date is None:
178 msg.read_date = datetime.datetime.now()
179 msg.save()
180
181 msg_dict = dict(subject=msg.subject,
182 sender=msg.sender.username,
183 receiver=msg.receiver.username,
184 content=msg.html,
185 re_subject=reply_subject(msg.subject),
186 re_content=quote_message(msg.sender.username, msg.message))
187
188 result = json.dumps(msg_dict, ensure_ascii=False)
189 return HttpResponse(result, content_type='application/json')
190
191
192 def options(request):
193 """
194 This view handles the displaying and changing of private message options.
195
196 """
197 if not request.user.is_authenticated():
198 return HttpResponseForbidden()
199
200 if request.method == "POST":
201 options = Options.objects.for_user(request.user)
202 form = OptionsForm(request.POST, instance=options, prefix='opts')
203 if form.is_valid():
204 form.save()
205 django_messages.success(request, 'Options saved.')
206 else:
207 options = Options.objects.for_user(request.user)
208 form = OptionsForm(instance=options, prefix='opts')
209
210 return render(request, 'messages/options_tab.html', {
211 'form': form,
212 })
213
214
215 def compose(request, receiver=None):
216 """
217 Process or prepare the compose form to create a new private message.
218
219 """
220 if not request.user.is_authenticated():
221 return HttpResponseForbidden()
222
223 if request.method == "POST":
224 compose_form = ComposeForm(request.user, request.POST)
225
226 # Is this a reply to another message?
227 parent_msg_id = request.POST.get('reply_to')
228 if parent_msg_id:
229 parent_msg = get_object_or_404(Message, id=parent_msg_id)
230 if (request.user != parent_msg.receiver and
231 request.user != parent_msg.sender):
232 return HttpResponseForbidden()
233 else:
234 parent_msg = None
235
236 if compose_form.is_valid():
237 compose_form.save(parent_msg=parent_msg)
238 django_messages.success(request, 'Message sent.')
239 compose_form = ComposeForm(request.user)
240 else:
241 if receiver is not None:
242 form_data = {'receiver': receiver}
243 compose_form = ComposeForm(request.user, initial=form_data)
244 else:
245 compose_form = ComposeForm(request.user)
246
247 _quota_check('outbox', Message.objects.outbox(request.user).count(), request)
248
249 return render(request, 'messages/compose_tab.html', {
250 'compose_form': compose_form,
251 })
252
253
254 def _only_integers(slist):
255 """
256 Accepts a list of strings. Returns a list of integers consisting of only
257 those elements from the original list that could be converted to integers
258
259 """
260 result = []
261 for s in slist:
262 try:
263 n = int(s)
264 except ValueError:
265 pass
266 else:
267 result.append(n)
268 return result
269
270
271 def _delete_msgs(user, msg_ids):
272 """
273 Deletes the messages given by the list of msg_ids. For this to succeed, the
274 user has to be either the sender or receiver on each message.
275
276 """
277 msg_ids = _only_integers(msg_ids)
278 msgs = Message.objects.filter(id__in=msg_ids)
279
280 for msg in msgs:
281 if msg.sender == user:
282 if (msg.receiver_delete_date is not None or
283 msg.read_date is None):
284 # Both parties deleted the message or receiver hasn't read it
285 # yet, we can delete it now
286 msg.delete()
287 else:
288 # receiver still has it in inbox
289 msg.sender_delete_date = datetime.datetime.now()
290 msg.save()
291
292 elif msg.receiver == user:
293 if msg.sender_delete_date is not None:
294 # both parties deleted the message, we can delete it now
295 msg.delete()
296 else:
297 # sender still has it in the outbox
298 msg.receiver_delete_date = datetime.datetime.now()
299 msg.save()
300
301
302 def _undelete_msgs(user, msg_ids):
303 """
304 Attempts to "undelete" the messages given by the msg_ids list.
305 This will only succeed if the user is either the sender or receiver.
306
307 """
308 msg_ids = _only_integers(msg_ids)
309 msgs = Message.objects.filter(id__in=msg_ids)
310 for msg in msgs:
311 if msg.sender == user:
312 msg.sender_delete_date = None
313 msg.save()
314 elif msg.receiver == user:
315 msg.receiver_delete_date = None
316 msg.save()
317
318
319 def bulk(request):
320 """
321 This view processes messages in bulk. Arrays of message ids are expected in
322 the POST query dict: inbox_ids and outbox_ids will be deleted; trash_ids will
323 be undeleted.
324
325 """
326 if not request.user.is_authenticated():
327 return HttpResponseForbidden()
328 if request.method != 'POST':
329 return HttpResponseNotAllowed(['POST'])
330
331 delete_ids = []
332 if 'inbox_ids' in request.POST:
333 delete_ids.extend(request.POST.getlist('inbox_ids'))
334 if 'outbox_ids' in request.POST:
335 delete_ids.extend(request.POST.getlist('outbox_ids'))
336
337 if len(delete_ids):
338 _delete_msgs(request.user, delete_ids)
339
340 if 'trash_ids' in request.POST:
341 _undelete_msgs(request.user, request.POST.getlist('trash_ids'))
342
343 return HttpResponse('');