Fixed double permission validation in debate creation
[e_cidadania.git] / src / apps / ecidadania / debate / views.py
blobf750bd532aab80c040591f522e9763116d33c7bc
1 # -*- coding: utf-8 -*-
3 # Copyright (c) 2010-2012 Cidadania S. Coop. Galega
5 # This file is part of e-cidadania.
7 # e-cidadania is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # e-cidadania is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with e-cidadania. If not, see <http://www.gnu.org/licenses/>.
20 """
21 These are the views that control the debates.
22 """
24 import json
25 import datetime
27 from django.views.generic.base import TemplateView, RedirectView
28 from django.views.generic.list import ListView
29 from django.views.generic.edit import CreateView, UpdateView, DeleteView
30 from django.views.generic.detail import DetailView
31 from django.contrib.auth.models import User, Group
32 from django.contrib import messages
33 from django.contrib.auth.decorators import login_required, permission_required
34 from django.contrib.comments import *
35 from django.contrib.contenttypes.models import ContentType
36 from django.contrib.comments.forms import CommentForm
37 from django.utils.decorators import method_decorator
38 from django.http import HttpResponse, HttpResponseRedirect, Http404
39 from django.shortcuts import render_to_response, get_object_or_404, redirect
40 from django.template import RequestContext
41 from django.forms.formsets import formset_factory, BaseFormSet
42 from django.core.exceptions import ObjectDoesNotExist
43 from django.core.serializers.json import DjangoJSONEncoder
44 from django.core.urlresolvers import reverse
45 from django.db import connection
46 from django.forms.models import modelformset_factory, inlineformset_factory
48 from apps.ecidadania.debate import url_names as urln
49 from apps.ecidadania.debate.models import Debate, Note, Row, Column
50 from apps.ecidadania.debate.forms import DebateForm, UpdateNoteForm, \
51 NoteForm, RowForm, ColumnForm, UpdateNotePosition
52 from core.spaces.models import Space
53 from core.permissions import has_space_permission, has_all_permissions
54 from helpers.cache import get_or_insert_object_in_cache
57 @permission_required('debate.add_debate')
58 def add_new_debate(request, space_url):
60 """
61 Create a new debate. This function returns two forms to create
62 a complete debate, debate form and phases formset.
64 .. versionadded:: 0.1.5
66 :attributes: debate_form, row_formset, column_formset
67 :context: form, rowform, colform, get_place, debateid
68 """
69 place = get_object_or_404(Space, url=space_url)
71 if has_space_permission(request.user, place, allow=['admins']) \
72 or has_all_permissions(request.user):
74 RowFormSet = inlineformset_factory(Debate, Row, extra=1)
75 ColumnFormSet = inlineformset_factory(Debate, Column, extra=1)
77 debate_form = DebateForm(request.POST or None)
78 row_formset = RowFormSet(request.POST or None, prefix="rowform")
79 column_formset = ColumnFormSet(request.POST or None, prefix="colform")
82 # Get the last PK and add 1 to get the current PK
83 try:
84 last_debate_id = Debate.objects.latest('id')
85 current_debate_id = last_debate_id.pk + 1
86 except ObjectDoesNotExist:
87 current_debate_id = 1
89 if request.method == 'POST':
90 if debate_form.is_valid() and row_formset.is_valid() \
91 and column_formset.is_valid():
92 debate_form_uncommited = debate_form.save(commit=False)
93 debate_form_uncommited.space = place
94 debate_form_uncommited.author = request.user
96 saved_debate = debate_form_uncommited.save()
97 debate_instance = get_object_or_404(Debate,
98 pk=current_debate_id)
100 row = row_formset.save(commit=False)
101 for form in row:
102 form.debate = debate_instance
103 form.save()
105 column = column_formset.save(commit=False)
106 for form in column:
107 form.debate = debate_instance
108 form.save()
110 return HttpResponseRedirect(reverse(urln.DEBATE_VIEW,
111 kwargs={'space_url': space_url,
112 'debate_id': str(debate_form_uncommited.id)}))
114 return render_to_response('debate/debate_add.html',
115 {'form': debate_form,
116 'rowform': row_formset,
117 'colform': column_formset,
118 'get_place': place,
119 'debateid': current_debate_id},
120 context_instance=RequestContext(request))
121 return render_to_response('not_allowed.html',
122 context_instance=RequestContext(request))
124 @permission_required('debate.edit_debate')
125 def edit_debate(request, space_url, pk):
128 place = get_object_or_404(Space, url=space_url)
130 if has_space_permission(request.user, place, allow=['admins']) \
131 or has_all_permissions(request.user):
133 RowFormSet = inlineformset_factory(Debate, Row, extra=1)
134 ColumnFormSet = inlineformset_factory(Debate, Column, extra=1)
136 instance = Debate.objects.get(pk=pk)
137 debate_form = DebateForm(request.POST or None, instance=instance)
138 row_formset = RowFormSet(request.POST or None, instance=instance, prefix="rowform")
139 column_formset = ColumnFormSet(request.POST or None, instance=instance, prefix="colform")
142 if request.user.has_perm('debate.debate_edit') \
143 or has_all_permissions(request.user):
144 if request.method == 'POST':
145 if debate_form.is_valid() and row_formset.is_valid() \
146 and column_formset.is_valid():
147 debate_form_uncommited = debate_form.save(commit=False)
148 debate_form_uncommited.space = place
149 debate_form_uncommited.author = request.user
151 saved_debate = debate_form_uncommited.save()
152 debate_instance = get_object_or_404(Debate,
153 pk=pk)
155 row = row_formset.save(commit=False)
156 for form in row:
157 form.debate = instance
158 form.save()
160 column = column_formset.save(commit=False)
161 for form in column:
162 form.debate = instance
163 form.save()
165 return HttpResponseRedirect(reverse(urln.DEBATE_VIEW,
166 kwargs={'space_url': space_url,
167 'debate_id': str(debate_form_uncommited.id)}))
169 return render_to_response('debate/debate_add.html',
170 {'form': debate_form,
171 'rowform': row_formset,
172 'colform': column_formset,
173 'get_place': place,
174 'debateid': pk},
175 context_instance=RequestContext(request))
176 return render_to_response('not_allowed.html',
177 context_instance=RequestContext(request))
178 def get_debates(request):
181 Get all debates and serve them through JSON.
183 data = [debate.title for debate in Debate.objects.order_by('title')]
184 return render_to_response(json.dumps(data), content_type='application/json')
187 def create_note(request, space_url):
190 This function creates a new note inside the debate board. It receives the
191 order from the createNote() AJAX function. To create the note first we
192 create the note in the DB, and if successful we return some of its
193 parameters to the debate board for the user. In case the petition had
194 errors, we return the error message that will be shown by jsnotify.
196 .. versionadded:: 0.1.5
198 note_form = NoteForm(request.POST or None)
200 if request.method == "POST" and request.is_ajax:
201 if note_form.is_valid():
202 note_form_uncommited = note_form.save(commit=False)
203 note_form_uncommited.author = request.user
204 note_form_uncommited.debate = get_object_or_404(Debate,
205 pk=request.POST['debateid'])
206 note_form_uncommited.title = request.POST['title']
207 note_form_uncommited.message = request.POST['message']
208 note_form_uncommited.column = get_object_or_404(Column,
209 pk=request.POST['column'])
210 note_form_uncommited.row = get_object_or_404(Row,
211 pk=request.POST['row'])
212 note_form_uncommited.save()
214 response_data = {}
215 response_data['id'] = note_form_uncommited.id
216 response_data['message'] = note_form_uncommited.message
217 response_data['title'] = note_form_uncommited.title
218 return HttpResponse(json.dumps(response_data),
219 mimetype="application/json")
221 else:
222 msg = "The note form didn't validate. This fields gave errors: " \
223 + str(note_form.errors)
224 else:
225 msg = "The petition was not POST."
227 return HttpResponse(json.dumps(msg), mimetype="application/json")
230 def update_note(request, space_url):
233 Updated the current note with the POST data. UpdateNoteForm is an incomplete
234 form that doesn't handle some properties, only the important for the note
235 editing.
238 if request.method == "GET" and request.is_ajax:
239 note = get_object_or_404(Note, pk=request.GET['noteid'])
240 ctype = ContentType.objects.get_for_model(Note)
241 latest_comments = Comment.objects.filter(is_public=True,
242 is_removed=False, content_type=ctype, object_pk=note.id) \
243 .order_by('-submit_date')[:5]
244 form = CommentForm(target_object = note)
246 response_data = {}
247 response_data['title'] = note.title
248 response_data['message'] = note.message
249 response_data['author'] = { 'name': note.author.username }
250 response_data['comments'] = [ {'username': c.user.username,
251 'comment': c.comment,
252 'submit_date': c.submit_date} for c in latest_comments]
253 response_data["form_html"] = form.as_p()
255 return HttpResponse(json.dumps(response_data, cls=DjangoJSONEncoder),
256 mimetype="application/json")
258 if request.method == "POST" and request.is_ajax:
259 note = get_object_or_404(Note, pk=request.POST['noteid'])
260 note_form = UpdateNoteForm(request.POST or None, instance=note)
261 if note_form.is_valid():
262 note_form_uncommited = note_form.save(commit=False)
263 note_form_uncommited.title = request.POST['title']
264 note_form_uncommited.message = request.POST['message']
265 note_form_uncommited.last_mod_author = request.user
267 note_form_uncommited.save()
268 msg = "The note has been updated."
269 else:
270 msg = "The form is not valid, check field(s): " + note_form.errors
271 else:
272 msg = "There was some error in the petition."
274 return HttpResponse(msg)
277 def update_position(request, space_url):
280 This view saves the new note position in the debate board. Instead of
281 reloading all the note form with all the data, we use the partial form
282 "UpdateNotePosition" which only handles the column and row of the note.
284 note = get_object_or_404(Note, pk=request.POST['noteid'])
285 position_form = UpdateNotePosition(request.POST or None, instance=note)
287 if request.method == "POST" and request.is_ajax:
288 if request.user == note.author or request.user.is_staff:
289 if position_form.is_valid():
290 position_form_uncommited = position_form.save(commit=False)
291 position_form_uncommited.column = get_object_or_404(Column,
292 pk=request.POST['column'])
293 position_form_uncommited.row = get_object_or_404(Row,
294 pk=request.POST['row'])
295 position_form_uncommited.save()
296 msg = "The note has been updated."
297 else:
298 msg = "There has been an error validating the form."
299 else:
300 msg = "There was some error in the petition."
302 return HttpResponse(msg)
305 def delete_note(request, space_url):
308 Deletes a note object.
310 note = get_object_or_404(Note, pk=request.POST['noteid'])
312 if note.author == request.user or has_all_permissions(request.user):
313 ctype = ContentType.objects.get_for_model(Note)
314 all_comments = Comment.objects.filter(is_public=True,
315 is_removed=False, content_type=ctype,
316 object_pk=note.id).all()
317 for i in range(note.comment_count):
318 all_comments[i].delete()
319 note.delete()
320 return HttpResponse("The note has been deleted.")
322 else:
323 return HttpResponse("You're not the author of the note. Can't delete.")
326 class ViewDebate(DetailView):
328 View a debate.
330 :context: get_place, notes, columns, rows
332 context_object_name = 'debate'
333 template_name = 'debate/debate_view.html'
335 def get_object(self):
336 key = self.kwargs['debate_id']
337 debate = get_or_insert_object_in_cache(Debate, key, pk=key)
339 # Check debate dates
340 if datetime.date.today() >= debate.end_date \
341 or datetime.date.today() < debate.start_date:
342 self.template_name = 'debate/debate_outdated.html'
343 return debate
344 # We can't return none, if we do, the platform cannot show
345 # the start and end dates and the title
346 #return Debate.objects.none()
348 return debate
350 def get_context_data(self, **kwargs):
351 context = super(ViewDebate, self).get_context_data(**kwargs)
352 columns = Column.objects.filter(debate=self.kwargs['debate_id'])
353 rows = Row.objects.filter(debate=self.kwargs['debate_id'])
354 space_key = self.kwargs['space_url']
355 current_space = get_or_insert_object_in_cache(Space, space_key,
356 url=space_key)
357 debate_key = self.kwargs['debate_id']
358 current_debate = get_or_insert_object_in_cache(Debate, debate_key,
359 pk=debate_key)
360 notes = Note.objects.filter(debate=current_debate.pk)
361 try:
362 last_note = Note.objects.latest('id')
363 except:
364 last_note = 0
366 context['get_place'] = current_space
367 context['notes'] = notes
368 context['columns'] = columns
369 context['rows'] = rows
370 if last_note == 0:
371 context['lastnote'] = 0
372 else:
373 context['lastnote'] = last_note.pk
375 return context
378 class ListDebates(ListView):
380 Return a list of debates for the current space.
382 :context: get_place
384 paginate_by = 10
386 def get_queryset(self):
387 key = self.kwargs['space_url']
388 current_space = get_or_insert_object_in_cache(Space, key, url=key)
389 debates = Debate.objects.filter(space=current_space)
391 # Here must go a validation so a user registered to the space
392 # can always see the debate list. While an anonymous or not
393 # registered user can't see anything unless the space is public
395 return debates
397 def get_context_data(self, **kwargs):
398 context = super(ListDebates, self).get_context_data(**kwargs)
399 key = self.kwargs['space_url']
400 space = get_or_insert_object_in_cache(Space, key, url=key)
401 context['get_place'] = space
402 return context