Merge branch 'stable/1.6.x' into 'master'.
Conflicts: CHANGELOG openslides/agenda/views.py openslides/assignment/views.py openslides/mediafile/views.py openslides/users/views.py openslides/utils/views.py tests/motion/test_pdf.py tests/settings.py
This commit is contained in:
commit
dcd8b7a639
11
CHANGELOG
11
CHANGELOG
@ -6,6 +6,7 @@ http://openslides.org
|
|||||||
|
|
||||||
Version 2.0.0 (unreleased)
|
Version 2.0.0 (unreleased)
|
||||||
==========================
|
==========================
|
||||||
|
[https://github.com/OpenSlides/OpenSlides/milestones/2.0]
|
||||||
|
|
||||||
Other:
|
Other:
|
||||||
- Changed supported Python version to >= 3.3.
|
- Changed supported Python version to >= 3.3.
|
||||||
@ -15,6 +16,16 @@ Other:
|
|||||||
template signals and slides.
|
template signals and slides.
|
||||||
|
|
||||||
|
|
||||||
|
Version 1.7.0 (unreleased)
|
||||||
|
==========================
|
||||||
|
[https://github.com/OpenSlides/OpenSlides/milestones/1.7]
|
||||||
|
|
||||||
|
Motions:
|
||||||
|
- Added possibility to hide motions from non staff users in some states.
|
||||||
|
Other:
|
||||||
|
- Cleaned up utils.views to increase performance when fetching single objects
|
||||||
|
from the database for a view (#1378).
|
||||||
|
|
||||||
|
|
||||||
Version 1.6.1 (2014-12-08)
|
Version 1.6.1 (2014-12-08)
|
||||||
==========================
|
==========================
|
||||||
|
@ -216,6 +216,21 @@ Komplexer Arbeitsablauf
|
|||||||
+---------------------+-----------------+---------------+------------+---------------+------------------------+---------------+
|
+---------------------+-----------------+---------------+------------+---------------+------------------------+---------------+
|
||||||
|
|
||||||
|
|
||||||
|
Experteneinstellung
|
||||||
|
'''''''''''''''''''
|
||||||
|
|
||||||
|
Es kann eingestellt werden, dass ein Antrag in einem bestimmten Status nur
|
||||||
|
für bestimmte Benutzer sichtbar ist. Die Anpassung kann vor dem ersten
|
||||||
|
Anlegen der Datenbank in der Datei ``openslides/motion/signals.py`` in der
|
||||||
|
Funktion ``create_builtin_workflows()`` erfolgen: Dazu beim gewünschten
|
||||||
|
Status beispielsweise die Zeile
|
||||||
|
``required_permission_to_see='motion.can_manage_motion',`` einfügen. Die
|
||||||
|
Anpassung kann aber auch bei bestehender Datenbank erfolgen: Dazu mit einem
|
||||||
|
entsprechenden Tool direkt auf die Datenbank zugreifen und beim gewünschten
|
||||||
|
Status das Feld ``required_permission_to_see`` beispielsweise mit der
|
||||||
|
Zeichenkette ``motion.can_manage_motion`` überschreiben.
|
||||||
|
|
||||||
|
|
||||||
Versionierung
|
Versionierung
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
@ -185,15 +185,14 @@ class AgendaItemView(SingleObjectMixin, FormView):
|
|||||||
return check_permission
|
return check_permission
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
self.object = self.get_object()
|
list_of_speakers = self.get_object().get_list_of_speakers()
|
||||||
list_of_speakers = self.object.get_list_of_speakers()
|
|
||||||
active_slide = get_active_slide()
|
active_slide = get_active_slide()
|
||||||
active_type = active_slide.get('type', None)
|
active_type = active_slide.get('type', None)
|
||||||
kwargs.update({
|
kwargs.update({
|
||||||
'object': self.object,
|
'object': self.get_object(),
|
||||||
'list_of_speakers': list_of_speakers,
|
'list_of_speakers': list_of_speakers,
|
||||||
'is_on_the_list_of_speakers': Speaker.objects.filter(
|
'is_on_the_list_of_speakers': Speaker.objects.filter(
|
||||||
item=self.object, begin_time=None, user=self.request.user).exists(),
|
item=self.get_object(), begin_time=None, user=self.request.user).exists(),
|
||||||
'active_type': active_type,
|
'active_type': active_type,
|
||||||
})
|
})
|
||||||
return super(AgendaItemView, self).get_context_data(**kwargs)
|
return super(AgendaItemView, self).get_context_data(**kwargs)
|
||||||
@ -208,7 +207,7 @@ class AgendaItemView(SingleObjectMixin, FormView):
|
|||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class SetClosed(RedirectView, SingleObjectMixin):
|
class SetClosed(SingleObjectMixin, RedirectView):
|
||||||
"""
|
"""
|
||||||
Close or open an item.
|
Close or open an item.
|
||||||
"""
|
"""
|
||||||
@ -220,15 +219,14 @@ class SetClosed(RedirectView, SingleObjectMixin):
|
|||||||
def get_ajax_context(self, **kwargs):
|
def get_ajax_context(self, **kwargs):
|
||||||
closed = self.kwargs['closed']
|
closed = self.kwargs['closed']
|
||||||
if closed:
|
if closed:
|
||||||
link = reverse('item_open', args=[self.object.pk])
|
link = reverse('item_open', args=[self.get_object().pk])
|
||||||
else:
|
else:
|
||||||
link = reverse('item_close', args=[self.object.pk])
|
link = reverse('item_close', args=[self.get_object().pk])
|
||||||
return super(SetClosed, self).get_ajax_context(closed=closed, link=link)
|
return super(SetClosed, self).get_ajax_context(closed=closed, link=link)
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
|
||||||
closed = kwargs['closed']
|
closed = kwargs['closed']
|
||||||
self.object.set_closed(closed)
|
self.get_object().set_closed(closed)
|
||||||
return super(SetClosed, self).pre_redirect(request, *args, **kwargs)
|
return super(SetClosed, self).pre_redirect(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
@ -247,7 +245,7 @@ class ItemUpdate(UpdateView):
|
|||||||
url_name_args = []
|
url_name_args = []
|
||||||
|
|
||||||
def get_form_class(self):
|
def get_form_class(self):
|
||||||
if self.object.content_object:
|
if self.get_object().content_object:
|
||||||
form = RelatedItemForm
|
form = RelatedItemForm
|
||||||
else:
|
else:
|
||||||
form = ItemForm
|
form = ItemForm
|
||||||
@ -288,7 +286,7 @@ class ItemDelete(DeleteView):
|
|||||||
try:
|
try:
|
||||||
options = self.item_delete_answer_options
|
options = self.item_delete_answer_options
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
if self.object.children.exists():
|
if self.get_object().children.exists():
|
||||||
options = [('all', _("Yes, with all child items."))] + self.answer_options
|
options = [('all', _("Yes, with all child items."))] + self.answer_options
|
||||||
else:
|
else:
|
||||||
options = self.answer_options
|
options = self.answer_options
|
||||||
@ -299,13 +297,13 @@ class ItemDelete(DeleteView):
|
|||||||
"""
|
"""
|
||||||
Deletes the item but not its children.
|
Deletes the item but not its children.
|
||||||
"""
|
"""
|
||||||
self.object.delete(with_children=False)
|
self.get_object().delete(with_children=False)
|
||||||
|
|
||||||
def on_clicked_all(self):
|
def on_clicked_all(self):
|
||||||
"""
|
"""
|
||||||
Deletes the item and its children.
|
Deletes the item and its children.
|
||||||
"""
|
"""
|
||||||
self.object.delete(with_children=True)
|
self.get_object().delete(with_children=True)
|
||||||
|
|
||||||
def get_final_message(self):
|
def get_final_message(self):
|
||||||
"""
|
"""
|
||||||
@ -314,9 +312,9 @@ class ItemDelete(DeleteView):
|
|||||||
# OpenSlidesError (invalid answer) should never be raised here because
|
# OpenSlidesError (invalid answer) should never be raised here because
|
||||||
# this method should only be called if the answer is 'yes' or 'all'.
|
# this method should only be called if the answer is 'yes' or 'all'.
|
||||||
if self.get_answer() == 'yes':
|
if self.get_answer() == 'yes':
|
||||||
message = _('Item %s was successfully deleted.') % html_strong(self.object)
|
message = _('Item %s was successfully deleted.') % html_strong(self.get_object())
|
||||||
else:
|
else:
|
||||||
message = _('Item %s and its children were successfully deleted.') % html_strong(self.object)
|
message = _('Item %s and its children were successfully deleted.') % html_strong(self.get_object())
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
@ -331,18 +329,11 @@ class CreateRelatedAgendaItemView(SingleObjectMixin, RedirectView):
|
|||||||
url_name = 'item_overview'
|
url_name = 'item_overview'
|
||||||
url_name_args = []
|
url_name_args = []
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Set self.object to the relevant object.
|
|
||||||
"""
|
|
||||||
self.object = self.get_object()
|
|
||||||
return super(CreateRelatedAgendaItemView, self).get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Create the agenda item.
|
Create the agenda item.
|
||||||
"""
|
"""
|
||||||
self.item = Item.objects.create(content_object=self.object)
|
self.item = Item.objects.create(content_object=self.get_object())
|
||||||
|
|
||||||
|
|
||||||
class AgendaNumberingView(QuestionView):
|
class AgendaNumberingView(QuestionView):
|
||||||
@ -390,12 +381,11 @@ class SpeakerAppendView(SingleObjectMixin, RedirectView):
|
|||||||
model = Item
|
model = Item
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
if self.get_object().speaker_list_closed:
|
||||||
if self.object.speaker_list_closed:
|
|
||||||
messages.error(request, _('The list of speakers is closed.'))
|
messages.error(request, _('The list of speakers is closed.'))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
Speaker.objects.add(item=self.object, user=request.user)
|
Speaker.objects.add(item=self.get_object(), user=request.user)
|
||||||
except OpenSlidesError as e:
|
except OpenSlidesError as e:
|
||||||
messages.error(request, e)
|
messages.error(request, e)
|
||||||
else:
|
else:
|
||||||
@ -420,11 +410,10 @@ class SpeakerDeleteView(DeleteView):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
try:
|
if self.get_object() is None:
|
||||||
return super(SpeakerDeleteView, self).get(*args, **kwargs)
|
|
||||||
except Speaker.DoesNotExist:
|
|
||||||
messages.error(self.request, _('You are not on the list of speakers.'))
|
|
||||||
return super(RedirectView, self).get(*args, **kwargs)
|
return super(RedirectView, self).get(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
return super().get(*args, **kwargs)
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
"""
|
"""
|
||||||
@ -434,10 +423,22 @@ class SpeakerDeleteView(DeleteView):
|
|||||||
object with the request.user as speaker.
|
object with the request.user as speaker.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return Speaker.objects.get(pk=self.kwargs['speaker'])
|
speaker = self._object
|
||||||
except KeyError:
|
except AttributeError:
|
||||||
return Speaker.objects.filter(
|
speaker_pk = self.kwargs.get('speaker')
|
||||||
item=self.kwargs['pk'], user=self.request.user).exclude(weight=None).get()
|
if speaker_pk is not None:
|
||||||
|
queryset = Speaker.objects.filter(pk=speaker_pk)
|
||||||
|
else:
|
||||||
|
queryset = Speaker.objects.filter(
|
||||||
|
item=self.kwargs['pk'], user=self.request.user).exclude(weight=None)
|
||||||
|
try:
|
||||||
|
speaker = queryset.get()
|
||||||
|
except Speaker.DoesNotExist:
|
||||||
|
speaker = None
|
||||||
|
if speaker_pk is not None:
|
||||||
|
messages.error(self.request, _('You are not on the list of speakers.'))
|
||||||
|
self._object = speaker
|
||||||
|
return speaker
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
return [self.kwargs['pk']]
|
return [self.kwargs['pk']]
|
||||||
@ -458,22 +459,21 @@ class SpeakerSpeakView(SingleObjectMixin, RedirectView):
|
|||||||
model = Item
|
model = Item
|
||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
|
||||||
try:
|
try:
|
||||||
speaker = Speaker.objects.filter(
|
speaker = Speaker.objects.filter(
|
||||||
user=kwargs['user_id'],
|
user=kwargs['user_id'],
|
||||||
item=self.object,
|
item=self.get_object(),
|
||||||
begin_time=None).get()
|
begin_time=None).get()
|
||||||
except Speaker.DoesNotExist: # TODO: Check the MultipleObjectsReturned error here?
|
except Speaker.DoesNotExist: # TODO: Check the MultipleObjectsReturned error here?
|
||||||
messages.error(
|
messages.error(
|
||||||
self.request,
|
self.request,
|
||||||
_('%(user)s is not on the list of %(item)s.')
|
_('%(user)s is not on the list of %(item)s.')
|
||||||
% {'user': kwargs['user_id'], 'item': self.object})
|
% {'user': kwargs['user_id'], 'item': self.get_object()})
|
||||||
else:
|
else:
|
||||||
speaker.begin_speach()
|
speaker.begin_speach()
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
return [self.object.pk]
|
return [self.get_object().pk]
|
||||||
|
|
||||||
|
|
||||||
class SpeakerEndSpeachView(SingleObjectMixin, RedirectView):
|
class SpeakerEndSpeachView(SingleObjectMixin, RedirectView):
|
||||||
@ -485,21 +485,20 @@ class SpeakerEndSpeachView(SingleObjectMixin, RedirectView):
|
|||||||
model = Item
|
model = Item
|
||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
|
||||||
try:
|
try:
|
||||||
speaker = Speaker.objects.filter(
|
speaker = Speaker.objects.filter(
|
||||||
item=self.object,
|
item=self.get_object(),
|
||||||
end_time=None).exclude(begin_time=None).get()
|
end_time=None).exclude(begin_time=None).get()
|
||||||
except Speaker.DoesNotExist:
|
except Speaker.DoesNotExist:
|
||||||
messages.error(
|
messages.error(
|
||||||
self.request,
|
self.request,
|
||||||
_('There is no one speaking at the moment according to %(item)s.')
|
_('There is no one speaking at the moment according to %(item)s.')
|
||||||
% {'item': self.object})
|
% {'item': self.get_object()})
|
||||||
else:
|
else:
|
||||||
speaker.end_speach()
|
speaker.end_speach()
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
return [self.object.pk]
|
return [self.get_object().pk]
|
||||||
|
|
||||||
|
|
||||||
class SpeakerListCloseView(SingleObjectMixin, RedirectView):
|
class SpeakerListCloseView(SingleObjectMixin, RedirectView):
|
||||||
@ -512,12 +511,11 @@ class SpeakerListCloseView(SingleObjectMixin, RedirectView):
|
|||||||
url_name = 'item_view'
|
url_name = 'item_view'
|
||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.get_object().speaker_list_closed = not self.reopen
|
||||||
self.object.speaker_list_closed = not self.reopen
|
self.get_object().save()
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
return [self.object.pk]
|
return [self.get_object().pk]
|
||||||
|
|
||||||
|
|
||||||
class SpeakerChangeOrderView(SingleObjectMixin, RedirectView):
|
class SpeakerChangeOrderView(SingleObjectMixin, RedirectView):
|
||||||
@ -530,17 +528,12 @@ class SpeakerChangeOrderView(SingleObjectMixin, RedirectView):
|
|||||||
model = Item
|
model = Item
|
||||||
url_name = 'item_view'
|
url_name = 'item_view'
|
||||||
|
|
||||||
def pre_redirect(self, args, **kwargs):
|
|
||||||
self.object = self.get_object()
|
|
||||||
|
|
||||||
def pre_post_redirect(self, request, *args, **kwargs):
|
def pre_post_redirect(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Reorder the list of speaker.
|
Reorder the list of speaker.
|
||||||
|
|
||||||
Take the string 'sort_order' from the post-data, and use this order.
|
Take the string 'sort_order' from the post-data, and use this order.
|
||||||
"""
|
"""
|
||||||
self.object = self.get_object()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
for (counter, speaker) in enumerate(self.request.POST['sort_order'].split(',')):
|
for (counter, speaker) in enumerate(self.request.POST['sort_order'].split(',')):
|
||||||
@ -549,7 +542,7 @@ class SpeakerChangeOrderView(SingleObjectMixin, RedirectView):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
raise IntegrityError
|
raise IntegrityError
|
||||||
try:
|
try:
|
||||||
speaker = Speaker.objects.filter(item=self.object).get(pk=speaker_pk)
|
speaker = Speaker.objects.filter(item=self.get_object()).get(pk=speaker_pk)
|
||||||
except Speaker.DoesNotExist:
|
except Speaker.DoesNotExist:
|
||||||
raise IntegrityError
|
raise IntegrityError
|
||||||
speaker.weight = counter + 1
|
speaker.weight = counter + 1
|
||||||
@ -558,7 +551,7 @@ class SpeakerChangeOrderView(SingleObjectMixin, RedirectView):
|
|||||||
messages.error(request, _('Could not change order. Invalid data.'))
|
messages.error(request, _('Could not change order. Invalid data.'))
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
return [self.object.pk]
|
return [self.get_object().pk]
|
||||||
|
|
||||||
|
|
||||||
class CurrentListOfSpeakersView(RedirectView):
|
class CurrentListOfSpeakersView(RedirectView):
|
||||||
|
@ -40,31 +40,30 @@ class AssignmentDetail(DetailView):
|
|||||||
context['form'] = self.form_class(self.request.POST)
|
context['form'] = self.form_class(self.request.POST)
|
||||||
else:
|
else:
|
||||||
context['form'] = self.form_class()
|
context['form'] = self.form_class()
|
||||||
polls = self.object.poll_set.all()
|
polls = self.get_object().poll_set.all()
|
||||||
if not self.request.user.has_perm('assignment.can_manage_assignment'):
|
if not self.request.user.has_perm('assignment.can_manage_assignment'):
|
||||||
polls = self.object.poll_set.filter(published=True)
|
polls = self.get_object().poll_set.filter(published=True)
|
||||||
vote_results = self.object.vote_results(only_published=True)
|
vote_results = self.get_object().vote_results(only_published=True)
|
||||||
else:
|
else:
|
||||||
polls = self.object.poll_set.all()
|
polls = self.get_object().poll_set.all()
|
||||||
vote_results = self.object.vote_results(only_published=False)
|
vote_results = self.get_object().vote_results(only_published=False)
|
||||||
|
|
||||||
blocked_candidates = [
|
blocked_candidates = [
|
||||||
candidate.person for candidate in
|
candidate.person for candidate in
|
||||||
self.object.assignment_candidates.filter(blocked=True)]
|
self.get_object().assignment_candidates.filter(blocked=True)]
|
||||||
context['polls'] = polls
|
context['polls'] = polls
|
||||||
context['vote_results'] = vote_results
|
context['vote_results'] = vote_results
|
||||||
context['blocked_candidates'] = blocked_candidates
|
context['blocked_candidates'] = blocked_candidates
|
||||||
context['user_is_candidate'] = self.object.is_candidate(self.request.user)
|
context['user_is_candidate'] = self.get_object().is_candidate(self.request.user)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
|
||||||
if self.request.user.has_perm('assignment.can_nominate_other'):
|
if self.request.user.has_perm('assignment.can_nominate_other'):
|
||||||
form = self.form_class(self.request.POST)
|
form = self.form_class(self.request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
user = form.cleaned_data['candidate']
|
user = form.cleaned_data['candidate']
|
||||||
try:
|
try:
|
||||||
self.object.run(user, self.request.user)
|
self.get_object().run(user, self.request.user)
|
||||||
except NameError as e:
|
except NameError as e:
|
||||||
messages.error(self.request, e)
|
messages.error(self.request, e)
|
||||||
else:
|
else:
|
||||||
@ -98,18 +97,17 @@ class AssignmentSetStatusView(SingleObjectMixin, RedirectView):
|
|||||||
url_name = 'assignment_detail'
|
url_name = 'assignment_detail'
|
||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
|
||||||
status = kwargs.get('status')
|
status = kwargs.get('status')
|
||||||
if status is not None:
|
if status is not None:
|
||||||
try:
|
try:
|
||||||
self.object.set_status(status)
|
self.get_object().set_status(status)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
messages.error(self.request, e)
|
messages.error(self.request, e)
|
||||||
else:
|
else:
|
||||||
messages.success(
|
messages.success(
|
||||||
self.request,
|
self.request,
|
||||||
_('Election status was set to: %s.') %
|
_('Election status was set to: %s.') %
|
||||||
html_strong(self.object.get_status_display())
|
html_strong(self.get_object().get_status_display())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -134,11 +132,10 @@ class AssignmentRunDeleteView(SingleObjectMixin, RedirectView):
|
|||||||
url_name = 'assignment_detail'
|
url_name = 'assignment_detail'
|
||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
if self.get_object().status == 'sea' or self.request.user.has_perm(
|
||||||
if self.object.status == 'sea' or self.request.user.has_perm(
|
|
||||||
"assignment.can_manage_assignment"):
|
"assignment.can_manage_assignment"):
|
||||||
try:
|
try:
|
||||||
self.object.delrun(self.request.user, blocked=True)
|
self.get_object().delrun(self.request.user, blocked=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# TODO: only catch relevant exception
|
# TODO: only catch relevant exception
|
||||||
messages.error(self.request, e)
|
messages.error(self.request, e)
|
||||||
@ -165,7 +162,7 @@ class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
|
|||||||
def on_clicked_yes(self):
|
def on_clicked_yes(self):
|
||||||
self._get_person_information()
|
self._get_person_information()
|
||||||
try:
|
try:
|
||||||
self.object.delrun(self.person, blocked=False)
|
self.get_object().delrun(self.person, blocked=False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# TODO: only catch relevant exception
|
# TODO: only catch relevant exception
|
||||||
self.error = e
|
self.error = e
|
||||||
@ -185,9 +182,8 @@ class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
|
|||||||
return message
|
return message
|
||||||
|
|
||||||
def _get_person_information(self):
|
def _get_person_information(self):
|
||||||
self.object = self.get_object()
|
|
||||||
self.person = User.objects.get(pk=self.kwargs.get('user_id'))
|
self.person = User.objects.get(pk=self.kwargs.get('user_id'))
|
||||||
self.is_blocked = self.object.is_blocked(self.person)
|
self.is_blocked = self.get_object().is_blocked(self.person)
|
||||||
|
|
||||||
|
|
||||||
class PollCreateView(SingleObjectMixin, RedirectView):
|
class PollCreateView(SingleObjectMixin, RedirectView):
|
||||||
@ -196,8 +192,7 @@ class PollCreateView(SingleObjectMixin, RedirectView):
|
|||||||
url_name = 'assignment_detail'
|
url_name = 'assignment_detail'
|
||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.get_object().gen_poll()
|
||||||
self.object.gen_poll()
|
|
||||||
messages.success(self.request, _("New ballot was successfully created."))
|
messages.success(self.request, _("New ballot was successfully created."))
|
||||||
|
|
||||||
|
|
||||||
@ -232,15 +227,15 @@ class SetPublishStatusView(SingleObjectMixin, RedirectView):
|
|||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
self.object = self.get_object()
|
poll = self.get_object()
|
||||||
except self.model.DoesNotExist:
|
except self.model.DoesNotExist:
|
||||||
messages.error(self.request, _('Ballot ID %d does not exist.') %
|
messages.error(self.request, _('Ballot ID %d does not exist.') %
|
||||||
int(kwargs['poll_id']))
|
int(kwargs['poll_id']))
|
||||||
else:
|
else:
|
||||||
if self.object.published:
|
if poll.published:
|
||||||
self.object.set_published(False)
|
poll.set_published(False)
|
||||||
else:
|
else:
|
||||||
self.object.set_published(True)
|
poll.set_published(True)
|
||||||
|
|
||||||
|
|
||||||
class SetElectedView(SingleObjectMixin, RedirectView):
|
class SetElectedView(SingleObjectMixin, RedirectView):
|
||||||
@ -250,19 +245,18 @@ class SetElectedView(SingleObjectMixin, RedirectView):
|
|||||||
allow_ajax = True
|
allow_ajax = True
|
||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
|
||||||
self.person = User.objects.get(pk=kwargs['user_id'])
|
self.person = User.objects.get(pk=kwargs['user_id'])
|
||||||
self.elected = kwargs['elected']
|
self.elected = kwargs['elected']
|
||||||
self.object.set_elected(self.person, self.elected)
|
self.get_object().set_elected(self.person, self.elected)
|
||||||
|
|
||||||
def get_ajax_context(self, **kwargs):
|
def get_ajax_context(self, **kwargs):
|
||||||
if self.elected:
|
if self.elected:
|
||||||
link = reverse('assignment_user_not_elected',
|
link = reverse('assignment_user_not_elected',
|
||||||
args=[self.object.id, self.person.person_id])
|
args=[self.get_object().id, self.person.person_id])
|
||||||
text = _('not elected')
|
text = _('not elected')
|
||||||
else:
|
else:
|
||||||
link = reverse('assignment_user_elected',
|
link = reverse('assignment_user_elected',
|
||||||
args=[self.object.id, self.person.person_id])
|
args=[self.get_object().id, self.person.person_id])
|
||||||
text = _('elected')
|
text = _('elected')
|
||||||
return {'elected': self.elected, 'link': link, 'text': text}
|
return {'elected': self.elected, 'link': link, 'text': text}
|
||||||
|
|
||||||
@ -283,7 +277,7 @@ class AssignmentPollDeleteView(DeleteView):
|
|||||||
super(AssignmentPollDeleteView, self).pre_post_redirect(request, *args, **kwargs)
|
super(AssignmentPollDeleteView, self).pre_post_redirect(request, *args, **kwargs)
|
||||||
|
|
||||||
def set_assignment(self):
|
def set_assignment(self):
|
||||||
self.assignment = self.object.assignment
|
self.assignment = self.get_object().assignment
|
||||||
|
|
||||||
def get_redirect_url(self, **kwargs):
|
def get_redirect_url(self, **kwargs):
|
||||||
return reverse('assignment_detail', args=[self.assignment.id])
|
return reverse('assignment_detail', args=[self.assignment.id])
|
||||||
|
@ -85,7 +85,7 @@ class MediafileUpdateView(MediafileViewMixin, UpdateView):
|
|||||||
|
|
||||||
def get_form_kwargs(self, *args, **kwargs):
|
def get_form_kwargs(self, *args, **kwargs):
|
||||||
form_kwargs = super(MediafileUpdateView, self).get_form_kwargs(*args, **kwargs)
|
form_kwargs = super(MediafileUpdateView, self).get_form_kwargs(*args, **kwargs)
|
||||||
form_kwargs['initial'].update({'uploader': self.object.uploader.pk})
|
form_kwargs['initial'].update({'uploader': self.get_object().uploader.pk})
|
||||||
return form_kwargs
|
return form_kwargs
|
||||||
|
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ class MediafileDeleteView(DeleteView):
|
|||||||
|
|
||||||
def on_clicked_yes(self, *args, **kwargs):
|
def on_clicked_yes(self, *args, **kwargs):
|
||||||
"""Deletes the file in the filesystem, if user clicks "Yes"."""
|
"""Deletes the file in the filesystem, if user clicks "Yes"."""
|
||||||
self.object.mediafile.delete()
|
self.get_object().mediafile.delete()
|
||||||
return super(MediafileDeleteView, self).on_clicked_yes(*args, **kwargs)
|
return super(MediafileDeleteView, self).on_clicked_yes(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -472,6 +472,7 @@ class Motion(SlideMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
|
|
||||||
The dictonary contains the following actions.
|
The dictonary contains the following actions.
|
||||||
|
|
||||||
|
* see
|
||||||
* update / edit
|
* update / edit
|
||||||
* delete
|
* delete
|
||||||
* create_poll
|
* create_poll
|
||||||
@ -481,9 +482,14 @@ class Motion(SlideMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
* reset_state
|
* reset_state
|
||||||
"""
|
"""
|
||||||
actions = {
|
actions = {
|
||||||
'update': ((self.is_submitter(person) and
|
'see': (person.has_perm('motion.can_see_motion') and
|
||||||
self.state.allow_submitter_edit) or
|
(not self.state.required_permission_to_see or
|
||||||
person.has_perm('motion.can_manage_motion')),
|
person.has_perm(self.state.required_permission_to_see) or
|
||||||
|
self.is_submitter(person))),
|
||||||
|
|
||||||
|
'update': (person.has_perm('motion.can_manage_motion') or
|
||||||
|
(self.is_submitter(person) and
|
||||||
|
self.state.allow_submitter_edit)),
|
||||||
|
|
||||||
'delete': person.has_perm('motion.can_manage_motion'),
|
'delete': person.has_perm('motion.can_manage_motion'),
|
||||||
|
|
||||||
@ -774,6 +780,16 @@ class State(models.Model):
|
|||||||
icon = models.CharField(max_length=255)
|
icon = models.CharField(max_length=255)
|
||||||
"""A string representing the url to the icon-image."""
|
"""A string representing the url to the icon-image."""
|
||||||
|
|
||||||
|
required_permission_to_see = models.CharField(max_length=255, blank=True)
|
||||||
|
"""
|
||||||
|
A permission string. If not empty, the user has to have this permission to
|
||||||
|
see a motion in this state.
|
||||||
|
|
||||||
|
To use this feature change the database entry of a state object and add
|
||||||
|
your favourite permission string. You can do this e. g. by editing the
|
||||||
|
definitions in create_builtin_workflows() in openslides/motion/signals.py.
|
||||||
|
"""
|
||||||
|
|
||||||
allow_support = models.BooleanField(default=False)
|
allow_support = models.BooleanField(default=False)
|
||||||
"""If true, persons can support the motion in this state."""
|
"""If true, persons can support the motion in this state."""
|
||||||
|
|
||||||
|
@ -12,17 +12,16 @@ from openslides.config.api import config
|
|||||||
from openslides.users.models import Group, User # TODO: remove this line
|
from openslides.users.models import Group, User # TODO: remove this line
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
|
|
||||||
from .models import Category, Motion
|
from .models import Category
|
||||||
|
|
||||||
# Needed to count the delegates
|
# Needed to count the delegates
|
||||||
# TODO: find another way to do this.
|
# TODO: find another way to do this.
|
||||||
|
|
||||||
|
|
||||||
def motions_to_pdf(pdf):
|
def motions_to_pdf(pdf, motions):
|
||||||
"""
|
"""
|
||||||
Create a PDF with all motions.
|
Create a PDF with all motions.
|
||||||
"""
|
"""
|
||||||
motions = Motion.objects.all()
|
|
||||||
motions = natsorted(motions, key=attrgetter('identifier'))
|
motions = natsorted(motions, key=attrgetter('identifier'))
|
||||||
all_motion_cover(pdf, motions)
|
all_motion_cover(pdf, motions)
|
||||||
for motion in motions:
|
for motion in motions:
|
||||||
|
@ -30,8 +30,30 @@ class MotionListView(ListView):
|
|||||||
"""
|
"""
|
||||||
View, to list all motions.
|
View, to list all motions.
|
||||||
"""
|
"""
|
||||||
required_permission = 'motion.can_see_motion'
|
|
||||||
model = Motion
|
model = Motion
|
||||||
|
required_permission = 'motion.can_see_motion'
|
||||||
|
# The template name must be set explicitly because the overridden method
|
||||||
|
# get_queryset() does not return a QuerySet any more so that Django can't
|
||||||
|
# generate the template name from the name of the model.
|
||||||
|
template_name = 'motion/motion_list.html'
|
||||||
|
# The attribute context_object_name must be set explicitly because the
|
||||||
|
# overridden method get_queryset() does not return a QuerySet any more so
|
||||||
|
# that Django can't generate the context_object_name from the name of the
|
||||||
|
# model.
|
||||||
|
context_object_name = 'motion_list'
|
||||||
|
|
||||||
|
def get_queryset(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns not a QuerySet but a filtered list of motions. Excludes motions
|
||||||
|
that the user is not allowed to see.
|
||||||
|
"""
|
||||||
|
queryset = super(MotionListView, self).get_queryset(*args, **kwargs)
|
||||||
|
motions = []
|
||||||
|
for motion in queryset:
|
||||||
|
if (not motion.state.required_permission_to_see or
|
||||||
|
self.request.user.has_perm(motion.state.required_permission_to_see)):
|
||||||
|
motions.append(motion)
|
||||||
|
return motions
|
||||||
|
|
||||||
motion_list = MotionListView.as_view()
|
motion_list = MotionListView.as_view()
|
||||||
|
|
||||||
@ -40,9 +62,14 @@ class MotionDetailView(DetailView):
|
|||||||
"""
|
"""
|
||||||
Show one motion.
|
Show one motion.
|
||||||
"""
|
"""
|
||||||
required_permission = 'motion.can_see_motion'
|
|
||||||
model = Motion
|
model = Motion
|
||||||
|
|
||||||
|
def check_permission(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Check if the request.user has the permission to see the motion.
|
||||||
|
"""
|
||||||
|
return self.get_object().get_allowed_actions(request.user)['see']
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return the template context.
|
Return the template context.
|
||||||
@ -53,14 +80,14 @@ class MotionDetailView(DetailView):
|
|||||||
version_number = self.kwargs.get('version_number', None)
|
version_number = self.kwargs.get('version_number', None)
|
||||||
if version_number is not None:
|
if version_number is not None:
|
||||||
try:
|
try:
|
||||||
version = self.object.versions.get(version_number=int(version_number))
|
version = self.get_object().versions.get(version_number=int(version_number))
|
||||||
except MotionVersion.DoesNotExist:
|
except MotionVersion.DoesNotExist:
|
||||||
raise Http404('Version %s not found' % version_number)
|
raise Http404('Version %s not found' % version_number)
|
||||||
else:
|
else:
|
||||||
version = self.object.get_active_version()
|
version = self.get_object().get_active_version()
|
||||||
|
|
||||||
kwargs.update({
|
kwargs.update({
|
||||||
'allowed_actions': self.object.get_allowed_actions(self.request.user),
|
'allowed_actions': self.get_object().get_allowed_actions(self.request.user),
|
||||||
'version': version,
|
'version': version,
|
||||||
'title': version.title,
|
'title': version.title,
|
||||||
'text': version.text,
|
'text': version.text,
|
||||||
@ -299,6 +326,9 @@ class VersionDeleteView(DeleteView):
|
|||||||
success_url_name = 'motion_detail'
|
success_url_name = 'motion_detail'
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
try:
|
||||||
|
version = self._object
|
||||||
|
except AttributeError:
|
||||||
try:
|
try:
|
||||||
motion = Motion.objects.get(pk=int(self.kwargs.get('pk')))
|
motion = Motion.objects.get(pk=int(self.kwargs.get('pk')))
|
||||||
except Motion.DoesNotExist:
|
except Motion.DoesNotExist:
|
||||||
@ -311,10 +341,11 @@ class VersionDeleteView(DeleteView):
|
|||||||
raise Http404('Version %s not found.' % self.kwargs.get('version_number'))
|
raise Http404('Version %s not found.' % self.kwargs.get('version_number'))
|
||||||
if version == motion.active_version:
|
if version == motion.active_version:
|
||||||
raise Http404('You can not delete the active version of a motion.')
|
raise Http404('You can not delete the active version of a motion.')
|
||||||
|
self._object = version
|
||||||
return version
|
return version
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
return (self.object.motion_id, )
|
return (self.get_object().motion_id, )
|
||||||
|
|
||||||
version_delete = VersionDeleteView.as_view()
|
version_delete = VersionDeleteView.as_view()
|
||||||
|
|
||||||
@ -330,12 +361,11 @@ class VersionPermitView(SingleObjectMixin, QuestionView):
|
|||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Set self.object to a motion.
|
Sets self.version to a motion version.
|
||||||
"""
|
"""
|
||||||
self.object = self.get_object()
|
|
||||||
version_number = self.kwargs.get('version_number', None)
|
version_number = self.kwargs.get('version_number', None)
|
||||||
try:
|
try:
|
||||||
self.version = self.object.versions.get(version_number=int(version_number))
|
self.version = self.get_object().versions.get(version_number=int(version_number))
|
||||||
except MotionVersion.DoesNotExist:
|
except MotionVersion.DoesNotExist:
|
||||||
raise Http404('Version %s not found.' % version_number)
|
raise Http404('Version %s not found.' % version_number)
|
||||||
return super(VersionPermitView, self).get(*args, **kwargs)
|
return super(VersionPermitView, self).get(*args, **kwargs)
|
||||||
@ -344,7 +374,7 @@ class VersionPermitView(SingleObjectMixin, QuestionView):
|
|||||||
"""
|
"""
|
||||||
Returns a list with arguments to create the success- and question_url.
|
Returns a list with arguments to create the success- and question_url.
|
||||||
"""
|
"""
|
||||||
return [self.object.pk, self.version.version_number]
|
return [self.get_object().pk, self.version.version_number]
|
||||||
|
|
||||||
def get_question_message(self):
|
def get_question_message(self):
|
||||||
"""
|
"""
|
||||||
@ -356,9 +386,9 @@ class VersionPermitView(SingleObjectMixin, QuestionView):
|
|||||||
"""
|
"""
|
||||||
Activate the version, if the user chooses 'yes'.
|
Activate the version, if the user chooses 'yes'.
|
||||||
"""
|
"""
|
||||||
self.object.active_version = self.version
|
self.get_object().active_version = self.version
|
||||||
self.object.save(update_fields=['active_version'])
|
self.get_object().save(update_fields=['active_version'])
|
||||||
self.object.write_log(
|
self.get_object().write_log(
|
||||||
message_list=[ugettext_noop('Version'),
|
message_list=[ugettext_noop('Version'),
|
||||||
' %d ' % self.version.version_number,
|
' %d ' % self.version.version_number,
|
||||||
ugettext_noop('permitted')],
|
ugettext_noop('permitted')],
|
||||||
@ -371,10 +401,15 @@ class VersionDiffView(DetailView):
|
|||||||
"""
|
"""
|
||||||
Show diff between two versions of a motion.
|
Show diff between two versions of a motion.
|
||||||
"""
|
"""
|
||||||
required_permission = 'motion.can_see_motion'
|
|
||||||
model = Motion
|
model = Motion
|
||||||
template_name = 'motion/motion_diff.html'
|
template_name = 'motion/motion_diff.html'
|
||||||
|
|
||||||
|
def check_permission(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Check if the request.user has the permission to see the motion.
|
||||||
|
"""
|
||||||
|
return self.get_object().get_allowed_actions(request.user)['see']
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return the template context with versions and html diff strings.
|
Return the template context with versions and html diff strings.
|
||||||
@ -382,8 +417,8 @@ class VersionDiffView(DetailView):
|
|||||||
try:
|
try:
|
||||||
rev1 = int(self.request.GET['rev1'])
|
rev1 = int(self.request.GET['rev1'])
|
||||||
rev2 = int(self.request.GET['rev2'])
|
rev2 = int(self.request.GET['rev2'])
|
||||||
version_rev1 = self.object.versions.get(version_number=rev1)
|
version_rev1 = self.get_object().versions.get(version_number=rev1)
|
||||||
version_rev2 = self.object.versions.get(version_number=rev2)
|
version_rev2 = self.get_object().versions.get(version_number=rev2)
|
||||||
diff_text = htmldiff(version_rev1.text, version_rev2.text)
|
diff_text = htmldiff(version_rev1.text, version_rev2.text)
|
||||||
diff_reason = htmldiff(version_rev1.reason, version_rev2.reason)
|
diff_reason = htmldiff(version_rev1.reason, version_rev2.reason)
|
||||||
except (KeyError, ValueError, MotionVersion.DoesNotExist):
|
except (KeyError, ValueError, MotionVersion.DoesNotExist):
|
||||||
@ -417,18 +452,11 @@ class SupportView(SingleObjectMixin, QuestionView):
|
|||||||
model = Motion
|
model = Motion
|
||||||
support = True
|
support = True
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Set self.object to a motion.
|
|
||||||
"""
|
|
||||||
self.object = self.get_object()
|
|
||||||
return super(SupportView, self).get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def check_permission(self, request):
|
def check_permission(self, request):
|
||||||
"""
|
"""
|
||||||
Return True if the user can support or unsupport the motion. Else: False.
|
Return True if the user can support or unsupport the motion. Else: False.
|
||||||
"""
|
"""
|
||||||
allowed_actions = self.object.get_allowed_actions(request.user)
|
allowed_actions = self.get_object().get_allowed_actions(request.user)
|
||||||
if self.support and not allowed_actions['support']:
|
if self.support and not allowed_actions['support']:
|
||||||
messages.error(request, _('You can not support this motion.'))
|
messages.error(request, _('You can not support this motion.'))
|
||||||
return False
|
return False
|
||||||
@ -457,11 +485,11 @@ class SupportView(SingleObjectMixin, QuestionView):
|
|||||||
if self.check_permission(self.request):
|
if self.check_permission(self.request):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
if self.support:
|
if self.support:
|
||||||
self.object.support(person=user)
|
self.get_object().support(person=user)
|
||||||
self.object.write_log([ugettext_noop('Motion supported')], user)
|
self.get_object().write_log([ugettext_noop('Motion supported')], user)
|
||||||
else:
|
else:
|
||||||
self.object.unsupport(person=user)
|
self.get_object().unsupport(person=user)
|
||||||
self.object.write_log([ugettext_noop('Motion unsupported')], user)
|
self.get_object().write_log([ugettext_noop('Motion unsupported')], user)
|
||||||
|
|
||||||
def get_final_message(self):
|
def get_final_message(self):
|
||||||
"""
|
"""
|
||||||
@ -484,26 +512,19 @@ class PollCreateView(SingleObjectMixin, RedirectView):
|
|||||||
model = Motion
|
model = Motion
|
||||||
url_name = 'motionpoll_detail'
|
url_name = 'motionpoll_detail'
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Set self.object to a motion.
|
|
||||||
"""
|
|
||||||
self.object = self.get_object()
|
|
||||||
return super(PollCreateView, self).get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Create the poll for the motion.
|
Create the poll for the motion.
|
||||||
"""
|
"""
|
||||||
self.poll = self.object.create_poll()
|
self.poll = self.get_object().create_poll()
|
||||||
self.object.write_log([ugettext_noop("Poll created")], request.user)
|
self.get_object().write_log([ugettext_noop("Poll created")], request.user)
|
||||||
messages.success(request, _("New vote was successfully created."))
|
messages.success(request, _("New vote was successfully created."))
|
||||||
|
|
||||||
def get_redirect_url(self, **kwargs):
|
def get_redirect_url(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return the URL to the UpdateView of the poll.
|
Return the URL to the UpdateView of the poll.
|
||||||
"""
|
"""
|
||||||
return reverse('motionpoll_update', args=[self.object.pk, self.poll.poll_number])
|
return reverse('motionpoll_update', args=[self.get_object().pk, self.poll.poll_number])
|
||||||
|
|
||||||
poll_create = PollCreateView.as_view()
|
poll_create = PollCreateView.as_view()
|
||||||
|
|
||||||
@ -523,16 +544,21 @@ class PollMixin(object):
|
|||||||
Use the motion id and the poll_number from the url kwargs to get the
|
Use the motion id and the poll_number from the url kwargs to get the
|
||||||
object.
|
object.
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
|
obj = self._object
|
||||||
|
except AttributeError:
|
||||||
queryset = MotionPoll.objects.filter(
|
queryset = MotionPoll.objects.filter(
|
||||||
motion=self.kwargs['pk'],
|
motion=self.kwargs['pk'],
|
||||||
poll_number=self.kwargs['poll_number'])
|
poll_number=self.kwargs['poll_number'])
|
||||||
return get_object_or_404(queryset)
|
obj = get_object_or_404(queryset)
|
||||||
|
self._object = obj
|
||||||
|
return obj
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
"""
|
"""
|
||||||
Return the arguments to create the url to the success_url.
|
Return the arguments to create the url to the success_url.
|
||||||
"""
|
"""
|
||||||
return [self.object.motion.pk]
|
return [self.get_object().motion.pk]
|
||||||
|
|
||||||
|
|
||||||
class PollUpdateView(PollMixin, PollFormView):
|
class PollUpdateView(PollMixin, PollFormView):
|
||||||
@ -564,7 +590,7 @@ class PollUpdateView(PollMixin, PollFormView):
|
|||||||
Write a log message, if the form is valid.
|
Write a log message, if the form is valid.
|
||||||
"""
|
"""
|
||||||
value = super(PollUpdateView, self).form_valid(form)
|
value = super(PollUpdateView, self).form_valid(form)
|
||||||
self.object.write_log([ugettext_noop('Poll updated')], self.request.user)
|
self.get_object().write_log([ugettext_noop('Poll updated')], self.request.user)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
poll_update = PollUpdateView.as_view()
|
poll_update = PollUpdateView.as_view()
|
||||||
@ -582,13 +608,13 @@ class PollDeleteView(PollMixin, DeleteView):
|
|||||||
Write a log message, if the form is valid.
|
Write a log message, if the form is valid.
|
||||||
"""
|
"""
|
||||||
super(PollDeleteView, self).on_clicked_yes()
|
super(PollDeleteView, self).on_clicked_yes()
|
||||||
self.object.motion.write_log([ugettext_noop('Poll deleted')], self.request.user)
|
self.get_object().motion.write_log([ugettext_noop('Poll deleted')], self.request.user)
|
||||||
|
|
||||||
def get_redirect_url(self, **kwargs):
|
def get_redirect_url(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return the URL to the DetailView of the motion.
|
Return the URL to the DetailView of the motion.
|
||||||
"""
|
"""
|
||||||
return reverse('motion_detail', args=[self.object.motion.pk])
|
return reverse('motion_detail', args=[self.get_object().motion.pk])
|
||||||
|
|
||||||
poll_delete = PollDeleteView.as_view()
|
poll_delete = PollDeleteView.as_view()
|
||||||
|
|
||||||
@ -601,15 +627,11 @@ class PollPDFView(PollMixin, PDFView):
|
|||||||
required_permission = 'motion.can_manage_motion'
|
required_permission = 'motion.can_manage_motion'
|
||||||
top_space = 0
|
top_space = 0
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
self.object = self.get_object()
|
|
||||||
return super(PollPDFView, self).get(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_filename(self):
|
def get_filename(self):
|
||||||
"""
|
"""
|
||||||
Return the filename for the PDF.
|
Return the filename for the PDF.
|
||||||
"""
|
"""
|
||||||
return u'%s%s_%s' % (_("Motion"), str(self.object.poll_number), _("Poll"))
|
return u'%s%s_%s' % (_("Motion"), str(self.get_object().poll_number), _("Poll"))
|
||||||
|
|
||||||
def get_template(self, buffer):
|
def get_template(self, buffer):
|
||||||
return SimpleDocTemplate(
|
return SimpleDocTemplate(
|
||||||
@ -623,7 +645,7 @@ class PollPDFView(PollMixin, PDFView):
|
|||||||
"""
|
"""
|
||||||
Append PDF objects.
|
Append PDF objects.
|
||||||
"""
|
"""
|
||||||
motion_poll_to_pdf(pdf, self.object)
|
motion_poll_to_pdf(pdf, self.get_object())
|
||||||
|
|
||||||
poll_pdf = PollPDFView.as_view()
|
poll_pdf = PollPDFView.as_view()
|
||||||
|
|
||||||
@ -644,26 +666,25 @@ class MotionSetStateView(SingleObjectMixin, RedirectView):
|
|||||||
"""
|
"""
|
||||||
Save the new state and write a log message.
|
Save the new state and write a log message.
|
||||||
"""
|
"""
|
||||||
self.object = self.get_object()
|
|
||||||
success = False
|
success = False
|
||||||
if self.reset:
|
if self.reset:
|
||||||
self.object.reset_state()
|
self.get_object().reset_state()
|
||||||
success = True
|
success = True
|
||||||
elif self.object.state.id == int(kwargs['state']):
|
elif self.get_object().state.id == int(kwargs['state']):
|
||||||
messages.error(request, _('You can not set the state of the motion. It is already done.'))
|
messages.error(request, _('You can not set the state of the motion. It is already done.'))
|
||||||
elif int(kwargs['state']) not in [state.id for state in self.object.state.next_states.all()]:
|
elif int(kwargs['state']) not in [state.id for state in self.get_object().state.next_states.all()]:
|
||||||
messages.error(request, _('You can not set the state of the motion to %s.') % _(State.objects.get(pk=int(kwargs['state'])).name))
|
messages.error(request, _('You can not set the state of the motion to %s.') % _(State.objects.get(pk=int(kwargs['state'])).name))
|
||||||
else:
|
else:
|
||||||
self.object.set_state(int(kwargs['state']))
|
self.get_object().set_state(int(kwargs['state']))
|
||||||
success = True
|
success = True
|
||||||
if success:
|
if success:
|
||||||
self.object.save(update_fields=['state', 'identifier'])
|
self.get_object().save(update_fields=['state', 'identifier'])
|
||||||
self.object.write_log(
|
self.get_object().write_log(
|
||||||
message_list=[ugettext_noop('State changed to'), ' ', self.object.state.name], # TODO: Change string to 'State set to ...'
|
message_list=[ugettext_noop('State changed to'), ' ', self.get_object().state.name], # TODO: Change string to 'State set to ...'
|
||||||
person=self.request.user)
|
person=self.request.user)
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('The state of the motion was set to %s.')
|
_('The state of the motion was set to %s.')
|
||||||
% html_strong(_(self.object.state.name)))
|
% html_strong(_(self.get_object().state.name)))
|
||||||
|
|
||||||
set_state = MotionSetStateView.as_view()
|
set_state = MotionSetStateView.as_view()
|
||||||
reset_state = MotionSetStateView.as_view(reset=True)
|
reset_state = MotionSetStateView.as_view(reset=True)
|
||||||
@ -680,32 +701,41 @@ class CreateRelatedAgendaItemView(_CreateRelatedAgendaItemView):
|
|||||||
Create the agenda item.
|
Create the agenda item.
|
||||||
"""
|
"""
|
||||||
super(CreateRelatedAgendaItemView, self).pre_redirect(request, *args, **kwargs)
|
super(CreateRelatedAgendaItemView, self).pre_redirect(request, *args, **kwargs)
|
||||||
self.object.write_log([ugettext_noop('Agenda item created')], self.request.user)
|
self.get_object().write_log([ugettext_noop('Agenda item created')], self.request.user)
|
||||||
|
|
||||||
create_agenda_item = CreateRelatedAgendaItemView.as_view()
|
create_agenda_item = CreateRelatedAgendaItemView.as_view()
|
||||||
|
|
||||||
|
|
||||||
class MotionPDFView(SingleObjectMixin, PDFView):
|
class MotionPDFView(SingleObjectMixin, PDFView):
|
||||||
"""
|
"""
|
||||||
Create the PDF for one, or all motions.
|
Create the PDF for one or for all motions.
|
||||||
|
|
||||||
If self.print_all_motions is True, the view returns a PDF with all motions.
|
If self.print_all_motions is True, the view returns a PDF with all motions.
|
||||||
|
|
||||||
If self.print_all_motions is False, the view returns a PDF with only one
|
If self.print_all_motions is False, the view returns a PDF with only one
|
||||||
motion.
|
motion.
|
||||||
"""
|
"""
|
||||||
required_permission = 'motion.can_see_motion'
|
|
||||||
model = Motion
|
model = Motion
|
||||||
top_space = 0
|
top_space = 0
|
||||||
print_all_motions = False
|
print_all_motions = False
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def check_permission(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Set self.object to a motion.
|
Checks if the requesting user has the permission to see the motion as
|
||||||
|
PDF.
|
||||||
"""
|
"""
|
||||||
if not self.print_all_motions:
|
if self.print_all_motions:
|
||||||
self.object = self.get_object()
|
is_allowed = request.user.has_perm('motion.can_see_motion')
|
||||||
return super(MotionPDFView, self).get(request, *args, **kwargs)
|
else:
|
||||||
|
is_allowed = self.get_object().get_allowed_actions(request.user)['see']
|
||||||
|
return is_allowed
|
||||||
|
|
||||||
|
def get_object(self, *args, **kwargs):
|
||||||
|
if self.print_all_motions:
|
||||||
|
obj = None
|
||||||
|
else:
|
||||||
|
obj = super(MotionPDFView, self).get_object(*args, **kwargs)
|
||||||
|
return obj
|
||||||
|
|
||||||
def get_filename(self):
|
def get_filename(self):
|
||||||
"""
|
"""
|
||||||
@ -714,10 +744,10 @@ class MotionPDFView(SingleObjectMixin, PDFView):
|
|||||||
if self.print_all_motions:
|
if self.print_all_motions:
|
||||||
return _("Motions")
|
return _("Motions")
|
||||||
else:
|
else:
|
||||||
if self.object.identifier:
|
if self.get_object().identifier:
|
||||||
suffix = self.object.identifier.replace(' ', '')
|
suffix = self.get_object().identifier.replace(' ', '')
|
||||||
else:
|
else:
|
||||||
suffix = self.object.title.replace(' ', '_')
|
suffix = self.get_object().title.replace(' ', '_')
|
||||||
suffix = slugify(suffix)
|
suffix = slugify(suffix)
|
||||||
return '%s-%s' % (_("Motion"), suffix)
|
return '%s-%s' % (_("Motion"), suffix)
|
||||||
|
|
||||||
@ -726,9 +756,14 @@ class MotionPDFView(SingleObjectMixin, PDFView):
|
|||||||
Append PDF objects.
|
Append PDF objects.
|
||||||
"""
|
"""
|
||||||
if self.print_all_motions:
|
if self.print_all_motions:
|
||||||
motions_to_pdf(pdf)
|
motions = []
|
||||||
|
for motion in Motion.objects.all():
|
||||||
|
if (not motion.state.required_permission_to_see or
|
||||||
|
self.request.user.has_perm(motion.state.required_permission_to_see)):
|
||||||
|
motions.append(motion)
|
||||||
|
motions_to_pdf(pdf, motions)
|
||||||
else:
|
else:
|
||||||
motion_to_pdf(pdf, self.object)
|
motion_to_pdf(pdf, self.get_object())
|
||||||
|
|
||||||
motion_list_pdf = MotionPDFView.as_view(print_all_motions=True)
|
motion_list_pdf = MotionPDFView.as_view(print_all_motions=True)
|
||||||
motion_detail_pdf = MotionPDFView.as_view(print_all_motions=False)
|
motion_detail_pdf = MotionPDFView.as_view(print_all_motions=False)
|
||||||
|
@ -9,11 +9,11 @@ class PollFormView(FormMixin, TemplateView):
|
|||||||
poll_class = None
|
poll_class = None
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.poll = self.object = self.get_object()
|
self.poll = self.get_object()
|
||||||
return super(PollFormView, self).get(request, *args, **kwargs)
|
return super(PollFormView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.poll = self.object = self.get_object()
|
self.poll = self.get_object()
|
||||||
option_forms = self.poll.get_vote_forms(data=self.request.POST)
|
option_forms = self.poll.get_vote_forms(data=self.request.POST)
|
||||||
|
|
||||||
FormClass = self.get_modelform_class()
|
FormClass = self.get_modelform_class()
|
||||||
@ -55,8 +55,13 @@ class PollFormView(FormMixin, TemplateView):
|
|||||||
"""
|
"""
|
||||||
Returns the poll object. Raises Http404 if the poll does not exist.
|
Returns the poll object. Raises Http404 if the poll does not exist.
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
|
obj = self._object
|
||||||
|
except AttributeError:
|
||||||
queryset = self.get_poll_class().objects.filter(pk=self.kwargs['poll_id'])
|
queryset = self.get_poll_class().objects.filter(pk=self.kwargs['poll_id'])
|
||||||
return get_object_or_404(queryset)
|
obj = get_object_or_404(queryset)
|
||||||
|
self._object = obj
|
||||||
|
return obj
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(PollFormView, self).get_context_data(**kwargs)
|
context = super(PollFormView, self).get_context_data(**kwargs)
|
||||||
|
@ -156,13 +156,13 @@ class UserDeleteView(DeleteView):
|
|||||||
url_name_args = []
|
url_name_args = []
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
if self.object == self.request.user:
|
if self.get_object() == self.request.user:
|
||||||
messages.error(request, _("You can not delete yourself."))
|
messages.error(request, _("You can not delete yourself."))
|
||||||
else:
|
else:
|
||||||
super().pre_redirect(request, *args, **kwargs)
|
super().pre_redirect(request, *args, **kwargs)
|
||||||
|
|
||||||
def pre_post_redirect(self, request, *args, **kwargs):
|
def pre_post_redirect(self, request, *args, **kwargs):
|
||||||
if self.object == self.request.user:
|
if self.get_object() == self.request.user:
|
||||||
messages.error(self.request, _("You can not delete yourself."))
|
messages.error(self.request, _("You can not delete yourself."))
|
||||||
else:
|
else:
|
||||||
super().pre_post_redirect(request, *args, **kwargs)
|
super().pre_post_redirect(request, *args, **kwargs)
|
||||||
@ -179,21 +179,20 @@ class SetUserStatusView(SingleObjectMixin, RedirectView):
|
|||||||
model = User
|
model = User
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
|
||||||
action = kwargs['action']
|
action = kwargs['action']
|
||||||
if action == 'activate':
|
if action == 'activate':
|
||||||
self.object.is_active = True
|
self.get_object().is_active = True
|
||||||
elif action == 'deactivate':
|
elif action == 'deactivate':
|
||||||
if self.object.user == self.request.user:
|
if self.get_object().user == self.request.user:
|
||||||
messages.error(request, _("You can not deactivate yourself."))
|
messages.error(request, _("You can not deactivate yourself."))
|
||||||
else:
|
else:
|
||||||
self.object.is_active = False
|
self.get_object().is_active = False
|
||||||
self.object.save()
|
self.get_object().save()
|
||||||
return super(SetUserStatusView, self).pre_redirect(request, *args, **kwargs)
|
return super(SetUserStatusView, self).pre_redirect(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_ajax_context(self, **kwargs):
|
def get_ajax_context(self, **kwargs):
|
||||||
context = super(SetUserStatusView, self).get_ajax_context(**kwargs)
|
context = super(SetUserStatusView, self).get_ajax_context(**kwargs)
|
||||||
context['active'] = self.object.is_active
|
context['active'] = self.get_object().is_active
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -249,19 +248,14 @@ class ResetPasswordView(SingleObjectMixin, QuestionView):
|
|||||||
allow_ajax = True
|
allow_ajax = True
|
||||||
question_message = ugettext_lazy('Do you really want to reset the password?')
|
question_message = ugettext_lazy('Do you really want to reset the password?')
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
self.object = self.get_object()
|
|
||||||
return super(ResetPasswordView, self).get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def get_redirect_url(self, **kwargs):
|
def get_redirect_url(self, **kwargs):
|
||||||
return reverse('user_edit', args=[self.object.id])
|
return reverse('user_edit', args=[self.get_object().id])
|
||||||
|
|
||||||
def on_clicked_yes(self):
|
def on_clicked_yes(self):
|
||||||
self.object.reset_password()
|
self.get_object().reset_password()
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
def get_final_message(self):
|
def get_final_message(self):
|
||||||
return _('The Password for %s was successfully reset.') % html_strong(self.object)
|
return _('The Password for %s was successfully reset.') % html_strong(self.get_object())
|
||||||
|
|
||||||
|
|
||||||
class GroupListView(ListView):
|
class GroupListView(ListView):
|
||||||
@ -367,12 +361,12 @@ class GroupDeleteView(DeleteView):
|
|||||||
"""
|
"""
|
||||||
Checks whether the group is protected.
|
Checks whether the group is protected.
|
||||||
"""
|
"""
|
||||||
if self.object.pk in [1, 2]:
|
if self.get_object().pk in [1, 2]:
|
||||||
messages.error(self.request, _('You can not delete this group.'))
|
messages.error(self.request, _('You can not delete this group.'))
|
||||||
return True
|
return True
|
||||||
if (not self.request.user.is_superuser and
|
if (not self.request.user.is_superuser and
|
||||||
get_protected_perm() in self.object.permissions.all() and
|
get_protected_perm() in self.get_object().permissions.all() and
|
||||||
not Group.objects.exclude(pk=self.object.pk).filter(
|
not Group.objects.exclude(pk=self.get_object().pk).filter(
|
||||||
permissions__in=[get_protected_perm()],
|
permissions__in=[get_protected_perm()],
|
||||||
user__pk=self.request.user.pk).exists()):
|
user__pk=self.request.user.pk).exists()):
|
||||||
messages.error(
|
messages.error(
|
||||||
|
@ -11,7 +11,6 @@ from django.http import (HttpResponse, HttpResponseRedirect)
|
|||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
from django.views import generic as django_views
|
from django.views import generic as django_views
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
|
||||||
from reportlab.lib.units import cm
|
from reportlab.lib.units import cm
|
||||||
from reportlab.platypus import SimpleDocTemplate, Spacer
|
from reportlab.platypus import SimpleDocTemplate, Spacer
|
||||||
|
|
||||||
@ -163,6 +162,32 @@ class UrlMixin(object):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class SingleObjectMixin(django_views.detail.SingleObjectMixin):
|
||||||
|
"""
|
||||||
|
Mixin for single objects from the database.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def dispatch(self, *args, **kwargs):
|
||||||
|
if not hasattr(self, 'object'):
|
||||||
|
# Save the object not only in the cache but in the public
|
||||||
|
# attribute self.object because Django expects this later.
|
||||||
|
# Because get_object() has an internal cache this line is not a
|
||||||
|
# performance problem.
|
||||||
|
self.object = self.get_object()
|
||||||
|
return super().dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_object(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns the single object from database or cache.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
obj = self._object
|
||||||
|
except AttributeError:
|
||||||
|
obj = super().get_object(*args, **kwargs)
|
||||||
|
self._object = obj
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class FormMixin(UrlMixin):
|
class FormMixin(UrlMixin):
|
||||||
"""
|
"""
|
||||||
Mixin for views with forms.
|
Mixin for views with forms.
|
||||||
@ -438,15 +463,15 @@ class FormView(PermissionMixin, ExtraContextMixin, FormMixin,
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(PermissionMixin, ExtraContextMixin, ModelFormMixin,
|
class UpdateView(PermissionMixin, ExtraContextMixin,
|
||||||
django_views.UpdateView):
|
ModelFormMixin, SingleObjectMixin, django_views.UpdateView):
|
||||||
"""
|
"""
|
||||||
View to update an model object.
|
View to update an model object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_success_message(self):
|
def get_success_message(self):
|
||||||
if self.success_message is None:
|
if self.success_message is None:
|
||||||
message = _('%s was successfully modified.') % html_strong(self.object)
|
message = _('%s was successfully modified.') % html_strong(self.get_object())
|
||||||
else:
|
else:
|
||||||
message = self.success_message
|
message = self.success_message
|
||||||
return message
|
return message
|
||||||
@ -456,6 +481,10 @@ class CreateView(PermissionMixin, ExtraContextMixin,
|
|||||||
ModelFormMixin, django_views.CreateView):
|
ModelFormMixin, django_views.CreateView):
|
||||||
"""
|
"""
|
||||||
View to create a model object.
|
View to create a model object.
|
||||||
|
|
||||||
|
Note: This class has a django method get_object() which is different form
|
||||||
|
the method in openslides.utils.views.SingleObjectMixin. The result
|
||||||
|
is not cached.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_success_message(self):
|
def get_success_message(self):
|
||||||
@ -473,10 +502,6 @@ class DeleteView(SingleObjectMixin, QuestionView):
|
|||||||
success_url = None
|
success_url = None
|
||||||
success_url_name = None
|
success_url_name = None
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
self.object = self.get_object()
|
|
||||||
return super().get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def get_redirect_url(self, **kwargs):
|
def get_redirect_url(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Returns the url on which the delete dialog is shown and the url after
|
Returns the url on which the delete dialog is shown and the url after
|
||||||
@ -506,22 +531,22 @@ class DeleteView(SingleObjectMixin, QuestionView):
|
|||||||
"""
|
"""
|
||||||
Returns the question for the delete dialog.
|
Returns the question for the delete dialog.
|
||||||
"""
|
"""
|
||||||
return _('Do you really want to delete %s?') % html_strong(self.object)
|
return _('Do you really want to delete %s?') % html_strong(self.get_object())
|
||||||
|
|
||||||
def on_clicked_yes(self):
|
def on_clicked_yes(self):
|
||||||
"""
|
"""
|
||||||
Deletes the object.
|
Deletes the object.
|
||||||
"""
|
"""
|
||||||
self.object.delete()
|
self.get_object().delete()
|
||||||
|
|
||||||
def get_final_message(self):
|
def get_final_message(self):
|
||||||
"""
|
"""
|
||||||
Prints the success message to the user.
|
Prints the success message to the user.
|
||||||
"""
|
"""
|
||||||
return _('%s was successfully deleted.') % html_strong(self.object)
|
return _('%s was successfully deleted.') % html_strong(self.get_object())
|
||||||
|
|
||||||
|
|
||||||
class DetailView(PermissionMixin, ExtraContextMixin, django_views.DetailView):
|
class DetailView(PermissionMixin, ExtraContextMixin, SingleObjectMixin, django_views.DetailView):
|
||||||
"""
|
"""
|
||||||
View to show an model object.
|
View to show an model object.
|
||||||
"""
|
"""
|
||||||
|
@ -205,8 +205,8 @@ class MediafileTest(TestCase):
|
|||||||
response = clients['client_normal_user'].get('/mediafile/1/del/')
|
response = clients['client_normal_user'].get('/mediafile/1/del/')
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
bad_client = Client()
|
bad_client = Client()
|
||||||
response = bad_client.get('/mediafile/2/del/')
|
response = bad_client.get('/mediafile/1/del/')
|
||||||
self.assertRedirects(response, expected_url='/login/?next=/mediafile/2/del/', status_code=302, target_status_code=200)
|
self.assertRedirects(response, expected_url='/login/?next=/mediafile/1/del/', status_code=302, target_status_code=200)
|
||||||
|
|
||||||
def test_delete_mediafile_get_request_own_file(self):
|
def test_delete_mediafile_get_request_own_file(self):
|
||||||
self.object.uploader = self.vip_user
|
self.object.uploader = self.vip_user
|
||||||
|
@ -14,6 +14,11 @@ class MotionPDFTest(TestCase):
|
|||||||
self.admin_client = Client()
|
self.admin_client = Client()
|
||||||
self.admin_client.login(username='admin', password='admin')
|
self.admin_client.login(username='admin', password='admin')
|
||||||
|
|
||||||
|
# Registered
|
||||||
|
self.registered = User.objects.create_user('registered', 'registered')
|
||||||
|
self.registered_client = Client()
|
||||||
|
self.registered_client.login(username='registered', password='registered')
|
||||||
|
|
||||||
def test_render_nested_list(self):
|
def test_render_nested_list(self):
|
||||||
Motion.objects.create(
|
Motion.objects.create(
|
||||||
title='Test Title chieM6Aing8Eegh9ePhu',
|
title='Test Title chieM6Aing8Eegh9ePhu',
|
||||||
@ -23,3 +28,17 @@ class MotionPDFTest(TestCase):
|
|||||||
'<li>Element 2 rel0liiGh0bi3ree6Jei</li></ul>')
|
'<li>Element 2 rel0liiGh0bi3ree6Jei</li></ul>')
|
||||||
response = self.admin_client.get('/motion/1/pdf/')
|
response = self.admin_client.get('/motion/1/pdf/')
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_get_without_required_permission_from_state(self):
|
||||||
|
motion = Motion.objects.create(title='motion_title_zthguis8qqespgknme52')
|
||||||
|
motion.state.required_permission_to_see = 'motion.can_manage_motion'
|
||||||
|
motion.state.save()
|
||||||
|
response = self.registered_client.get('/motion/1/pdf/')
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
def test_get_with_filtered_motion_list(self):
|
||||||
|
motion = Motion.objects.create(title='motion_title_qwgvzf6487guni0oikcc')
|
||||||
|
motion.state.required_permission_to_see = 'motion.can_manage_motion'
|
||||||
|
motion.state.save()
|
||||||
|
response = self.registered_client.get('/motion/pdf/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
@ -52,6 +52,20 @@ class TestMotionListView(MotionViewTestCase):
|
|||||||
def test_get(self):
|
def test_get(self):
|
||||||
self.check_url('/motion/', self.admin_client, 200)
|
self.check_url('/motion/', self.admin_client, 200)
|
||||||
|
|
||||||
|
def test_get_with_motion(self):
|
||||||
|
self.motion1.title = 'motion1_iozaixeeDuMah8sheGhe'
|
||||||
|
self.motion1.save()
|
||||||
|
response = self.admin_client.get('/motion/')
|
||||||
|
self.assertContains(response, 'motion1_iozaixeeDuMah8sheGhe')
|
||||||
|
|
||||||
|
def test_get_with_filtered_motion_list(self):
|
||||||
|
self.motion1.state.required_permission_to_see = 'motion.can_manage_motion'
|
||||||
|
self.motion1.state.save()
|
||||||
|
self.motion1.title = 'motion1_djfplquczyxasvvgdnmbr'
|
||||||
|
self.motion1.save()
|
||||||
|
response = self.registered_client.get('/motion/')
|
||||||
|
self.assertNotContains(response, 'motion1_djfplquczyxasvvgdnmbr')
|
||||||
|
|
||||||
|
|
||||||
class TestMotionDetailView(MotionViewTestCase):
|
class TestMotionDetailView(MotionViewTestCase):
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
@ -96,6 +110,21 @@ class TestMotionDetailView(MotionViewTestCase):
|
|||||||
self.registered.delete()
|
self.registered.delete()
|
||||||
self.assertNotContains(self.admin_client.get('/motion/1/'), 'registered')
|
self.assertNotContains(self.admin_client.get('/motion/1/'), 'registered')
|
||||||
|
|
||||||
|
def test_get_without_required_permission_from_state(self):
|
||||||
|
self.motion1.state.required_permission_to_see = 'motion.can_manage_motion'
|
||||||
|
self.motion1.state.save()
|
||||||
|
self.check_url('/motion/1/', self.admin_client, 200)
|
||||||
|
self.check_url('/motion/1/', self.registered_client, 403)
|
||||||
|
self.motion1.set_state(state=State.objects.get(name='permitted'))
|
||||||
|
self.motion1.save()
|
||||||
|
self.check_url('/motion/1/', self.registered_client, 200)
|
||||||
|
|
||||||
|
def test_get_without_required_permission_from_state_but_by_submitter(self):
|
||||||
|
self.motion1.state.required_permission_to_see = 'motion.can_manage_motion'
|
||||||
|
self.motion1.state.save()
|
||||||
|
self.motion1.add_submitter(self.registered)
|
||||||
|
self.check_url('/motion/1/', self.registered_client, 200)
|
||||||
|
|
||||||
|
|
||||||
class TestMotionDetailVersionView(MotionViewTestCase):
|
class TestMotionDetailVersionView(MotionViewTestCase):
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
@ -107,6 +136,28 @@ class TestMotionDetailVersionView(MotionViewTestCase):
|
|||||||
self.check_url('/motion/1/version/500/', self.admin_client, 404)
|
self.check_url('/motion/1/version/500/', self.admin_client, 404)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMotionVersionDiffView(MotionViewTestCase):
|
||||||
|
def test_get_without_required_permission_from_state(self):
|
||||||
|
self.motion1.reason = 'reason1_bnmkjiutufjbnvcde334'
|
||||||
|
self.motion1.save()
|
||||||
|
self.motion1.title = 'motion1_bnvhfzqsgxcyvasfr57t'
|
||||||
|
self.motion1.save(use_version=self.motion1.get_new_version())
|
||||||
|
|
||||||
|
response = self.registered_client.get(
|
||||||
|
'/motion/1/diff/',
|
||||||
|
{'rev1': '1', 'rev2': '2'})
|
||||||
|
self.assertNotContains(response, 'At least one version number is not valid.')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
self.motion1.state.required_permission_to_see = 'motion.can_manage_motion'
|
||||||
|
self.motion1.state.save()
|
||||||
|
|
||||||
|
response = self.registered_client.get(
|
||||||
|
'/motion/1/diff/',
|
||||||
|
{'rev1': '1', 'rev2': '2'})
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
|
||||||
class TestMotionCreateView(MotionViewTestCase):
|
class TestMotionCreateView(MotionViewTestCase):
|
||||||
url = '/motion/new/'
|
url = '/motion/new/'
|
||||||
|
|
||||||
|
@ -25,6 +25,13 @@ DATABASES = {
|
|||||||
'HOST': '',
|
'HOST': '',
|
||||||
'PORT': ''}}
|
'PORT': ''}}
|
||||||
|
|
||||||
|
# Add OpenSlides plugins to this list
|
||||||
|
INSTALLED_PLUGINS = (
|
||||||
|
'tests.utils',
|
||||||
|
)
|
||||||
|
|
||||||
|
INSTALLED_APPS += INSTALLED_PLUGINS
|
||||||
|
|
||||||
# Some other settings
|
# Some other settings
|
||||||
TIME_ZONE = 'Europe/Berlin'
|
TIME_ZONE = 'Europe/Berlin'
|
||||||
|
|
||||||
|
8
tests/utils/models.py
Normal file
8
tests/utils/models.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class DummyModel(models.Model):
|
||||||
|
"""
|
||||||
|
Dummy model to test some model views.
|
||||||
|
"""
|
||||||
|
title = models.CharField(max_length=255)
|
5
tests/utils/templates/utils/dummymodel_detail.html
Normal file
5
tests/utils/templates/utils/dummymodel_detail.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
{{ object.title }}
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -3,6 +3,7 @@ from unittest.mock import patch
|
|||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.urlresolvers import clear_url_caches
|
from django.core.urlresolvers import clear_url_caches
|
||||||
|
from django.db import connection, reset_queries
|
||||||
from django.test import RequestFactory
|
from django.test import RequestFactory
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
@ -12,6 +13,7 @@ from openslides.utils.signals import template_manipulation
|
|||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
from . import views as test_views
|
from . import views as test_views
|
||||||
|
from .models import DummyModel
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='tests.utils.urls')
|
@override_settings(ROOT_URLCONF='tests.utils.urls')
|
||||||
@ -192,6 +194,17 @@ class QuestionViewTest(ViewTestCase):
|
|||||||
self.assertIn('the question', question)
|
self.assertIn('the question', question)
|
||||||
|
|
||||||
|
|
||||||
|
class DetailViewTest(ViewTestCase):
|
||||||
|
def test_get_object_cache(self):
|
||||||
|
with self.settings(DEBUG=True):
|
||||||
|
DummyModel.objects.create(title='title_ooth8she7yos1Oi8Boh3')
|
||||||
|
reset_queries()
|
||||||
|
client = Client()
|
||||||
|
response = client.get('/dummy_detail_view/1/')
|
||||||
|
self.assertContains(response, 'title_ooth8she7yos1Oi8Boh3')
|
||||||
|
self.assertEqual(len(connection.queries), 3)
|
||||||
|
|
||||||
|
|
||||||
def set_context(sender, request, context, **kwargs):
|
def set_context(sender, request, context, **kwargs):
|
||||||
"""
|
"""
|
||||||
receiver for testing the ExtraContextMixin
|
receiver for testing the ExtraContextMixin
|
||||||
|
@ -26,4 +26,7 @@ urlpatterns += patterns(
|
|||||||
|
|
||||||
url(r'^permission_mixin3/$',
|
url(r'^permission_mixin3/$',
|
||||||
views.PermissionMixinView.as_view(required_permission='agenda.can_see_agenda')),
|
views.PermissionMixinView.as_view(required_permission='agenda.can_see_agenda')),
|
||||||
|
|
||||||
|
url(r'^dummy_detail_view/(?P<pk>\d+)/$',
|
||||||
|
views.DummyDetailView.as_view()),
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,8 @@ from django.http import HttpResponse
|
|||||||
|
|
||||||
from openslides.utils import views
|
from openslides.utils import views
|
||||||
|
|
||||||
|
from .models import DummyModel
|
||||||
|
|
||||||
|
|
||||||
class GetAbsoluteUrl(object):
|
class GetAbsoluteUrl(object):
|
||||||
"""
|
"""
|
||||||
@ -45,3 +47,15 @@ class UrlMixinView(views.UrlMixin, views.View):
|
|||||||
|
|
||||||
class UrlMixinViewWithObject(views.UrlMixin, views.View):
|
class UrlMixinViewWithObject(views.UrlMixin, views.View):
|
||||||
object = GetAbsoluteUrl()
|
object = GetAbsoluteUrl()
|
||||||
|
|
||||||
|
|
||||||
|
class DummyDetailView(views.DetailView):
|
||||||
|
model = DummyModel
|
||||||
|
|
||||||
|
def get_context_data(self, **context):
|
||||||
|
context = super(DummyDetailView, self).get_context_data(**context)
|
||||||
|
# Just call get_object() some times to test the cache
|
||||||
|
self.get_object()
|
||||||
|
self.get_object()
|
||||||
|
self.get_object()
|
||||||
|
return context
|
||||||
|
Loading…
Reference in New Issue
Block a user