Refactory of the participant app
* New user model (used Django's AbstractBaseUser) * Renamed the app to users * removed person api See #861 Fixed #576 #478
This commit is contained in:
parent
f17f1a723a
commit
3527b0a75b
@ -263,7 +263,7 @@ def createsuperuser(settings, args):
|
||||
ensure_settings(settings, args)
|
||||
# can't be imported in global scope as it already requires
|
||||
# the settings module during import
|
||||
from openslides.participant.api import create_or_reset_admin_user
|
||||
from openslides.users.api import create_or_reset_admin_user
|
||||
if create_or_reset_admin_user():
|
||||
print('Admin user successfully created.')
|
||||
else:
|
||||
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy
|
||||
from mptt.forms import TreeNodeChoiceField
|
||||
|
||||
from openslides.utils.forms import CssClassMixin, CleanHtmlFormMixin
|
||||
from openslides.utils.person.forms import PersonFormField
|
||||
from openslides.users.models import User
|
||||
|
||||
from .models import Item, Speaker
|
||||
|
||||
@ -59,7 +59,8 @@ class AppendSpeakerForm(CssClassMixin, forms.Form):
|
||||
"""
|
||||
Form to set an user to a list of speakers.
|
||||
"""
|
||||
speaker = PersonFormField(
|
||||
speaker = forms.ModelChoiceField(
|
||||
User.objects.all(),
|
||||
widget=forms.Select(attrs={'class': 'medium-input'}),
|
||||
label=ugettext_lazy("Add participant"))
|
||||
|
||||
@ -72,7 +73,7 @@ class AppendSpeakerForm(CssClassMixin, forms.Form):
|
||||
Checks, that the user is not already on the list.
|
||||
"""
|
||||
speaker = self.cleaned_data['speaker']
|
||||
if Speaker.objects.filter(person=speaker, item=self.item, begin_time=None).exists():
|
||||
if Speaker.objects.filter(user=speaker, item=self.item, begin_time=None).exists():
|
||||
raise forms.ValidationError(ugettext_lazy(
|
||||
'%s is already on the list of speakers.'
|
||||
% str(speaker)))
|
||||
|
@ -17,8 +17,8 @@ from openslides.projector.api import (get_active_slide, reset_countdown,
|
||||
from openslides.projector.models import SlideMixin
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.utils.person.models import PersonField
|
||||
from openslides.utils.utils import to_roman
|
||||
from openslides.users.models import User
|
||||
|
||||
|
||||
class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
|
||||
@ -279,7 +279,7 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
|
||||
|
||||
def get_next_speaker(self):
|
||||
"""
|
||||
Returns the speaker object of the person who is next.
|
||||
Returns the speaker object of the user who is next.
|
||||
"""
|
||||
try:
|
||||
return self.speaker_set.filter(begin_time=None).order_by('weight')[0]
|
||||
@ -337,17 +337,17 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
|
||||
|
||||
|
||||
class SpeakerManager(models.Manager):
|
||||
def add(self, person, item):
|
||||
if self.filter(person=person, item=item, begin_time=None).exists():
|
||||
def add(self, user, item):
|
||||
if self.filter(user=user, item=item, begin_time=None).exists():
|
||||
raise OpenSlidesError(_(
|
||||
'%(person)s is already on the list of speakers of item %(id)s.')
|
||||
% {'person': person, 'id': item.id})
|
||||
if isinstance(person, AnonymousUser):
|
||||
'%(user)s is already on the list of speakers of item %(id)s.')
|
||||
% {'user': user, 'id': item.id})
|
||||
if isinstance(user, AnonymousUser):
|
||||
raise OpenSlidesError(
|
||||
_('An anonymous user can not be on lists of speakers.'))
|
||||
weight = (self.filter(item=item).aggregate(
|
||||
models.Max('weight'))['weight__max'] or 0)
|
||||
return self.create(item=item, person=person, weight=weight + 1)
|
||||
return self.create(item=item, user=user, weight=weight + 1)
|
||||
|
||||
|
||||
class Speaker(AbsoluteUrlMixin, models.Model):
|
||||
@ -357,14 +357,14 @@ class Speaker(AbsoluteUrlMixin, models.Model):
|
||||
|
||||
objects = SpeakerManager()
|
||||
|
||||
person = PersonField()
|
||||
user = models.ForeignKey(User)
|
||||
"""
|
||||
ForeinKey to the person who speaks.
|
||||
ForeinKey to the user who speaks.
|
||||
"""
|
||||
|
||||
item = models.ForeignKey(Item)
|
||||
"""
|
||||
ForeinKey to the AgendaItem to which the person want to speak.
|
||||
ForeinKey to the AgendaItem to which the user want to speak.
|
||||
"""
|
||||
|
||||
begin_time = models.DateTimeField(null=True)
|
||||
@ -396,11 +396,11 @@ class Speaker(AbsoluteUrlMixin, models.Model):
|
||||
self.check_and_update_projector()
|
||||
|
||||
def __str__(self):
|
||||
return str(self.person)
|
||||
return str(self.user)
|
||||
|
||||
def get_absolute_url(self, link='detail'):
|
||||
if link == 'detail':
|
||||
url = self.person.get_absolute_url('detail')
|
||||
url = self.user.get_absolute_url('detail')
|
||||
elif link == 'delete':
|
||||
url = reverse('agenda_speaker_delete',
|
||||
args=[self.item.pk, self.pk])
|
||||
@ -421,7 +421,7 @@ class Speaker(AbsoluteUrlMixin, models.Model):
|
||||
|
||||
def begin_speach(self):
|
||||
"""
|
||||
Let the person speak.
|
||||
Let the user speak.
|
||||
|
||||
Set the weight to None and the time to now. If anyone is still
|
||||
speaking, end his speach.
|
||||
|
@ -7,12 +7,12 @@ from .models import Item
|
||||
|
||||
class AgendaPersonalInfo(PersonalInfo):
|
||||
"""
|
||||
Class for personal info block for the agenda app.
|
||||
Class for user info block for the agenda app.
|
||||
"""
|
||||
headline = ugettext_lazy('I am on the list of speakers of the following items')
|
||||
default_weight = 10
|
||||
|
||||
def get_queryset(self):
|
||||
return Item.objects.filter(
|
||||
speaker__person=self.request.user,
|
||||
speaker__user=self.request.user,
|
||||
speaker__begin_time=None)
|
||||
|
@ -121,7 +121,7 @@
|
||||
{% if speaker_dict.type == 'actual_speaker' %}
|
||||
<a href="{% url 'agenda_speaker_end_speach' item.pk %}" class="btn btn-mini btn-danger"><i class="icon-bell icon-white"></i> {% trans 'End speach' %}</a>
|
||||
{% elif speaker_dict.type == 'coming_speaker' %}
|
||||
<a href="{% url 'agenda_speaker_speak' item.pk speaker_dict.speaker.person.person_id %}"
|
||||
<a href="{% url 'agenda_speaker_speak' item.pk speaker_dict.speaker.user.pk %}"
|
||||
class="btn btn-mini"><i class="icon-bell"></i> {% trans "Begin speach" %}</a>
|
||||
{% endif %}
|
||||
<a href="{{ speaker_dict.speaker|absolute_url:'delete' }}"
|
||||
@ -150,8 +150,8 @@
|
||||
<div class="control-group input-append {% if field.errors %}error{% endif %}">
|
||||
{{ field }}
|
||||
<button class="btn btn-primary tooltip-bottom" type="submit" data-original-title="{% trans 'Apply' %}"><i class="icon-ok icon-white"></i></button>
|
||||
{% if perms.participant.can_see_participant and perms.participant.can_manage_participant %}
|
||||
<a href="{% url 'user_new' %}" class="btn" rel="tooltip" data-original-title="{% trans 'Add new participant' %}"><i class="icon-add-user"></i></a>
|
||||
{% if perms.users.can_see and perms.users.can_manage %}
|
||||
<a href="{% url 'user_new' %}" class="btn" rel="tooltip" data-original-title="{% trans 'Add new user' %}"><i class="icon-add-user"></i></a>
|
||||
{% endif %}
|
||||
{% if field.errors %}
|
||||
<span class="help-inline">{{ field.errors }}</span>
|
||||
|
@ -63,7 +63,7 @@ urlpatterns = patterns(
|
||||
views.SpeakerDeleteView.as_view(),
|
||||
name='agenda_speaker_delete'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/speaker/(?P<person_id>[^/]+)/speak/$',
|
||||
url(r'^(?P<pk>\d+)/speaker/(?P<user_id>[^/]+)/speak/$',
|
||||
views.SpeakerSpeakView.as_view(),
|
||||
name='agenda_speaker_speak'),
|
||||
|
||||
|
@ -190,13 +190,13 @@ class AgendaItemView(SingleObjectMixin, FormView):
|
||||
'object': self.object,
|
||||
'list_of_speakers': list_of_speakers,
|
||||
'is_on_the_list_of_speakers': Speaker.objects.filter(
|
||||
item=self.object, begin_time=None, person=self.request.user).exists(),
|
||||
item=self.object, begin_time=None, user=self.request.user).exists(),
|
||||
'active_type': active_type,
|
||||
})
|
||||
return super(AgendaItemView, self).get_context_data(**kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
Speaker.objects.add(person=form.cleaned_data['speaker'], item=self.get_object())
|
||||
Speaker.objects.add(user=form.cleaned_data['speaker'], item=self.get_object())
|
||||
return self.render_to_response(self.get_context_data(form=form))
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@ -392,7 +392,7 @@ class SpeakerAppendView(SingleObjectMixin, RedirectView):
|
||||
messages.error(request, _('The list of speakers is closed.'))
|
||||
else:
|
||||
try:
|
||||
Speaker.objects.add(item=self.object, person=request.user)
|
||||
Speaker.objects.add(item=self.object, user=request.user)
|
||||
except OpenSlidesError as e:
|
||||
messages.error(request, e)
|
||||
else:
|
||||
@ -413,7 +413,7 @@ class SpeakerDeleteView(DeleteView):
|
||||
if 'speaker' in kwargs:
|
||||
return request.user.has_perm('agenda.can_manage_agenda')
|
||||
else:
|
||||
# Any person who is on the list of speakers can delete himself from the list.
|
||||
# Any user who is on the list of speakers can delete himself from the list.
|
||||
return True
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
@ -434,7 +434,7 @@ class SpeakerDeleteView(DeleteView):
|
||||
return Speaker.objects.get(pk=self.kwargs['speaker'])
|
||||
except KeyError:
|
||||
return Speaker.objects.filter(
|
||||
item=self.kwargs['pk'], person=self.request.user).exclude(weight=None).get()
|
||||
item=self.kwargs['pk'], user=self.request.user).exclude(weight=None).get()
|
||||
|
||||
def get_url_name_args(self):
|
||||
return [self.kwargs['pk']]
|
||||
@ -448,7 +448,7 @@ class SpeakerDeleteView(DeleteView):
|
||||
|
||||
class SpeakerSpeakView(SingleObjectMixin, RedirectView):
|
||||
"""
|
||||
Mark the speaking person.
|
||||
Mark the speaking user.
|
||||
"""
|
||||
required_permission = 'agenda.can_manage_agenda'
|
||||
url_name = 'item_view'
|
||||
@ -458,14 +458,14 @@ class SpeakerSpeakView(SingleObjectMixin, RedirectView):
|
||||
self.object = self.get_object()
|
||||
try:
|
||||
speaker = Speaker.objects.filter(
|
||||
person=kwargs['person_id'],
|
||||
user=kwargs['user_id'],
|
||||
item=self.object,
|
||||
begin_time=None).get()
|
||||
except Speaker.DoesNotExist: # TODO: Check the MultipleObjectsReturned error here?
|
||||
messages.error(
|
||||
self.request,
|
||||
_('%(person)s is not on the list of %(item)s.')
|
||||
% {'person': kwargs['person_id'], 'item': self.object})
|
||||
_('%(user)s is not on the list of %(item)s.')
|
||||
% {'user': kwargs['user_id'], 'item': self.object})
|
||||
else:
|
||||
speaker.begin_speach()
|
||||
|
||||
|
@ -1 +1 @@
|
||||
from . import main_menu, personal_info, signals, slides, template, widgets # noqa
|
||||
# TODO: apploader
|
||||
|
@ -1,8 +1,8 @@
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.forms import CssClassMixin
|
||||
from openslides.utils.person import PersonFormField
|
||||
|
||||
from .models import Assignment
|
||||
|
||||
@ -17,6 +17,7 @@ class AssignmentForm(CssClassMixin, forms.ModelForm):
|
||||
|
||||
|
||||
class AssignmentRunForm(CssClassMixin, forms.Form):
|
||||
candidate = PersonFormField(
|
||||
candidate = forms.ModelChoiceField(
|
||||
queryset=User.objects.all(),
|
||||
widget=forms.Select(attrs={'class': 'medium-input'}),
|
||||
label=ugettext_lazy("Nominate a participant"))
|
||||
|
@ -14,8 +14,8 @@ from openslides.projector.api import get_active_object, update_projector
|
||||
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.utils.person import PersonField
|
||||
from openslides.utils.utils import html_strong
|
||||
from openslides.users.models import User
|
||||
|
||||
|
||||
class AssignmentCandidate(RelatedModelMixin, models.Model):
|
||||
@ -23,7 +23,7 @@ class AssignmentCandidate(RelatedModelMixin, models.Model):
|
||||
Many2Many table between an assignment and the candidates.
|
||||
"""
|
||||
assignment = models.ForeignKey("Assignment")
|
||||
person = PersonField(db_index=True)
|
||||
person = models.ForeignKey(User, db_index=True)
|
||||
elected = models.BooleanField(default=False)
|
||||
blocked = models.BooleanField(default=False)
|
||||
|
||||
@ -178,12 +178,11 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
if only_candidate:
|
||||
candidates = candidates.filter(elected=False)
|
||||
|
||||
# TODO: rewrite this with a queryset
|
||||
participants = []
|
||||
for candidate in candidates.all():
|
||||
participants.append(candidate.person)
|
||||
participants.sort(key=lambda person: person.sort_name)
|
||||
return participants
|
||||
# return candidates.values_list('person', flat=True)
|
||||
|
||||
def set_elected(self, person, value=True):
|
||||
candidate = self.assignment_candidates.get(person=person)
|
||||
@ -251,8 +250,6 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
for poll in polls:
|
||||
options += poll.get_options()
|
||||
|
||||
options.sort(key=lambda option: option.candidate.sort_name)
|
||||
|
||||
for option in options:
|
||||
candidate = option.candidate
|
||||
if candidate in vote_results_dict:
|
||||
@ -284,7 +281,7 @@ class AssignmentVote(BaseVote):
|
||||
|
||||
class AssignmentOption(BaseOption):
|
||||
poll = models.ForeignKey('AssignmentPoll')
|
||||
candidate = PersonField()
|
||||
candidate = models.ForeignKey(User)
|
||||
vote_class = AssignmentVote
|
||||
|
||||
def __str__(self):
|
||||
@ -345,3 +342,7 @@ class AssignmentPoll(SlideMixin, RelatedModelMixin, CollectDefaultVotesMixin,
|
||||
|
||||
def get_slide_context(self, **context):
|
||||
return super(AssignmentPoll, self).get_slide_context(poll=self)
|
||||
|
||||
|
||||
# TODO: use the app framework
|
||||
from . import main_menu, personal_info, signals, slides, template, widgets # noqa
|
||||
|
@ -69,14 +69,14 @@
|
||||
<a href="{{ person|absolute_url }}">{{ person }}</a>
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if assignment.status == "sea" or assignment.status == "vot" %}
|
||||
<a href="{% url 'assignment_delother' assignment.id person.person_id %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Remove candidate' %}"><i class="icon-remove"></i></a>
|
||||
<a href="{% url 'assignment_delother' assignment.id person.pk %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Remove candidate' %}"><i class="icon-remove"></i></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if person in assignment.elected %}
|
||||
| <b>{% trans "elected" %}</b>
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if assignment.status == "sea" or assignment.status == "vot" %}
|
||||
<a class="btn btn-mini" href="{% url 'assignment_user_not_elected' assignment.id person.person_id %}" rel="tooltip" data-original-title="{% trans 'Mark candidate as not elected' %}">
|
||||
<a class="btn btn-mini" href="{% url 'assignment_user_not_elected' assignment.id person.pk %}" rel="tooltip" data-original-title="{% trans 'Mark candidate as not elected' %}">
|
||||
<i class="icon-ban-circle"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -128,7 +128,7 @@
|
||||
{% for person in blocked_candidates %}
|
||||
<li>
|
||||
<a href="{{ person|absolute_url }}">{{ person }}</a>
|
||||
<a class="btn btn-mini" href="{% url 'assignment_delother' assignment.id person.person_id %}"
|
||||
<a class="btn btn-mini" href="{% url 'assignment_delother' assignment.id person.pk %}"
|
||||
rel="tooltip" data-original-title="{% trans 'Remove candidate' %}">
|
||||
<i class="icon-ban-circle"></i>
|
||||
</a>
|
||||
@ -190,7 +190,7 @@
|
||||
<td>
|
||||
{% if candidate in assignment.elected %}
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
<a class="election_link elected tooltip-bottom" href="{% url 'assignment_user_not_elected' assignment.id candidate.person_id %}"
|
||||
<a class="election_link elected tooltip-bottom" href="{% url 'assignment_user_not_elected' assignment.id candidate.pk %}"
|
||||
data-original-title="{% trans 'Mark candidate as elected' %}"></a>
|
||||
{% else %}
|
||||
<a class="elected">
|
||||
@ -199,7 +199,7 @@
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
<a class="election_link tooltip-bottom" href="{% url 'assignment_user_elected' assignment.id candidate.person_id %}"
|
||||
<a class="election_link tooltip-bottom" href="{% url 'assignment_user_elected' assignment.id candidate.pk %}"
|
||||
data-original-title="{% trans 'Mark candidate as elected' %}"></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
@ -10,10 +10,9 @@ from reportlab.platypus import (PageBreak, Paragraph, SimpleDocTemplate, Spacer,
|
||||
|
||||
from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelatedAgendaItemView
|
||||
from openslides.config.api import config
|
||||
from openslides.participant.models import Group, User
|
||||
from openslides.users.models import Group, User # TODO: remove this
|
||||
from openslides.poll.views import PollFormView
|
||||
from openslides.utils.pdf import stylesheet
|
||||
from openslides.utils.person import get_person
|
||||
from openslides.utils.utils import html_strong
|
||||
from openslides.utils.views import (CreateView, DeleteView, DetailView,
|
||||
ListView, PDFView, PermissionMixin,
|
||||
@ -187,7 +186,7 @@ class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
|
||||
|
||||
def _get_person_information(self):
|
||||
self.object = self.get_object()
|
||||
self.person = get_person(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)
|
||||
|
||||
|
||||
@ -252,7 +251,7 @@ class SetElectedView(SingleObjectMixin, RedirectView):
|
||||
|
||||
def pre_redirect(self, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
self.person = get_person(kwargs['user_id'])
|
||||
self.person = User.objects.get(pk=kwargs['user_id'])
|
||||
self.elected = kwargs['elected']
|
||||
self.object.set_elected(self.person, self.elected)
|
||||
|
||||
@ -406,8 +405,8 @@ class AssignmentPDF(PDFView):
|
||||
candidate_string = candidate.clean_name
|
||||
if candidate in elected_candidates:
|
||||
candidate_string = "* " + candidate_string
|
||||
if candidate.name_suffix:
|
||||
candidate_string += "\n(%s)" % candidate.name_suffix
|
||||
if candidate.structure_level:
|
||||
candidate_string += "\n(%s)" % candidate.structure_level
|
||||
row.append(candidate_string)
|
||||
for vote in poll_list:
|
||||
if vote is None:
|
||||
@ -505,7 +504,7 @@ class AssignmentPollPDF(PDFView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.poll = AssignmentPoll.objects.get(id=self.kwargs['poll_id'])
|
||||
return super(AssignmentPollPDF, self).get(request, *args, **kwargs)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_filename(self):
|
||||
filename = u'%s-%s_%s' % (
|
||||
@ -570,10 +569,10 @@ class AssignmentPollPDF(PDFView):
|
||||
counter += 1
|
||||
candidate = option.candidate
|
||||
cell.append(Paragraph(
|
||||
candidate.clean_name, stylesheet['Ballot_option_name_YNA']))
|
||||
if candidate.name_suffix:
|
||||
candidate.get_short_name(), stylesheet['Ballot_option_name_YNA']))
|
||||
if candidate.structure_level:
|
||||
cell.append(Paragraph(
|
||||
"(%s)" % candidate.name_suffix,
|
||||
"(%s)" % candidate.structure_level,
|
||||
stylesheet['Ballot_option_suffix_YNA']))
|
||||
else:
|
||||
cell.append(Paragraph(
|
||||
@ -616,9 +615,9 @@ class AssignmentPollPDF(PDFView):
|
||||
cell.append(Paragraph("<font name='circlefont' size='15'>%s</font> \
|
||||
<font name='Ubuntu'>%s</font>" %
|
||||
(circle, candidate.clean_name), stylesheet['Ballot_option_name']))
|
||||
if candidate.name_suffix:
|
||||
if candidate.structure_level:
|
||||
cell.append(Paragraph(
|
||||
"(%s)" % candidate.name_suffix,
|
||||
"(%s)" % candidate.structure_level,
|
||||
stylesheet['Ballot_option_suffix']))
|
||||
else:
|
||||
cell.append(Paragraph(
|
||||
|
@ -1 +1 @@
|
||||
from . import main_menu, signals, slides, widgets # noqa
|
||||
# TODO: apploading
|
||||
|
@ -16,7 +16,7 @@ class ChatboxSocketHandler(SockJSConnection):
|
||||
"""
|
||||
Checks connecting user and adds his client to the clients list.
|
||||
"""
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
|
||||
# TODO: Use the django way to get the session to be compatible with
|
||||
# other auth-backends; see comment in pull request #1220:
|
||||
|
@ -1,10 +1,14 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.projector.models import SlideMixin
|
||||
|
||||
# Imports the default user so that other apps can import it from here.
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class CustomSlide(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
"""
|
||||
@ -38,3 +42,7 @@ class CustomSlide(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
else:
|
||||
url = super(CustomSlide, self).get_absolute_url(link)
|
||||
return url
|
||||
|
||||
|
||||
# TODO: apploader
|
||||
from . import main_menu, signals, slides, widgets # noqa
|
||||
|
@ -11,6 +11,8 @@ AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'openslides.utils.auth.AnonymousAuth',)
|
||||
|
||||
AUTH_USER_MODEL = 'users.User'
|
||||
|
||||
LOGIN_URL = '/login/'
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
|
||||
@ -66,7 +68,7 @@ MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'openslides.participant.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'openslides.config.middleware.ConfigCacheMiddleware',
|
||||
)
|
||||
@ -90,7 +92,7 @@ INSTALLED_APPS = (
|
||||
'openslides.agenda',
|
||||
'openslides.motion',
|
||||
'openslides.assignment',
|
||||
'openslides.participant',
|
||||
'openslides.users',
|
||||
'openslides.mediafile',
|
||||
'openslides.config',
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
|
||||
from openslides.projector.models import SlideMixin
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.utils.person.models import PersonField
|
||||
from openslides.users.models import User
|
||||
|
||||
|
||||
class Mediafile(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
@ -17,17 +17,17 @@ class Mediafile(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
slide_callback_name = 'mediafile'
|
||||
PRESENTABLE_FILE_TYPES = ['application/pdf']
|
||||
|
||||
mediafile = models.FileField(upload_to='file', verbose_name=ugettext_lazy("File"))
|
||||
mediafile = models.FileField(upload_to='file', verbose_name=ugettext_lazy('File'))
|
||||
"""
|
||||
See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield
|
||||
for more information.
|
||||
"""
|
||||
|
||||
title = models.CharField(max_length=255, unique=True, verbose_name=ugettext_lazy("Title"))
|
||||
title = models.CharField(max_length=255, unique=True, verbose_name=ugettext_lazy('Title'))
|
||||
"""A string representing the title of the file."""
|
||||
|
||||
uploader = PersonField(blank=True, verbose_name=ugettext_lazy("Uploaded by"))
|
||||
"""A person – the uploader of a file."""
|
||||
uploader = models.ForeignKey(User, null=True, blank=True, verbose_name=ugettext_lazy('Uploaded by'))
|
||||
"""A user – the uploader of a file."""
|
||||
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
"""A DateTimeField to save the upload date and time."""
|
||||
|
@ -70,7 +70,7 @@ class MediafileCreateView(MediafileViewMixin, CreateView):
|
||||
def get_form_kwargs(self, *args, **kwargs):
|
||||
form_kwargs = super(MediafileCreateView, self).get_form_kwargs(*args, **kwargs)
|
||||
if self.request.method == 'GET':
|
||||
form_kwargs['initial'].update({'uploader': self.request.user.person_id})
|
||||
form_kwargs['initial'].update({'uploader': self.request.user})
|
||||
return form_kwargs
|
||||
|
||||
|
||||
@ -80,11 +80,12 @@ class MediafileUpdateView(MediafileViewMixin, UpdateView):
|
||||
"""
|
||||
def check_permission(self, request, *args, **kwargs):
|
||||
return (request.user.has_perm('mediafile.can_manage') or
|
||||
(request.user.has_perm('mediafile.can_upload') and self.get_object().uploader == self.request.user))
|
||||
(request.user.has_perm('mediafile.can_upload') and
|
||||
self.get_object().uploader == self.request.user))
|
||||
|
||||
def get_form_kwargs(self, *args, **kwargs):
|
||||
form_kwargs = super(MediafileUpdateView, self).get_form_kwargs(*args, **kwargs)
|
||||
form_kwargs['initial'].update({'uploader': self.object.uploader.person_id})
|
||||
form_kwargs['initial'].update({'uploader': self.object.uploader.pk})
|
||||
return form_kwargs
|
||||
|
||||
|
||||
|
@ -1 +1 @@
|
||||
from . import main_menu, personal_info, signals, slides, widgets # noqa
|
||||
# TODO: Apploading
|
||||
|
@ -9,7 +9,7 @@ from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_noop
|
||||
|
||||
from openslides.utils import csv_ext
|
||||
from openslides.utils.person.api import Persons
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.utils import html_strong
|
||||
|
||||
from .models import Category, Motion
|
||||
@ -90,8 +90,8 @@ def import_motions(csvfile, default_submitter, override, importing_person=None):
|
||||
# Add submitter
|
||||
person_found = False
|
||||
if submitter:
|
||||
for person in Persons():
|
||||
if person.clean_name == submitter:
|
||||
for person in User.objects.all():
|
||||
if person.get_short_name() == submitter:
|
||||
if person_found:
|
||||
warning.append(_('Several suitable submitters found.'))
|
||||
person_found = False
|
||||
|
@ -5,7 +5,7 @@ from openslides.config.api import config
|
||||
from openslides.mediafile.models import Mediafile
|
||||
from openslides.utils.forms import (CleanHtmlFormMixin, CssClassMixin,
|
||||
CSVImportForm, LocalizedModelChoiceField)
|
||||
from openslides.utils.person import MultiplePersonFormField, PersonFormField
|
||||
from openslides.users.models import User
|
||||
|
||||
from ckeditor.widgets import CKEditorWidget
|
||||
|
||||
@ -72,14 +72,14 @@ class BaseMotionForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm):
|
||||
class MotionSubmitterMixin(forms.ModelForm):
|
||||
"""Mixin to append the submitter field to a MotionForm."""
|
||||
|
||||
submitter = MultiplePersonFormField(label=ugettext_lazy("Submitter"),
|
||||
required=False)
|
||||
submitter = forms.ModelMultipleChoiceField(
|
||||
User.objects, label=ugettext_lazy("Submitter"), required=False)
|
||||
"""Submitter of the motion. Can be one or more persons."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Fill in the submitter of the motion as default value."""
|
||||
if self.motion is not None:
|
||||
submitter = [submitter.person.person_id for submitter in self.motion.submitter.all()]
|
||||
submitter = [submitter.person.id for submitter in self.motion.submitter.all()]
|
||||
self.initial['submitter'] = submitter
|
||||
super(MotionSubmitterMixin, self).__init__(*args, **kwargs)
|
||||
|
||||
@ -87,13 +87,14 @@ class MotionSubmitterMixin(forms.ModelForm):
|
||||
class MotionSupporterMixin(forms.ModelForm):
|
||||
"""Mixin to append the supporter field to a Motionform."""
|
||||
|
||||
supporter = MultiplePersonFormField(required=False, label=ugettext_lazy("Supporters"))
|
||||
supporter = forms.ModelMultipleChoiceField(
|
||||
User.objects, required=False, label=ugettext_lazy("Supporters"))
|
||||
"""Supporter of the motion. Can be one or more persons."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Fill in the supporter of the motions as default value."""
|
||||
if self.motion is not None:
|
||||
supporter = [supporter.person.person_id for supporter in self.motion.supporter.all()]
|
||||
supporter = [supporter.person.id for supporter in self.motion.supporter.all()]
|
||||
self.initial['supporter'] = supporter
|
||||
super(MotionSupporterMixin, self).__init__(*args, **kwargs)
|
||||
|
||||
@ -169,7 +170,8 @@ class MotionCSVImportForm(CSVImportForm):
|
||||
should be overridden.
|
||||
"""
|
||||
|
||||
default_submitter = PersonFormField(
|
||||
default_submitter = forms.ModelChoiceField(
|
||||
User.objects.all(),
|
||||
required=True,
|
||||
label=ugettext_lazy('Default submitter'),
|
||||
help_text=ugettext_lazy('This person is used as submitter for any line of your csv file which does not contain valid submitter data.'))
|
||||
|
@ -11,7 +11,7 @@ from openslides.poll.models import (BaseOption, BasePoll, BaseVote, CollectDefau
|
||||
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
||||
from jsonfield import JSONField
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.utils.person import PersonField
|
||||
from openslides.users.models import User
|
||||
|
||||
from .exceptions import WorkflowError
|
||||
|
||||
@ -366,8 +366,7 @@ class Motion(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
|
||||
@property
|
||||
def supporters(self):
|
||||
return sorted([object.person for object in self.supporter.all()],
|
||||
key=lambda person: person.sort_name)
|
||||
return [supporter.person for supporter in self.supporter.all()]
|
||||
|
||||
def add_submitter(self, person):
|
||||
MotionSubmitter.objects.create(motion=self, person=person)
|
||||
@ -582,8 +581,8 @@ class MotionSubmitter(RelatedModelMixin, models.Model):
|
||||
motion = models.ForeignKey('Motion', related_name="submitter")
|
||||
"""The motion to witch the object belongs."""
|
||||
|
||||
person = PersonField()
|
||||
"""The person, who is the submitter."""
|
||||
person = models.ForeignKey(User)
|
||||
"""The user, who is the submitter."""
|
||||
|
||||
def __str__(self):
|
||||
"""Return the name of the submitter as string."""
|
||||
@ -599,7 +598,7 @@ class MotionSupporter(models.Model):
|
||||
motion = models.ForeignKey('Motion', related_name="supporter")
|
||||
"""The motion to witch the object belongs."""
|
||||
|
||||
person = PersonField()
|
||||
person = models.ForeignKey(User)
|
||||
"""The person, who is the supporter."""
|
||||
|
||||
def __str__(self):
|
||||
@ -632,12 +631,6 @@ class Category(AbsoluteUrlMixin, models.Model):
|
||||
class Meta:
|
||||
ordering = ['prefix']
|
||||
|
||||
# class Comment(models.Model):
|
||||
# motion_version = models.ForeignKey(MotionVersion)
|
||||
# text = models.TextField()
|
||||
# author = PersonField()
|
||||
# creation_time = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
class MotionLog(models.Model):
|
||||
"""Save a logmessage for a motion."""
|
||||
@ -650,7 +643,7 @@ class MotionLog(models.Model):
|
||||
The log message. It should be a list of strings in english.
|
||||
"""
|
||||
|
||||
person = PersonField(null=True)
|
||||
person = models.ForeignKey(User, null=True)
|
||||
"""A person object, who created the log message. Optional."""
|
||||
|
||||
time = models.DateTimeField(auto_now=True)
|
||||
@ -856,4 +849,10 @@ class Workflow(models.Model):
|
||||
def check_first_state(self):
|
||||
"""Checks whether the first_state itself belongs to the workflow."""
|
||||
if self.first_state and not self.first_state.workflow == self:
|
||||
raise WorkflowError('%s can not be first state of %s because it does not belong to it.' % (self.first_state, self))
|
||||
raise WorkflowError(
|
||||
'%s can not be first state of %s because it '
|
||||
'does not belong to it.' % (self.first_state, self))
|
||||
|
||||
|
||||
# TODO: Apploading
|
||||
from . import main_menu, personal_info, signals, slides, widgets # noqa
|
||||
|
@ -9,7 +9,7 @@ from reportlab.lib.units import cm
|
||||
from reportlab.platypus import PageBreak, Paragraph, Spacer, Table, TableStyle
|
||||
|
||||
from openslides.config.api import config
|
||||
from openslides.participant.models import Group, User
|
||||
from openslides.users.models import Group, User # TODO: remove this line
|
||||
from openslides.utils.pdf import stylesheet
|
||||
|
||||
from .models import Category, Motion
|
||||
@ -281,9 +281,9 @@ def motion_poll_to_pdf(pdf, poll):
|
||||
|
||||
# set number of ballot papers
|
||||
if ballot_papers_selection == "NUMBER_OF_DELEGATES":
|
||||
# TODO: get this number from persons
|
||||
# TODO: get this number from users
|
||||
try:
|
||||
if Group.objects.get(pk=3):
|
||||
if Group.objects.get(pk=3): # TODO: Find a better way
|
||||
number = User.objects.filter(groups__pk=3).count()
|
||||
except Group.DoesNotExist:
|
||||
number = 0
|
||||
|
@ -1,10 +0,0 @@
|
||||
from django.contrib.auth.middleware import AuthenticationMiddleware as _AuthenticationMiddleware
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
|
||||
|
||||
class AuthenticationMiddleware(_AuthenticationMiddleware):
|
||||
def process_request(self, request):
|
||||
super(AuthenticationMiddleware, self).process_request(request)
|
||||
|
||||
if not isinstance(request.user, AnonymousUser):
|
||||
request.user = request.user.user
|
@ -1,225 +0,0 @@
|
||||
from django.contrib.auth.models import Group as DjangoGroup
|
||||
from django.contrib.auth.models import User as DjangoUser
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.db.models import signals
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
|
||||
from openslides.config.api import config
|
||||
from openslides.projector.models import SlideMixin
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.utils.person import Person, PersonMixin
|
||||
from openslides.utils.person.signals import receive_persons
|
||||
|
||||
|
||||
class User(SlideMixin, PersonMixin, Person, AbsoluteUrlMixin, DjangoUser):
|
||||
slide_callback_name = 'user'
|
||||
person_prefix = 'user'
|
||||
|
||||
GENDER_CHOICES = (
|
||||
('male', ugettext_lazy('Male')),
|
||||
('female', ugettext_lazy('Female')),
|
||||
)
|
||||
|
||||
django_user = models.OneToOneField(DjangoUser, editable=False, parent_link=True)
|
||||
structure_level = models.CharField(
|
||||
max_length=255, blank=True, default='', verbose_name=ugettext_lazy("Structure level"),
|
||||
help_text=ugettext_lazy('Will be shown after the name.'))
|
||||
title = models.CharField(
|
||||
max_length=50, blank=True, default='', verbose_name=ugettext_lazy("Title"),
|
||||
help_text=ugettext_lazy('Will be shown before the name.'))
|
||||
gender = models.CharField(
|
||||
max_length=50, choices=GENDER_CHOICES, blank=True,
|
||||
verbose_name=ugettext_lazy("Gender"), help_text=ugettext_lazy('Only for filtering the participant list.'))
|
||||
committee = models.CharField(
|
||||
max_length=255, blank=True, default='', verbose_name=ugettext_lazy("Committee"),
|
||||
help_text=ugettext_lazy('Only for filtering the participant list.'))
|
||||
about_me = models.TextField(
|
||||
blank=True, default='', verbose_name=ugettext_lazy('About me'),
|
||||
help_text=ugettext_lazy('Your profile text'))
|
||||
comment = models.TextField(
|
||||
blank=True, default='', verbose_name=ugettext_lazy('Comment'),
|
||||
help_text=ugettext_lazy('Only for notes.'))
|
||||
default_password = models.CharField(
|
||||
max_length=100, blank=True, default='',
|
||||
verbose_name=ugettext_lazy("Default password"))
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('can_see_participant', ugettext_noop('Can see participants')),
|
||||
('can_manage_participant', ugettext_noop('Can manage participants')),
|
||||
)
|
||||
ordering = ('last_name',)
|
||||
|
||||
def __str__(self):
|
||||
if self.name_suffix:
|
||||
return u"%s (%s)" % (self.clean_name, self.name_suffix)
|
||||
return u"%s" % self.clean_name
|
||||
|
||||
def get_absolute_url(self, link='detail'):
|
||||
"""
|
||||
Return the URL to the user.
|
||||
"""
|
||||
if link == 'detail':
|
||||
url = reverse('user_view', args=[str(self.pk)])
|
||||
elif link == 'update':
|
||||
url = reverse('user_edit', args=[str(self.pk)])
|
||||
elif link == 'delete':
|
||||
url = reverse('user_delete', args=[str(self.pk)])
|
||||
else:
|
||||
url = super(User, self).get_absolute_url(link)
|
||||
return url
|
||||
|
||||
def get_slide_context(self, **context):
|
||||
# Does not call super. In this case the context would override the name
|
||||
# 'user'.
|
||||
return {'shown_user': self}
|
||||
|
||||
@property
|
||||
def clean_name(self):
|
||||
if self.title:
|
||||
name = "%s %s" % (self.title, self.get_full_name())
|
||||
else:
|
||||
name = self.get_full_name()
|
||||
return name or self.username
|
||||
|
||||
def get_name_suffix(self):
|
||||
return self.structure_level
|
||||
|
||||
def set_name_suffix(self, value):
|
||||
self.structure_level = value
|
||||
|
||||
name_suffix = property(get_name_suffix, set_name_suffix)
|
||||
|
||||
def reset_password(self, password=None):
|
||||
"""
|
||||
Reset the password for the user to his default-password.
|
||||
"""
|
||||
if password is None:
|
||||
password = self.default_password
|
||||
self.set_password(password)
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def sort_name(self):
|
||||
if config['participant_sort_users_by_first_name']:
|
||||
return self.first_name.lower()
|
||||
return self.last_name.lower()
|
||||
|
||||
|
||||
class Group(SlideMixin, PersonMixin, Person, AbsoluteUrlMixin, DjangoGroup):
|
||||
slide_callback_name = 'group'
|
||||
person_prefix = 'group'
|
||||
|
||||
django_group = models.OneToOneField(DjangoGroup, editable=False, parent_link=True)
|
||||
group_as_person = models.BooleanField(
|
||||
default=False, verbose_name=ugettext_lazy("Use this group as participant"),
|
||||
help_text=ugettext_lazy('For example as submitter of a motion.'))
|
||||
description = models.TextField(blank=True, verbose_name=ugettext_lazy("Description"))
|
||||
|
||||
class Meta:
|
||||
ordering = ('name',)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name)
|
||||
|
||||
def get_absolute_url(self, link='detail'):
|
||||
"""
|
||||
Return the URL to the user group.
|
||||
"""
|
||||
if link == 'detail':
|
||||
url = reverse('user_group_view', args=[str(self.pk)])
|
||||
elif link == 'update':
|
||||
url = reverse('user_group_edit', args=[str(self.pk)])
|
||||
elif link == 'delete':
|
||||
url = reverse('user_group_delete', args=[str(self.pk)])
|
||||
else:
|
||||
url = super(Group, self).get_absolute_url(link)
|
||||
return url
|
||||
|
||||
|
||||
class UsersAndGroupsToPersons(object):
|
||||
"""
|
||||
Object to send all Users and Groups or a special User or Group to
|
||||
the Person-API via receice_persons()
|
||||
"""
|
||||
def __init__(self, person_prefix_filter=None, id_filter=None):
|
||||
self.person_prefix_filter = person_prefix_filter
|
||||
self.id_filter = id_filter
|
||||
if config['participant_sort_users_by_first_name']:
|
||||
self.users = User.objects.all().order_by('first_name')
|
||||
else:
|
||||
self.users = User.objects.all().order_by('last_name')
|
||||
self.groups = Group.objects.filter(group_as_person=True)
|
||||
|
||||
def __iter__(self):
|
||||
if (not self.person_prefix_filter or
|
||||
self.person_prefix_filter == User.person_prefix):
|
||||
if self.id_filter:
|
||||
try:
|
||||
yield self.users.get(pk=self.id_filter)
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
for user in self.users:
|
||||
yield user
|
||||
|
||||
if (not self.person_prefix_filter or
|
||||
self.person_prefix_filter == Group.person_prefix):
|
||||
if self.id_filter:
|
||||
try:
|
||||
yield self.groups.get(pk=self.id_filter)
|
||||
except Group.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
for group in self.groups:
|
||||
yield group
|
||||
|
||||
|
||||
@receiver(receive_persons, dispatch_uid="participant")
|
||||
def receive_persons(sender, **kwargs):
|
||||
"""
|
||||
Answers to the Person-API
|
||||
"""
|
||||
return UsersAndGroupsToPersons(
|
||||
person_prefix_filter=kwargs['person_prefix_filter'],
|
||||
id_filter=kwargs['id_filter'])
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=DjangoUser)
|
||||
def djangouser_post_save(sender, instance, signal, *args, **kwargs):
|
||||
try:
|
||||
instance.user
|
||||
except User.DoesNotExist:
|
||||
User(django_user=instance).save_base(raw=True)
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=DjangoGroup)
|
||||
def djangogroup_post_save(sender, instance, signal, *args, **kwargs):
|
||||
try:
|
||||
instance.group
|
||||
except Group.DoesNotExist:
|
||||
Group(django_group=instance).save_base(raw=True)
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=User)
|
||||
def user_post_save(sender, instance, *args, **kwargs):
|
||||
if not kwargs['created']:
|
||||
return
|
||||
from openslides.participant.api import get_registered_group # TODO: Test, if global import is possible
|
||||
registered = get_registered_group()
|
||||
instance.groups.add(registered)
|
||||
instance.save()
|
||||
|
||||
|
||||
def get_protected_perm():
|
||||
"""
|
||||
Returns the permission to manage participants. This function is a helper
|
||||
function used to protect manager users from locking out themselves.
|
||||
"""
|
||||
return Permission.objects.get(
|
||||
content_type=ContentType.objects.get(app_label='participant', model='user'),
|
||||
codename='can_manage_participant')
|
@ -1,47 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load tags %}
|
||||
|
||||
{% block title %}{% trans group.name %} – {{ block.super }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{% trans group.name %}
|
||||
<small class="pull-right">
|
||||
<a href="{% url 'user_group_overview' %}" class="btn btn-mini"><i class="icon-chevron-left"></i><span class="optional-small"> {% trans "Back to overview" %}</span></a>
|
||||
<!-- activate projector -->
|
||||
{% if perms.core.can_manage_projector %}
|
||||
<a href="{% url 'projector_activate_slide' group.sid %}" class="activate_link btn {% if group.active %}btn-primary{% endif %} btn-mini" rel="tooltip" data-original-title="{% trans 'Show group' %}">
|
||||
<i class="icon-facetime-video {% if group.active %}icon-white{% endif %}"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.participant.can_manage_participant and group.pk != 1 and group.pk != 2 %}
|
||||
<div class="btn-group">
|
||||
<a data-toggle="dropdown" class="btn btn-mini dropdown-toggle">
|
||||
<span class="optional-small">{% trans 'More actions' %} </span><span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<!-- edit -->
|
||||
<li><a href="{% url 'user_group_edit' group.id %}"><i class="icon-pencil"></i> {% trans 'Edit group' %}</a></li>
|
||||
<!-- delete -->
|
||||
<li><a href="{% url 'user_group_delete' group.id %}"><i class="icon-remove"></i> {% trans 'Delete group' %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
<p>{{ group.description }}</p>
|
||||
|
||||
<h4>{% trans "Members" %}</h4>
|
||||
|
||||
<ol>
|
||||
{% for member in group_members %}
|
||||
<li><a href="{{ member|absolute_url }}">{{ member }}</a></li>
|
||||
{% empty %}
|
||||
<p>{% trans "No members available." %}</p>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
|
||||
{% endblock %}
|
@ -1,14 +0,0 @@
|
||||
{% load i18n %}
|
||||
|
||||
<h1>
|
||||
{% trans group.name %}
|
||||
<small>{% trans "Group" %}</small>
|
||||
</h1>
|
||||
|
||||
<p><i>{{ group.user_set.all.count }} {% trans "participants" %}</i></p>
|
||||
|
||||
<ol>
|
||||
{% for member in group.user_set.all %}
|
||||
<li>{{ member }}</li>
|
||||
{% endfor %}
|
||||
</ol>
|
@ -1,28 +0,0 @@
|
||||
{% extends 'core/widget.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load tags %}
|
||||
|
||||
{% block content %}
|
||||
<ul style="line-height: 180%">
|
||||
{% for group in groups %}
|
||||
<li class="{% if group.is_active_slide %}activeline{% endif %}">
|
||||
<a href="{{ group|absolute_url:'projector' }}" class="activate_link btn {% if group.is_active_slide %}btn-primary{% endif %} btn-mini"
|
||||
rel="tooltip" data-original-title="{% trans 'Show' %}">
|
||||
<i class="icon-facetime-video {% if group.is_active_slide %}icon-white{% endif %}"></i>
|
||||
</a>
|
||||
{% if perms.participant.can_manage_participant %}
|
||||
<a href="{{ group|absolute_url:'update' }}"
|
||||
rel="tooltip" data-original-title="{% trans 'Edit' %}" class="btn btn-mini right">
|
||||
<i class="icon-pencil"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{{ group|absolute_url:'projector_preview' }}"
|
||||
rel="tooltip" data-original-title="{% trans 'Preview' %}" class="btn btn-mini right">
|
||||
<i class="icon-search"></i>
|
||||
</a>
|
||||
<a href="{{ group|absolute_url }}">{% trans group.name %}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
@ -1,45 +0,0 @@
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from openslides.utils.widgets import Widget
|
||||
|
||||
from .models import Group, User
|
||||
|
||||
|
||||
class UserWidget(Widget):
|
||||
"""
|
||||
Provides a widget with all users. This is for short activation of
|
||||
user slides.
|
||||
"""
|
||||
name = 'user'
|
||||
verbose_name = ugettext_lazy('Participants')
|
||||
required_permission = 'core.can_manage_projector'
|
||||
default_column = 1
|
||||
default_weight = 60
|
||||
default_active = False
|
||||
template_name = 'participant/widget_user.html'
|
||||
more_link_pattern_name = 'user_overview'
|
||||
|
||||
def get_context_data(self, **context):
|
||||
return super(UserWidget, self).get_context_data(
|
||||
users=User.objects.all(),
|
||||
**context)
|
||||
|
||||
|
||||
class GroupWidget(Widget):
|
||||
"""
|
||||
Provides a widget with all groups. This is for short activation of
|
||||
group slides.
|
||||
"""
|
||||
name = 'group'
|
||||
verbose_name = ugettext_lazy('Groups')
|
||||
required_permission = 'core.can_manage_projector'
|
||||
default_column = 1
|
||||
default_weight = 70
|
||||
default_active = False
|
||||
template_name = 'participant/widget_group.html'
|
||||
more_link_pattern_name = 'user_group_overview'
|
||||
|
||||
def get_context_data(self, **context):
|
||||
return super(GroupWidget, self).get_context_data(
|
||||
groups=Group.objects.all(),
|
||||
**context)
|
@ -23,7 +23,7 @@ urlpatterns += patterns(
|
||||
(r'^agenda/', include('openslides.agenda.urls')),
|
||||
(r'^motion/', include('openslides.motion.urls')),
|
||||
(r'^assignment/', include('openslides.assignment.urls')),
|
||||
(r'^participant/', include('openslides.participant.urls')),
|
||||
(r'^user/', include('openslides.users.urls')),
|
||||
(r'^mediafile/', include('openslides.mediafile.urls')),
|
||||
(r'^config/', include('openslides.config.urls')),
|
||||
(r'^projector/', include('openslides.projector.urls')),
|
||||
@ -32,12 +32,13 @@ urlpatterns += patterns(
|
||||
)
|
||||
|
||||
# TODO: move this patterns into core or the participant app
|
||||
from openslides.users.views import UserSettingsView, UserPasswordSettingsView
|
||||
urlpatterns += patterns(
|
||||
'',
|
||||
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
|
||||
|
||||
url(r'^login/$',
|
||||
'openslides.participant.views.login',
|
||||
'openslides.users.views.login',
|
||||
name='user_login'),
|
||||
|
||||
url(r'^logout/$',
|
||||
@ -45,11 +46,11 @@ urlpatterns += patterns(
|
||||
name='user_logout'),
|
||||
|
||||
url(r'^usersettings/$',
|
||||
'openslides.participant.views.user_settings',
|
||||
UserSettingsView.as_view(),
|
||||
name='user_settings'),
|
||||
|
||||
url(r'^usersettings/changepassword/$',
|
||||
'openslides.participant.views.user_settings_password',
|
||||
UserPasswordSettingsView.as_view(),
|
||||
name='password_change'),
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
from random import choice
|
||||
|
||||
from .models import Group, User
|
||||
from django.contrib.auth.models import Permission, Group
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from .models import User
|
||||
|
||||
|
||||
def gen_password():
|
||||
@ -62,3 +65,13 @@ def create_or_reset_admin_user():
|
||||
admin.save()
|
||||
admin.groups.add(group_staff)
|
||||
return created
|
||||
|
||||
|
||||
def get_protected_perm():
|
||||
"""
|
||||
Returns the permission to manage users. This function is a helper
|
||||
function used to protect manager users from locking out themselves.
|
||||
"""
|
||||
return Permission.objects.get(
|
||||
content_type=ContentType.objects.get(app_label='users', model='user'),
|
||||
codename='can_manage')
|
@ -63,6 +63,7 @@ def import_users(csvfile):
|
||||
error_messages.append(_('Group id %(id)s does not exists (line %(line)d).') % {'id': groupid, 'line': line_no + 1})
|
||||
continue
|
||||
user.reset_password()
|
||||
user.save()
|
||||
count_success += 1
|
||||
except csv.Error:
|
||||
error_messages.append(_('Import aborted because of severe errors in the input file.'))
|
||||
@ -71,7 +72,7 @@ def import_users(csvfile):
|
||||
|
||||
# Build final success message
|
||||
if count_success:
|
||||
success_message = _('%d new participants were successfully imported.') % count_success
|
||||
success_message = _('%d new users were successfully imported.') % count_success
|
||||
else:
|
||||
success_message = ''
|
||||
|
@ -8,76 +8,70 @@ from openslides.config.api import config
|
||||
from openslides.utils.forms import (CssClassMixin,
|
||||
LocalizedModelMultipleChoiceField)
|
||||
|
||||
from .models import get_protected_perm, Group, User
|
||||
from .models import Group, User
|
||||
from .api import get_protected_perm
|
||||
|
||||
|
||||
class UserCreateForm(CssClassMixin, forms.ModelForm):
|
||||
groups = LocalizedModelMultipleChoiceField(
|
||||
# Hide the built-in groups 'Anonymous' (pk=1) and 'Registered' (pk=2)
|
||||
queryset=Group.objects.exclude(pk=1).exclude(pk=2),
|
||||
queryset=Group.objects.exclude(pk__in=[1, 2]),
|
||||
label=ugettext_lazy('Groups'), required=False)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('title', 'first_name', 'last_name', 'gender', 'email',
|
||||
'groups', 'structure_level', 'committee', 'about_me', 'comment',
|
||||
'is_active', 'default_password')
|
||||
fields = ('title', 'first_name', 'last_name', 'groups',
|
||||
'structure_level', 'about_me', 'comment', 'is_active',
|
||||
'default_password')
|
||||
|
||||
def clean(self, *args, **kwargs):
|
||||
"""
|
||||
Ensures that a user has either a first name or a last name.
|
||||
"""
|
||||
cleaned_data = super(UserCreateForm, self).clean(*args, **kwargs)
|
||||
if not cleaned_data['first_name'] and not cleaned_data['last_name']:
|
||||
if not (cleaned_data['first_name'] or cleaned_data['last_name']):
|
||||
error_msg = _('First name and last name can not both be empty.')
|
||||
raise forms.ValidationError(error_msg)
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class UserMultipleCreateForm(forms.Form):
|
||||
participants_block = forms.CharField(
|
||||
users_block = forms.CharField(
|
||||
widget=forms.Textarea,
|
||||
label=ugettext_lazy('Participants'),
|
||||
help_text=ugettext_lazy('Use one line per participant for its name (first name and last name).'))
|
||||
label=ugettext_lazy('Users'),
|
||||
help_text=ugettext_lazy('Use one line per user for its name '
|
||||
'(first name and last name).'))
|
||||
|
||||
|
||||
class UserUpdateForm(UserCreateForm):
|
||||
"""
|
||||
Form to update an user. It raises a validation error, if a non-superuser
|
||||
user edits himself and removes the last group containing the permission
|
||||
to manage participants.
|
||||
"""
|
||||
user_name = forms.CharField(label=ugettext_lazy('Username'))
|
||||
"""
|
||||
Field to save the username.
|
||||
|
||||
The field username (without the underscore) from the UserModel does not
|
||||
allow whitespaces and umlauts.
|
||||
to manage users.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('user_name', 'title', 'first_name', 'last_name', 'gender', 'email',
|
||||
'groups', 'structure_level', 'committee', 'about_me', 'comment',
|
||||
fields = ('username', 'title', 'first_name', 'last_name',
|
||||
'groups', 'structure_level', 'about_me', 'comment',
|
||||
'is_active', 'default_password')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.request = kwargs.pop('request')
|
||||
kwargs['initial']['user_name'] = kwargs['instance'].username
|
||||
return super(UserUpdateForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, *args, **kwargs):
|
||||
"""
|
||||
Raises a validation error if a non-superuser user edits himself
|
||||
and removes the last group containing the permission to manage participants.
|
||||
and removes the last group containing the permission to manage users.
|
||||
"""
|
||||
# TODO: Check this in clean_groups
|
||||
if (self.request.user == self.instance and
|
||||
not self.instance.is_superuser and
|
||||
not self.cleaned_data['groups'].filter(permissions__in=[get_protected_perm()]).exists()):
|
||||
error_msg = _('You can not remove the last group containing the permission to manage participants.')
|
||||
error_msg = _('You can not remove the last group containing the permission to manage users.')
|
||||
raise forms.ValidationError(error_msg)
|
||||
return super(UserUpdateForm, self).clean(*args, **kwargs)
|
||||
return super().clean(*args, **kwargs)
|
||||
|
||||
|
||||
class GroupForm(forms.ModelForm, CssClassMixin):
|
||||
@ -85,7 +79,7 @@ class GroupForm(forms.ModelForm, CssClassMixin):
|
||||
queryset=Permission.objects.all(), label=ugettext_lazy('Permissions'),
|
||||
required=False)
|
||||
users = forms.ModelMultipleChoiceField(
|
||||
queryset=User.objects.all(), label=ugettext_lazy('Participants'), required=False)
|
||||
queryset=User.objects.all(), label=ugettext_lazy('Users'), required=False)
|
||||
|
||||
class Meta:
|
||||
model = Group
|
||||
@ -96,10 +90,10 @@ class GroupForm(forms.ModelForm, CssClassMixin):
|
||||
# Initial users
|
||||
if kwargs.get('instance', None) is not None:
|
||||
initial = kwargs.setdefault('initial', {})
|
||||
initial['users'] = [django_user.user.pk for django_user in kwargs['instance'].user_set.all()]
|
||||
initial['users'] = kwargs['instance'].user_set.all()
|
||||
|
||||
super(GroupForm, self).__init__(*args, **kwargs)
|
||||
if config['participant_sort_users_by_first_name']:
|
||||
super().__init__(*args, **kwargs)
|
||||
if config['users_sort_users_by_first_name']:
|
||||
self.fields['users'].queryset = self.fields['users'].queryset.order_by('first_name')
|
||||
|
||||
def save(self, commit=True):
|
||||
@ -124,10 +118,10 @@ class GroupForm(forms.ModelForm, CssClassMixin):
|
||||
def clean(self, *args, **kwargs):
|
||||
"""
|
||||
Raises a validation error if a non-superuser user removes himself
|
||||
from the last group containing the permission to manage participants.
|
||||
from the last group containing the permission to manage users.
|
||||
|
||||
Raises also a validation error if a non-superuser removes his last
|
||||
permission to manage participants from the (last) group.
|
||||
permission to manage users from the (last) group.
|
||||
"""
|
||||
# TODO: Check this in clean_users or clean_permissions
|
||||
if (self.request and
|
||||
@ -136,37 +130,24 @@ class GroupForm(forms.ModelForm, CssClassMixin):
|
||||
not Group.objects.exclude(pk=self.instance.pk).filter(
|
||||
permissions__in=[get_protected_perm()],
|
||||
user__pk=self.request.user.pk).exists()):
|
||||
error_msg = _('You can not remove yourself from the last group containing the permission to manage participants.')
|
||||
error_msg = _('You can not remove yourself from the last group containing the permission to manage users.')
|
||||
raise forms.ValidationError(error_msg)
|
||||
|
||||
if (self.request and
|
||||
not self.request.user.is_superuser and
|
||||
not get_protected_perm() in self.cleaned_data['permissions'] and
|
||||
not Group.objects.exclude(pk=self.instance.pk).filter(
|
||||
permissions__in=[get_protected_perm()],
|
||||
user__pk=self.request.user.pk).exists()):
|
||||
error_msg = _('You can not remove the permission to manage participants from the last group you are in.')
|
||||
error_msg = _('You can not remove the permission to manage users from the last group you are in.')
|
||||
raise forms.ValidationError(error_msg)
|
||||
return super(GroupForm, self).clean(*args, **kwargs)
|
||||
|
||||
|
||||
class UsersettingsForm(CssClassMixin, forms.ModelForm):
|
||||
user_name = forms.CharField(label=ugettext_lazy('Username'))
|
||||
"""
|
||||
Field to save the username.
|
||||
|
||||
The field username (without the underscore) from the UserModel does not
|
||||
allow whitespaces and umlauts.
|
||||
"""
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('username', 'title', 'first_name', 'last_name', 'about_me')
|
||||
|
||||
language = forms.ChoiceField(
|
||||
choices=settings.LANGUAGES, label=ugettext_lazy('Language'))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['initial'] = kwargs.get('initial', {})
|
||||
kwargs['initial']['user_name'] = kwargs['instance'].username
|
||||
return super(UsersettingsForm, self).__init__(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('user_name', 'title', 'first_name', 'last_name', 'gender', 'email',
|
||||
'committee', 'about_me')
|
@ -3,12 +3,12 @@ from django.utils.translation import ugettext_lazy
|
||||
from openslides.utils.main_menu import MainMenuEntry
|
||||
|
||||
|
||||
class ParticipantMainMenuEntry(MainMenuEntry):
|
||||
class UserMainMenuEntry(MainMenuEntry):
|
||||
"""
|
||||
Main menu entry for the participant app.
|
||||
"""
|
||||
verbose_name = ugettext_lazy('Participants')
|
||||
required_permission = 'participant.can_see_participant'
|
||||
verbose_name = ugettext_lazy('Users')
|
||||
required_permission = 'users.can_see'
|
||||
default_weight = 50
|
||||
pattern_name = 'user_overview'
|
||||
pattern_name = 'user_list'
|
||||
icon_css_class = 'icon-user'
|
136
openslides/users/models.py
Normal file
136
openslides/users/models.py
Normal file
@ -0,0 +1,136 @@
|
||||
# TODO: Check every app, that they do not import Group or User from here.
|
||||
|
||||
from django.contrib.auth.models import (PermissionsMixin, AbstractBaseUser,
|
||||
BaseUserManager)
|
||||
|
||||
# TODO: Do not import the Group in here, but in core.models (if necessary)
|
||||
from django.contrib.auth.models import Group # noqa
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
|
||||
from openslides.projector.models import SlideMixin
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
|
||||
|
||||
class UserManager(BaseUserManager):
|
||||
def create_user(self, username, password, **kwargs):
|
||||
user = self.model(username=username, **kwargs)
|
||||
user.set_password(password)
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
|
||||
class User(SlideMixin, AbsoluteUrlMixin, PermissionsMixin, AbstractBaseUser):
|
||||
USERNAME_FIELD = 'username'
|
||||
slide_callback_name = 'user'
|
||||
|
||||
username = models.CharField(
|
||||
ugettext_lazy('Username'), max_length=255, unique=True)
|
||||
|
||||
first_name = models.CharField(
|
||||
ugettext_lazy('First name'), max_length=255, blank=True)
|
||||
|
||||
last_name = models.CharField(
|
||||
ugettext_lazy('Last name'), max_length=255, blank=True)
|
||||
|
||||
# TODO: try to remove the default argument in the following fields
|
||||
structure_level = models.CharField(
|
||||
max_length=255, blank=True, default='',
|
||||
verbose_name=ugettext_lazy('Structure level'),
|
||||
help_text=ugettext_lazy('Will be shown after the name.'))
|
||||
|
||||
title = models.CharField(
|
||||
max_length=50, blank=True, default='',
|
||||
verbose_name=ugettext_lazy('Title'),
|
||||
help_text=ugettext_lazy('Will be shown before the name.'))
|
||||
|
||||
about_me = models.TextField(
|
||||
blank=True, default='', verbose_name=ugettext_lazy('About me'),
|
||||
help_text=ugettext_lazy('Your profile text'))
|
||||
|
||||
comment = models.TextField(
|
||||
blank=True, default='', verbose_name=ugettext_lazy('Comment'),
|
||||
help_text=ugettext_lazy('Only for notes.'))
|
||||
|
||||
default_password = models.CharField(
|
||||
max_length=100, blank=True, default='',
|
||||
verbose_name=ugettext_lazy('Default password'))
|
||||
|
||||
is_active = models.BooleanField(
|
||||
ugettext_lazy('active'), default=True,
|
||||
help_text=ugettext_lazy(
|
||||
'Designates whether this user should be treated as '
|
||||
'active. Unselect this instead of deleting accounts.'))
|
||||
|
||||
is_present = models.BooleanField(
|
||||
ugettext_lazy('present'), default=False,
|
||||
help_text=ugettext_lazy('Designates whether this user is in the room '
|
||||
'or not.'))
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('can_see', ugettext_noop('Can see users')),
|
||||
('can_manage', ugettext_noop('Can manage users')),
|
||||
)
|
||||
ordering = ('last_name',)
|
||||
|
||||
def __str__(self):
|
||||
return self.get_full_name()
|
||||
|
||||
def get_absolute_url(self, link='detail'):
|
||||
"""
|
||||
Returns the URL to the user.
|
||||
"""
|
||||
if link == 'detail':
|
||||
url = reverse('user_view', args=[str(self.pk)])
|
||||
elif link == 'update':
|
||||
url = reverse('user_edit', args=[str(self.pk)])
|
||||
elif link == 'delete':
|
||||
url = reverse('user_delete', args=[str(self.pk)])
|
||||
else:
|
||||
url = super().get_absolute_url(link)
|
||||
return url
|
||||
|
||||
def get_slide_context(self, **context):
|
||||
"""
|
||||
Returns the context for the user slide.
|
||||
"""
|
||||
# Does not call super. In this case the context would override the name
|
||||
# 'user'.
|
||||
return {'shown_user': self}
|
||||
|
||||
def get_full_name(self):
|
||||
"""
|
||||
Returns a long form of the name.
|
||||
|
||||
E. g.: * Dr. Max Mustermann (Villingen)
|
||||
* Professor Dr. Enders, Christoph (Leipzig)
|
||||
"""
|
||||
if self.structure_level:
|
||||
structure = '(%s)' % self.structure_level
|
||||
else:
|
||||
structure = ''
|
||||
|
||||
return ' '.join((self.title, self.get_short_name(), structure)).strip()
|
||||
|
||||
def get_short_name(self):
|
||||
"""
|
||||
Returns only the name of the user.
|
||||
|
||||
E. g.: * Max Mustermann
|
||||
* Enders, Christoph
|
||||
"""
|
||||
# TODO: Order of name. See config.
|
||||
name = ('%s %s' % (self.first_name, self.last_name)).strip()
|
||||
return name or self.username
|
||||
|
||||
def reset_password(self, password=None):
|
||||
"""
|
||||
Reset the password for the user to his default-password.
|
||||
"""
|
||||
if password is None:
|
||||
password = self.default_password
|
||||
self.set_password(password)
|
@ -12,13 +12,13 @@ from openslides.utils.pdf import stylesheet
|
||||
from .models import User
|
||||
|
||||
|
||||
def participants_to_pdf(pdf):
|
||||
def users_to_pdf(pdf):
|
||||
"""
|
||||
Create a list of all participants as PDF
|
||||
Create a list of all users as PDF
|
||||
"""
|
||||
data = [['#', _('Title'), _('Last Name'), _('First Name'),
|
||||
_('Structure level'), _('Group')]]
|
||||
if config['participant_sort_users_by_first_name']:
|
||||
if config['users_sort_users_by_first_name']:
|
||||
sort = 'first_name'
|
||||
else:
|
||||
sort = 'last_name'
|
||||
@ -48,30 +48,30 @@ def participants_to_pdf(pdf):
|
||||
return pdf
|
||||
|
||||
|
||||
def participants_passwords_to_pdf(pdf):
|
||||
def users_passwords_to_pdf(pdf):
|
||||
"""
|
||||
Create access data sheets for all participants as PDF
|
||||
Create access data sheets for all users as PDF
|
||||
"""
|
||||
participant_pdf_wlan_ssid = config["participant_pdf_wlan_ssid"] or "-"
|
||||
participant_pdf_wlan_password = config["participant_pdf_wlan_password"] or "-"
|
||||
participant_pdf_wlan_encryption = config["participant_pdf_wlan_encryption"] or "-"
|
||||
participant_pdf_url = config["participant_pdf_url"] or "-"
|
||||
participant_pdf_welcometitle = config["participant_pdf_welcometitle"]
|
||||
participant_pdf_welcometext = config["participant_pdf_welcometext"]
|
||||
if config['participant_sort_users_by_first_name']:
|
||||
users_pdf_wlan_ssid = config["users_pdf_wlan_ssid"] or "-"
|
||||
users_pdf_wlan_password = config["users_pdf_wlan_password"] or "-"
|
||||
users_pdf_wlan_encryption = config["users_pdf_wlan_encryption"] or "-"
|
||||
users_pdf_url = config["users_pdf_url"] or "-"
|
||||
users_pdf_welcometitle = config["users_pdf_welcometitle"]
|
||||
users_pdf_welcometext = config["users_pdf_welcometext"]
|
||||
if config['users_sort_users_by_first_name']:
|
||||
sort = 'first_name'
|
||||
else:
|
||||
sort = 'last_name'
|
||||
qrcode_size = 2 * cm
|
||||
# qrcode for system url
|
||||
qrcode_url = QrCodeWidget(participant_pdf_url)
|
||||
qrcode_url = QrCodeWidget(users_pdf_url)
|
||||
qrcode_url.barHeight = qrcode_size
|
||||
qrcode_url.barWidth = qrcode_size
|
||||
qrcode_url.barBorder = 0
|
||||
qrcode_url_draw = Drawing(45, 45)
|
||||
qrcode_url_draw.add(qrcode_url)
|
||||
# qrcode for wlan
|
||||
text = "WIFI:S:%s;T:%s;P:%s;;" % (participant_pdf_wlan_ssid, participant_pdf_wlan_encryption, participant_pdf_wlan_password)
|
||||
text = "WIFI:S:%s;T:%s;P:%s;;" % (users_pdf_wlan_ssid, users_pdf_wlan_encryption, users_pdf_wlan_password)
|
||||
qrcode_wlan = QrCodeWidget(text)
|
||||
qrcode_wlan.barHeight = qrcode_size
|
||||
qrcode_wlan.barWidth = qrcode_size
|
||||
@ -89,15 +89,15 @@ def participants_passwords_to_pdf(pdf):
|
||||
stylesheet['h2']))
|
||||
cell.append(Paragraph("%s:" % _("WLAN name (SSID)"),
|
||||
stylesheet['formfield']))
|
||||
cell.append(Paragraph(participant_pdf_wlan_ssid,
|
||||
cell.append(Paragraph(users_pdf_wlan_ssid,
|
||||
stylesheet['formfield_value']))
|
||||
cell.append(Paragraph("%s:" % _("WLAN password"),
|
||||
stylesheet['formfield']))
|
||||
cell.append(Paragraph(participant_pdf_wlan_password,
|
||||
cell.append(Paragraph(users_pdf_wlan_password,
|
||||
stylesheet['formfield_value']))
|
||||
cell.append(Paragraph("%s:" % _("WLAN encryption"),
|
||||
stylesheet['formfield']))
|
||||
cell.append(Paragraph(participant_pdf_wlan_encryption,
|
||||
cell.append(Paragraph(users_pdf_wlan_encryption,
|
||||
stylesheet['formfield_value']))
|
||||
cell.append(Spacer(0, 0.5 * cm))
|
||||
# OpenSlides access data
|
||||
@ -114,17 +114,17 @@ def participants_passwords_to_pdf(pdf):
|
||||
stylesheet['formfield_value']))
|
||||
cell2.append(Paragraph("URL:",
|
||||
stylesheet['formfield']))
|
||||
cell2.append(Paragraph(participant_pdf_url,
|
||||
cell2.append(Paragraph(users_pdf_url,
|
||||
stylesheet['formfield_value']))
|
||||
data.append([cell, cell2])
|
||||
# QRCodes
|
||||
cell = []
|
||||
if participant_pdf_wlan_ssid != "-" and participant_pdf_wlan_encryption != "-":
|
||||
if users_pdf_wlan_ssid != "-" and users_pdf_wlan_encryption != "-":
|
||||
cell.append(qrcode_wlan_draw)
|
||||
cell.append(Paragraph(_("Scan this QRCode to connect WLAN."),
|
||||
stylesheet['qrcode_comment']))
|
||||
cell2 = []
|
||||
if participant_pdf_url != "-":
|
||||
if users_pdf_url != "-":
|
||||
cell2.append(qrcode_url_draw)
|
||||
cell2.append(Paragraph(_("Scan this QRCode to open URL."),
|
||||
stylesheet['qrcode_comment']))
|
||||
@ -138,8 +138,8 @@ def participants_passwords_to_pdf(pdf):
|
||||
pdf.append(Spacer(0, 2 * cm))
|
||||
|
||||
# welcome title and text
|
||||
pdf.append(Paragraph(participant_pdf_welcometitle, stylesheet['h2']))
|
||||
pdf.append(Paragraph(participant_pdf_welcometext.replace('\r\n', '<br/>'),
|
||||
pdf.append(Paragraph(users_pdf_welcometitle, stylesheet['h2']))
|
||||
pdf.append(Paragraph(users_pdf_welcometext.replace('\r\n', '<br/>'),
|
||||
stylesheet['Paragraph12']))
|
||||
pdf.append(PageBreak())
|
||||
return pdf
|
@ -5,8 +5,8 @@ from .models import User
|
||||
class Index(indexes.SearchIndex, indexes.Indexable):
|
||||
text = indexes.EdgeNgramField(document=True, use_template=True)
|
||||
text = indexes.EdgeNgramField(document=True, use_template=True)
|
||||
modelfilter_name = "Participants" # verbose_name of model
|
||||
modelfilter_value = "participant.user" # 'app_name.model_name'
|
||||
modelfilter_name = "Users" # verbose_name of model
|
||||
modelfilter_value = "users.user" # 'app_name.model_name'
|
||||
|
||||
def get_model(self):
|
||||
return User
|
@ -4,36 +4,37 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
from django.db.models import signals
|
||||
|
||||
from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable
|
||||
from openslides.config.signals import config_signal
|
||||
from openslides.core.signals import post_database_setup
|
||||
|
||||
from .api import create_or_reset_admin_user
|
||||
from .models import Group
|
||||
from .models import Group, User
|
||||
|
||||
|
||||
@receiver(config_signal, dispatch_uid='setup_participant_config')
|
||||
def setup_participant_config(sender, **kwargs):
|
||||
@receiver(config_signal, dispatch_uid='setup_users_config')
|
||||
def setup_users_config(sender, **kwargs):
|
||||
"""
|
||||
Participant config variables.
|
||||
"""
|
||||
# General
|
||||
participant_sort_users_by_first_name = ConfigVariable(
|
||||
name='participant_sort_users_by_first_name',
|
||||
users_sort_users_by_first_name = ConfigVariable(
|
||||
name='users_sort_users_by_first_name',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
required=False,
|
||||
label=ugettext_lazy('Sort participants by first name'),
|
||||
label=ugettext_lazy('Sort users by first name'),
|
||||
help_text=ugettext_lazy('Disable for sorting by last name')))
|
||||
|
||||
group_general = ConfigGroup(
|
||||
title=ugettext_lazy('Sorting'),
|
||||
variables=(participant_sort_users_by_first_name,))
|
||||
variables=(users_sort_users_by_first_name,))
|
||||
|
||||
# PDF
|
||||
participant_pdf_welcometitle = ConfigVariable(
|
||||
name='participant_pdf_welcometitle',
|
||||
users_pdf_welcometitle = ConfigVariable(
|
||||
name='users_pdf_welcometitle',
|
||||
default_value=_('Welcome to OpenSlides!'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
@ -41,8 +42,8 @@ def setup_participant_config(sender, **kwargs):
|
||||
required=False,
|
||||
label=ugettext_lazy('Title for access data and welcome PDF')))
|
||||
|
||||
participant_pdf_welcometext = ConfigVariable(
|
||||
name='participant_pdf_welcometext',
|
||||
users_pdf_welcometext = ConfigVariable(
|
||||
name='users_pdf_welcometext',
|
||||
default_value=_('[Place for your welcome and help text.]'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
@ -50,8 +51,8 @@ def setup_participant_config(sender, **kwargs):
|
||||
required=False,
|
||||
label=ugettext_lazy('Help text for access data and welcome PDF')))
|
||||
|
||||
participant_pdf_url = ConfigVariable(
|
||||
name='participant_pdf_url',
|
||||
users_pdf_url = ConfigVariable(
|
||||
name='users_pdf_url',
|
||||
default_value='http://example.com:8000',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
@ -59,8 +60,8 @@ def setup_participant_config(sender, **kwargs):
|
||||
label=ugettext_lazy('System URL'),
|
||||
help_text=ugettext_lazy('Used for QRCode in PDF of access data.')))
|
||||
|
||||
participant_pdf_wlan_ssid = ConfigVariable(
|
||||
name='participant_pdf_wlan_ssid',
|
||||
users_pdf_wlan_ssid = ConfigVariable(
|
||||
name='users_pdf_wlan_ssid',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
@ -68,8 +69,8 @@ def setup_participant_config(sender, **kwargs):
|
||||
label=ugettext_lazy('WLAN name (SSID)'),
|
||||
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.')))
|
||||
|
||||
participant_pdf_wlan_password = ConfigVariable(
|
||||
name='participant_pdf_wlan_password',
|
||||
users_pdf_wlan_password = ConfigVariable(
|
||||
name='users_pdf_wlan_password',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
@ -77,8 +78,8 @@ def setup_participant_config(sender, **kwargs):
|
||||
label=ugettext_lazy('WLAN password'),
|
||||
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.')))
|
||||
|
||||
participant_pdf_wlan_encryption = ConfigVariable(
|
||||
name='participant_pdf_wlan_encryption',
|
||||
users_pdf_wlan_encryption = ConfigVariable(
|
||||
name='users_pdf_wlan_encryption',
|
||||
default_value='',
|
||||
form_field=forms.ChoiceField(
|
||||
widget=forms.Select(),
|
||||
@ -93,21 +94,21 @@ def setup_participant_config(sender, **kwargs):
|
||||
|
||||
group_pdf = ConfigGroup(
|
||||
title=ugettext_lazy('PDF'),
|
||||
variables=(participant_pdf_welcometitle,
|
||||
participant_pdf_welcometext,
|
||||
participant_pdf_url,
|
||||
participant_pdf_wlan_ssid,
|
||||
participant_pdf_wlan_password,
|
||||
participant_pdf_wlan_encryption))
|
||||
variables=(users_pdf_welcometitle,
|
||||
users_pdf_welcometext,
|
||||
users_pdf_url,
|
||||
users_pdf_wlan_ssid,
|
||||
users_pdf_wlan_password,
|
||||
users_pdf_wlan_encryption))
|
||||
|
||||
return ConfigGroupedCollection(
|
||||
title=ugettext_noop('Participant'),
|
||||
url='participant',
|
||||
title=ugettext_noop('Users'),
|
||||
url='users',
|
||||
weight=50,
|
||||
groups=(group_general, group_pdf))
|
||||
|
||||
|
||||
@receiver(post_database_setup, dispatch_uid='participant_create_builtin_groups_and_admin')
|
||||
@receiver(post_database_setup, dispatch_uid='users_create_builtin_groups_and_admin')
|
||||
def create_builtin_groups_and_admin(sender, **kwargs):
|
||||
"""
|
||||
Creates the buildin groups and the admin user.
|
||||
@ -138,8 +139,8 @@ def create_builtin_groups_and_admin(sender, **kwargs):
|
||||
ct_assignment = ContentType.objects.get(app_label='assignment', model='assignment')
|
||||
perm_16 = Permission.objects.get(content_type=ct_assignment, codename='can_see_assignment')
|
||||
|
||||
ct_participant = ContentType.objects.get(app_label='participant', model='user')
|
||||
perm_17 = Permission.objects.get(content_type=ct_participant, codename='can_see_participant')
|
||||
ct_users = ContentType.objects.get(app_label='users', model='user')
|
||||
perm_17 = Permission.objects.get(content_type=ct_users, codename='can_see')
|
||||
|
||||
ct_mediafile = ContentType.objects.get(app_label='mediafile', model='mediafile')
|
||||
perm_18 = Permission.objects.get(content_type=ct_mediafile, codename='can_see')
|
||||
@ -163,7 +164,7 @@ def create_builtin_groups_and_admin(sender, **kwargs):
|
||||
perm_41 = Permission.objects.get(content_type=ct_agenda, codename='can_manage_agenda')
|
||||
perm_42 = Permission.objects.get(content_type=ct_motion, codename='can_manage_motion')
|
||||
perm_43 = Permission.objects.get(content_type=ct_assignment, codename='can_manage_assignment')
|
||||
perm_44 = Permission.objects.get(content_type=ct_participant, codename='can_manage_participant')
|
||||
perm_44 = Permission.objects.get(content_type=ct_users, codename='can_manage')
|
||||
perm_45 = Permission.objects.get(content_type=ct_core, codename='can_manage_projector')
|
||||
perm_46 = Permission.objects.get(content_type=ct_core, codename='can_use_chat')
|
||||
perm_47 = Permission.objects.get(content_type=ct_mediafile, codename='can_manage')
|
||||
@ -176,8 +177,19 @@ def create_builtin_groups_and_admin(sender, **kwargs):
|
||||
group_staff.permissions.add(perm_31, perm_33, perm_34, perm_35)
|
||||
# add staff permissions
|
||||
group_staff.permissions.add(perm_41, perm_42, perm_43, perm_44, perm_45, perm_46, perm_47, perm_48)
|
||||
# add can_see_participant permission
|
||||
# add can_see_user permission
|
||||
group_staff.permissions.add(perm_17) # TODO: Remove this redundancy after cleanup of the permission system
|
||||
|
||||
# Admin user
|
||||
create_or_reset_admin_user()
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=User)
|
||||
def user_post_save(sender, instance, *args, **kwargs):
|
||||
if not kwargs['created']:
|
||||
return
|
||||
|
||||
from openslides.users.api import get_registered_group # TODO: Test, if global import is possible
|
||||
registered = get_registered_group()
|
||||
instance.groups.add(registered)
|
||||
instance.save()
|
@ -1,6 +1,5 @@
|
||||
from openslides.projector.api import register_slide_model
|
||||
|
||||
from .models import Group, User
|
||||
from .models import User
|
||||
|
||||
register_slide_model(User, 'participant/user_slide.html')
|
||||
register_slide_model(Group, 'participant/group_slide.html')
|
@ -1,10 +1,10 @@
|
||||
{% load i18n %}
|
||||
{% load highlight %}
|
||||
|
||||
{% if perms.participant.can_see_participant %}
|
||||
{% if perms.users.can_see %}
|
||||
<li>
|
||||
<a href="{{ result.object.get_absolute_url }}">{{ result.object }}</a><br>
|
||||
<span class="app">{% trans "Participant" %}</a></span><br>
|
||||
<span class="app">{% trans "User" %}</a></span><br>
|
||||
{% highlight result.text with request.GET.q %}
|
||||
</li>
|
||||
{% endif %}
|
55
openslides/users/templates/users/group_detail.html
Normal file
55
openslides/users/templates/users/group_detail.html
Normal file
@ -0,0 +1,55 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load tags %}
|
||||
|
||||
{% block title %}{% trans group.name %} – {{ block.super }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{% trans group.name %}
|
||||
<small class="pull-right">
|
||||
<a href="{% url 'group_list' %}" class="btn btn-mini">
|
||||
<i class="icon-chevron-left"></i>
|
||||
<span class="optional-small">{% trans "Back to overview" %}</span>
|
||||
</a>
|
||||
{% if perms.users.can_manage and group.pk != 1 and group.pk != 2 %}
|
||||
<div class="btn-group">
|
||||
<a data-toggle="dropdown" class="btn btn-mini dropdown-toggle">
|
||||
<span class="optional-small">{% trans 'More actions' %} </span>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<!-- edit -->
|
||||
<li>
|
||||
<a href="{% url 'group_update' group.id %}">
|
||||
<i class="icon-pencil"></i>
|
||||
{% trans 'Edit group' %}
|
||||
</a>
|
||||
</li>
|
||||
<!-- delete -->
|
||||
<li>
|
||||
<a href="{% url 'group_delete' group.id %}">
|
||||
<i class="icon-remove"></i>
|
||||
{% trans 'Delete group' %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
<p>{{ group.description }}</p>
|
||||
|
||||
<h4>{% trans "Members" %}</h4>
|
||||
|
||||
<ol>
|
||||
{% for member in group_members %}
|
||||
<li><a href="{{ member|absolute_url }}">{{ member }}</a></li>
|
||||
{% empty %}
|
||||
<p>{% trans "No members available." %}</p>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
|
||||
{% endblock %}
|
@ -19,7 +19,10 @@
|
||||
{% trans "New group" %}
|
||||
{% endif %}
|
||||
<small class="pull-right">
|
||||
<a href="{% url 'user_group_overview' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to overview" %}</a>
|
||||
<a href="{% url 'group_list' %}" class="btn btn-mini">
|
||||
<i class="icon-chevron-left"></i>
|
||||
{% trans "Back to overview" %}
|
||||
</a>
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
@ -27,7 +30,7 @@
|
||||
{% include "form.html" %}
|
||||
<p>
|
||||
{% include "formbuttons_saveapply.html" %}
|
||||
<a href="{% url 'user_group_overview' %}" class="btn">
|
||||
<a href="{% url 'group_list' %}" class="btn">
|
||||
{% trans 'Cancel' %}
|
||||
</a>
|
||||
</p>
|
@ -19,8 +19,16 @@
|
||||
<h1>
|
||||
{% trans "Groups" %}
|
||||
<small class="pull-right">
|
||||
<a href="{% url 'user_group_new' %}" class="btn btn-mini btn-primary" rel="tooltip" data-original-title="{% trans 'New group' %}"><i class="icon-plus icon-white"></i> {% trans "New" %}</a>
|
||||
<a href="{% url 'user_overview' %}" class="btn btn-mini"><i class="icon-chevron-left"></i><span class="optional-small"> {% trans "Back to participants overview" %}</span></a>
|
||||
<a href="{% url 'group_create' %}" class="btn btn-mini btn-primary" rel="tooltip" data-original-title="{% trans 'New group' %}">
|
||||
<i class="icon-plus icon-white"></i>
|
||||
{% trans "New" %}
|
||||
</a>
|
||||
<a href="{% url 'user_list' %}" class="btn btn-mini">
|
||||
<i class="icon-chevron-left"></i>
|
||||
<span class="optional-small">
|
||||
{% trans "Back to users overview" %}
|
||||
</span>
|
||||
</a>
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
@ -37,29 +45,22 @@
|
||||
<tr class="{% if group.is_active_slide %}activeline{% endif %}">
|
||||
<td class="nobr">{{ group.pk }}
|
||||
{% if group.pk == 1 or group.pk == 2 %}
|
||||
<a title="{% blocktrans %}The groups 1 ('Anonymous') and 2 ('Registered') are fixed default groups which can not be deleted. Each created or imported participant is a member of group 2. Use custom groups to set additional permissions for a subset of participants.{% endblocktrans %}"><i class="icon-info-sign"></i></a>
|
||||
<a title="{% blocktrans %}The groups 1 ('Anonymous') and 2 ('Registered') are fixed default groups which can not be deleted. Each created or imported user is a member of group 2. Use custom groups to set additional permissions for a subset of users.{% endblocktrans %}"><i class="icon-info-sign"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ group|absolute_url }}">{% trans group.name %}</a>
|
||||
<a href="{% url 'group_detail' group.pk %}">{% trans group.name %}</a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-info">{{ group.user_set.all.count }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span style="width: 1px; white-space: nowrap;">
|
||||
{% if perms.core.can_manage_projector %}
|
||||
<a href="{{ group|absolute_url:'projector' }}"
|
||||
class="activate_link btn {% if group.is_active_slide %}btn-primary{% endif %} btn-mini"
|
||||
rel="tooltip" data-original-title="{% trans 'Show group' %}">
|
||||
<i class="icon-facetime-video {% if group.is_active_slide %}icon-white{% endif %}"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{% url 'user_group_edit' group.id %}" title="{% trans 'Edit' %}" class="btn btn-mini">
|
||||
<a href="{% url 'group_update' group.id %}" title="{% trans 'Edit' %}" class="btn btn-mini">
|
||||
<i class="icon-pencil"></i>
|
||||
</a>
|
||||
{% if group.pk != 1 and group.pk != 2 %}
|
||||
<a href="{% url 'user_group_delete' group.id %}" title="{% trans 'Delete' %}" class="btn btn-mini">
|
||||
<a href="{% url 'group_delete' group.id %}" title="{% trans 'Delete' %}" class="btn btn-mini">
|
||||
<i class="icon-remove"></i>
|
||||
</a>
|
||||
{% endif %}
|
@ -9,23 +9,33 @@
|
||||
<h1>
|
||||
{{ shown_user.clean_name }}
|
||||
<small class="pull-right">
|
||||
<a href="{% url 'user_overview' %}" class="btn btn-mini"><i class="icon-chevron-left"></i><span class="optional-small"> {% trans "Back to overview" %}</span></a>
|
||||
<a href="{% url 'user_list' %}" class="btn btn-mini">
|
||||
<i class="icon-chevron-left"></i>
|
||||
<span class="optional-small">
|
||||
{% trans "Back to overview" %}
|
||||
</span>
|
||||
</a>
|
||||
<!-- activate projector -->
|
||||
{% if perms.core.can_manage_projector %}
|
||||
<a href="{% url 'projector_activate_slide' shown_user.sid %}" class="activate_link btn {% if shown_user.active %}btn-primary{% endif %} btn-mini" rel="tooltip" data-original-title="{% trans 'Show participant' %}">
|
||||
<a href="{% url 'projector_activate_slide' shown_user.sid %}" class="activate_link btn {% if shown_user.active %}btn-primary{% endif %} btn-mini" rel="tooltip" data-original-title="{% trans 'Show users' %}">
|
||||
<i class="icon-facetime-video {% if shown_user.active %}icon-white{% endif %}"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.participant.can_manage_participant %}
|
||||
{% if perms.users.can_manage %}
|
||||
<div class="btn-group">
|
||||
<a data-toggle="dropdown" class="btn btn-mini dropdown-toggle">
|
||||
<span class="optional-small">{% trans 'More actions' %} </span><span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<!-- edit -->
|
||||
<li><a href="{% url 'user_edit' shown_user.id %}"><i class="icon-pencil"></i> {% trans 'Edit participant' %}</a></li>
|
||||
<li>
|
||||
<a href="{% url 'user_edit' shown_user.id %}">
|
||||
<i class="icon-pencil"></i>
|
||||
{% trans 'Edit user' %}
|
||||
</a>
|
||||
</li>
|
||||
<!-- delete -->
|
||||
<li><a href="{% url 'user_delete' shown_user.id %}"><i class="icon-remove"></i> {% trans 'Delete participant' %}</a></li>
|
||||
<li><a href="{% url 'user_delete' shown_user.id %}"><i class="icon-remove"></i> {% trans 'Delete user' %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -59,11 +69,11 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% trans "The participant is not member of any group." %}
|
||||
{% trans "The user is not member of any group." %}
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
|
||||
{% if perms.participant.can_manage_participant %}
|
||||
{% if perms.users.can_manage %}
|
||||
<fieldset>
|
||||
<legend>{% trans "Administrative data" %}</legend>
|
||||
<label>{% trans "Username" %}</label>
|
||||
@ -74,7 +84,7 @@
|
||||
{% if shown_user.last_login > shown_user.date_joined %}
|
||||
{{ shown_user.last_login }}
|
||||
{% else %}
|
||||
{% trans "The participant has not logged in yet." %}
|
||||
{% trans "The user has not logged in yet." %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
@ -24,9 +24,9 @@
|
||||
|
||||
{% block title %}
|
||||
{% if edit_user %}
|
||||
{% trans "Edit participant" %}
|
||||
{% trans "Edit user" %}
|
||||
{% else %}
|
||||
{% trans "New participant" %}
|
||||
{% trans "New user" %}
|
||||
{% endif %}
|
||||
– {{ block.super }}
|
||||
{% endblock %}
|
||||
@ -34,12 +34,15 @@
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% if edit_user %}
|
||||
{% trans "Edit participant" %}
|
||||
{% trans "Edit user" %}
|
||||
{% else %}
|
||||
{% trans "New participant" %}
|
||||
{% trans "New user" %}
|
||||
{% endif %}
|
||||
<small class="pull-right">
|
||||
<a href="{% url 'user_overview' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to overview" %}</a>
|
||||
<a href="{% url 'user_list' %}" class="btn btn-mini">
|
||||
<i class="icon-chevron-left"></i>
|
||||
{% trans "Back to overview" %}
|
||||
</a>
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
@ -47,12 +50,15 @@
|
||||
{% include "form.html" %}
|
||||
{% if edit_user %}
|
||||
<p style="margin: -15px 0 25px 0;">
|
||||
<a class="btn btn-mini" href="{% url 'user_reset_password' edit_user.id %}"><i class="icon-exclamation-sign"></i> {% trans 'Reset to First Password' %}</a>
|
||||
<a class="btn btn-mini" href="{% url 'user_reset_password' edit_user.id %}">
|
||||
<i class="icon-exclamation-sign"></i>
|
||||
{% trans 'Reset to First Password' %}
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
{% include "formbuttons_saveapply.html" %}
|
||||
<a href="{% url 'user_overview' %}" class="btn">
|
||||
<a href="{% url 'user_list' %}" class="btn">
|
||||
{% trans 'Cancel' %}
|
||||
</a>
|
||||
</p>
|
@ -2,17 +2,20 @@
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Import participants" %} – {{ block.super }}{% endblock %}
|
||||
{% block title %}{% trans "Import users" %} – {{ block.super }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% trans 'Import participants' %}
|
||||
{% trans 'Import users' %}
|
||||
<small class="pull-right">
|
||||
<a href="{% url 'user_overview' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to overview" %}</a>
|
||||
<a href="{% url 'user_list' %}" class="btn btn-mini">
|
||||
<i class="icon-chevron-left"></i>
|
||||
{% trans "Back to overview" %}
|
||||
</a>
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
<p>{% trans 'Select a CSV file to import participants' %}.</p>
|
||||
<p>{% trans 'Select a CSV file to import users' %}.</p>
|
||||
|
||||
<p>{% trans 'Please note' %}:</p>
|
||||
<ul>
|
||||
@ -40,7 +43,7 @@
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<span class="icon import">{% trans 'Import' %}</span>
|
||||
</button>
|
||||
<a href="{% url 'user_overview' %}" class="btn">
|
||||
<a href="{% url 'user_list' %}" class="btn">
|
||||
{% trans 'Cancel' %}
|
||||
</a>
|
||||
</p>
|
@ -3,14 +3,14 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}
|
||||
{% trans 'New multiple participants' %} – {{ block.super }}
|
||||
{% trans 'New multiple users' %} – {{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% trans 'New multiple participants' %}
|
||||
{% trans 'New multiple users' %}
|
||||
<small class="pull-right">
|
||||
<a href="{% url 'user_overview' %}" class="btn btn-mini">
|
||||
<a href="{% url 'user_list' %}" class="btn btn-mini">
|
||||
<i class="icon-chevron-left"></i> {% trans 'Back to overview' %}
|
||||
</a>
|
||||
</small>
|
||||
@ -19,7 +19,7 @@
|
||||
{% include 'form.html' %}
|
||||
<p>
|
||||
{% include 'formbuttons_save.html' %}
|
||||
<a href="{% url 'user_overview' %}" class="btn">
|
||||
<a href="{% url 'user_list' %}" class="btn">
|
||||
{% trans 'Cancel' %}
|
||||
</a>
|
||||
</p>
|
@ -4,7 +4,7 @@
|
||||
{% load staticfiles %}
|
||||
{% load tags %}
|
||||
|
||||
{% block title %}{% trans "Participants" %} – {{ block.super }}{% endblock %}
|
||||
{% block title %}{% trans "Users" %} – {{ block.super }}{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<link href="{% static 'css/dataTables/dataTables.bootstrap.css' %}" type="text/css" rel="stylesheet">
|
||||
@ -17,27 +17,51 @@
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% trans "Participants" %}
|
||||
{% trans "Users" %}
|
||||
<small class="pull-right">
|
||||
{% if perms.participant.can_manage_participant %}
|
||||
<a href="{% url 'user_new' %}" class="btn btn-mini btn-primary" rel="tooltip" data-original-title="{% trans 'New participant' %}"><i class="icon-plus icon-white"></i> {% trans "New" %}</a>
|
||||
<a href="{% url 'user_new_multiple' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'New multiple participants' %}"><i class="icon-plus"></i> {% trans 'New multiple' %}</a>
|
||||
<a href="{% url 'user_group_overview' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'All groups' %}"><i class="icon-group"></i><span class="optional-small"> {% trans "Groups" %}</span></a>
|
||||
<a href="{% url 'user_csv_import' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Import participants' %}"><i class="icon-import"></i><span class="optional-small"> {% trans 'Import' %}</span></a>
|
||||
{% if perms.users.can_manage %}
|
||||
<a href="{% url 'user_new' %}" class="btn btn-mini btn-primary" rel="tooltip" data-original-title="{% trans 'New user' %}">
|
||||
<i class="icon-plus icon-white"></i>
|
||||
{% trans "New" %}
|
||||
</a>
|
||||
<a href="{% url 'user_new_multiple' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'New multiple users' %}">
|
||||
<i class="icon-plus"></i>
|
||||
{% trans 'New multiple' %}
|
||||
</a>
|
||||
<a href="{% url 'group_list' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'All groups' %}">
|
||||
<i class="icon-group"></i>
|
||||
<span class="optional-small">{% trans "Groups" %}</span>
|
||||
</a>
|
||||
<a href="{% url 'user_csv_import' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Import users' %}">
|
||||
<i class="icon-import"></i>
|
||||
<span class="optional-small"> {% trans 'Import' %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.participant.can_see_participant and perms.participant.can_manage_participant %}
|
||||
{% if perms.users.can_see and perms.users.can_manage %}
|
||||
<div class="btn-group">
|
||||
<a data-toggle="dropdown" class="btn btn-mini dropdown-toggle">
|
||||
<i class="icon-print"></i><span class="optional-small"> PDF</span> <span class="caret"></span>
|
||||
<i class="icon-print"></i>
|
||||
<span class="optional-small"> PDF</span>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
{% url 'user_settings' as url_usersettings %}
|
||||
<li><a href="{% url 'user_print' %}" target="_blank"><i class="icon-list"></i> {% trans 'List of participants' %}</a></li>
|
||||
<li><a href="{% url 'print_passwords' %}" target="_blank"><i class="icon-th-large"></i> {% trans 'List of access data' %}</a></li>
|
||||
<li>
|
||||
<a href="{% url 'user_print' %}" target="_blank">
|
||||
<i class="icon-list"></i>
|
||||
{% trans 'List of users' %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'print_passwords' %}" target="_blank">
|
||||
<i class="icon-th-large"></i>
|
||||
{% trans 'List of access data' %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="{% url 'user_print' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Print list of participants as PDF' %}" target="_blank"><i class="icon-print"></i> PDF</a>
|
||||
<a href="{% url 'user_print' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Print list of users as PDF' %}" target="_blank"><i class="icon-print"></i> PDF</a>
|
||||
{% endif %}
|
||||
</small>
|
||||
</h1>
|
||||
@ -51,7 +75,7 @@
|
||||
<th class="optional">{% trans "Structure level" %}</th>
|
||||
<th class="optional">{% trans "Group" %}</th>
|
||||
<th class="optional">{% trans "Committee" %}</th>
|
||||
{% if perms.participant.can_manage_participant %}
|
||||
{% if perms.users.can_manage %}
|
||||
<th class="optional">{% trans "Comment" %}</th>
|
||||
<th class="optional">{% trans "Last Login" %}</th>
|
||||
<th class="mini_width">{% trans "Actions" %}</th>
|
||||
@ -61,7 +85,7 @@
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr class="{% if user.is_active_slide %}activeline{% endif %}">
|
||||
<td>{% if perms.participant.can_manage_participant %}
|
||||
<td>{% if perms.users.can_manage %}
|
||||
{% if user != request_user %}
|
||||
<a href="{% url 'user_status_toggle' user.id %}"
|
||||
class="status_link btn btn-mini {% if user.is_active %}btn-success{% endif %}"
|
||||
@ -77,7 +101,7 @@
|
||||
</td>
|
||||
<td class="optional">{{ user.title }}</td>
|
||||
<td>
|
||||
{% if 'participant_sort_users_by_first_name'|get_config %}
|
||||
{% if 'users_sort_users_by_first_name'|get_config %}
|
||||
<a href="{% url 'user_view' user.id %}">{{ user.first_name }} {{ user.last_name }}</a>
|
||||
{% else %}
|
||||
<a href="{% url 'user_view' user.id %}">{{ user.last_name }}{% if user.last_name and user.first_name %},{% endif %} {{ user.first_name }}</a>
|
||||
@ -93,7 +117,7 @@
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="optional">{{ user.committee }}</td>
|
||||
{% if perms.participant.can_manage_participant %}
|
||||
{% if perms.users.can_manage %}
|
||||
<td class="optional">
|
||||
{% if user.comment %}
|
||||
<a class="btn btn-mini" rel="popover" data-content="{{ user.comment|linebreaks }}">
|
||||
@ -110,7 +134,7 @@
|
||||
<span style="width: 1px; white-space: nowrap;">
|
||||
{% if perms.core.can_manage_projector %}
|
||||
<a href="{{ user|absolute_url:'projector' }}" class="activate_link btn {% if user.is_active_slide %}btn-primary{% endif %} btn-mini"
|
||||
rel="tooltip" data-original-title="{% trans 'Show participant' %}">
|
||||
rel="tooltip" data-original-title="{% trans 'Show user' %}">
|
||||
<i class="icon-facetime-video {% if user.is_active_slide %}icon-white{% endif %}"></i>
|
||||
</a>
|
||||
{% endif %}
|
@ -11,7 +11,7 @@
|
||||
rel="tooltip" data-original-title="{% trans 'Show' %}">
|
||||
<i class="icon-facetime-video {% if user.is_active_slide %}icon-white{% endif %}"></i>
|
||||
</a>
|
||||
{% if perms.participant.can_manage_participant %}
|
||||
{% if perms.users.can_manage %}
|
||||
<a href="{{ user|absolute_url:'update' }}"
|
||||
rel="tooltip" data-original-title="{% trans 'Edit' %}" class="btn btn-mini right">
|
||||
<i class="icon-pencil"></i>
|
||||
@ -24,7 +24,7 @@
|
||||
<a href="{{ user|absolute_url }}">{{ user }}</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<li>{% trans 'No participants available.' %}</li>
|
||||
<li>{% trans 'No users available.' %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
@ -6,8 +6,8 @@ urlpatterns = patterns(
|
||||
'',
|
||||
# User
|
||||
url(r'^$',
|
||||
views.UserOverview.as_view(),
|
||||
name='user_overview'), # TODO: Rename this to user_list
|
||||
views.UserListView.as_view(),
|
||||
name='user_list'),
|
||||
|
||||
url(r'^new/$',
|
||||
views.UserCreateView.as_view(),
|
||||
@ -54,31 +54,31 @@ urlpatterns = patterns(
|
||||
|
||||
# Group
|
||||
url(r'^group/$',
|
||||
views.GroupOverview.as_view(),
|
||||
name='user_group_overview'), # TODO: Rename this to group_list
|
||||
views.GroupListView.as_view(),
|
||||
name='group_list'),
|
||||
|
||||
url(r'^group/new/$',
|
||||
views.GroupCreateView.as_view(),
|
||||
name='user_group_new'),
|
||||
name='group_create'),
|
||||
|
||||
url(r'^group/(?P<pk>\d+)/$',
|
||||
views.GroupDetailView.as_view(),
|
||||
name='user_group_view'),
|
||||
name='group_detail'),
|
||||
|
||||
url(r'^group/(?P<pk>\d+)/edit/$',
|
||||
views.GroupUpdateView.as_view(),
|
||||
name='user_group_edit'),
|
||||
name='group_update'),
|
||||
|
||||
url(r'^group/(?P<pk>\d+)/del/$',
|
||||
views.GroupDeleteView.as_view(),
|
||||
name='user_group_delete'),
|
||||
name='group_delete'),
|
||||
|
||||
# PDF
|
||||
url(r'^print/$',
|
||||
views.ParticipantsListPDF.as_view(),
|
||||
views.UsersListPDF.as_view(),
|
||||
name='user_print'),
|
||||
|
||||
url(r'^passwords/print/$',
|
||||
views.ParticipantsPasswordsPDF.as_view(),
|
||||
views.UsersPasswordsPDF.as_view(),
|
||||
name='print_passwords'),
|
||||
)
|
@ -1,47 +1,43 @@
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.contrib.auth.views import login as django_login
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import activate, ugettext_lazy
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy, activate
|
||||
|
||||
from openslides.config.api import config
|
||||
from openslides.utils.utils import (delete_default_permissions, html_strong,
|
||||
template)
|
||||
from openslides.utils.views import (CreateView, CSVImportView, DeleteView, DetailView,
|
||||
FormView, ListView, PDFView,
|
||||
PermissionMixin, QuestionView,
|
||||
RedirectView, SingleObjectMixin, UpdateView)
|
||||
from openslides.utils.utils import delete_default_permissions, html_strong
|
||||
from openslides.utils.views import (
|
||||
CreateView, CSVImportView, DeleteView, DetailView, FormView, ListView,
|
||||
PDFView, PermissionMixin, QuestionView, RedirectView, SingleObjectMixin,
|
||||
UpdateView, LoginMixin)
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
|
||||
from .api import gen_password, gen_username
|
||||
from .api import gen_password, gen_username, get_protected_perm
|
||||
from .csv_import import import_users
|
||||
from .forms import (GroupForm, UserCreateForm, UserMultipleCreateForm,
|
||||
UsersettingsForm, UserUpdateForm)
|
||||
from .models import get_protected_perm, Group, User
|
||||
from .pdf import participants_to_pdf, participants_passwords_to_pdf
|
||||
from .models import Group, User
|
||||
from .pdf import users_to_pdf, users_passwords_to_pdf
|
||||
|
||||
|
||||
class UserOverview(ListView):
|
||||
class UserListView(ListView):
|
||||
"""
|
||||
Show all participants (users).
|
||||
Show all users.
|
||||
"""
|
||||
required_permission = 'participant.can_see_participant'
|
||||
template_name = 'participant/overview.html'
|
||||
required_permission = 'users.can_see'
|
||||
context_object_name = 'users'
|
||||
|
||||
def get_queryset(self):
|
||||
query = User.objects
|
||||
if config['participant_sort_users_by_first_name']:
|
||||
if config['users_sort_users_by_first_name']:
|
||||
query = query.order_by('first_name')
|
||||
else:
|
||||
query = query.order_by('last_name')
|
||||
return query.all()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserOverview, self).get_context_data(**kwargs)
|
||||
context = super().get_context_data(**kwargs)
|
||||
all_users = User.objects.count()
|
||||
# context vars
|
||||
context.update({
|
||||
@ -54,29 +50,29 @@ class UserDetailView(DetailView, PermissionMixin):
|
||||
"""
|
||||
Classed based view to show a specific user in the interface.
|
||||
"""
|
||||
required_permission = 'participant.can_see_participant'
|
||||
required_permission = 'users.can_see'
|
||||
model = User
|
||||
template_name = 'participant/user_detail.html'
|
||||
context_object_name = 'shown_user'
|
||||
|
||||
|
||||
class UserCreateView(CreateView):
|
||||
"""
|
||||
Create a new participant.
|
||||
Create a new user.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
template_name = 'participant/edit.html'
|
||||
required_permission = 'users.can_manage'
|
||||
model = User
|
||||
context_object_name = 'edit_user'
|
||||
form_class = UserCreateForm
|
||||
success_url_name = 'user_overview'
|
||||
success_url_name = 'user_list'
|
||||
url_name_args = []
|
||||
|
||||
def manipulate_object(self, form):
|
||||
self.object.username = gen_username(
|
||||
form.cleaned_data['first_name'], form.cleaned_data['last_name'])
|
||||
|
||||
if not self.object.default_password:
|
||||
self.object.default_password = gen_password()
|
||||
|
||||
self.object.set_password(self.object.default_password)
|
||||
|
||||
def post_save(self, form):
|
||||
@ -86,7 +82,7 @@ class UserCreateView(CreateView):
|
||||
# to new user but super(..).post_save(form) removes it and sets only the
|
||||
# groups selected in the form (without 'registered')
|
||||
# workaround: add registered group again manually
|
||||
from openslides.participant.api import get_registered_group # TODO: Test, if global import is possible
|
||||
from openslides.users.api import get_registered_group # TODO: Test, if global import is possible
|
||||
registered = get_registered_group()
|
||||
self.object.groups.add(registered)
|
||||
|
||||
@ -94,15 +90,19 @@ class UserCreateView(CreateView):
|
||||
class UserMultipleCreateView(FormView):
|
||||
"""
|
||||
View to create multiple users at once using a big text field.
|
||||
|
||||
Sets the password with md5. It is the same password as in the
|
||||
default_password field in cleartext. A stronger password hasher is used,
|
||||
when the password is changed by the user.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
template_name = 'participant/user_form_multiple.html'
|
||||
required_permission = 'users.can_manage'
|
||||
template_name = 'users/user_form_multiple.html'
|
||||
form_class = UserMultipleCreateForm
|
||||
success_url_name = 'user_overview'
|
||||
success_url_name = 'user_list'
|
||||
|
||||
def form_valid(self, form):
|
||||
# TODO: Use bulk_create after rework of participant.models.User
|
||||
for number, line in enumerate(form.cleaned_data['participants_block'].splitlines()):
|
||||
# TODO: Use bulk_create
|
||||
for number, line in enumerate(form.cleaned_data['users_block'].splitlines()):
|
||||
names_list = line.split()
|
||||
first_name = ' '.join(names_list[:-1])
|
||||
last_name = names_list[-1]
|
||||
@ -114,20 +114,19 @@ class UserMultipleCreateView(FormView):
|
||||
last_name=last_name,
|
||||
default_password=default_password,
|
||||
password=make_password(default_password, '', 'md5'))
|
||||
messages.success(self.request, _('%(number)d participants successfully created.') % {'number': number + 1})
|
||||
messages.success(self.request, _('%(number)d users successfully created.') % {'number': number + 1})
|
||||
return super(UserMultipleCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class UserUpdateView(UpdateView):
|
||||
"""
|
||||
Update an existing participant.
|
||||
Update an existing users.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
template_name = 'participant/edit.html'
|
||||
required_permission = 'users.can_manage'
|
||||
model = User
|
||||
context_object_name = 'edit_user'
|
||||
form_class = UserUpdateForm
|
||||
success_url_name = 'user_overview'
|
||||
success_url_name = 'user_list'
|
||||
url_name_args = []
|
||||
|
||||
def get_form_kwargs(self, *args, **kwargs):
|
||||
@ -135,50 +134,47 @@ class UserUpdateView(UpdateView):
|
||||
form_kwargs.update({'request': self.request})
|
||||
return form_kwargs
|
||||
|
||||
def manipulate_object(self, form):
|
||||
self.object.username = form.cleaned_data['user_name']
|
||||
|
||||
def post_save(self, form):
|
||||
super(UserUpdateView, self).post_save(form)
|
||||
# TODO: find a better solution that makes the following lines obsolete
|
||||
# TODO: Find a better solution that makes the following lines obsolete
|
||||
# Background: motion.models.use_post_save adds already the registerd group
|
||||
# to new user but super(..).post_save(form) removes it and sets only the
|
||||
# groups selected in the form (without 'registered')
|
||||
# workaround: add registered group again manually
|
||||
from openslides.participant.api import get_registered_group # TODO: Test, if global import is possible
|
||||
from openslides.users.api import get_registered_group # TODO: Test, if global import is possible
|
||||
registered = get_registered_group()
|
||||
self.object.groups.add(registered)
|
||||
|
||||
|
||||
class UserDeleteView(DeleteView):
|
||||
"""
|
||||
Delete an participant.
|
||||
Delete a user.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
required_permission = 'users.can_manage'
|
||||
model = User
|
||||
success_url_name = 'user_overview'
|
||||
success_url_name = 'user_list'
|
||||
url_name_args = []
|
||||
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
if self.object == self.request.user:
|
||||
messages.error(request, _("You can not delete yourself."))
|
||||
else:
|
||||
super(UserDeleteView, self).pre_redirect(request, *args, **kwargs)
|
||||
super().pre_redirect(request, *args, **kwargs)
|
||||
|
||||
def pre_post_redirect(self, request, *args, **kwargs):
|
||||
if self.object == self.request.user:
|
||||
messages.error(self.request, _("You can not delete yourself."))
|
||||
else:
|
||||
super(UserDeleteView, self).pre_post_redirect(request, *args, **kwargs)
|
||||
super().pre_post_redirect(request, *args, **kwargs)
|
||||
|
||||
|
||||
class SetUserStatusView(SingleObjectMixin, RedirectView):
|
||||
"""
|
||||
Activate or deactivate an user.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
required_permission = 'users.can_manage'
|
||||
allow_ajax = True
|
||||
url_name = 'user_overview'
|
||||
url_name = 'user_list'
|
||||
url_name_args = []
|
||||
model = User
|
||||
|
||||
@ -192,8 +188,6 @@ class SetUserStatusView(SingleObjectMixin, RedirectView):
|
||||
messages.error(request, _("You can not deactivate yourself."))
|
||||
else:
|
||||
self.object.is_active = False
|
||||
elif action == 'toggle':
|
||||
self.object.is_active = not self.object.is_active
|
||||
self.object.save()
|
||||
return super(SetUserStatusView, self).pre_redirect(request, *args, **kwargs)
|
||||
|
||||
@ -203,27 +197,27 @@ class SetUserStatusView(SingleObjectMixin, RedirectView):
|
||||
return context
|
||||
|
||||
|
||||
class ParticipantsListPDF(PDFView):
|
||||
class UsersListPDF(PDFView):
|
||||
"""
|
||||
Generate the userliste as PDF.
|
||||
"""
|
||||
required_permission = 'participant.can_see_participant'
|
||||
filename = ugettext_lazy("Participant-list")
|
||||
document_title = ugettext_lazy('List of Participants')
|
||||
required_permission = 'users.can_see'
|
||||
filename = ugettext_lazy("user-list")
|
||||
document_title = ugettext_lazy('List of Users')
|
||||
|
||||
def append_to_pdf(self, pdf):
|
||||
"""
|
||||
Append PDF objects.
|
||||
"""
|
||||
participants_to_pdf(pdf)
|
||||
users_to_pdf(pdf)
|
||||
|
||||
|
||||
class ParticipantsPasswordsPDF(PDFView):
|
||||
class UsersPasswordsPDF(PDFView):
|
||||
"""
|
||||
Generate the access data welcome paper for all participants as PDF.
|
||||
Generate the access data welcome paper for all users as PDF.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
filename = ugettext_lazy("Participant-access-data")
|
||||
required_permission = 'users.can_manage'
|
||||
filename = ugettext_lazy("User-access-data")
|
||||
top_space = 0
|
||||
|
||||
def build_document(self, pdf_document, story):
|
||||
@ -233,7 +227,7 @@ class ParticipantsPasswordsPDF(PDFView):
|
||||
"""
|
||||
Append PDF objects.
|
||||
"""
|
||||
participants_passwords_to_pdf(pdf)
|
||||
users_passwords_to_pdf(pdf)
|
||||
|
||||
|
||||
class UserCSVImportView(CSVImportView):
|
||||
@ -241,16 +235,16 @@ class UserCSVImportView(CSVImportView):
|
||||
Import users via CSV.
|
||||
"""
|
||||
import_function = staticmethod(import_users)
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
success_url_name = 'user_overview'
|
||||
template_name = 'participant/user_form_csv_import.html'
|
||||
required_permission = 'users.can_manage'
|
||||
success_url_name = 'user_list'
|
||||
template_name = 'users/user_form_csv_import.html'
|
||||
|
||||
|
||||
class ResetPasswordView(SingleObjectMixin, QuestionView):
|
||||
"""
|
||||
Set the Passwort for a user to his default password.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
required_permission = 'users.can_manage'
|
||||
model = User
|
||||
allow_ajax = True
|
||||
question_message = ugettext_lazy('Do you really want to reset the password?')
|
||||
@ -264,17 +258,18 @@ class ResetPasswordView(SingleObjectMixin, QuestionView):
|
||||
|
||||
def on_clicked_yes(self):
|
||||
self.object.reset_password()
|
||||
self.object.save()
|
||||
|
||||
def get_final_message(self):
|
||||
return _('The Password for %s was successfully reset.') % html_strong(self.object)
|
||||
|
||||
|
||||
class GroupOverview(ListView):
|
||||
class GroupListView(ListView):
|
||||
"""
|
||||
Overview over all groups.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
template_name = 'participant/group_overview.html'
|
||||
required_permission = 'users.can_manage'
|
||||
template_name = 'users/group_list.html'
|
||||
context_object_name = 'groups'
|
||||
model = Group
|
||||
|
||||
@ -283,19 +278,19 @@ class GroupDetailView(DetailView, PermissionMixin):
|
||||
"""
|
||||
Classed based view to show a specific group in the interface.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
required_permission = 'users.can_manage'
|
||||
model = Group
|
||||
template_name = 'participant/group_detail.html'
|
||||
template_name = 'users/group_detail.html'
|
||||
context_object_name = 'group'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super(GroupDetailView, self).get_context_data(*args, **kwargs)
|
||||
query = User.objects
|
||||
if config['participant_sort_users_by_first_name']:
|
||||
if config['users_sort_users_by_first_name']:
|
||||
query = query.order_by('first_name')
|
||||
else:
|
||||
query = query.order_by('last_name')
|
||||
context['group_members'] = query.filter(django_user__groups__in=[context['group']])
|
||||
context['group_members'] = query.filter(groups__in=[context['group']])
|
||||
return context
|
||||
|
||||
|
||||
@ -303,57 +298,70 @@ class GroupCreateView(CreateView):
|
||||
"""
|
||||
Create a new group.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
template_name = 'participant/group_edit.html'
|
||||
required_permission = 'users.can_manage'
|
||||
template_name = 'users/group_form.html'
|
||||
context_object_name = 'group'
|
||||
model = Group
|
||||
form_class = GroupForm
|
||||
success_url_name = 'user_group_overview'
|
||||
success_url_name = 'group_list'
|
||||
url_name_args = []
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
delete_default_permissions()
|
||||
return super(GroupCreateView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_apply_url(self):
|
||||
"""
|
||||
Returns the url when the user clicks on 'apply'.
|
||||
"""
|
||||
return self.get_url('group_update', args=[self.object.pk])
|
||||
|
||||
|
||||
class GroupUpdateView(UpdateView):
|
||||
"""
|
||||
Update an existing group.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
template_name = 'participant/group_edit.html'
|
||||
required_permission = 'users.can_manage'
|
||||
template_name = 'users/group_form.html'
|
||||
model = Group
|
||||
context_object_name = 'group'
|
||||
form_class = GroupForm
|
||||
success_url_name = 'user_group_overview'
|
||||
url_name_args = []
|
||||
success_url_name = 'group_list'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
delete_default_permissions()
|
||||
return super(GroupUpdateView, self).get(request, *args, **kwargs)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_form_kwargs(self, *args, **kwargs):
|
||||
form_kwargs = super(GroupUpdateView, self).get_form_kwargs(*args, **kwargs)
|
||||
form_kwargs = super().get_form_kwargs(*args, **kwargs)
|
||||
form_kwargs.update({'request': self.request})
|
||||
return form_kwargs
|
||||
|
||||
def get_apply_url(self):
|
||||
"""
|
||||
Returns the url when the user clicks on 'apply'.
|
||||
"""
|
||||
return self.get_url('group_update', args=[self.object.pk])
|
||||
|
||||
|
||||
class GroupDeleteView(DeleteView):
|
||||
"""
|
||||
Delete a group.
|
||||
"""
|
||||
required_permission = 'participant.can_manage_participant'
|
||||
required_permission = 'users.can_manage'
|
||||
model = Group
|
||||
success_url_name = 'user_group_overview'
|
||||
success_url_name = 'group_list'
|
||||
question_url_name = 'group_detail'
|
||||
url_name_args = []
|
||||
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
if not self.is_protected_from_deleting():
|
||||
super(GroupDeleteView, self).pre_redirect(request, *args, **kwargs)
|
||||
super().pre_redirect(request, *args, **kwargs)
|
||||
|
||||
def pre_post_redirect(self, request, *args, **kwargs):
|
||||
if not self.is_protected_from_deleting():
|
||||
super(GroupDeleteView, self).pre_post_redirect(request, *args, **kwargs)
|
||||
super().pre_post_redirect(request, *args, **kwargs)
|
||||
|
||||
def is_protected_from_deleting(self):
|
||||
"""
|
||||
@ -370,10 +378,21 @@ class GroupDeleteView(DeleteView):
|
||||
messages.error(
|
||||
self.request,
|
||||
_('You can not delete the last group containing the permission '
|
||||
'to manage participants you are in.'))
|
||||
'to manage users you are in.'))
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_url_name_args(self):
|
||||
try:
|
||||
answer = self.get_answer()
|
||||
except OpenSlidesError:
|
||||
answer = 'no'
|
||||
|
||||
if self.request.method == 'POST' and answer != 'no':
|
||||
return []
|
||||
else:
|
||||
return [self.object.pk]
|
||||
|
||||
|
||||
def login(request):
|
||||
extra_content = {}
|
||||
@ -394,54 +413,41 @@ def login(request):
|
||||
extra_content['next'] = reverse('password_change')
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
return django_login(request, template_name='participant/login.html', extra_context=extra_content)
|
||||
return django_login(request, template_name='users/login.html', extra_context=extra_content)
|
||||
|
||||
|
||||
@login_required
|
||||
@template('participant/settings.html')
|
||||
def user_settings(request):
|
||||
"""
|
||||
Edit own user account.
|
||||
"""
|
||||
if request.method == 'POST':
|
||||
form_user = UsersettingsForm(request.POST, instance=request.user)
|
||||
if form_user.is_valid():
|
||||
user = form_user.save(commit=False)
|
||||
user.username = form_user.cleaned_data['user_name']
|
||||
user.save()
|
||||
language = request.LANGUAGE_CODE = \
|
||||
request.session['django_language'] = form_user.cleaned_data['language']
|
||||
activate(language)
|
||||
messages.success(request, _('User settings successfully saved.'))
|
||||
else:
|
||||
messages.error(request, _('Please check the form for errors.'))
|
||||
else:
|
||||
language = request.session.get('django_language', request.LANGUAGE_CODE)
|
||||
form_user = UsersettingsForm(instance=request.user, initial={'language': language})
|
||||
class UserSettingsView(LoginMixin, UpdateView):
|
||||
model = User
|
||||
form_class = UsersettingsForm
|
||||
success_url_name = 'user_settings'
|
||||
url_name_args = []
|
||||
template_name = 'users/settings.html'
|
||||
|
||||
return {
|
||||
'form': form_user,
|
||||
'edituser': request.user,
|
||||
}
|
||||
def get_initial(self):
|
||||
initial = super().get_initial()
|
||||
initial['language'] = self.request.session.get('django_language', self.request.LANGUAGE_CODE)
|
||||
return initial
|
||||
|
||||
def form_valid(self, form):
|
||||
self.request.LANGUAGE_CODE = self.request.session['django_language'] = form.cleaned_data['language']
|
||||
activate(self.request.LANGUAGE_CODE)
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user
|
||||
|
||||
|
||||
@login_required
|
||||
@template('participant/password_change.html')
|
||||
def user_settings_password(request):
|
||||
"""
|
||||
Edit own password.
|
||||
"""
|
||||
if request.method == 'POST':
|
||||
form = PasswordChangeForm(request.user, request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, _('Password successfully changed.'))
|
||||
return redirect(reverse('core_dashboard'))
|
||||
else:
|
||||
messages.error(request, _('Please check the form for errors.'))
|
||||
else:
|
||||
form = PasswordChangeForm(user=request.user)
|
||||
class UserPasswordSettingsView(LoginMixin, FormView):
|
||||
form_class = PasswordChangeForm
|
||||
success_url_name = 'core_dashboard'
|
||||
template_name = 'users/password_change.html'
|
||||
|
||||
return {
|
||||
'form': form,
|
||||
}
|
||||
def form_valid(self, form):
|
||||
form.save()
|
||||
messages.success(self.request, _('Password successfully changed.'))
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['user'] = self.request.user
|
||||
return kwargs
|
25
openslides/users/widgets.py
Normal file
25
openslides/users/widgets.py
Normal file
@ -0,0 +1,25 @@
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from openslides.utils.widgets import Widget
|
||||
|
||||
from .models import User
|
||||
|
||||
|
||||
class UserWidget(Widget):
|
||||
"""
|
||||
Provides a widget with all users. This is for short activation of
|
||||
user slides.
|
||||
"""
|
||||
name = 'user'
|
||||
verbose_name = ugettext_lazy('Users')
|
||||
required_permission = 'core.can_manage_projector'
|
||||
default_column = 1
|
||||
default_weight = 60
|
||||
default_active = False
|
||||
template_name = 'users/widget_user.html'
|
||||
more_link_pattern_name = 'user_list'
|
||||
|
||||
def get_context_data(self, **context):
|
||||
return super(UserWidget, self).get_context_data(
|
||||
users=User.objects.all(),
|
||||
**context)
|
@ -1,15 +0,0 @@
|
||||
from openslides.utils.person.signals import receive_persons
|
||||
from openslides.utils.person.api import (
|
||||
generate_person_id, get_person, Person, Persons)
|
||||
from openslides.utils.person.forms import PersonFormField, MultiplePersonFormField
|
||||
from openslides.utils.person.models import PersonField, PersonMixin
|
||||
|
||||
__all__ = ['receive_persons', 'generate_person_id', 'get_person', 'Person',
|
||||
'Persons', 'PersonFormField', 'MultiplePersonFormField',
|
||||
'PersonField', 'PersonMixin', 'EmptyPerson']
|
||||
|
||||
|
||||
class EmptyPerson(PersonMixin, Person):
|
||||
@property
|
||||
def person_id(self):
|
||||
return 'empty'
|
@ -1,116 +0,0 @@
|
||||
from .signals import receive_persons
|
||||
|
||||
|
||||
class Person(object):
|
||||
"""
|
||||
Meta-class for all person objects
|
||||
"""
|
||||
def person_id(self):
|
||||
"""
|
||||
Return an id for representation of ths person. Has to be unique.
|
||||
"""
|
||||
raise NotImplementedError('Any person object needs a person_id')
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Return a string for this person.
|
||||
"""
|
||||
return str(self.person_id)
|
||||
|
||||
@property
|
||||
def sort_name(self):
|
||||
"""
|
||||
Return the part of the name, which is used for sorting.
|
||||
For example the pre-name or the last-name
|
||||
"""
|
||||
return self.clean_name.lower()
|
||||
|
||||
@property
|
||||
def clean_name(self):
|
||||
"""
|
||||
Return the name of this person without a suffix
|
||||
"""
|
||||
return str(self)
|
||||
|
||||
@property
|
||||
def name_suffix(self):
|
||||
"""
|
||||
Return a suffix for the person-name.
|
||||
"""
|
||||
return ''
|
||||
|
||||
def get_absolute_url(self, link='detail'):
|
||||
"""
|
||||
Return an absolute url for the person.
|
||||
|
||||
The argument 'link' affects which url it is. Typically it is one of:
|
||||
* detail
|
||||
* edit
|
||||
* delete
|
||||
|
||||
You should raise an 'ValueError', if your person does not have
|
||||
one of this links, or use the AbsoluteURLMixin, which raises the
|
||||
ValueError for you.
|
||||
"""
|
||||
try:
|
||||
url = super(Person, self).get_absolute_url(link)
|
||||
except AttributeError:
|
||||
raise ValueError('This person object has no url.')
|
||||
return url
|
||||
|
||||
|
||||
class Persons(object):
|
||||
"""
|
||||
A Storage for a multiplicity of different Person-Objects.
|
||||
"""
|
||||
def __init__(self, person_prefix_filter=None, id_filter=None):
|
||||
self.person_prefix_filter = person_prefix_filter
|
||||
self.id_filter = id_filter
|
||||
|
||||
def __iter__(self):
|
||||
try:
|
||||
return iter(self._cache)
|
||||
except AttributeError:
|
||||
return self.iter_persons()
|
||||
|
||||
def __len__(self):
|
||||
return len(list(self.__iter__()))
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return list(self)[key]
|
||||
except IndexError:
|
||||
from openslides.utils.person import EmptyPerson
|
||||
return EmptyPerson()
|
||||
|
||||
def iter_persons(self):
|
||||
self._cache = list()
|
||||
for receiver, persons in receive_persons.send(
|
||||
sender='persons', person_prefix_filter=self.person_prefix_filter, id_filter=self.id_filter):
|
||||
for person in persons:
|
||||
self._cache.append(person)
|
||||
yield person
|
||||
|
||||
|
||||
def generate_person_id(prefix, id):
|
||||
assert prefix is not None
|
||||
assert id is not None
|
||||
if ':' in prefix:
|
||||
raise ValueError("':' is not allowed in a the 'person_prefix'")
|
||||
return "%s:%d" % (prefix, id)
|
||||
|
||||
|
||||
def split_person_id(person_id):
|
||||
data = person_id.split(':', 1)
|
||||
if len(data) == 2 and data[0] and data[1]:
|
||||
return data
|
||||
raise TypeError("Invalid person_id: '%s'" % person_id)
|
||||
|
||||
|
||||
def get_person(person_id):
|
||||
try:
|
||||
person_prefix, id = split_person_id(person_id)
|
||||
except TypeError:
|
||||
from openslides.utils.person import EmptyPerson
|
||||
return EmptyPerson()
|
||||
return Persons(person_prefix_filter=person_prefix, id_filter=id)[0]
|
@ -1,67 +0,0 @@
|
||||
from django import forms
|
||||
|
||||
from openslides.utils.person.api import get_person, Persons
|
||||
|
||||
|
||||
class PersonChoices(object):
|
||||
def __init__(self, field):
|
||||
self.field = field
|
||||
|
||||
def __iter__(self):
|
||||
if self.field.empty_label is not None:
|
||||
yield (u'', self.field.empty_label)
|
||||
for person in sorted(Persons(), key=lambda person: person.sort_name):
|
||||
yield (person.person_id, person)
|
||||
|
||||
|
||||
class PersonFormField(forms.fields.ChoiceField):
|
||||
def __init__(self, required=True, initial=None, empty_label=u"---------",
|
||||
*args, **kwargs):
|
||||
if required and (initial is not None):
|
||||
self.empty_label = None
|
||||
else:
|
||||
self.empty_label = empty_label
|
||||
forms.fields.Field.__init__(self, required=required, initial=initial,
|
||||
*args, **kwargs)
|
||||
self.widget.choices = self.choices
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
result = super(forms.fields.ChoiceField, self).__deepcopy__(memo)
|
||||
return result
|
||||
|
||||
def _get_choices(self):
|
||||
# If self._choices is set, then somebody must have manually set
|
||||
# the property self.choices. In this case, just return self._choices.
|
||||
if hasattr(self, '_choices'):
|
||||
return self._choices
|
||||
return PersonChoices(self)
|
||||
|
||||
choices = property(_get_choices, forms.fields.ChoiceField._set_choices)
|
||||
|
||||
def to_python(self, value):
|
||||
if value == u'':
|
||||
return u''
|
||||
return get_person(value)
|
||||
|
||||
def valid_value(self, value):
|
||||
return super(PersonFormField, self).valid_value(value.person_id)
|
||||
|
||||
|
||||
class MultiplePersonFormField(PersonFormField):
|
||||
widget = forms.widgets.SelectMultiple
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MultiplePersonFormField, self).__init__(
|
||||
empty_label=None, *args, **kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
if hasattr(value, '__iter__'):
|
||||
return [super(MultiplePersonFormField, self).to_python(v)
|
||||
for v in value]
|
||||
return super(MultiplePersonFormField, self).to_python(value)
|
||||
|
||||
def valid_value(self, value):
|
||||
if hasattr(value, '__iter__'):
|
||||
return [super(MultiplePersonFormField, self).valid_value(v)
|
||||
for v in value]
|
||||
return super(MultiplePersonFormField, self).valid_value(value)
|
@ -1,77 +0,0 @@
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.db import models
|
||||
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
|
||||
from .api import generate_person_id, get_person
|
||||
from .forms import PersonFormField
|
||||
|
||||
|
||||
class PersonField(models.fields.Field, metaclass=models.SubfieldBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['max_length'] = 255
|
||||
super(PersonField, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_internal_type(self):
|
||||
return "CharField"
|
||||
|
||||
def to_python(self, value):
|
||||
"""
|
||||
Convert an object to an user Object.
|
||||
|
||||
'value' has to be an object derivated from PersonMixin, None or has to
|
||||
have an attribute 'person_id'.
|
||||
"""
|
||||
if isinstance(value, PersonMixin):
|
||||
return value
|
||||
elif value is None:
|
||||
return None
|
||||
elif isinstance(value, AnonymousUser):
|
||||
raise AttributeError('An AnonymousUser can not be saved into the database.')
|
||||
else:
|
||||
try:
|
||||
return get_person(value)
|
||||
except AttributeError:
|
||||
raise AttributeError('You can not save \'%s\' into a person field.'
|
||||
% type(value))
|
||||
|
||||
def get_prep_value(self, value):
|
||||
"""
|
||||
Convert a person object to a string, to store it in the database.
|
||||
"""
|
||||
if value is None:
|
||||
# For Fields with null=True
|
||||
return None
|
||||
elif isinstance(value, str):
|
||||
# The object is already a a person_id
|
||||
return value
|
||||
|
||||
elif hasattr(value, 'person_id'):
|
||||
# The object is a person
|
||||
return value.person_id
|
||||
else:
|
||||
OpenSlidesError('%s (%s) is no person' % (value, type(value)))
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': PersonFormField}
|
||||
defaults.update(kwargs)
|
||||
return super(PersonField, self).formfield(**defaults)
|
||||
|
||||
|
||||
class PersonMixin(object):
|
||||
@property
|
||||
def person_id(self):
|
||||
try:
|
||||
return generate_person_id(self.person_prefix, self.pk)
|
||||
except AttributeError:
|
||||
raise AttributeError("%s has to have a attribute 'person_prefix'"
|
||||
% self)
|
||||
|
||||
def __str__(self):
|
||||
return self.person_id
|
||||
|
||||
def prepare_database_save(self, field):
|
||||
if type(field) is PersonField:
|
||||
return self.person_id
|
||||
else:
|
||||
return super(PersonMixin, self).prepare_database_save(field)
|
@ -1,3 +0,0 @@
|
||||
from django.dispatch import Signal
|
||||
|
||||
receive_persons = Signal(providing_args=['person_prefix_filter', 'id_filter'])
|
@ -439,8 +439,8 @@ class FormView(PermissionMixin, ExtraContextMixin, FormMixin,
|
||||
pass
|
||||
|
||||
|
||||
class UpdateView(PermissionMixin, ExtraContextMixin,
|
||||
ModelFormMixin, django_views.UpdateView):
|
||||
class UpdateView(PermissionMixin, ExtraContextMixin, ModelFormMixin,
|
||||
django_views.UpdateView):
|
||||
"""
|
||||
View to update an model object.
|
||||
"""
|
||||
@ -471,13 +471,12 @@ class DeleteView(SingleObjectMixin, QuestionView):
|
||||
"""
|
||||
View to delete an model object.
|
||||
"""
|
||||
|
||||
success_url = None
|
||||
success_url_name = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return super(DeleteView, self).get(request, *args, **kwargs)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_redirect_url(self, **kwargs):
|
||||
"""
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.test.client import Client
|
||||
|
||||
from openslides.config.api import config
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -43,8 +43,7 @@ class PersonalInfoWidget(TestCase):
|
||||
return assignment
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(username='HansMeiser')
|
||||
self.user.reset_password('default')
|
||||
self.user = User.objects.create_user('HansMeiser', 'default')
|
||||
self.client = Client()
|
||||
self.client.login(username='HansMeiser', password='default')
|
||||
|
||||
@ -56,7 +55,7 @@ class PersonalInfoWidget(TestCase):
|
||||
agenda = self.import_agenda()
|
||||
if agenda:
|
||||
item_1 = agenda.models.Item.objects.create(title='My Item Title iw5ohNgee4eiYahb5Eiv')
|
||||
speaker = agenda.models.Speaker.objects.add(item=item_1, person=self.user)
|
||||
speaker = agenda.models.Speaker.objects.add(item=item_1, user=self.user)
|
||||
response = self.client.get('/dashboard/')
|
||||
self.assertContains(response, 'I am on the list of speakers of the following items:', status_code=200)
|
||||
self.assertContains(response, 'My Item Title iw5ohNgee4eiYahb5Eiv', status_code=200)
|
||||
|
@ -6,7 +6,7 @@ from django.test.client import Client
|
||||
from openslides.agenda.models import Item, Speaker
|
||||
from openslides.agenda.signals import agenda_list_of_speakers
|
||||
from openslides.config.api import config
|
||||
from openslides.participant.models import Group, User
|
||||
from openslides.users.models import User
|
||||
from openslides.projector.api import set_active_slide, register_slide_model
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.test import TestCase
|
||||
@ -24,15 +24,15 @@ class ListOfSpeakerModelTests(TestCase):
|
||||
def test_append_speaker(self):
|
||||
# Append speaker1 to the list of item1
|
||||
speaker1_item1 = Speaker.objects.add(self.speaker1, self.item1)
|
||||
self.assertTrue(Speaker.objects.filter(person=self.speaker1, item=self.item1).exists())
|
||||
self.assertTrue(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists())
|
||||
|
||||
# Append speaker1 to the list of item2
|
||||
speaker1_item2 = Speaker.objects.add(self.speaker1, self.item2)
|
||||
self.assertTrue(Speaker.objects.filter(person=self.speaker1, item=self.item2).exists())
|
||||
self.assertTrue(Speaker.objects.filter(user=self.speaker1, item=self.item2).exists())
|
||||
|
||||
# Append speaker2 to the list of item1
|
||||
speaker2_item1 = Speaker.objects.add(self.speaker2, self.item1)
|
||||
self.assertTrue(Speaker.objects.filter(person=self.speaker2, item=self.item1).exists())
|
||||
self.assertTrue(Speaker.objects.filter(user=self.speaker2, item=self.item1).exists())
|
||||
|
||||
# Try to append speaker 1 again to the list of item1
|
||||
with self.assertRaises(OpenSlidesError):
|
||||
@ -69,7 +69,7 @@ class ListOfSpeakerModelTests(TestCase):
|
||||
self.assertIsNone(speaker1_item1.end_time)
|
||||
self.assertIsNone(speaker2_item1.begin_time)
|
||||
speaker2_item1.begin_speach()
|
||||
self.assertIsNotNone(Speaker.objects.get(person=self.speaker1, item=self.item1).end_time)
|
||||
self.assertIsNotNone(Speaker.objects.get(user=self.speaker1, item=self.item1).end_time)
|
||||
self.assertIsNotNone(speaker2_item1.begin_time)
|
||||
|
||||
@patch('openslides.agenda.models.update_projector_overlay')
|
||||
@ -109,12 +109,12 @@ class SpeakerViewTestCase(TestCase):
|
||||
self.admin_client.login(username='admin', password='admin')
|
||||
|
||||
# Speaker1
|
||||
self.speaker1 = User.objects.create_user('speaker1', 'speaker1@user.user', 'speaker')
|
||||
self.speaker1 = User.objects.create_user('speaker1', 'speaker')
|
||||
self.speaker1_client = Client()
|
||||
self.speaker1_client.login(username='speaker1', password='speaker')
|
||||
|
||||
# Speaker2
|
||||
self.speaker2 = User.objects.create_user('speaker2', 'speaker2@user.user', 'speaker')
|
||||
self.speaker2 = User.objects.create_user('speaker2', 'speaker')
|
||||
self.speaker2_client = Client()
|
||||
self.speaker2_client.login(username='speaker2', password='speaker')
|
||||
|
||||
@ -135,12 +135,12 @@ class SpeakerViewTestCase(TestCase):
|
||||
|
||||
class TestSpeakerAppendView(SpeakerViewTestCase):
|
||||
def test_get(self):
|
||||
self.assertFalse(Speaker.objects.filter(person=self.speaker1, item=self.item1).exists())
|
||||
self.assertFalse(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists())
|
||||
self.assertEqual(Speaker.objects.filter(item=self.item1).count(), 0)
|
||||
|
||||
# Set speaker1 to item1
|
||||
response = self.check_url('/agenda/1/speaker/', self.speaker1_client, 302)
|
||||
self.assertTrue(Speaker.objects.filter(person=self.speaker1, item=self.item1).exists())
|
||||
self.assertTrue(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists())
|
||||
self.assertEqual(Speaker.objects.filter(item=self.item1).count(), 1)
|
||||
self.assertMessage(response, 'You were successfully added to the list of speakers.')
|
||||
|
||||
@ -162,23 +162,14 @@ class TestAgendaItemView(SpeakerViewTestCase):
|
||||
def test_post(self):
|
||||
# Set speaker1 to item1
|
||||
response = self.admin_client.post(
|
||||
'/agenda/1/', {'speaker': self.speaker1.person_id})
|
||||
self.assertTrue(Speaker.objects.filter(person=self.speaker1, item=self.item1).exists())
|
||||
'/agenda/1/', {'speaker': self.speaker1.id})
|
||||
self.assertTrue(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists())
|
||||
|
||||
# Try it again
|
||||
response = self.admin_client.post(
|
||||
'/agenda/1/', {'speaker': self.speaker1.person_id})
|
||||
'/agenda/1/', {'speaker': self.speaker1.id})
|
||||
self.assertFormError(response, 'form', 'speaker', 'speaker1 is already on the list of speakers.')
|
||||
|
||||
def test_group_as_speaker(self):
|
||||
"""
|
||||
Test to show a group as a speaker on the agenda-view.
|
||||
"""
|
||||
group = Group.objects.create(name='test', group_as_person=True)
|
||||
Speaker.objects.add(group, self.item1)
|
||||
self.assertTrue(Speaker.objects.filter(person=group.person_id, item=self.item1).exists())
|
||||
self.admin_client.get('/agenda/1/')
|
||||
|
||||
|
||||
class TestSpeakerDeleteView(SpeakerViewTestCase):
|
||||
def test_get(self):
|
||||
@ -190,7 +181,7 @@ class TestSpeakerDeleteView(SpeakerViewTestCase):
|
||||
response = self.admin_client.post(
|
||||
'/agenda/1/speaker/%d/del/' % speaker.pk, {'yes': 'yes'})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertFalse(Speaker.objects.filter(person=self.speaker1, item=self.item1).exists())
|
||||
self.assertFalse(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists())
|
||||
|
||||
def test_post_as_user(self):
|
||||
Speaker.objects.add(self.speaker1, self.item1)
|
||||
@ -198,14 +189,14 @@ class TestSpeakerDeleteView(SpeakerViewTestCase):
|
||||
response = self.speaker1_client.post(
|
||||
'/agenda/1/speaker/del/', {'yes': 'yes'})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertFalse(Speaker.objects.filter(person=self.speaker1, item=self.item1).exists())
|
||||
self.assertFalse(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists())
|
||||
|
||||
|
||||
class TestSpeakerSpeakView(SpeakerViewTestCase):
|
||||
def test_get(self):
|
||||
url = '/agenda/1/speaker/%s/speak/' % self.speaker1.person_id
|
||||
url = '/agenda/1/speaker/%s/speak/' % self.speaker1.pk
|
||||
response = self.check_url(url, self.admin_client, 302)
|
||||
self.assertMessage(response, 'user:2 is not on the list of item1.')
|
||||
self.assertMessage(response, '2 is not on the list of item1.')
|
||||
|
||||
speaker = Speaker.objects.add(self.speaker1, self.item1)
|
||||
response = self.check_url(url, self.admin_client, 302)
|
||||
@ -257,7 +248,7 @@ class GlobalListOfSpeakersLinks(SpeakerViewTestCase):
|
||||
set_active_slide('agenda', pk=1)
|
||||
response = self.speaker1_client.get('/agenda/list_of_speakers/add/')
|
||||
self.assertRedirects(response, '/agenda/1/')
|
||||
self.assertEqual(Speaker.objects.get(item__pk='1').person, self.speaker1)
|
||||
self.assertEqual(Speaker.objects.get(item__pk='1').user, self.speaker1)
|
||||
self.assertMessage(response, 'You were successfully added to the list of speakers.')
|
||||
|
||||
perm = Permission.objects.filter(name='Can see agenda').get()
|
||||
@ -281,7 +272,7 @@ class GlobalListOfSpeakersLinks(SpeakerViewTestCase):
|
||||
response = self.speaker1_client.get('/agenda/list_of_speakers/add/')
|
||||
|
||||
self.assertRedirects(response, '/agenda/%d/' % agenda_item.pk)
|
||||
self.assertEqual(Speaker.objects.get(item__pk=agenda_item.pk).person, self.speaker1)
|
||||
self.assertEqual(Speaker.objects.get(item__pk=agenda_item.pk).user, self.speaker1)
|
||||
self.assertMessage(response, 'You were successfully added to the list of speakers.')
|
||||
|
||||
def test_global_next_speaker_url(self):
|
||||
|
@ -7,7 +7,7 @@ from django.test.client import Client
|
||||
|
||||
from openslides.agenda.models import Item
|
||||
from openslides.agenda.slides import agenda_slide
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
from openslides.projector.api import set_active_slide
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
@ -127,8 +127,7 @@ class ViewTest(TestCase):
|
||||
self.refreshItems()
|
||||
|
||||
self.admin = User.objects.get(pk=1)
|
||||
self.anonym, created = User.objects.get_or_create(username='testanonym')
|
||||
self.anonym.reset_password('default')
|
||||
self.anonym = User.objects.create_user('testanonym', 'default')
|
||||
|
||||
def refreshItems(self):
|
||||
self.item1 = Item.objects.get(pk=self.item1.id)
|
||||
@ -268,8 +267,7 @@ class ViewTest(TestCase):
|
||||
# Prepare
|
||||
self.item1.type = Item.ORGANIZATIONAL_ITEM
|
||||
self.item1.save()
|
||||
user = User.objects.create(username='testuser_EeBoPh5uyookoowoodii')
|
||||
user.reset_password('default')
|
||||
user = User.objects.create_user('testuser_EeBoPh5uyookoowoodii', 'default')
|
||||
client = Client()
|
||||
client.login(username='testuser_EeBoPh5uyookoowoodii', password='default')
|
||||
# Test view with permission
|
||||
@ -334,12 +332,8 @@ class ViewTest(TestCase):
|
||||
|
||||
class ConfigTest(TestCase):
|
||||
def setUp(self):
|
||||
self.admin = User.objects.create(username='config_test_admin')
|
||||
self.admin.reset_password('default')
|
||||
self.admin.is_superuser = True
|
||||
self.admin.save()
|
||||
self.client = Client()
|
||||
self.client.login(username='config_test_admin', password='default')
|
||||
self.client.login(username='admin', password='admin')
|
||||
|
||||
def test_config_collection_css_javascript(self):
|
||||
response = self.client.get('/config/agenda/')
|
||||
|
@ -2,7 +2,7 @@ from django.test.client import Client
|
||||
|
||||
from openslides.agenda.models import Item, Speaker
|
||||
from openslides.assignment.models import Assignment
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -34,6 +34,6 @@ class AssignmentModelTest(TestCase):
|
||||
self.assertEqual(item.speaker_set.count(), 1)
|
||||
|
||||
assignment.gen_poll()
|
||||
self.assertTrue(item.speaker_set.filter(person=person_1).exists())
|
||||
self.assertTrue(item.speaker_set.filter(person=person_2).exists())
|
||||
self.assertTrue(item.speaker_set.filter(person=person_3).exists())
|
||||
self.assertTrue(item.speaker_set.filter(user=person_1).exists())
|
||||
self.assertTrue(item.speaker_set.filter(user=person_2).exists())
|
||||
self.assertTrue(item.speaker_set.filter(user=person_3).exists())
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.test.client import Client
|
||||
|
||||
from openslides.assignment.models import Assignment
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@ from django.test.client import Client
|
||||
|
||||
from openslides.assignment.models import Assignment, AssignmentPoll
|
||||
from openslides.config.api import config
|
||||
from openslides.participant.models import Group, User
|
||||
from openslides.users.models import Group, User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ class AssignmentViewTestCase(TestCase):
|
||||
self.admin_client.login(username='admin', password='admin')
|
||||
|
||||
# Staff
|
||||
self.staff = User.objects.create_user('staff', 'staff@user.user', 'staff')
|
||||
self.staff = User.objects.create_user('staff', 'staff')
|
||||
staff_group = Group.objects.get(name='Staff')
|
||||
self.staff.groups.add(staff_group)
|
||||
self.staff.save()
|
||||
@ -22,7 +22,7 @@ class AssignmentViewTestCase(TestCase):
|
||||
self.staff_client.login(username='staff', password='staff')
|
||||
|
||||
# Delegate
|
||||
self.delegate = User.objects.create_user('delegate', 'delegate@user.user', 'delegate')
|
||||
self.delegate = User.objects.create_user(username='delegate', password='delegate')
|
||||
delegate_group = Group.objects.get(name='Delegates')
|
||||
self.delegate.groups.add(delegate_group)
|
||||
self.delegate.save()
|
||||
@ -30,7 +30,7 @@ class AssignmentViewTestCase(TestCase):
|
||||
self.delegate_client.login(username='delegate', password='delegate')
|
||||
|
||||
# Registered
|
||||
self.registered = User.objects.create_user('registered', 'registered@user.user', 'registered')
|
||||
self.registered = User.objects.create_user(username='registered', password='registered')
|
||||
self.registered_client = Client()
|
||||
self.registered_client.login(username='registered', password='registered')
|
||||
|
||||
|
@ -12,7 +12,7 @@ from openslides.config.api import (config, ConfigCollection, ConfigGroup,
|
||||
ConfigGroupedCollection, ConfigVariable)
|
||||
from openslides.config.exceptions import ConfigError, ConfigNotFound
|
||||
from openslides.config.signals import config_signal
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -105,12 +105,10 @@ class ConfigFormTest(TestCase):
|
||||
perm = Permission.objects.get(content_type=ct, codename='can_manage')
|
||||
|
||||
# Setup two users
|
||||
self.manager = User.objects.create(username='config_test_manager')
|
||||
self.manager.reset_password('default')
|
||||
self.manager = User.objects.create_user('config_test_manager', 'default')
|
||||
self.manager.user_permissions.add(perm)
|
||||
|
||||
self.normal_user = User.objects.create(username='config_test_normal_user')
|
||||
self.normal_user.reset_password('default')
|
||||
self.normal_user = User.objects.create_user('config_test_normal_user', 'default')
|
||||
|
||||
# Login
|
||||
self.client_manager = Client()
|
||||
@ -254,8 +252,7 @@ class ConfigWeightTest(TestCase):
|
||||
perm = Permission.objects.get(content_type=ct, codename='can_manage')
|
||||
|
||||
# Setup two users
|
||||
self.manager = User.objects.create(username='config_test_manager')
|
||||
self.manager.reset_password('default')
|
||||
self.manager = User.objects.create_user('config_test_manager', 'default')
|
||||
self.manager.user_permissions.add(perm)
|
||||
|
||||
# Login
|
||||
|
@ -7,7 +7,7 @@ from openslides.agenda.models import Item
|
||||
from openslides.config.api import config
|
||||
from openslides.core import views
|
||||
from openslides.core.models import CustomSlide
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ class SelectWidgetsViewTest(TestCase):
|
||||
|
||||
class VersionViewTest(TestCase):
|
||||
def setUp(self):
|
||||
User.objects.create_user('CoreMaximilian', 'xxx@xx.xx', 'default')
|
||||
User.objects.create_user('CoreMaximilian', 'default')
|
||||
self.client = Client()
|
||||
self.client.login(username='CoreMaximilian', password='default')
|
||||
|
||||
@ -87,7 +87,7 @@ class VersionViewTest(TestCase):
|
||||
class SearchViewTest(TestCase):
|
||||
def test_simple_search(self):
|
||||
Item.objects.create(title='agenda_item_bnghfdjkgndkjdfg')
|
||||
User.objects.create_user('CoreMaximilian', 'xxx@xx.xx', 'default')
|
||||
User.objects.create_user('CoreMaximilian', 'default')
|
||||
self.client = Client()
|
||||
self.client.login(username='CoreMaximilian', password='default')
|
||||
response = self.client.get('/search/?q=agenda_item_bnghfd')
|
||||
|
@ -8,7 +8,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.test.client import Client
|
||||
|
||||
from openslides.mediafile.models import Mediafile
|
||||
from openslides.participant.models import User
|
||||
from openslides.core.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -24,11 +24,9 @@ class MediafileTest(TestCase):
|
||||
|
||||
# Setup three different users
|
||||
self.manager = User.objects.get(pk=1)
|
||||
self.vip_user = User.objects.create(username='mediafile_test_vip_user')
|
||||
self.vip_user.reset_password('default')
|
||||
self.vip_user = User.objects.create_user('mediafile_test_vip_user', 'default')
|
||||
self.vip_user.user_permissions.add(perm_1, perm_2)
|
||||
self.normal_user = User.objects.create(username='mediafile_test_normal_user')
|
||||
self.normal_user.reset_password('default')
|
||||
self.normal_user = User.objects.create_user('mediafile_test_normal_user', 'default')
|
||||
|
||||
# Setup a mediafile object
|
||||
self.tmp_dir = settings.MEDIA_ROOT
|
||||
@ -74,13 +72,16 @@ class MediafileTest(TestCase):
|
||||
clients = self.login_clients()
|
||||
response = clients['client_manager'].get('/mediafile/new/')
|
||||
self.assertContains(response, '---------', status_code=200)
|
||||
self.assertContains(response, '<option value="user:1" selected="selected">Administrator</option>', status_code=200)
|
||||
self.assertContains(response, '<option value="1" selected="selected">Administrator</option>', status_code=200)
|
||||
self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
|
||||
|
||||
response = clients['client_vip_user'].get('/mediafile/new/')
|
||||
self.assertNotContains(response, '<select id="id_uploader" name="uploader">', status_code=200)
|
||||
self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
|
||||
|
||||
response = clients['client_normal_user'].get('/mediafile/new/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
bad_client = Client()
|
||||
response = bad_client.get('/mediafile/new/')
|
||||
self.assertRedirects(response, expected_url='/login/?next=/mediafile/new/', status_code=302, target_status_code=200)
|
||||
@ -92,7 +93,7 @@ class MediafileTest(TestCase):
|
||||
response_1 = client_1.post('/mediafile/new/',
|
||||
{'title': 'new_test_file_title_1',
|
||||
'mediafile': new_file_1,
|
||||
'uploader': self.normal_user.person_id})
|
||||
'uploader': self.normal_user.pk})
|
||||
self.assertRedirects(response_1, expected_url='/mediafile/', status_code=302, target_status_code=200)
|
||||
object_1 = Mediafile.objects.latest('timestamp')
|
||||
self.assertEqual(object_1.mediafile.url, '/media/file/new_test_file.txt')
|
||||
@ -129,12 +130,15 @@ class MediafileTest(TestCase):
|
||||
clients = self.login_clients()
|
||||
response = clients['client_manager'].get('/mediafile/1/edit/')
|
||||
self.assertContains(response, '---------', status_code=200)
|
||||
self.assertContains(response, '<option value="user:3" selected="selected">mediafile_test_normal_user</option>', status_code=200)
|
||||
self.assertContains(response, '<option value="3" selected="selected">mediafile_test_normal_user</option>', status_code=200)
|
||||
self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
|
||||
|
||||
response = clients['client_vip_user'].get('/mediafile/1/edit/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
response = clients['client_normal_user'].get('/mediafile/1/edit/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
bad_client = Client()
|
||||
response = bad_client.get('/mediafile/1/edit/')
|
||||
self.assertRedirects(response, expected_url='/login/?next=/mediafile/1/edit/', status_code=302, target_status_code=200)
|
||||
@ -145,7 +149,7 @@ class MediafileTest(TestCase):
|
||||
self.object.save()
|
||||
response = clients['client_vip_user'].get('/mediafile/1/edit/')
|
||||
self.assertNotContains(response, '---------', status_code=200)
|
||||
self.assertNotContains(response, '<option value="user:2" selected="selected">mediafile_test_vip_user</option>', status_code=200)
|
||||
self.assertNotContains(response, '<option value="2" selected="selected">mediafile_test_vip_user</option>', status_code=200)
|
||||
self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
|
||||
|
||||
def test_edit_mediafile_post_request(self):
|
||||
@ -158,7 +162,7 @@ class MediafileTest(TestCase):
|
||||
response_1 = client_1.post('/mediafile/2/edit/',
|
||||
{'title': 'new_test_file_title_1',
|
||||
'mediafile': new_file_1,
|
||||
'uploader': self.manager.person_id})
|
||||
'uploader': self.manager.pk})
|
||||
self.assertEqual(response_1.status_code, 302)
|
||||
object_2 = Mediafile.objects.get(pk=2)
|
||||
self.assertEqual(object_2.mediafile.url, '/media/file/new_test_file.txt')
|
||||
|
@ -1,29 +1,23 @@
|
||||
import os
|
||||
from io import BytesIO
|
||||
|
||||
from django.test.client import Client
|
||||
|
||||
from openslides.motion.csv_import import import_motions
|
||||
from openslides.motion.models import Category, Motion
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
class CSVImport(TestCase):
|
||||
def setUp(self):
|
||||
# Admin
|
||||
self.admin = User.objects.create_superuser('Admin_ieY0Eereimeimeizuosh', 'admin@admin.admin', 'eHiK1aiRahxaix0Iequ2')
|
||||
self.admin_client = Client()
|
||||
self.admin_client.login(username='Admin_ieY0Eereimeimeizuosh', password='eHiK1aiRahxaix0Iequ2')
|
||||
# User1
|
||||
self.user1 = User.objects.create_user('Admin_ieY0Eereimeimeizuosh', 'eHiK1aiRahxaix0Iequ2')
|
||||
|
||||
# Normal user
|
||||
self.normal_user = User.objects.create_user('User_CiuNgo4giqueeChie5oi', 'user@user.user', 'eihi1Eequaek4eagaiKu')
|
||||
self.normal_client = Client()
|
||||
self.normal_client.login(username='User_CiuNgo4giqueeChie5oi', password='eihi1Eequaek4eagaiKu')
|
||||
self.normal_user = User.objects.create_user('User_CiuNgo4giqueeChie5oi', 'eihi1Eequaek4eagaiKu')
|
||||
|
||||
# Category
|
||||
self.category1 = Category.objects.create(name='Bildung', prefix='B1')
|
||||
self.category2 = Category.objects.create(name='Bildung', prefix='B2')
|
||||
Category.objects.create(name='Bildung', prefix='B1')
|
||||
Category.objects.create(name='Bildung', prefix='B2')
|
||||
|
||||
def test_example_file_de(self):
|
||||
special_user = User.objects.create_user(username='Harry_Holland',
|
||||
@ -41,7 +35,7 @@ class CSVImport(TestCase):
|
||||
self.assertEqual(Motion.objects.count(), 0)
|
||||
with open(csv_dir + '/motions-demo_de.csv', 'rb') as f:
|
||||
success_message, warning_message, error_message = import_motions(
|
||||
csvfile=f, default_submitter=self.normal_user.person_id, override=False, importing_person=self.admin)
|
||||
csvfile=f, default_submitter=self.normal_user, override=False, importing_person=self.user1)
|
||||
self.assertEqual(Motion.objects.count(), 11)
|
||||
|
||||
motion1 = Motion.objects.get(pk=1)
|
||||
@ -74,7 +68,7 @@ class CSVImport(TestCase):
|
||||
csv_file = BytesIO()
|
||||
csv_file.write(bytes('Header\nMalformed data,\n,Title,Text,,,\n', 'utf8'))
|
||||
success_message, warning_message, error_message = import_motions(
|
||||
csvfile=csv_file, default_submitter=self.normal_user.person_id, override=False)
|
||||
csvfile=csv_file, default_submitter=self.normal_user.id, override=False)
|
||||
self.assertEqual(success_message, '')
|
||||
self.assertTrue('Line is malformed.' in error_message)
|
||||
|
||||
@ -82,7 +76,7 @@ class CSVImport(TestCase):
|
||||
csv_file = BytesIO(bytes('Müller', 'iso-8859-15'))
|
||||
success_message, warning_message, error_message = import_motions(
|
||||
csvfile=csv_file,
|
||||
default_submitter=self.normal_user.person_id,
|
||||
default_submitter=self.normal_user.id,
|
||||
override=False)
|
||||
self.assertEqual(success_message, '')
|
||||
self.assertIn('Import file has wrong character encoding, only UTF-8 is supported!', error_message)
|
||||
|
@ -1,7 +1,7 @@
|
||||
from openslides.config.api import config
|
||||
from openslides.motion.exceptions import WorkflowError
|
||||
from openslides.motion.models import Motion, State, Workflow
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.test.client import Client
|
||||
from openslides.motion.models import Motion
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@ from django.test.client import Client
|
||||
from openslides.config.api import config
|
||||
from openslides.mediafile.models import Mediafile
|
||||
from openslides.motion.models import Category, Motion, MotionLog, State
|
||||
from openslides.participant.models import Group, User
|
||||
from openslides.users.models import Group, User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ class MotionViewTestCase(TestCase):
|
||||
self.admin_client.login(username='admin', password='admin')
|
||||
|
||||
# Staff
|
||||
self.staff = User.objects.create_user('staff', 'staff@user.user', 'staff')
|
||||
self.staff = User.objects.create_user('staff', 'staff')
|
||||
staff_group = Group.objects.get(name='Staff')
|
||||
self.staff.groups.add(staff_group)
|
||||
self.staff.save()
|
||||
@ -27,7 +27,7 @@ class MotionViewTestCase(TestCase):
|
||||
self.staff_client.login(username='staff', password='staff')
|
||||
|
||||
# Delegate
|
||||
self.delegate = User.objects.create_user('delegate', 'delegate@user.user', 'delegate')
|
||||
self.delegate = User.objects.create_user('delegate', 'delegate')
|
||||
delegate_group = Group.objects.get(name='Delegates')
|
||||
self.delegate.groups.add(delegate_group)
|
||||
self.delegate.save()
|
||||
@ -35,7 +35,7 @@ class MotionViewTestCase(TestCase):
|
||||
self.delegate_client.login(username='delegate', password='delegate')
|
||||
|
||||
# Registered
|
||||
self.registered = User.objects.create_user('registered', 'registered@user.user', 'registered')
|
||||
self.registered = User.objects.create_user('registered', 'registered')
|
||||
self.registered_client = Client()
|
||||
self.registered_client.login(username='registered', password='registered')
|
||||
|
||||
@ -95,7 +95,6 @@ class TestMotionDetailView(MotionViewTestCase):
|
||||
self.assertContains(self.admin_client.get('/motion/1/'), 'registered')
|
||||
self.registered.delete()
|
||||
self.assertNotContains(self.admin_client.get('/motion/1/'), 'registered')
|
||||
self.assertContains(self.admin_client.get('/motion/1/'), 'empty')
|
||||
|
||||
|
||||
class TestMotionDetailVersionView(MotionViewTestCase):
|
||||
@ -126,7 +125,7 @@ class TestMotionCreateView(MotionViewTestCase):
|
||||
response = self.delegate_client.post(self.url, {'title': 'delegate motion',
|
||||
'text': 'motion text',
|
||||
'reason': 'motion reason',
|
||||
'submitter': self.admin.person_id})
|
||||
'submitter': self.admin.id})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
motion = Motion.objects.get(versions__title='delegate motion')
|
||||
self.assertTrue(motion.is_submitter(self.delegate))
|
||||
@ -135,7 +134,7 @@ class TestMotionCreateView(MotionViewTestCase):
|
||||
response = self.registered_client.post(self.url, {'title': 'registered motion',
|
||||
'text': 'motion text',
|
||||
'reason': 'motion reason',
|
||||
'submitter': self.admin.person_id})
|
||||
'submitter': self.admin.id})
|
||||
self.assertEqual(response.status_code, 403)
|
||||
self.assertFalse(Motion.objects.filter(versions__title='registered motion').exists())
|
||||
|
||||
@ -164,13 +163,13 @@ class TestMotionCreateView(MotionViewTestCase):
|
||||
config['motion_identifier'] = 'manually'
|
||||
response = self.admin_client.post(self.url, {'title': 'something',
|
||||
'text': 'bar',
|
||||
'submitter': self.admin.person_id,
|
||||
'submitter': self.admin.id,
|
||||
'identifier': 'uufag5faoX0thahBi8Fo'})
|
||||
self.assertFormError(response, 'form', 'identifier', 'Motion with this Identifier already exists.')
|
||||
|
||||
def test_empty_text_field(self):
|
||||
response = self.admin_client.post(self.url, {'title': 'foo',
|
||||
'submitter': self.admin.person_id})
|
||||
'submitter': self.admin.id})
|
||||
self.assertFormError(response, 'form', 'text', 'This field is required.')
|
||||
|
||||
def test_identifier_with_category_prefix(self):
|
||||
@ -201,7 +200,7 @@ class TestMotionUpdateView(MotionViewTestCase):
|
||||
response = self.admin_client.post(self.url, {'title': 'new motion_title',
|
||||
'text': 'motion text',
|
||||
'reason': 'motion reason',
|
||||
'submitter': self.admin.person_id,
|
||||
'submitter': self.admin.id,
|
||||
'workflow': 1})
|
||||
self.assertRedirects(response, '/motion/1/')
|
||||
motion = Motion.objects.get(pk=1)
|
||||
@ -235,7 +234,7 @@ class TestMotionUpdateView(MotionViewTestCase):
|
||||
'text': 'another motion text',
|
||||
'reason': 'another motion reason',
|
||||
'workflow': workflow.pk,
|
||||
'submitter': self.admin.person_id})
|
||||
'submitter': self.admin.id})
|
||||
self.assertRedirects(response, '/motion/1/')
|
||||
motion = Motion.objects.get(pk=self.motion1.pk)
|
||||
self.assertEqual(motion.versions.count(), 2)
|
||||
@ -254,7 +253,7 @@ class TestMotionUpdateView(MotionViewTestCase):
|
||||
response = self.admin_client.post(self.url, {'title': 'another new motion_title',
|
||||
'text': 'another motion text',
|
||||
'reason': 'another motion reason',
|
||||
'submitter': self.admin.person_id,
|
||||
'submitter': self.admin.id,
|
||||
'workflow': workflow.pk,
|
||||
'disable_versioning': 'true'})
|
||||
self.assertRedirects(response, '/motion/1/')
|
||||
@ -278,7 +277,7 @@ class TestMotionUpdateView(MotionViewTestCase):
|
||||
'text': 'eedieFoothae2iethuo3',
|
||||
'reason': 'ier2laiy1veeGoo0mau2',
|
||||
'workflow': workflow.pk,
|
||||
'submitter': self.admin.person_id})
|
||||
'submitter': self.admin.id})
|
||||
self.assertRedirects(response, '/motion/1/')
|
||||
motion = Motion.objects.get(pk=self.motion1.pk)
|
||||
self.assertEqual(motion.versions.count(), 1)
|
||||
@ -287,11 +286,11 @@ class TestMotionUpdateView(MotionViewTestCase):
|
||||
self.assertEqual(self.motion1.state.workflow.pk, 1)
|
||||
response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2',
|
||||
'text': 'eequei1Tee1aegeNgee0',
|
||||
'submitter': self.admin.person_id})
|
||||
'submitter': self.admin.id})
|
||||
self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 1)
|
||||
response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2',
|
||||
'text': 'eequei1Tee1aegeNgee0',
|
||||
'submitter': self.admin.person_id,
|
||||
'submitter': self.admin.id,
|
||||
'workflow': 2})
|
||||
self.assertRedirects(response, '/motion/1/')
|
||||
self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 2)
|
||||
@ -302,6 +301,7 @@ class TestMotionUpdateView(MotionViewTestCase):
|
||||
motion = Motion.objects.create(title='cuoPhoX4Baifoxoothi3', text='zee7xei3taediR9loote')
|
||||
response = self.staff_client.get('/motion/%s/' % motion.id)
|
||||
self.assertNotContains(response, 'aengeing3quair3fieGi')
|
||||
|
||||
motion.support(self.registered)
|
||||
self.registered.last_name = 'aengeing3quair3fieGi'
|
||||
self.registered.save()
|
||||
@ -313,7 +313,7 @@ class TestMotionUpdateView(MotionViewTestCase):
|
||||
'/motion/%s/edit/' % motion.id,
|
||||
{'title': 'oori4KiaghaeSeuzaim2',
|
||||
'text': 'eequei1Tee1aegeNgee0',
|
||||
'submitter': self.delegate.person_id})
|
||||
'submitter': self.delegate.id})
|
||||
self.assertEqual(response.status_code, 403)
|
||||
motion.add_submitter(self.delegate)
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
VERSION = (9999, 9999, 9999, 'alpha', 1)
|
@ -1,48 +0,0 @@
|
||||
from django.db import models
|
||||
from django.dispatch import receiver
|
||||
|
||||
from openslides.utils.person.models import PersonField, PersonMixin
|
||||
from openslides.utils.person.signals import receive_persons
|
||||
|
||||
|
||||
class TestPerson(PersonMixin, models.Model):
|
||||
person_prefix = 'test'
|
||||
name = models.CharField(max_length='255')
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self, link='detail'):
|
||||
return 'absolute_url_of_test_person'
|
||||
|
||||
|
||||
class TestPersonToPerson(object):
|
||||
def __init__(self, person_prefix_filter=None, id_filter=None):
|
||||
self.person_prefix_filter = person_prefix_filter
|
||||
self.id_filter = id_filter
|
||||
|
||||
def __iter__(self):
|
||||
if (not self.person_prefix_filter or
|
||||
self.person_prefix_filter == TestPerson.person_prefix):
|
||||
if self.id_filter:
|
||||
try:
|
||||
yield TestPerson.objects.get(pk=self.id_filter)
|
||||
except TestPerson.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
for user in TestPerson.objects.all():
|
||||
yield user
|
||||
|
||||
|
||||
@receiver(receive_persons, dispatch_uid="test_person")
|
||||
def receive_persons(sender, **kwargs):
|
||||
return TestPersonToPerson(
|
||||
person_prefix_filter=kwargs['person_prefix_filter'],
|
||||
id_filter=kwargs['id_filter'])
|
||||
|
||||
|
||||
class TestModel(models.Model):
|
||||
person = PersonField()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.person
|
@ -1,43 +0,0 @@
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
|
||||
from openslides.utils.person.api import get_person
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
from .models import TestModel, TestPerson
|
||||
|
||||
|
||||
class PersonTest(TestCase):
|
||||
def setUp(self):
|
||||
self.person1 = TestPerson.objects.create(name='test1')
|
||||
|
||||
def test_update_of_person_field(self):
|
||||
self.assertEqual(self.person1.person_id, 'test:1')
|
||||
|
||||
# save person field
|
||||
test_object = TestModel.objects.create(person=self.person1)
|
||||
self.assertEqual(test_object.person, self.person1)
|
||||
|
||||
# update person field
|
||||
test_object.save()
|
||||
self.assertEqual(TestModel.objects.get(pk=test_object.pk).person, self.person1)
|
||||
|
||||
def test_save_anonymous_user_in_person_field(self):
|
||||
with self.assertRaisesRegex(
|
||||
AttributeError,
|
||||
'An AnonymousUser can not be saved into the database.'):
|
||||
TestModel.objects.create(person=AnonymousUser())
|
||||
|
||||
def test_save_unsupported_object_in_person_field(self):
|
||||
with self.assertRaisesRegex(
|
||||
AttributeError,
|
||||
'You can not save \'<class \'int\'>\' into a person field.'):
|
||||
TestModel.objects.create(person=5)
|
||||
|
||||
def test_get_absolute_url_with_deleted_person(self):
|
||||
person2 = TestPerson.objects.create(name='test2')
|
||||
self.assertEqual(person2.get_absolute_url(), 'absolute_url_of_test_person')
|
||||
person_id = person2.person_id
|
||||
self.assertEqual(get_person(person_id).get_absolute_url(), 'absolute_url_of_test_person')
|
||||
person2.delete()
|
||||
with self.assertRaisesRegex(ValueError, 'This person object has no url.'):
|
||||
get_person(person_id).get_absolute_url()
|
@ -9,11 +9,11 @@ from openslides.global_settings import * # noqa
|
||||
OPENSLIDES_USER_DATA_PATH = os.path.realpath(os.path.dirname(__file__))
|
||||
|
||||
# Use 'DEBUG = True' to get more details for server errors. Default for releases: False
|
||||
DEBUG = True
|
||||
DEBUG = False
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = 'secred'
|
||||
SECRET_KEY = 'secret'
|
||||
|
||||
# Database settings. Change this to use MySQL or PostgreSQL
|
||||
DATABASES = {
|
||||
@ -25,13 +25,6 @@ DATABASES = {
|
||||
'HOST': '',
|
||||
'PORT': ''}}
|
||||
|
||||
# Add OpenSlides plugins to this list
|
||||
INSTALLED_PLUGINS = (
|
||||
'tests.person_api',
|
||||
)
|
||||
|
||||
INSTALLED_APPS += INSTALLED_PLUGINS
|
||||
|
||||
# Some other settings
|
||||
TIME_ZONE = 'Europe/Berlin'
|
||||
|
||||
|
@ -2,7 +2,7 @@ from io import BytesIO
|
||||
from textwrap import dedent
|
||||
|
||||
from openslides.utils.test import TestCase
|
||||
from openslides.participant.csv_import import import_users
|
||||
from openslides.users.csv_import import import_users
|
||||
|
||||
|
||||
class TestCSVImport(TestCase):
|
@ -1,31 +1,25 @@
|
||||
from openslides.participant.api import gen_password, gen_username
|
||||
from openslides.participant.models import Group, User
|
||||
from openslides.utils.person import get_person, Persons
|
||||
from openslides.users.api import gen_password, gen_username
|
||||
from openslides.users.models import Group, User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
class UserTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user1 = User()
|
||||
self.user1.first_name = u'Max'
|
||||
self.user1.last_name = u'Mustermann'
|
||||
self.user1.first_name = 'Max'
|
||||
self.user1.last_name = 'Mustermann'
|
||||
self.user1.username = gen_username(
|
||||
self.user1.first_name, self.user1.last_name)
|
||||
self.user1.default_password = gen_password()
|
||||
self.user1.save()
|
||||
self.django_user1 = self.user1.django_user
|
||||
|
||||
def test_participant_user(self):
|
||||
self.assertEqual(self.django_user1.user, self.user1)
|
||||
self.assertEqual(self.django_user1, self.user1.django_user)
|
||||
|
||||
def test_str(self):
|
||||
self.assertEqual(str(self.user1), 'Max Mustermann')
|
||||
|
||||
def test_name_suffix(self):
|
||||
self.user1.structure_level = u'München'
|
||||
self.user1.structure_level = 'München'
|
||||
self.user1.save()
|
||||
self.assertEqual(str(self.user1), u'Max Mustermann (München)')
|
||||
self.assertEqual(str(self.user1), 'Max Mustermann (München)')
|
||||
|
||||
def test_reset_password(self):
|
||||
self.assertIsInstance(self.user1.default_password, str)
|
||||
@ -35,39 +29,14 @@ class UserTest(TestCase):
|
||||
self.user1.reset_password()
|
||||
self.assertTrue(self.user1.check_password(self.user1.default_password))
|
||||
|
||||
def test_person_api(self):
|
||||
self.assertTrue(hasattr(self.user1, 'person_id'))
|
||||
self.assertEqual(self.user1.person_id, 'user:2')
|
||||
self.assertEqual(get_person('user:2'), self.user1)
|
||||
self.assertEqual(len(Persons(person_prefix_filter='user')), 2)
|
||||
|
||||
def test_get_absolute_url(self):
|
||||
urls = (('detail', '/participant/2/'),
|
||||
('update', '/participant/2/edit/'),
|
||||
('delete', '/participant/2/del/'))
|
||||
urls = (('detail', '/user/2/'),
|
||||
('update', '/user/2/edit/'),
|
||||
('delete', '/user/2/del/'))
|
||||
for link, url in urls:
|
||||
self.assertEqual(self.user1.get_absolute_url(link), url)
|
||||
|
||||
|
||||
class GroupTest(TestCase):
|
||||
def setUp(self):
|
||||
self.group1 = Group.objects.create(name='Test Group')
|
||||
self.django_group1 = self.group1.django_group
|
||||
|
||||
def test_group_group(self):
|
||||
self.assertEqual(self.group1.django_group, self.django_group1)
|
||||
self.assertEqual(self.group1, self.django_group1.group)
|
||||
|
||||
def test_person_api(self):
|
||||
self.assertTrue(hasattr(self.group1, 'person_id'))
|
||||
person_id = "group:%d" % self.group1.id
|
||||
self.assertEqual(self.group1.person_id, person_id)
|
||||
self.assertRaises(Group.DoesNotExist)
|
||||
self.group1.group_as_person = True
|
||||
self.group1.save()
|
||||
self.assertEqual(get_person(person_id), self.group1)
|
||||
|
||||
|
||||
class DefaultGroups(TestCase):
|
||||
|
||||
def test_pks_of_default_groups(self):
|
||||
@ -86,7 +55,7 @@ class DefaultGroups(TestCase):
|
||||
'agenda.can_see_orga_items',
|
||||
'motion.can_see_motion',
|
||||
'assignment.can_see_assignment',
|
||||
'participant.can_see_participant',
|
||||
'users.can_see',
|
||||
'mediafile.can_see')
|
||||
for perm_string in default_perms:
|
||||
perm_string_list = []
|
@ -1,7 +1,7 @@
|
||||
from django.test.client import Client
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from openslides.participant.models import Group, User
|
||||
from openslides.users.models import Group, User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -12,8 +12,7 @@ class TestUmlautUser(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(username='äöü')
|
||||
self.user.reset_password('äöü')
|
||||
self.user = User.objects.create_user('äöü', 'äöü')
|
||||
self.client = Client()
|
||||
self.client.login(username='äöü', password='äöü')
|
||||
|
||||
@ -32,10 +31,10 @@ class TestUmlautUser(TestCase):
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_permission(self):
|
||||
response = self.client.get('/participant/1/edit/')
|
||||
response = self.client.get('/user/1/edit/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
self.user.groups.add(Group.objects.get(pk=4))
|
||||
|
||||
response = self.client.get('/participant/1/edit/')
|
||||
response = self.client.get('/user/1/edit/')
|
||||
self.assertEqual(response.status_code, 200)
|
@ -1,5 +1,5 @@
|
||||
from openslides.participant.api import gen_username
|
||||
from openslides.participant.models import User
|
||||
from openslides.users.api import gen_username
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
@ -5,8 +5,8 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.test.client import Client
|
||||
|
||||
from openslides.config.api import config
|
||||
from openslides.participant.api import get_registered_group
|
||||
from openslides.participant.models import get_protected_perm, Group, User
|
||||
from openslides.users.api import get_registered_group
|
||||
from openslides.users.models import Group, User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -20,37 +20,41 @@ class UserViews(TestCase):
|
||||
self.client.login(username='admin', password='admin')
|
||||
|
||||
def test_create(self):
|
||||
response = self.client.get('/participant/new/')
|
||||
self.assertTemplateUsed(response, 'participant/edit.html')
|
||||
self.assertContains(response, 'New participant')
|
||||
response = self.client.post('/participant/new/', {'first_name': 'test_name_ho8hui2niz4nohSupahb'})
|
||||
self.assertRedirects(response, '/participant/')
|
||||
response = self.client.get('/user/new/')
|
||||
|
||||
self.assertTemplateUsed(response, 'users/user_form.html')
|
||||
self.assertContains(response, 'New user')
|
||||
response = self.client.post('/user/new/', {'first_name': 'test_name_ho8hui2niz4nohSupahb'})
|
||||
self.assertRedirects(response, '/user/')
|
||||
|
||||
def test_create_multiple(self):
|
||||
response = self.client.get('/participant/new_multiple/')
|
||||
self.assertTemplateUsed(response, 'participant/user_form_multiple.html')
|
||||
self.assertContains(response, 'New multiple participants')
|
||||
response = self.client.get('/user/new_multiple/')
|
||||
self.assertTemplateUsed(response, 'users/user_form_multiple.html')
|
||||
self.assertContains(response, 'New multiple users')
|
||||
self.assertEqual(User.objects.count(), 1)
|
||||
block = ('first_name_ksdjfhkjsdhf75utgeitrten last_name_khonizt958zh8fh\n'
|
||||
'first_name_1_bmgnf7z8ru first_name_2_kjc98vivt last_name_dfg76kjkjuibv')
|
||||
response = self.client.post('/participant/new_multiple/',
|
||||
{'participants_block': block})
|
||||
response = self.client.post('/user/new_multiple/',
|
||||
{'users_block': block})
|
||||
self.assertEqual(User.objects.count(), 3)
|
||||
|
||||
def test_update(self):
|
||||
response = self.client.get('/participant/1/edit/')
|
||||
self.assertTemplateUsed(response, 'participant/edit.html')
|
||||
self.assertContains(response, 'Edit participant')
|
||||
response = self.client.get('/user/1/edit/')
|
||||
|
||||
self.assertTemplateUsed(response, 'users/user_form.html')
|
||||
self.assertContains(response, 'Edit user')
|
||||
|
||||
response = self.client.post(
|
||||
'/participant/1/edit/',
|
||||
{'user_name': 'test_name_unaewae5Ir0saijeac2I',
|
||||
'/user/1/edit/',
|
||||
{'username': 'test_name_unaewae5Ir0saijeac2I',
|
||||
'first_name': 'test_name_aJi5jaizaVingaeF3Ohj',
|
||||
'groups': '4',
|
||||
'is_active': 'yes'})
|
||||
self.assertRedirects(response, '/participant/')
|
||||
|
||||
self.assertRedirects(response, '/user/')
|
||||
|
||||
def test_activate(self):
|
||||
response = self.client.get('/participant/1/status/activate/')
|
||||
response = self.client.get('/user/1/status/activate/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
|
||||
@ -71,45 +75,49 @@ class GroupViews(TestCase):
|
||||
self.user_2.groups.add(self.delegate)
|
||||
|
||||
self.client = Client()
|
||||
login_user = User.objects.create(username='loginusername', is_superuser=True)
|
||||
login_user.reset_password('default')
|
||||
self.client.login(username='loginusername', password='default')
|
||||
self.client.login(username='admin', password='admin')
|
||||
|
||||
def test_detail(self):
|
||||
response = self.client.get('/participant/group/3/')
|
||||
response = self.client.get('/user/group/3/')
|
||||
pattern = r'admins_first_name Administrator|aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer'
|
||||
match = re.findall(pattern, response.content.decode('utf8'))
|
||||
self.assertEqual(match[0], 'admins_first_name Administrator')
|
||||
self.assertEqual(match[1], 'aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer')
|
||||
|
||||
config['participant_sort_users_by_first_name'] = True
|
||||
self.assertTrue(config['participant_sort_users_by_first_name'])
|
||||
response = self.client.get('/participant/group/3/')
|
||||
config['users_sort_users_by_first_name'] = True
|
||||
self.assertTrue(config['users_sort_users_by_first_name'])
|
||||
response = self.client.get('/user/group/3/')
|
||||
pattern = r'admins_first_name Administrator|aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer'
|
||||
match = re.findall(pattern, response.content.decode('utf8'))
|
||||
self.assertEqual(match[1], 'admins_first_name Administrator')
|
||||
self.assertEqual(match[0], 'aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer')
|
||||
|
||||
def test_create(self):
|
||||
response = self.client.get('/participant/group/new/')
|
||||
self.assertTemplateUsed(response, 'participant/group_edit.html')
|
||||
response = self.client.get('/user/group/new/')
|
||||
|
||||
self.assertTemplateUsed(response, 'users/group_form.html')
|
||||
self.assertContains(response, 'New group')
|
||||
response = self.client.post('/participant/group/new/', {'name': 'test_group_name_Oeli1aeXoobohv8eikai'})
|
||||
self.assertRedirects(response, '/participant/group/')
|
||||
|
||||
response = self.client.post('/user/group/new/', {'name': 'test_group_name_Oeli1aeXoobohv8eikai'})
|
||||
|
||||
self.assertRedirects(response, '/user/group/')
|
||||
|
||||
def test_update(self):
|
||||
response = self.client.get('/participant/group/1/edit/')
|
||||
self.assertTemplateUsed(response, 'participant/group_edit.html')
|
||||
response = self.client.get('/user/group/1/edit/')
|
||||
|
||||
self.assertTemplateUsed(response, 'users/group_form.html')
|
||||
self.assertContains(response, 'Edit group')
|
||||
response = self.client.post('/participant/group/1/edit/', {'name': 'test_group_name_ahFeicoz5jedie4Fop0U'})
|
||||
self.assertRedirects(response, '/participant/group/')
|
||||
|
||||
response = self.client.post('/user/group/1/edit/', {'name': 'test_group_name_ahFeicoz5jedie4Fop0U'})
|
||||
|
||||
self.assertRedirects(response, '/user/group/')
|
||||
|
||||
|
||||
class LockoutProtection(TestCase):
|
||||
"""
|
||||
Tests that a manager user can not lockout himself by doing
|
||||
something that removes his last permission to manage participants. Tests
|
||||
also that he can see the participant app (although there is no absolute
|
||||
something that removes his last permission to manage users. Tests
|
||||
also that he can see the user app (although there is no absolute
|
||||
protection).
|
||||
"""
|
||||
def setUp(self):
|
||||
@ -122,70 +130,69 @@ class LockoutProtection(TestCase):
|
||||
self.assertFalse(self.user.is_superuser)
|
||||
|
||||
def test_delete_yourself(self):
|
||||
response = self.client.get('/participant/1/del/')
|
||||
self.assertRedirects(response, '/participant/1/')
|
||||
response = self.client.get('/user/1/del/')
|
||||
self.assertRedirects(response, '/user/1/')
|
||||
self.assertTrue('You can not delete yourself.' in response.cookies['messages'].value)
|
||||
response = self.client.post('/participant/1/del/',
|
||||
response = self.client.post('/user/1/del/',
|
||||
{'yes': 'yes'})
|
||||
self.assertTrue('You can not delete yourself.' in response.cookies['messages'].value)
|
||||
self.assertRedirects(response, '/participant/')
|
||||
self.assertRedirects(response, '/user/')
|
||||
self.assertEqual(User.objects.count(), 1)
|
||||
|
||||
def test_delete_last_manager_group(self):
|
||||
response = self.client.get('/participant/group/4/del/')
|
||||
self.assertRedirects(response, '/participant/group/4/')
|
||||
response = self.client.get('/user/group/4/del/')
|
||||
self.assertRedirects(response, '/user/group/4/')
|
||||
self.assertTrue('You can not delete the last group containing the permission '
|
||||
'to manage participants you are in.' in response.cookies['messages'].value)
|
||||
response = self.client.post('/participant/group/4/del/',
|
||||
'to manage users you are in.' in response.cookies['messages'].value)
|
||||
response = self.client.post('/user/group/4/del/',
|
||||
{'yes': 'yes'})
|
||||
self.assertTrue('You can not delete the last group containing the permission '
|
||||
'to manage participants you are in.' in response.cookies['messages'].value)
|
||||
self.assertRedirects(response, '/participant/group/')
|
||||
'to manage users you are in.' in response.cookies['messages'].value)
|
||||
self.assertRedirects(response, '/user/group/')
|
||||
self.assertEqual(Group.objects.count(), 4)
|
||||
|
||||
def test_remove_user_from_last_manager_group_via_UserUpdateView(self):
|
||||
response = self.client.post('/participant/1/edit/',
|
||||
response = self.client.post('/user/1/edit/',
|
||||
{'username': 'arae0eQu8eeghoogeik0',
|
||||
'groups': '3'})
|
||||
self.assertFormError(
|
||||
response=response,
|
||||
form='form',
|
||||
field=None,
|
||||
errors='You can not remove the last group containing the permission to manage participants.')
|
||||
errors='You can not remove the last group containing the permission to manage users.')
|
||||
|
||||
def test_remove_user_from_last_manager_group_via_GroupUpdateView(self):
|
||||
User.objects.get_or_create(username='foo', pk=2)
|
||||
response = self.client.post('/participant/group/4/edit/',
|
||||
response = self.client.post('/user/group/4/edit/',
|
||||
{'name': 'ChaeFaev4leephaiChae',
|
||||
'users': '2'})
|
||||
self.assertFormError(
|
||||
response=response,
|
||||
form='form',
|
||||
field=None,
|
||||
errors='You can not remove yourself from the last group containing the permission to manage participants.')
|
||||
errors='You can not remove yourself from the last group containing the permission to manage users.')
|
||||
|
||||
def test_remove_perm_from_last_manager_group(self):
|
||||
self.assertNotEqual(get_protected_perm().pk, 90)
|
||||
response = self.client.post('/participant/group/4/edit/',
|
||||
response = self.client.post('/user/group/4/edit/',
|
||||
{'name': 'ChaeFaev4leephaiChae',
|
||||
'users': '1',
|
||||
'permissions': '90'})
|
||||
'permissions': []})
|
||||
self.assertFormError(
|
||||
response=response,
|
||||
form='form',
|
||||
field=None,
|
||||
errors='You can not remove the permission to manage participants from the last group you are in.')
|
||||
errors='You can not remove the permission to manage users from the last group you are in.')
|
||||
|
||||
def test_remove_permission_can_see_participant_from_registered(self):
|
||||
self.assertTrue(self.user.has_perm('participant.can_see_participant'))
|
||||
def test_remove_permission_can_see_user_from_registered(self):
|
||||
self.assertTrue(self.user.has_perm('users.can_see'))
|
||||
# Remove perm from registered group
|
||||
can_see_perm = Permission.objects.get(
|
||||
content_type=ContentType.objects.get(app_label='participant', model='user'),
|
||||
codename='can_see_participant')
|
||||
content_type=ContentType.objects.get(app_label='users', model='user'),
|
||||
codename='can_see')
|
||||
get_registered_group().permissions.remove(can_see_perm)
|
||||
# Reload user
|
||||
self.user = User.objects.get(pk=1)
|
||||
self.assertTrue(self.user.has_perm('participant.can_see_participant'))
|
||||
self.assertTrue(self.user.has_perm('users.can_see'))
|
||||
|
||||
|
||||
class TestUserSettings(TestCase):
|
||||
@ -198,11 +205,13 @@ class TestUserSettings(TestCase):
|
||||
response = self.admin_client.get('/usersettings/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_pst(self):
|
||||
def test_post(self):
|
||||
response = self.admin_client.post('/usersettings/', {
|
||||
'user_name': 'new_name',
|
||||
'username': 'new_name',
|
||||
'language': 'de'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.assertRedirects(response, '/usersettings/')
|
||||
|
||||
admin = User.objects.get(pk=1)
|
||||
|
||||
self.assertEqual(admin.username, 'new_name')
|
Loading…
Reference in New Issue
Block a user