Merge pull request #1343 from ostcar/user

New users app.
This commit is contained in:
Norman Jäckel 2014-10-12 10:32:41 +02:00
commit 0a2923f7e3
87 changed files with 881 additions and 1382 deletions

View File

@ -263,7 +263,7 @@ def createsuperuser(settings, args):
ensure_settings(settings, args) ensure_settings(settings, args)
# can't be imported in global scope as it already requires # can't be imported in global scope as it already requires
# the settings module during import # 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(): if create_or_reset_admin_user():
print('Admin user successfully created.') print('Admin user successfully created.')
else: else:

View File

@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy
from mptt.forms import TreeNodeChoiceField from mptt.forms import TreeNodeChoiceField
from openslides.utils.forms import CssClassMixin, CleanHtmlFormMixin 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 from .models import Item, Speaker
@ -59,7 +59,8 @@ class AppendSpeakerForm(CssClassMixin, forms.Form):
""" """
Form to set an user to a list of speakers. 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'}), widget=forms.Select(attrs={'class': 'medium-input'}),
label=ugettext_lazy("Add participant")) label=ugettext_lazy("Add participant"))
@ -72,7 +73,7 @@ class AppendSpeakerForm(CssClassMixin, forms.Form):
Checks, that the user is not already on the list. Checks, that the user is not already on the list.
""" """
speaker = self.cleaned_data['speaker'] 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( raise forms.ValidationError(ugettext_lazy(
'%s is already on the list of speakers.' '%s is already on the list of speakers.'
% str(speaker))) % str(speaker)))

View File

@ -17,8 +17,8 @@ from openslides.projector.api import (get_active_slide, reset_countdown,
from openslides.projector.models import SlideMixin from openslides.projector.models import SlideMixin
from openslides.utils.exceptions import OpenSlidesError from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import AbsoluteUrlMixin from openslides.utils.models import AbsoluteUrlMixin
from openslides.utils.person.models import PersonField
from openslides.utils.utils import to_roman from openslides.utils.utils import to_roman
from openslides.users.models import User
class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel): class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
@ -279,7 +279,7 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
def get_next_speaker(self): 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: try:
return self.speaker_set.filter(begin_time=None).order_by('weight')[0] 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): class SpeakerManager(models.Manager):
def add(self, person, item): def add(self, user, item):
if self.filter(person=person, item=item, begin_time=None).exists(): if self.filter(user=user, item=item, begin_time=None).exists():
raise OpenSlidesError(_( raise OpenSlidesError(_(
'%(person)s is already on the list of speakers of item %(id)s.') '%(user)s is already on the list of speakers of item %(id)s.')
% {'person': person, 'id': item.id}) % {'user': user, 'id': item.id})
if isinstance(person, AnonymousUser): if isinstance(user, AnonymousUser):
raise OpenSlidesError( raise OpenSlidesError(
_('An anonymous user can not be on lists of speakers.')) _('An anonymous user can not be on lists of speakers.'))
weight = (self.filter(item=item).aggregate( weight = (self.filter(item=item).aggregate(
models.Max('weight'))['weight__max'] or 0) 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): class Speaker(AbsoluteUrlMixin, models.Model):
@ -357,14 +357,14 @@ class Speaker(AbsoluteUrlMixin, models.Model):
objects = SpeakerManager() objects = SpeakerManager()
person = PersonField() user = models.ForeignKey(User)
""" """
ForeinKey to the person who speaks. ForeinKey to the user who speaks.
""" """
item = models.ForeignKey(Item) 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) begin_time = models.DateTimeField(null=True)
@ -396,11 +396,11 @@ class Speaker(AbsoluteUrlMixin, models.Model):
self.check_and_update_projector() self.check_and_update_projector()
def __str__(self): def __str__(self):
return str(self.person) return str(self.user)
def get_absolute_url(self, link='detail'): def get_absolute_url(self, link='detail'):
if link == 'detail': if link == 'detail':
url = self.person.get_absolute_url('detail') url = self.user.get_absolute_url('detail')
elif link == 'delete': elif link == 'delete':
url = reverse('agenda_speaker_delete', url = reverse('agenda_speaker_delete',
args=[self.item.pk, self.pk]) args=[self.item.pk, self.pk])
@ -421,7 +421,7 @@ class Speaker(AbsoluteUrlMixin, models.Model):
def begin_speach(self): 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 Set the weight to None and the time to now. If anyone is still
speaking, end his speach. speaking, end his speach.

View File

@ -7,12 +7,12 @@ from .models import Item
class AgendaPersonalInfo(PersonalInfo): 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') headline = ugettext_lazy('I am on the list of speakers of the following items')
default_weight = 10 default_weight = 10
def get_queryset(self): def get_queryset(self):
return Item.objects.filter( return Item.objects.filter(
speaker__person=self.request.user, speaker__user=self.request.user,
speaker__begin_time=None) speaker__begin_time=None)

View File

@ -121,7 +121,7 @@
{% if speaker_dict.type == 'actual_speaker' %} {% 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> <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' %} {% 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> class="btn btn-mini"><i class="icon-bell"></i> {% trans "Begin speach" %}</a>
{% endif %} {% endif %}
<a href="{{ speaker_dict.speaker|absolute_url:'delete' }}" <a href="{{ speaker_dict.speaker|absolute_url:'delete' }}"
@ -150,8 +150,8 @@
<div class="control-group input-append {% if field.errors %}error{% endif %}"> <div class="control-group input-append {% if field.errors %}error{% endif %}">
{{ field }} {{ field }}
<button class="btn btn-primary tooltip-bottom" type="submit" data-original-title="{% trans 'Apply' %}"><i class="icon-ok icon-white"></i></button> <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 %} {% 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 participant' %}"><i class="icon-add-user"></i></a> <a href="{% url 'user_new' %}" class="btn" rel="tooltip" data-original-title="{% trans 'Add new user' %}"><i class="icon-add-user"></i></a>
{% endif %} {% endif %}
{% if field.errors %} {% if field.errors %}
<span class="help-inline">{{ field.errors }}</span> <span class="help-inline">{{ field.errors }}</span>

View File

@ -63,7 +63,7 @@ urlpatterns = patterns(
views.SpeakerDeleteView.as_view(), views.SpeakerDeleteView.as_view(),
name='agenda_speaker_delete'), 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(), views.SpeakerSpeakView.as_view(),
name='agenda_speaker_speak'), name='agenda_speaker_speak'),

View File

@ -190,13 +190,13 @@ class AgendaItemView(SingleObjectMixin, FormView):
'object': self.object, 'object': self.object,
'list_of_speakers': list_of_speakers, 'list_of_speakers': list_of_speakers,
'is_on_the_list_of_speakers': Speaker.objects.filter( 'is_on_the_list_of_speakers': Speaker.objects.filter(
item=self.object, begin_time=None, person=self.request.user).exists(), item=self.object, begin_time=None, user=self.request.user).exists(),
'active_type': active_type, 'active_type': active_type,
}) })
return super(AgendaItemView, self).get_context_data(**kwargs) return super(AgendaItemView, self).get_context_data(**kwargs)
def form_valid(self, form): 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)) return self.render_to_response(self.get_context_data(form=form))
def get_form_kwargs(self): def get_form_kwargs(self):
@ -392,7 +392,7 @@ class SpeakerAppendView(SingleObjectMixin, RedirectView):
messages.error(request, _('The list of speakers is closed.')) messages.error(request, _('The list of speakers is closed.'))
else: else:
try: try:
Speaker.objects.add(item=self.object, person=request.user) Speaker.objects.add(item=self.object, user=request.user)
except OpenSlidesError as e: except OpenSlidesError as e:
messages.error(request, e) messages.error(request, e)
else: else:
@ -413,7 +413,7 @@ class SpeakerDeleteView(DeleteView):
if 'speaker' in kwargs: if 'speaker' in kwargs:
return request.user.has_perm('agenda.can_manage_agenda') return request.user.has_perm('agenda.can_manage_agenda')
else: 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 return True
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
@ -434,7 +434,7 @@ class SpeakerDeleteView(DeleteView):
return Speaker.objects.get(pk=self.kwargs['speaker']) return Speaker.objects.get(pk=self.kwargs['speaker'])
except KeyError: except KeyError:
return Speaker.objects.filter( 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): def get_url_name_args(self):
return [self.kwargs['pk']] return [self.kwargs['pk']]
@ -448,7 +448,7 @@ class SpeakerDeleteView(DeleteView):
class SpeakerSpeakView(SingleObjectMixin, RedirectView): class SpeakerSpeakView(SingleObjectMixin, RedirectView):
""" """
Mark the speaking person. Mark the speaking user.
""" """
required_permission = 'agenda.can_manage_agenda' required_permission = 'agenda.can_manage_agenda'
url_name = 'item_view' url_name = 'item_view'
@ -458,14 +458,14 @@ class SpeakerSpeakView(SingleObjectMixin, RedirectView):
self.object = self.get_object() self.object = self.get_object()
try: try:
speaker = Speaker.objects.filter( speaker = Speaker.objects.filter(
person=kwargs['person_id'], user=kwargs['user_id'],
item=self.object, item=self.object,
begin_time=None).get() begin_time=None).get()
except Speaker.DoesNotExist: # TODO: Check the MultipleObjectsReturned error here? except Speaker.DoesNotExist: # TODO: Check the MultipleObjectsReturned error here?
messages.error( messages.error(
self.request, self.request,
_('%(person)s is not on the list of %(item)s.') _('%(user)s is not on the list of %(item)s.')
% {'person': kwargs['person_id'], 'item': self.object}) % {'user': kwargs['user_id'], 'item': self.object})
else: else:
speaker.begin_speach() speaker.begin_speach()

View File

@ -1 +1 @@
from . import main_menu, personal_info, signals, slides, template, widgets # noqa # TODO: apploader

View File

@ -1,8 +1,8 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from openslides.users.models import User
from openslides.utils.forms import CssClassMixin from openslides.utils.forms import CssClassMixin
from openslides.utils.person import PersonFormField
from .models import Assignment from .models import Assignment
@ -17,6 +17,7 @@ class AssignmentForm(CssClassMixin, forms.ModelForm):
class AssignmentRunForm(CssClassMixin, forms.Form): class AssignmentRunForm(CssClassMixin, forms.Form):
candidate = PersonFormField( candidate = forms.ModelChoiceField(
queryset=User.objects.all(),
widget=forms.Select(attrs={'class': 'medium-input'}), widget=forms.Select(attrs={'class': 'medium-input'}),
label=ugettext_lazy("Nominate a participant")) label=ugettext_lazy("Nominate a participant"))

View File

@ -14,8 +14,8 @@ from openslides.projector.api import get_active_object, update_projector
from openslides.projector.models import RelatedModelMixin, SlideMixin from openslides.projector.models import RelatedModelMixin, SlideMixin
from openslides.utils.exceptions import OpenSlidesError from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import AbsoluteUrlMixin from openslides.utils.models import AbsoluteUrlMixin
from openslides.utils.person import PersonField
from openslides.utils.utils import html_strong from openslides.utils.utils import html_strong
from openslides.users.models import User
class AssignmentCandidate(RelatedModelMixin, models.Model): class AssignmentCandidate(RelatedModelMixin, models.Model):
@ -23,7 +23,7 @@ class AssignmentCandidate(RelatedModelMixin, models.Model):
Many2Many table between an assignment and the candidates. Many2Many table between an assignment and the candidates.
""" """
assignment = models.ForeignKey("Assignment") assignment = models.ForeignKey("Assignment")
person = PersonField(db_index=True) person = models.ForeignKey(User, db_index=True)
elected = models.BooleanField(default=False) elected = models.BooleanField(default=False)
blocked = models.BooleanField(default=False) blocked = models.BooleanField(default=False)
@ -178,12 +178,11 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
if only_candidate: if only_candidate:
candidates = candidates.filter(elected=False) candidates = candidates.filter(elected=False)
# TODO: rewrite this with a queryset
participants = [] participants = []
for candidate in candidates.all(): for candidate in candidates.all():
participants.append(candidate.person) participants.append(candidate.person)
participants.sort(key=lambda person: person.sort_name)
return participants return participants
# return candidates.values_list('person', flat=True)
def set_elected(self, person, value=True): def set_elected(self, person, value=True):
candidate = self.assignment_candidates.get(person=person) candidate = self.assignment_candidates.get(person=person)
@ -251,8 +250,6 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
for poll in polls: for poll in polls:
options += poll.get_options() options += poll.get_options()
options.sort(key=lambda option: option.candidate.sort_name)
for option in options: for option in options:
candidate = option.candidate candidate = option.candidate
if candidate in vote_results_dict: if candidate in vote_results_dict:
@ -284,7 +281,7 @@ class AssignmentVote(BaseVote):
class AssignmentOption(BaseOption): class AssignmentOption(BaseOption):
poll = models.ForeignKey('AssignmentPoll') poll = models.ForeignKey('AssignmentPoll')
candidate = PersonField() candidate = models.ForeignKey(User)
vote_class = AssignmentVote vote_class = AssignmentVote
def __str__(self): def __str__(self):
@ -345,3 +342,7 @@ class AssignmentPoll(SlideMixin, RelatedModelMixin, CollectDefaultVotesMixin,
def get_slide_context(self, **context): def get_slide_context(self, **context):
return super(AssignmentPoll, self).get_slide_context(poll=self) 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

View File

@ -69,14 +69,14 @@
<a href="{{ person|absolute_url }}">{{ person }}</a> <a href="{{ person|absolute_url }}">{{ person }}</a>
{% if perms.assignment.can_manage_assignment %} {% if perms.assignment.can_manage_assignment %}
{% if assignment.status == "sea" or assignment.status == "vot" %} {% 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 %}
{% endif %} {% endif %}
{% if person in assignment.elected %} {% if person in assignment.elected %}
| <b>{% trans "elected" %}</b> | <b>{% trans "elected" %}</b>
{% if perms.assignment.can_manage_assignment %} {% if perms.assignment.can_manage_assignment %}
{% if assignment.status == "sea" or assignment.status == "vot" %} {% 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> <i class="icon-ban-circle"></i>
</a> </a>
{% endif %} {% endif %}
@ -128,7 +128,7 @@
{% for person in blocked_candidates %} {% for person in blocked_candidates %}
<li> <li>
<a href="{{ person|absolute_url }}">{{ person }}</a> <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' %}"> rel="tooltip" data-original-title="{% trans 'Remove candidate' %}">
<i class="icon-ban-circle"></i> <i class="icon-ban-circle"></i>
</a> </a>
@ -190,7 +190,7 @@
<td> <td>
{% if candidate in assignment.elected %} {% if candidate in assignment.elected %}
{% if perms.assignment.can_manage_assignment %} {% 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> data-original-title="{% trans 'Mark candidate as elected' %}"></a>
{% else %} {% else %}
<a class="elected"> <a class="elected">
@ -199,7 +199,7 @@
{% endif %} {% endif %}
{% else %} {% else %}
{% if perms.assignment.can_manage_assignment %} {% 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> data-original-title="{% trans 'Mark candidate as elected' %}"></a>
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@ -10,10 +10,9 @@ from reportlab.platypus import (PageBreak, Paragraph, SimpleDocTemplate, Spacer,
from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelatedAgendaItemView from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelatedAgendaItemView
from openslides.config.api import config 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.poll.views import PollFormView
from openslides.utils.pdf import stylesheet from openslides.utils.pdf import stylesheet
from openslides.utils.person import get_person
from openslides.utils.utils import html_strong from openslides.utils.utils import html_strong
from openslides.utils.views import (CreateView, DeleteView, DetailView, from openslides.utils.views import (CreateView, DeleteView, DetailView,
ListView, PDFView, PermissionMixin, ListView, PDFView, PermissionMixin,
@ -187,7 +186,7 @@ class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
def _get_person_information(self): def _get_person_information(self):
self.object = self.get_object() 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) self.is_blocked = self.object.is_blocked(self.person)
@ -252,7 +251,7 @@ class SetElectedView(SingleObjectMixin, RedirectView):
def pre_redirect(self, *args, **kwargs): def pre_redirect(self, *args, **kwargs):
self.object = self.get_object() 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.elected = kwargs['elected']
self.object.set_elected(self.person, self.elected) self.object.set_elected(self.person, self.elected)
@ -406,8 +405,8 @@ class AssignmentPDF(PDFView):
candidate_string = candidate.clean_name candidate_string = candidate.clean_name
if candidate in elected_candidates: if candidate in elected_candidates:
candidate_string = "* " + candidate_string candidate_string = "* " + candidate_string
if candidate.name_suffix: if candidate.structure_level:
candidate_string += "\n(%s)" % candidate.name_suffix candidate_string += "\n(%s)" % candidate.structure_level
row.append(candidate_string) row.append(candidate_string)
for vote in poll_list: for vote in poll_list:
if vote is None: if vote is None:
@ -505,7 +504,7 @@ class AssignmentPollPDF(PDFView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.poll = AssignmentPoll.objects.get(id=self.kwargs['poll_id']) 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): def get_filename(self):
filename = u'%s-%s_%s' % ( filename = u'%s-%s_%s' % (
@ -570,10 +569,10 @@ class AssignmentPollPDF(PDFView):
counter += 1 counter += 1
candidate = option.candidate candidate = option.candidate
cell.append(Paragraph( cell.append(Paragraph(
candidate.clean_name, stylesheet['Ballot_option_name_YNA'])) candidate.get_short_name(), stylesheet['Ballot_option_name_YNA']))
if candidate.name_suffix: if candidate.structure_level:
cell.append(Paragraph( cell.append(Paragraph(
"(%s)" % candidate.name_suffix, "(%s)" % candidate.structure_level,
stylesheet['Ballot_option_suffix_YNA'])) stylesheet['Ballot_option_suffix_YNA']))
else: else:
cell.append(Paragraph( cell.append(Paragraph(
@ -616,9 +615,9 @@ class AssignmentPollPDF(PDFView):
cell.append(Paragraph("<font name='circlefont' size='15'>%s</font> \ cell.append(Paragraph("<font name='circlefont' size='15'>%s</font> \
<font name='Ubuntu'>%s</font>" % <font name='Ubuntu'>%s</font>" %
(circle, candidate.clean_name), stylesheet['Ballot_option_name'])) (circle, candidate.clean_name), stylesheet['Ballot_option_name']))
if candidate.name_suffix: if candidate.structure_level:
cell.append(Paragraph( cell.append(Paragraph(
"(%s)" % candidate.name_suffix, "(%s)" % candidate.structure_level,
stylesheet['Ballot_option_suffix'])) stylesheet['Ballot_option_suffix']))
else: else:
cell.append(Paragraph( cell.append(Paragraph(

View File

@ -1 +1 @@
from . import main_menu, signals, slides, widgets # noqa # TODO: apploading

View File

@ -16,7 +16,7 @@ class ChatboxSocketHandler(SockJSConnection):
""" """
Checks connecting user and adds his client to the clients list. 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 # TODO: Use the django way to get the session to be compatible with
# other auth-backends; see comment in pull request #1220: # other auth-backends; see comment in pull request #1220:

View File

@ -1,10 +1,14 @@
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy, ugettext_noop 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.utils.models import AbsoluteUrlMixin
from openslides.projector.models import SlideMixin 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): class CustomSlide(SlideMixin, AbsoluteUrlMixin, models.Model):
""" """
@ -38,3 +42,7 @@ class CustomSlide(SlideMixin, AbsoluteUrlMixin, models.Model):
else: else:
url = super(CustomSlide, self).get_absolute_url(link) url = super(CustomSlide, self).get_absolute_url(link)
return url return url
# TODO: apploader
from . import main_menu, signals, slides, widgets # noqa

View File

@ -11,6 +11,8 @@ AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', 'django.contrib.auth.backends.ModelBackend',
'openslides.utils.auth.AnonymousAuth',) 'openslides.utils.auth.AnonymousAuth',)
AUTH_USER_MODEL = 'users.User'
LOGIN_URL = '/login/' LOGIN_URL = '/login/'
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = '/'
@ -66,7 +68,7 @@ MIDDLEWARE_CLASSES = (
'django.middleware.locale.LocaleMiddleware', 'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'openslides.participant.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'openslides.config.middleware.ConfigCacheMiddleware', 'openslides.config.middleware.ConfigCacheMiddleware',
) )
@ -90,7 +92,7 @@ INSTALLED_APPS = (
'openslides.agenda', 'openslides.agenda',
'openslides.motion', 'openslides.motion',
'openslides.assignment', 'openslides.assignment',
'openslides.participant', 'openslides.users',
'openslides.mediafile', 'openslides.mediafile',
'openslides.config', 'openslides.config',
) )

View File

@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.projector.models import SlideMixin from openslides.projector.models import SlideMixin
from openslides.utils.models import AbsoluteUrlMixin 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): class Mediafile(SlideMixin, AbsoluteUrlMixin, models.Model):
@ -17,17 +17,17 @@ class Mediafile(SlideMixin, AbsoluteUrlMixin, models.Model):
slide_callback_name = 'mediafile' slide_callback_name = 'mediafile'
PRESENTABLE_FILE_TYPES = ['application/pdf'] 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 See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield
for more information. 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.""" """A string representing the title of the file."""
uploader = PersonField(blank=True, verbose_name=ugettext_lazy("Uploaded by")) uploader = models.ForeignKey(User, null=True, blank=True, verbose_name=ugettext_lazy('Uploaded by'))
"""A person the uploader of a file.""" """A user the uploader of a file."""
timestamp = models.DateTimeField(auto_now_add=True) timestamp = models.DateTimeField(auto_now_add=True)
"""A DateTimeField to save the upload date and time.""" """A DateTimeField to save the upload date and time."""

View File

@ -70,7 +70,7 @@ class MediafileCreateView(MediafileViewMixin, CreateView):
def get_form_kwargs(self, *args, **kwargs): def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(MediafileCreateView, self).get_form_kwargs(*args, **kwargs) form_kwargs = super(MediafileCreateView, self).get_form_kwargs(*args, **kwargs)
if self.request.method == 'GET': 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 return form_kwargs
@ -80,11 +80,12 @@ class MediafileUpdateView(MediafileViewMixin, UpdateView):
""" """
def check_permission(self, request, *args, **kwargs): def check_permission(self, request, *args, **kwargs):
return (request.user.has_perm('mediafile.can_manage') or 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): def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(MediafileUpdateView, self).get_form_kwargs(*args, **kwargs) form_kwargs = super(MediafileUpdateView, self).get_form_kwargs(*args, **kwargs)
form_kwargs['initial'].update({'uploader': self.object.uploader.person_id}) form_kwargs['initial'].update({'uploader': self.object.uploader.pk})
return form_kwargs return form_kwargs

View File

@ -1 +1 @@
from . import main_menu, personal_info, signals, slides, widgets # noqa # TODO: Apploading

View File

@ -9,7 +9,7 @@ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_noop from django.utils.translation import ugettext_noop
from openslides.utils import csv_ext 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 openslides.utils.utils import html_strong
from .models import Category, Motion from .models import Category, Motion
@ -90,8 +90,8 @@ def import_motions(csvfile, default_submitter, override, importing_person=None):
# Add submitter # Add submitter
person_found = False person_found = False
if submitter: if submitter:
for person in Persons(): for person in User.objects.all():
if person.clean_name == submitter: if person.get_short_name() == submitter:
if person_found: if person_found:
warning.append(_('Several suitable submitters found.')) warning.append(_('Several suitable submitters found.'))
person_found = False person_found = False

View File

@ -5,7 +5,7 @@ from openslides.config.api import config
from openslides.mediafile.models import Mediafile from openslides.mediafile.models import Mediafile
from openslides.utils.forms import (CleanHtmlFormMixin, CssClassMixin, from openslides.utils.forms import (CleanHtmlFormMixin, CssClassMixin,
CSVImportForm, LocalizedModelChoiceField) CSVImportForm, LocalizedModelChoiceField)
from openslides.utils.person import MultiplePersonFormField, PersonFormField from openslides.users.models import User
from ckeditor.widgets import CKEditorWidget from ckeditor.widgets import CKEditorWidget
@ -72,14 +72,14 @@ class BaseMotionForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm):
class MotionSubmitterMixin(forms.ModelForm): class MotionSubmitterMixin(forms.ModelForm):
"""Mixin to append the submitter field to a MotionForm.""" """Mixin to append the submitter field to a MotionForm."""
submitter = MultiplePersonFormField(label=ugettext_lazy("Submitter"), submitter = forms.ModelMultipleChoiceField(
required=False) User.objects, label=ugettext_lazy("Submitter"), required=False)
"""Submitter of the motion. Can be one or more persons.""" """Submitter of the motion. Can be one or more persons."""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Fill in the submitter of the motion as default value.""" """Fill in the submitter of the motion as default value."""
if self.motion is not None: 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 self.initial['submitter'] = submitter
super(MotionSubmitterMixin, self).__init__(*args, **kwargs) super(MotionSubmitterMixin, self).__init__(*args, **kwargs)
@ -87,13 +87,14 @@ class MotionSubmitterMixin(forms.ModelForm):
class MotionSupporterMixin(forms.ModelForm): class MotionSupporterMixin(forms.ModelForm):
"""Mixin to append the supporter field to a Motionform.""" """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.""" """Supporter of the motion. Can be one or more persons."""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Fill in the supporter of the motions as default value.""" """Fill in the supporter of the motions as default value."""
if self.motion is not None: 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 self.initial['supporter'] = supporter
super(MotionSupporterMixin, self).__init__(*args, **kwargs) super(MotionSupporterMixin, self).__init__(*args, **kwargs)
@ -169,7 +170,8 @@ class MotionCSVImportForm(CSVImportForm):
should be overridden. should be overridden.
""" """
default_submitter = PersonFormField( default_submitter = forms.ModelChoiceField(
User.objects.all(),
required=True, required=True,
label=ugettext_lazy('Default submitter'), 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.')) help_text=ugettext_lazy('This person is used as submitter for any line of your csv file which does not contain valid submitter data.'))

View File

@ -11,7 +11,7 @@ from openslides.poll.models import (BaseOption, BasePoll, BaseVote, CollectDefau
from openslides.projector.models import RelatedModelMixin, SlideMixin from openslides.projector.models import RelatedModelMixin, SlideMixin
from jsonfield import JSONField from jsonfield import JSONField
from openslides.utils.models import AbsoluteUrlMixin from openslides.utils.models import AbsoluteUrlMixin
from openslides.utils.person import PersonField from openslides.users.models import User
from .exceptions import WorkflowError from .exceptions import WorkflowError
@ -366,8 +366,7 @@ class Motion(SlideMixin, AbsoluteUrlMixin, models.Model):
@property @property
def supporters(self): def supporters(self):
return sorted([object.person for object in self.supporter.all()], return [supporter.person for supporter in self.supporter.all()]
key=lambda person: person.sort_name)
def add_submitter(self, person): def add_submitter(self, person):
MotionSubmitter.objects.create(motion=self, person=person) MotionSubmitter.objects.create(motion=self, person=person)
@ -582,8 +581,8 @@ class MotionSubmitter(RelatedModelMixin, models.Model):
motion = models.ForeignKey('Motion', related_name="submitter") motion = models.ForeignKey('Motion', related_name="submitter")
"""The motion to witch the object belongs.""" """The motion to witch the object belongs."""
person = PersonField() person = models.ForeignKey(User)
"""The person, who is the submitter.""" """The user, who is the submitter."""
def __str__(self): def __str__(self):
"""Return the name of the submitter as string.""" """Return the name of the submitter as string."""
@ -599,7 +598,7 @@ class MotionSupporter(models.Model):
motion = models.ForeignKey('Motion', related_name="supporter") motion = models.ForeignKey('Motion', related_name="supporter")
"""The motion to witch the object belongs.""" """The motion to witch the object belongs."""
person = PersonField() person = models.ForeignKey(User)
"""The person, who is the supporter.""" """The person, who is the supporter."""
def __str__(self): def __str__(self):
@ -632,12 +631,6 @@ class Category(AbsoluteUrlMixin, models.Model):
class Meta: class Meta:
ordering = ['prefix'] 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): class MotionLog(models.Model):
"""Save a logmessage for a motion.""" """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. 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.""" """A person object, who created the log message. Optional."""
time = models.DateTimeField(auto_now=True) time = models.DateTimeField(auto_now=True)
@ -856,4 +849,10 @@ class Workflow(models.Model):
def check_first_state(self): def check_first_state(self):
"""Checks whether the first_state itself belongs to the workflow.""" """Checks whether the first_state itself belongs to the workflow."""
if self.first_state and not self.first_state.workflow == self: 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

View File

@ -9,7 +9,7 @@ from reportlab.lib.units import cm
from reportlab.platypus import PageBreak, Paragraph, Spacer, Table, TableStyle from reportlab.platypus import PageBreak, Paragraph, Spacer, Table, TableStyle
from openslides.config.api import config 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 openslides.utils.pdf import stylesheet
from .models import Category, Motion from .models import Category, Motion
@ -281,9 +281,9 @@ def motion_poll_to_pdf(pdf, poll):
# set number of ballot papers # set number of ballot papers
if ballot_papers_selection == "NUMBER_OF_DELEGATES": if ballot_papers_selection == "NUMBER_OF_DELEGATES":
# TODO: get this number from persons # TODO: get this number from users
try: 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() number = User.objects.filter(groups__pk=3).count()
except Group.DoesNotExist: except Group.DoesNotExist:
number = 0 number = 0

View File

@ -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

View File

@ -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')

View File

@ -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 %}

View File

@ -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>

View File

@ -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>&nbsp;
{% 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 %}

View File

@ -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)

View File

@ -23,7 +23,7 @@ urlpatterns += patterns(
(r'^agenda/', include('openslides.agenda.urls')), (r'^agenda/', include('openslides.agenda.urls')),
(r'^motion/', include('openslides.motion.urls')), (r'^motion/', include('openslides.motion.urls')),
(r'^assignment/', include('openslides.assignment.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'^mediafile/', include('openslides.mediafile.urls')),
(r'^config/', include('openslides.config.urls')), (r'^config/', include('openslides.config.urls')),
(r'^projector/', include('openslides.projector.urls')), (r'^projector/', include('openslides.projector.urls')),
@ -32,12 +32,13 @@ urlpatterns += patterns(
) )
# TODO: move this patterns into core or the participant app # TODO: move this patterns into core or the participant app
from openslides.users.views import UserSettingsView, UserPasswordSettingsView
urlpatterns += patterns( urlpatterns += patterns(
'', '',
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
url(r'^login/$', url(r'^login/$',
'openslides.participant.views.login', 'openslides.users.views.login',
name='user_login'), name='user_login'),
url(r'^logout/$', url(r'^logout/$',
@ -45,11 +46,11 @@ urlpatterns += patterns(
name='user_logout'), name='user_logout'),
url(r'^usersettings/$', url(r'^usersettings/$',
'openslides.participant.views.user_settings', UserSettingsView.as_view(),
name='user_settings'), name='user_settings'),
url(r'^usersettings/changepassword/$', url(r'^usersettings/changepassword/$',
'openslides.participant.views.user_settings_password', UserPasswordSettingsView.as_view(),
name='password_change'), name='password_change'),
) )

View File

@ -1,6 +1,9 @@
from random import choice 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(): def gen_password():
@ -62,3 +65,13 @@ def create_or_reset_admin_user():
admin.save() admin.save()
admin.groups.add(group_staff) admin.groups.add(group_staff)
return created 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')

View File

@ -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}) error_messages.append(_('Group id %(id)s does not exists (line %(line)d).') % {'id': groupid, 'line': line_no + 1})
continue continue
user.reset_password() user.reset_password()
user.save()
count_success += 1 count_success += 1
except csv.Error: except csv.Error:
error_messages.append(_('Import aborted because of severe errors in the input file.')) 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 # Build final success message
if count_success: if count_success:
success_message = _('%d new participants were successfully imported.') % count_success success_message = _('%d new users were successfully imported.') % count_success
else: else:
success_message = '' success_message = ''

View File

@ -8,76 +8,70 @@ from openslides.config.api import config
from openslides.utils.forms import (CssClassMixin, from openslides.utils.forms import (CssClassMixin,
LocalizedModelMultipleChoiceField) 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): class UserCreateForm(CssClassMixin, forms.ModelForm):
groups = LocalizedModelMultipleChoiceField( groups = LocalizedModelMultipleChoiceField(
# Hide the built-in groups 'Anonymous' (pk=1) and 'Registered' (pk=2) # 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) label=ugettext_lazy('Groups'), required=False)
class Meta: class Meta:
model = User model = User
fields = ('title', 'first_name', 'last_name', 'gender', 'email', fields = ('title', 'first_name', 'last_name', 'groups',
'groups', 'structure_level', 'committee', 'about_me', 'comment', 'structure_level', 'about_me', 'comment', 'is_active',
'is_active', 'default_password') 'default_password')
def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
""" """
Ensures that a user has either a first name or a last name. Ensures that a user has either a first name or a last name.
""" """
cleaned_data = super(UserCreateForm, self).clean(*args, **kwargs) 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.') error_msg = _('First name and last name can not both be empty.')
raise forms.ValidationError(error_msg) raise forms.ValidationError(error_msg)
return cleaned_data return cleaned_data
class UserMultipleCreateForm(forms.Form): class UserMultipleCreateForm(forms.Form):
participants_block = forms.CharField( users_block = forms.CharField(
widget=forms.Textarea, widget=forms.Textarea,
label=ugettext_lazy('Participants'), label=ugettext_lazy('Users'),
help_text=ugettext_lazy('Use one line per participant for its name (first name and last name).')) help_text=ugettext_lazy('Use one line per user for its name '
'(first name and last name).'))
class UserUpdateForm(UserCreateForm): class UserUpdateForm(UserCreateForm):
""" """
Form to update an user. It raises a validation error, if a non-superuser 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 user edits himself and removes the last group containing the permission
to manage participants. to manage users.
"""
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: class Meta:
model = User model = User
fields = ('user_name', 'title', 'first_name', 'last_name', 'gender', 'email', fields = ('username', 'title', 'first_name', 'last_name',
'groups', 'structure_level', 'committee', 'about_me', 'comment', 'groups', 'structure_level', 'about_me', 'comment',
'is_active', 'default_password') 'is_active', 'default_password')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request') self.request = kwargs.pop('request')
kwargs['initial']['user_name'] = kwargs['instance'].username
return super(UserUpdateForm, self).__init__(*args, **kwargs) return super(UserUpdateForm, self).__init__(*args, **kwargs)
def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
""" """
Raises a validation error if a non-superuser user edits himself 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 # TODO: Check this in clean_groups
if (self.request.user == self.instance and if (self.request.user == self.instance and
not self.instance.is_superuser and not self.instance.is_superuser and
not self.cleaned_data['groups'].filter(permissions__in=[get_protected_perm()]).exists()): 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) raise forms.ValidationError(error_msg)
return super(UserUpdateForm, self).clean(*args, **kwargs) return super().clean(*args, **kwargs)
class GroupForm(forms.ModelForm, CssClassMixin): class GroupForm(forms.ModelForm, CssClassMixin):
@ -85,7 +79,7 @@ class GroupForm(forms.ModelForm, CssClassMixin):
queryset=Permission.objects.all(), label=ugettext_lazy('Permissions'), queryset=Permission.objects.all(), label=ugettext_lazy('Permissions'),
required=False) required=False)
users = forms.ModelMultipleChoiceField( 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: class Meta:
model = Group model = Group
@ -96,10 +90,10 @@ class GroupForm(forms.ModelForm, CssClassMixin):
# Initial users # Initial users
if kwargs.get('instance', None) is not None: if kwargs.get('instance', None) is not None:
initial = kwargs.setdefault('initial', {}) 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) super().__init__(*args, **kwargs)
if config['participant_sort_users_by_first_name']: if config['users_sort_users_by_first_name']:
self.fields['users'].queryset = self.fields['users'].queryset.order_by('first_name') self.fields['users'].queryset = self.fields['users'].queryset.order_by('first_name')
def save(self, commit=True): def save(self, commit=True):
@ -124,10 +118,10 @@ class GroupForm(forms.ModelForm, CssClassMixin):
def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
""" """
Raises a validation error if a non-superuser user removes himself 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 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 # TODO: Check this in clean_users or clean_permissions
if (self.request and if (self.request and
@ -136,37 +130,24 @@ class GroupForm(forms.ModelForm, CssClassMixin):
not Group.objects.exclude(pk=self.instance.pk).filter( not Group.objects.exclude(pk=self.instance.pk).filter(
permissions__in=[get_protected_perm()], permissions__in=[get_protected_perm()],
user__pk=self.request.user.pk).exists()): user__pk=self.request.user.pk).exists()):
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) raise forms.ValidationError(error_msg)
if (self.request and if (self.request and
not self.request.user.is_superuser and not self.request.user.is_superuser and
not get_protected_perm() in self.cleaned_data['permissions'] and not get_protected_perm() in self.cleaned_data['permissions'] and
not Group.objects.exclude(pk=self.instance.pk).filter( not Group.objects.exclude(pk=self.instance.pk).filter(
permissions__in=[get_protected_perm()], permissions__in=[get_protected_perm()],
user__pk=self.request.user.pk).exists()): user__pk=self.request.user.pk).exists()):
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) raise forms.ValidationError(error_msg)
return super(GroupForm, self).clean(*args, **kwargs) return super(GroupForm, self).clean(*args, **kwargs)
class UsersettingsForm(CssClassMixin, forms.ModelForm): class UsersettingsForm(CssClassMixin, forms.ModelForm):
user_name = forms.CharField(label=ugettext_lazy('Username')) class Meta:
""" model = User
Field to save the username. fields = ('username', 'title', 'first_name', 'last_name', 'about_me')
The field username (without the underscore) from the UserModel does not
allow whitespaces and umlauts.
"""
language = forms.ChoiceField( language = forms.ChoiceField(
choices=settings.LANGUAGES, label=ugettext_lazy('Language')) 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')

View File

@ -3,12 +3,12 @@ from django.utils.translation import ugettext_lazy
from openslides.utils.main_menu import MainMenuEntry from openslides.utils.main_menu import MainMenuEntry
class ParticipantMainMenuEntry(MainMenuEntry): class UserMainMenuEntry(MainMenuEntry):
""" """
Main menu entry for the participant app. Main menu entry for the participant app.
""" """
verbose_name = ugettext_lazy('Participants') verbose_name = ugettext_lazy('Users')
required_permission = 'participant.can_see_participant' required_permission = 'users.can_see'
default_weight = 50 default_weight = 50
pattern_name = 'user_overview' pattern_name = 'user_list'
icon_css_class = 'icon-user' icon_css_class = 'icon-user'

136
openslides/users/models.py Normal file
View 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)

View File

@ -12,13 +12,13 @@ from openslides.utils.pdf import stylesheet
from .models import User 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'), data = [['#', _('Title'), _('Last Name'), _('First Name'),
_('Structure level'), _('Group')]] _('Structure level'), _('Group')]]
if config['participant_sort_users_by_first_name']: if config['users_sort_users_by_first_name']:
sort = 'first_name' sort = 'first_name'
else: else:
sort = 'last_name' sort = 'last_name'
@ -48,30 +48,30 @@ def participants_to_pdf(pdf):
return 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 "-" users_pdf_wlan_ssid = config["users_pdf_wlan_ssid"] or "-"
participant_pdf_wlan_password = config["participant_pdf_wlan_password"] or "-" users_pdf_wlan_password = config["users_pdf_wlan_password"] or "-"
participant_pdf_wlan_encryption = config["participant_pdf_wlan_encryption"] or "-" users_pdf_wlan_encryption = config["users_pdf_wlan_encryption"] or "-"
participant_pdf_url = config["participant_pdf_url"] or "-" users_pdf_url = config["users_pdf_url"] or "-"
participant_pdf_welcometitle = config["participant_pdf_welcometitle"] users_pdf_welcometitle = config["users_pdf_welcometitle"]
participant_pdf_welcometext = config["participant_pdf_welcometext"] users_pdf_welcometext = config["users_pdf_welcometext"]
if config['participant_sort_users_by_first_name']: if config['users_sort_users_by_first_name']:
sort = 'first_name' sort = 'first_name'
else: else:
sort = 'last_name' sort = 'last_name'
qrcode_size = 2 * cm qrcode_size = 2 * cm
# qrcode for system url # qrcode for system url
qrcode_url = QrCodeWidget(participant_pdf_url) qrcode_url = QrCodeWidget(users_pdf_url)
qrcode_url.barHeight = qrcode_size qrcode_url.barHeight = qrcode_size
qrcode_url.barWidth = qrcode_size qrcode_url.barWidth = qrcode_size
qrcode_url.barBorder = 0 qrcode_url.barBorder = 0
qrcode_url_draw = Drawing(45, 45) qrcode_url_draw = Drawing(45, 45)
qrcode_url_draw.add(qrcode_url) qrcode_url_draw.add(qrcode_url)
# qrcode for wlan # 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 = QrCodeWidget(text)
qrcode_wlan.barHeight = qrcode_size qrcode_wlan.barHeight = qrcode_size
qrcode_wlan.barWidth = qrcode_size qrcode_wlan.barWidth = qrcode_size
@ -89,15 +89,15 @@ def participants_passwords_to_pdf(pdf):
stylesheet['h2'])) stylesheet['h2']))
cell.append(Paragraph("%s:" % _("WLAN name (SSID)"), cell.append(Paragraph("%s:" % _("WLAN name (SSID)"),
stylesheet['formfield'])) stylesheet['formfield']))
cell.append(Paragraph(participant_pdf_wlan_ssid, cell.append(Paragraph(users_pdf_wlan_ssid,
stylesheet['formfield_value'])) stylesheet['formfield_value']))
cell.append(Paragraph("%s:" % _("WLAN password"), cell.append(Paragraph("%s:" % _("WLAN password"),
stylesheet['formfield'])) stylesheet['formfield']))
cell.append(Paragraph(participant_pdf_wlan_password, cell.append(Paragraph(users_pdf_wlan_password,
stylesheet['formfield_value'])) stylesheet['formfield_value']))
cell.append(Paragraph("%s:" % _("WLAN encryption"), cell.append(Paragraph("%s:" % _("WLAN encryption"),
stylesheet['formfield'])) stylesheet['formfield']))
cell.append(Paragraph(participant_pdf_wlan_encryption, cell.append(Paragraph(users_pdf_wlan_encryption,
stylesheet['formfield_value'])) stylesheet['formfield_value']))
cell.append(Spacer(0, 0.5 * cm)) cell.append(Spacer(0, 0.5 * cm))
# OpenSlides access data # OpenSlides access data
@ -114,17 +114,17 @@ def participants_passwords_to_pdf(pdf):
stylesheet['formfield_value'])) stylesheet['formfield_value']))
cell2.append(Paragraph("URL:", cell2.append(Paragraph("URL:",
stylesheet['formfield'])) stylesheet['formfield']))
cell2.append(Paragraph(participant_pdf_url, cell2.append(Paragraph(users_pdf_url,
stylesheet['formfield_value'])) stylesheet['formfield_value']))
data.append([cell, cell2]) data.append([cell, cell2])
# QRCodes # QRCodes
cell = [] 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(qrcode_wlan_draw)
cell.append(Paragraph(_("Scan this QRCode to connect WLAN."), cell.append(Paragraph(_("Scan this QRCode to connect WLAN."),
stylesheet['qrcode_comment'])) stylesheet['qrcode_comment']))
cell2 = [] cell2 = []
if participant_pdf_url != "-": if users_pdf_url != "-":
cell2.append(qrcode_url_draw) cell2.append(qrcode_url_draw)
cell2.append(Paragraph(_("Scan this QRCode to open URL."), cell2.append(Paragraph(_("Scan this QRCode to open URL."),
stylesheet['qrcode_comment'])) stylesheet['qrcode_comment']))
@ -138,8 +138,8 @@ def participants_passwords_to_pdf(pdf):
pdf.append(Spacer(0, 2 * cm)) pdf.append(Spacer(0, 2 * cm))
# welcome title and text # welcome title and text
pdf.append(Paragraph(participant_pdf_welcometitle, stylesheet['h2'])) pdf.append(Paragraph(users_pdf_welcometitle, stylesheet['h2']))
pdf.append(Paragraph(participant_pdf_welcometext.replace('\r\n', '<br/>'), pdf.append(Paragraph(users_pdf_welcometext.replace('\r\n', '<br/>'),
stylesheet['Paragraph12'])) stylesheet['Paragraph12']))
pdf.append(PageBreak()) pdf.append(PageBreak())
return pdf return pdf

View File

@ -5,8 +5,8 @@ from .models import User
class Index(indexes.SearchIndex, indexes.Indexable): class Index(indexes.SearchIndex, indexes.Indexable):
text = indexes.EdgeNgramField(document=True, use_template=True) text = indexes.EdgeNgramField(document=True, use_template=True)
text = indexes.EdgeNgramField(document=True, use_template=True) text = indexes.EdgeNgramField(document=True, use_template=True)
modelfilter_name = "Participants" # verbose_name of model modelfilter_name = "Users" # verbose_name of model
modelfilter_value = "participant.user" # 'app_name.model_name' modelfilter_value = "users.user" # 'app_name.model_name'
def get_model(self): def get_model(self):
return User return User

View File

@ -4,36 +4,37 @@ from django.contrib.contenttypes.models import ContentType
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop 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.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable
from openslides.config.signals import config_signal from openslides.config.signals import config_signal
from openslides.core.signals import post_database_setup from openslides.core.signals import post_database_setup
from .api import create_or_reset_admin_user 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') @receiver(config_signal, dispatch_uid='setup_users_config')
def setup_participant_config(sender, **kwargs): def setup_users_config(sender, **kwargs):
""" """
Participant config variables. Participant config variables.
""" """
# General # General
participant_sort_users_by_first_name = ConfigVariable( users_sort_users_by_first_name = ConfigVariable(
name='participant_sort_users_by_first_name', name='users_sort_users_by_first_name',
default_value=False, default_value=False,
form_field=forms.BooleanField( form_field=forms.BooleanField(
required=False, 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'))) help_text=ugettext_lazy('Disable for sorting by last name')))
group_general = ConfigGroup( group_general = ConfigGroup(
title=ugettext_lazy('Sorting'), title=ugettext_lazy('Sorting'),
variables=(participant_sort_users_by_first_name,)) variables=(users_sort_users_by_first_name,))
# PDF # PDF
participant_pdf_welcometitle = ConfigVariable( users_pdf_welcometitle = ConfigVariable(
name='participant_pdf_welcometitle', name='users_pdf_welcometitle',
default_value=_('Welcome to OpenSlides!'), default_value=_('Welcome to OpenSlides!'),
translatable=True, translatable=True,
form_field=forms.CharField( form_field=forms.CharField(
@ -41,8 +42,8 @@ def setup_participant_config(sender, **kwargs):
required=False, required=False,
label=ugettext_lazy('Title for access data and welcome PDF'))) label=ugettext_lazy('Title for access data and welcome PDF')))
participant_pdf_welcometext = ConfigVariable( users_pdf_welcometext = ConfigVariable(
name='participant_pdf_welcometext', name='users_pdf_welcometext',
default_value=_('[Place for your welcome and help text.]'), default_value=_('[Place for your welcome and help text.]'),
translatable=True, translatable=True,
form_field=forms.CharField( form_field=forms.CharField(
@ -50,8 +51,8 @@ def setup_participant_config(sender, **kwargs):
required=False, required=False,
label=ugettext_lazy('Help text for access data and welcome PDF'))) label=ugettext_lazy('Help text for access data and welcome PDF')))
participant_pdf_url = ConfigVariable( users_pdf_url = ConfigVariable(
name='participant_pdf_url', name='users_pdf_url',
default_value='http://example.com:8000', default_value='http://example.com:8000',
form_field=forms.CharField( form_field=forms.CharField(
widget=forms.TextInput(), widget=forms.TextInput(),
@ -59,8 +60,8 @@ def setup_participant_config(sender, **kwargs):
label=ugettext_lazy('System URL'), label=ugettext_lazy('System URL'),
help_text=ugettext_lazy('Used for QRCode in PDF of access data.'))) help_text=ugettext_lazy('Used for QRCode in PDF of access data.')))
participant_pdf_wlan_ssid = ConfigVariable( users_pdf_wlan_ssid = ConfigVariable(
name='participant_pdf_wlan_ssid', name='users_pdf_wlan_ssid',
default_value='', default_value='',
form_field=forms.CharField( form_field=forms.CharField(
widget=forms.TextInput(), widget=forms.TextInput(),
@ -68,8 +69,8 @@ def setup_participant_config(sender, **kwargs):
label=ugettext_lazy('WLAN name (SSID)'), label=ugettext_lazy('WLAN name (SSID)'),
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'))) help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.')))
participant_pdf_wlan_password = ConfigVariable( users_pdf_wlan_password = ConfigVariable(
name='participant_pdf_wlan_password', name='users_pdf_wlan_password',
default_value='', default_value='',
form_field=forms.CharField( form_field=forms.CharField(
widget=forms.TextInput(), widget=forms.TextInput(),
@ -77,8 +78,8 @@ def setup_participant_config(sender, **kwargs):
label=ugettext_lazy('WLAN password'), label=ugettext_lazy('WLAN password'),
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'))) help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.')))
participant_pdf_wlan_encryption = ConfigVariable( users_pdf_wlan_encryption = ConfigVariable(
name='participant_pdf_wlan_encryption', name='users_pdf_wlan_encryption',
default_value='', default_value='',
form_field=forms.ChoiceField( form_field=forms.ChoiceField(
widget=forms.Select(), widget=forms.Select(),
@ -93,21 +94,21 @@ def setup_participant_config(sender, **kwargs):
group_pdf = ConfigGroup( group_pdf = ConfigGroup(
title=ugettext_lazy('PDF'), title=ugettext_lazy('PDF'),
variables=(participant_pdf_welcometitle, variables=(users_pdf_welcometitle,
participant_pdf_welcometext, users_pdf_welcometext,
participant_pdf_url, users_pdf_url,
participant_pdf_wlan_ssid, users_pdf_wlan_ssid,
participant_pdf_wlan_password, users_pdf_wlan_password,
participant_pdf_wlan_encryption)) users_pdf_wlan_encryption))
return ConfigGroupedCollection( return ConfigGroupedCollection(
title=ugettext_noop('Participant'), title=ugettext_noop('Users'),
url='participant', url='users',
weight=50, weight=50,
groups=(group_general, group_pdf)) 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): def create_builtin_groups_and_admin(sender, **kwargs):
""" """
Creates the buildin groups and the admin user. 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') ct_assignment = ContentType.objects.get(app_label='assignment', model='assignment')
perm_16 = Permission.objects.get(content_type=ct_assignment, codename='can_see_assignment') perm_16 = Permission.objects.get(content_type=ct_assignment, codename='can_see_assignment')
ct_participant = ContentType.objects.get(app_label='participant', model='user') ct_users = ContentType.objects.get(app_label='users', model='user')
perm_17 = Permission.objects.get(content_type=ct_participant, codename='can_see_participant') perm_17 = Permission.objects.get(content_type=ct_users, codename='can_see')
ct_mediafile = ContentType.objects.get(app_label='mediafile', model='mediafile') ct_mediafile = ContentType.objects.get(app_label='mediafile', model='mediafile')
perm_18 = Permission.objects.get(content_type=ct_mediafile, codename='can_see') 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_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_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_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_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_46 = Permission.objects.get(content_type=ct_core, codename='can_use_chat')
perm_47 = Permission.objects.get(content_type=ct_mediafile, codename='can_manage') 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) group_staff.permissions.add(perm_31, perm_33, perm_34, perm_35)
# add staff permissions # add staff permissions
group_staff.permissions.add(perm_41, perm_42, perm_43, perm_44, perm_45, perm_46, perm_47, perm_48) 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 group_staff.permissions.add(perm_17) # TODO: Remove this redundancy after cleanup of the permission system
# Admin user # Admin user
create_or_reset_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()

View File

@ -1,6 +1,5 @@
from openslides.projector.api import register_slide_model 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(User, 'participant/user_slide.html')
register_slide_model(Group, 'participant/group_slide.html')

View File

@ -1,10 +1,10 @@
{% load i18n %} {% load i18n %}
{% load highlight %} {% load highlight %}
{% if perms.participant.can_see_participant %} {% if perms.users.can_see %}
<li> <li>
<a href="{{ result.object.get_absolute_url }}">{{ result.object }}</a><br> <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 %} {% highlight result.text with request.GET.q %}
</li> </li>
{% endif %} {% endif %}

View 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 %}

View File

@ -19,7 +19,10 @@
{% trans "New group" %} {% trans "New group" %}
{% endif %} {% endif %}
<small class="pull-right"> <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> </small>
</h1> </h1>
@ -27,7 +30,7 @@
{% include "form.html" %} {% include "form.html" %}
<p> <p>
{% include "formbuttons_saveapply.html" %} {% include "formbuttons_saveapply.html" %}
<a href="{% url 'user_group_overview' %}" class="btn"> <a href="{% url 'group_list' %}" class="btn">
{% trans 'Cancel' %} {% trans 'Cancel' %}
</a> </a>
</p> </p>

View File

@ -19,8 +19,16 @@
<h1> <h1>
{% trans "Groups" %} {% trans "Groups" %}
<small class="pull-right"> <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 'group_create' %}" class="btn btn-mini btn-primary" rel="tooltip" data-original-title="{% trans 'New group' %}">
<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> <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> </small>
</h1> </h1>
@ -37,29 +45,22 @@
<tr class="{% if group.is_active_slide %}activeline{% endif %}"> <tr class="{% if group.is_active_slide %}activeline{% endif %}">
<td class="nobr">{{ group.pk }} <td class="nobr">{{ group.pk }}
{% if group.pk == 1 or group.pk == 2 %} {% 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 %} {% endif %}
</td> </td>
<td> <td>
<a href="{{ group|absolute_url }}">{% trans group.name %}</a> <a href="{% url 'group_detail' group.pk %}">{% trans group.name %}</a>
</td> </td>
<td> <td>
<span class="badge badge-info">{{ group.user_set.all.count }}</span> <span class="badge badge-info">{{ group.user_set.all.count }}</span>
</td> </td>
<td> <td>
<span style="width: 1px; white-space: nowrap;"> <span style="width: 1px; white-space: nowrap;">
{% if perms.core.can_manage_projector %} <a href="{% url 'group_update' group.id %}" title="{% trans 'Edit' %}" class="btn btn-mini">
<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">
<i class="icon-pencil"></i> <i class="icon-pencil"></i>
</a> </a>
{% if group.pk != 1 and group.pk != 2 %} {% 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> <i class="icon-remove"></i>
</a> </a>
{% endif %} {% endif %}

View File

@ -9,23 +9,33 @@
<h1> <h1>
{{ shown_user.clean_name }} {{ shown_user.clean_name }}
<small class="pull-right"> <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 --> <!-- activate projector -->
{% if perms.core.can_manage_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> <i class="icon-facetime-video {% if shown_user.active %}icon-white{% endif %}"></i>
</a> </a>
{% endif %} {% endif %}
{% if perms.participant.can_manage_participant %} {% if perms.users.can_manage %}
<div class="btn-group"> <div class="btn-group">
<a data-toggle="dropdown" class="btn btn-mini dropdown-toggle"> <a data-toggle="dropdown" class="btn btn-mini dropdown-toggle">
<span class="optional-small">{% trans 'More actions' %} </span><span class="caret"></span> <span class="optional-small">{% trans 'More actions' %} </span><span class="caret"></span>
</a> </a>
<ul class="dropdown-menu pull-right"> <ul class="dropdown-menu pull-right">
<!-- edit --> <!-- 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 --> <!-- 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> </ul>
</div> </div>
{% endif %} {% endif %}
@ -59,11 +69,11 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% else %} {% else %}
{% trans "The participant is not member of any group." %} {% trans "The user is not member of any group." %}
{% endif %} {% endif %}
</fieldset> </fieldset>
{% if perms.participant.can_manage_participant %} {% if perms.users.can_manage %}
<fieldset> <fieldset>
<legend>{% trans "Administrative data" %}</legend> <legend>{% trans "Administrative data" %}</legend>
<label>{% trans "Username" %}</label> <label>{% trans "Username" %}</label>
@ -74,7 +84,7 @@
{% if shown_user.last_login > shown_user.date_joined %} {% if shown_user.last_login > shown_user.date_joined %}
{{ shown_user.last_login }} {{ shown_user.last_login }}
{% else %} {% else %}
{% trans "The participant has not logged in yet." %} {% trans "The user has not logged in yet." %}
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>

View File

@ -24,9 +24,9 @@
{% block title %} {% block title %}
{% if edit_user %} {% if edit_user %}
{% trans "Edit participant" %} {% trans "Edit user" %}
{% else %} {% else %}
{% trans "New participant" %} {% trans "New user" %}
{% endif %} {% endif %}
{{ block.super }} {{ block.super }}
{% endblock %} {% endblock %}
@ -34,12 +34,15 @@
{% block content %} {% block content %}
<h1> <h1>
{% if edit_user %} {% if edit_user %}
{% trans "Edit participant" %} {% trans "Edit user" %}
{% else %} {% else %}
{% trans "New participant" %} {% trans "New user" %}
{% endif %} {% endif %}
<small class="pull-right"> <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> </small>
</h1> </h1>
@ -47,12 +50,15 @@
{% include "form.html" %} {% include "form.html" %}
{% if edit_user %} {% if edit_user %}
<p style="margin: -15px 0 25px 0;"> <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> </p>
{% endif %} {% endif %}
<p> <p>
{% include "formbuttons_saveapply.html" %} {% include "formbuttons_saveapply.html" %}
<a href="{% url 'user_overview' %}" class="btn"> <a href="{% url 'user_list' %}" class="btn">
{% trans 'Cancel' %} {% trans 'Cancel' %}
</a> </a>
</p> </p>

View File

@ -2,18 +2,21 @@
{% load i18n %} {% load i18n %}
{% block title %}{% trans "Import participants" %} {{ block.super }}{% endblock %} {% block title %}{% trans "Import users" %} {{ block.super }}{% endblock %}
{% block content %} {% block content %}
<h1> <h1>
{% trans 'Import participants' %} {% trans 'Import users' %}
<small class="pull-right"> <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> </small>
</h1> </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> <p>{% trans 'Please note' %}:</p>
<ul> <ul>
<li> <li>
@ -40,7 +43,7 @@
<button class="btn btn-primary" type="submit"> <button class="btn btn-primary" type="submit">
<span class="icon import">{% trans 'Import' %}</span> <span class="icon import">{% trans 'Import' %}</span>
</button> </button>
<a href="{% url 'user_overview' %}" class="btn"> <a href="{% url 'user_list' %}" class="btn">
{% trans 'Cancel' %} {% trans 'Cancel' %}
</a> </a>
</p> </p>

View File

@ -3,14 +3,14 @@
{% load i18n %} {% load i18n %}
{% block title %} {% block title %}
{% trans 'New multiple participants' %} {{ block.super }} {% trans 'New multiple users' %} {{ block.super }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1> <h1>
{% trans 'New multiple participants' %} {% trans 'New multiple users' %}
<small class="pull-right"> <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' %} <i class="icon-chevron-left"></i> {% trans 'Back to overview' %}
</a> </a>
</small> </small>
@ -19,7 +19,7 @@
{% include 'form.html' %} {% include 'form.html' %}
<p> <p>
{% include 'formbuttons_save.html' %} {% include 'formbuttons_save.html' %}
<a href="{% url 'user_overview' %}" class="btn"> <a href="{% url 'user_list' %}" class="btn">
{% trans 'Cancel' %} {% trans 'Cancel' %}
</a> </a>
</p> </p>

View File

@ -4,7 +4,7 @@
{% load staticfiles %} {% load staticfiles %}
{% load tags %} {% load tags %}
{% block title %}{% trans "Participants" %} {{ block.super }}{% endblock %} {% block title %}{% trans "Users" %} {{ block.super }}{% endblock %}
{% block header %} {% block header %}
<link href="{% static 'css/dataTables/dataTables.bootstrap.css' %}" type="text/css" rel="stylesheet"> <link href="{% static 'css/dataTables/dataTables.bootstrap.css' %}" type="text/css" rel="stylesheet">
@ -17,27 +17,51 @@
{% block content %} {% block content %}
<h1> <h1>
{% trans "Participants" %} {% trans "Users" %}
<small class="pull-right"> <small class="pull-right">
{% if perms.participant.can_manage_participant %} {% if perms.users.can_manage %}
<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' %}" class="btn btn-mini btn-primary" rel="tooltip" data-original-title="{% trans 'New user' %}">
<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> <i class="icon-plus icon-white"></i>
<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> {% trans "New" %}
<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> </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 %} {% 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"> <div class="btn-group">
<a data-toggle="dropdown" class="btn btn-mini dropdown-toggle"> <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> </a>
<ul class="dropdown-menu pull-right"> <ul class="dropdown-menu pull-right">
{% url 'user_settings' as url_usersettings %} {% 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>
<li><a href="{% url 'print_passwords' %}" target="_blank"><i class="icon-th-large"></i> {% trans 'List of access data' %}</a></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> </ul>
</div> </div>
{% else %} {% 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 %} {% endif %}
</small> </small>
</h1> </h1>
@ -51,7 +75,7 @@
<th class="optional">{% trans "Structure level" %}</th> <th class="optional">{% trans "Structure level" %}</th>
<th class="optional">{% trans "Group" %}</th> <th class="optional">{% trans "Group" %}</th>
<th class="optional">{% trans "Committee" %}</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 "Comment" %}</th>
<th class="optional">{% trans "Last Login" %}</th> <th class="optional">{% trans "Last Login" %}</th>
<th class="mini_width">{% trans "Actions" %}</th> <th class="mini_width">{% trans "Actions" %}</th>
@ -61,7 +85,7 @@
<tbody> <tbody>
{% for user in users %} {% for user in users %}
<tr class="{% if user.is_active_slide %}activeline{% endif %}"> <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 %} {% if user != request_user %}
<a href="{% url 'user_status_toggle' user.id %}" <a href="{% url 'user_status_toggle' user.id %}"
class="status_link btn btn-mini {% if user.is_active %}btn-success{% endif %}" class="status_link btn btn-mini {% if user.is_active %}btn-success{% endif %}"
@ -77,7 +101,7 @@
</td> </td>
<td class="optional">{{ user.title }}</td> <td class="optional">{{ user.title }}</td>
<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> <a href="{% url 'user_view' user.id %}">{{ user.first_name }} {{ user.last_name }}</a>
{% else %} {% else %}
<a href="{% url 'user_view' user.id %}">{{ user.last_name }}{% if user.last_name and user.first_name %},{% endif %} {{ user.first_name }}</a> <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 %} {% endfor %}
</td> </td>
<td class="optional">{{ user.committee }}</td> <td class="optional">{{ user.committee }}</td>
{% if perms.participant.can_manage_participant %} {% if perms.users.can_manage %}
<td class="optional"> <td class="optional">
{% if user.comment %} {% if user.comment %}
<a class="btn btn-mini" rel="popover" data-content="{{ user.comment|linebreaks }}"> <a class="btn btn-mini" rel="popover" data-content="{{ user.comment|linebreaks }}">
@ -110,7 +134,7 @@
<span style="width: 1px; white-space: nowrap;"> <span style="width: 1px; white-space: nowrap;">
{% if perms.core.can_manage_projector %} {% 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" <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> <i class="icon-facetime-video {% if user.is_active_slide %}icon-white{% endif %}"></i>
</a> </a>
{% endif %} {% endif %}

View File

@ -11,7 +11,7 @@
rel="tooltip" data-original-title="{% trans 'Show' %}"> rel="tooltip" data-original-title="{% trans 'Show' %}">
<i class="icon-facetime-video {% if user.is_active_slide %}icon-white{% endif %}"></i> <i class="icon-facetime-video {% if user.is_active_slide %}icon-white{% endif %}"></i>
</a>&nbsp; </a>&nbsp;
{% if perms.participant.can_manage_participant %} {% if perms.users.can_manage %}
<a href="{{ user|absolute_url:'update' }}" <a href="{{ user|absolute_url:'update' }}"
rel="tooltip" data-original-title="{% trans 'Edit' %}" class="btn btn-mini right"> rel="tooltip" data-original-title="{% trans 'Edit' %}" class="btn btn-mini right">
<i class="icon-pencil"></i> <i class="icon-pencil"></i>
@ -24,7 +24,7 @@
<a href="{{ user|absolute_url }}">{{ user }}</a> <a href="{{ user|absolute_url }}">{{ user }}</a>
</li> </li>
{% empty %} {% empty %}
<li>{% trans 'No participants available.' %}</li> <li>{% trans 'No users available.' %}</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endblock %} {% endblock %}

View File

@ -6,8 +6,8 @@ urlpatterns = patterns(
'', '',
# User # User
url(r'^$', url(r'^$',
views.UserOverview.as_view(), views.UserListView.as_view(),
name='user_overview'), # TODO: Rename this to user_list name='user_list'),
url(r'^new/$', url(r'^new/$',
views.UserCreateView.as_view(), views.UserCreateView.as_view(),
@ -54,31 +54,31 @@ urlpatterns = patterns(
# Group # Group
url(r'^group/$', url(r'^group/$',
views.GroupOverview.as_view(), views.GroupListView.as_view(),
name='user_group_overview'), # TODO: Rename this to group_list name='group_list'),
url(r'^group/new/$', url(r'^group/new/$',
views.GroupCreateView.as_view(), views.GroupCreateView.as_view(),
name='user_group_new'), name='group_create'),
url(r'^group/(?P<pk>\d+)/$', url(r'^group/(?P<pk>\d+)/$',
views.GroupDetailView.as_view(), views.GroupDetailView.as_view(),
name='user_group_view'), name='group_detail'),
url(r'^group/(?P<pk>\d+)/edit/$', url(r'^group/(?P<pk>\d+)/edit/$',
views.GroupUpdateView.as_view(), views.GroupUpdateView.as_view(),
name='user_group_edit'), name='group_update'),
url(r'^group/(?P<pk>\d+)/del/$', url(r'^group/(?P<pk>\d+)/del/$',
views.GroupDeleteView.as_view(), views.GroupDeleteView.as_view(),
name='user_group_delete'), name='group_delete'),
# PDF # PDF
url(r'^print/$', url(r'^print/$',
views.ParticipantsListPDF.as_view(), views.UsersListPDF.as_view(),
name='user_print'), name='user_print'),
url(r'^passwords/print/$', url(r'^passwords/print/$',
views.ParticipantsPasswordsPDF.as_view(), views.UsersPasswordsPDF.as_view(),
name='print_passwords'), name='print_passwords'),
) )

View File

@ -1,47 +1,43 @@
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import PasswordChangeForm from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.hashers import make_password from django.contrib.auth.hashers import make_password
from django.contrib.auth.views import login as django_login from django.contrib.auth.views import login as django_login
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.shortcuts import redirect from django.utils.translation import ugettext as _, ugettext_lazy, activate
from django.utils.translation import ugettext as _
from django.utils.translation import activate, ugettext_lazy
from openslides.config.api import config from openslides.config.api import config
from openslides.utils.utils import (delete_default_permissions, html_strong, from openslides.utils.utils import delete_default_permissions, html_strong
template) from openslides.utils.views import (
from openslides.utils.views import (CreateView, CSVImportView, DeleteView, DetailView, CreateView, CSVImportView, DeleteView, DetailView, FormView, ListView,
FormView, ListView, PDFView, PDFView, PermissionMixin, QuestionView, RedirectView, SingleObjectMixin,
PermissionMixin, QuestionView, UpdateView, LoginMixin)
RedirectView, SingleObjectMixin, UpdateView) 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 .csv_import import import_users
from .forms import (GroupForm, UserCreateForm, UserMultipleCreateForm, from .forms import (GroupForm, UserCreateForm, UserMultipleCreateForm,
UsersettingsForm, UserUpdateForm) UsersettingsForm, UserUpdateForm)
from .models import get_protected_perm, Group, User from .models import Group, User
from .pdf import participants_to_pdf, participants_passwords_to_pdf 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' required_permission = 'users.can_see'
template_name = 'participant/overview.html'
context_object_name = 'users' context_object_name = 'users'
def get_queryset(self): def get_queryset(self):
query = User.objects 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') query = query.order_by('first_name')
else: else:
query = query.order_by('last_name') query = query.order_by('last_name')
return query.all() return query.all()
def get_context_data(self, **kwargs): 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() all_users = User.objects.count()
# context vars # context vars
context.update({ context.update({
@ -54,29 +50,29 @@ class UserDetailView(DetailView, PermissionMixin):
""" """
Classed based view to show a specific user in the interface. Classed based view to show a specific user in the interface.
""" """
required_permission = 'participant.can_see_participant' required_permission = 'users.can_see'
model = User model = User
template_name = 'participant/user_detail.html'
context_object_name = 'shown_user' context_object_name = 'shown_user'
class UserCreateView(CreateView): class UserCreateView(CreateView):
""" """
Create a new participant. Create a new user.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
template_name = 'participant/edit.html'
model = User model = User
context_object_name = 'edit_user' context_object_name = 'edit_user'
form_class = UserCreateForm form_class = UserCreateForm
success_url_name = 'user_overview' success_url_name = 'user_list'
url_name_args = [] url_name_args = []
def manipulate_object(self, form): def manipulate_object(self, form):
self.object.username = gen_username( self.object.username = gen_username(
form.cleaned_data['first_name'], form.cleaned_data['last_name']) form.cleaned_data['first_name'], form.cleaned_data['last_name'])
if not self.object.default_password: if not self.object.default_password:
self.object.default_password = gen_password() self.object.default_password = gen_password()
self.object.set_password(self.object.default_password) self.object.set_password(self.object.default_password)
def post_save(self, form): 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 # to new user but super(..).post_save(form) removes it and sets only the
# groups selected in the form (without 'registered') # groups selected in the form (without 'registered')
# workaround: add registered group again manually # 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() registered = get_registered_group()
self.object.groups.add(registered) self.object.groups.add(registered)
@ -94,15 +90,19 @@ class UserCreateView(CreateView):
class UserMultipleCreateView(FormView): class UserMultipleCreateView(FormView):
""" """
View to create multiple users at once using a big text field. 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' required_permission = 'users.can_manage'
template_name = 'participant/user_form_multiple.html' template_name = 'users/user_form_multiple.html'
form_class = UserMultipleCreateForm form_class = UserMultipleCreateForm
success_url_name = 'user_overview' success_url_name = 'user_list'
def form_valid(self, form): def form_valid(self, form):
# TODO: Use bulk_create after rework of participant.models.User # TODO: Use bulk_create
for number, line in enumerate(form.cleaned_data['participants_block'].splitlines()): for number, line in enumerate(form.cleaned_data['users_block'].splitlines()):
names_list = line.split() names_list = line.split()
first_name = ' '.join(names_list[:-1]) first_name = ' '.join(names_list[:-1])
last_name = names_list[-1] last_name = names_list[-1]
@ -114,20 +114,19 @@ class UserMultipleCreateView(FormView):
last_name=last_name, last_name=last_name,
default_password=default_password, default_password=default_password,
password=make_password(default_password, '', 'md5')) 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) return super(UserMultipleCreateView, self).form_valid(form)
class UserUpdateView(UpdateView): class UserUpdateView(UpdateView):
""" """
Update an existing participant. Update an existing users.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
template_name = 'participant/edit.html'
model = User model = User
context_object_name = 'edit_user' context_object_name = 'edit_user'
form_class = UserUpdateForm form_class = UserUpdateForm
success_url_name = 'user_overview' success_url_name = 'user_list'
url_name_args = [] url_name_args = []
def get_form_kwargs(self, *args, **kwargs): def get_form_kwargs(self, *args, **kwargs):
@ -135,50 +134,47 @@ class UserUpdateView(UpdateView):
form_kwargs.update({'request': self.request}) form_kwargs.update({'request': self.request})
return form_kwargs return form_kwargs
def manipulate_object(self, form):
self.object.username = form.cleaned_data['user_name']
def post_save(self, form): def post_save(self, form):
super(UserUpdateView, self).post_save(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 # 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 # to new user but super(..).post_save(form) removes it and sets only the
# groups selected in the form (without 'registered') # groups selected in the form (without 'registered')
# workaround: add registered group again manually # 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() registered = get_registered_group()
self.object.groups.add(registered) self.object.groups.add(registered)
class UserDeleteView(DeleteView): class UserDeleteView(DeleteView):
""" """
Delete an participant. Delete a user.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
model = User model = User
success_url_name = 'user_overview' success_url_name = 'user_list'
url_name_args = [] url_name_args = []
def pre_redirect(self, request, *args, **kwargs): def pre_redirect(self, request, *args, **kwargs):
if self.object == self.request.user: if self.object == self.request.user:
messages.error(request, _("You can not delete yourself.")) messages.error(request, _("You can not delete yourself."))
else: else:
super(UserDeleteView, self).pre_redirect(request, *args, **kwargs) super().pre_redirect(request, *args, **kwargs)
def pre_post_redirect(self, request, *args, **kwargs): def pre_post_redirect(self, request, *args, **kwargs):
if self.object == self.request.user: if self.object == self.request.user:
messages.error(self.request, _("You can not delete yourself.")) messages.error(self.request, _("You can not delete yourself."))
else: else:
super(UserDeleteView, self).pre_post_redirect(request, *args, **kwargs) super().pre_post_redirect(request, *args, **kwargs)
class SetUserStatusView(SingleObjectMixin, RedirectView): class SetUserStatusView(SingleObjectMixin, RedirectView):
""" """
Activate or deactivate an user. Activate or deactivate an user.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
allow_ajax = True allow_ajax = True
url_name = 'user_overview' url_name = 'user_list'
url_name_args = [] url_name_args = []
model = User model = User
@ -192,8 +188,6 @@ class SetUserStatusView(SingleObjectMixin, RedirectView):
messages.error(request, _("You can not deactivate yourself.")) messages.error(request, _("You can not deactivate yourself."))
else: else:
self.object.is_active = False self.object.is_active = False
elif action == 'toggle':
self.object.is_active = not self.object.is_active
self.object.save() self.object.save()
return super(SetUserStatusView, self).pre_redirect(request, *args, **kwargs) return super(SetUserStatusView, self).pre_redirect(request, *args, **kwargs)
@ -203,27 +197,27 @@ class SetUserStatusView(SingleObjectMixin, RedirectView):
return context return context
class ParticipantsListPDF(PDFView): class UsersListPDF(PDFView):
""" """
Generate the userliste as PDF. Generate the userliste as PDF.
""" """
required_permission = 'participant.can_see_participant' required_permission = 'users.can_see'
filename = ugettext_lazy("Participant-list") filename = ugettext_lazy("user-list")
document_title = ugettext_lazy('List of Participants') document_title = ugettext_lazy('List of Users')
def append_to_pdf(self, pdf): def append_to_pdf(self, pdf):
""" """
Append PDF objects. 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' required_permission = 'users.can_manage'
filename = ugettext_lazy("Participant-access-data") filename = ugettext_lazy("User-access-data")
top_space = 0 top_space = 0
def build_document(self, pdf_document, story): def build_document(self, pdf_document, story):
@ -233,7 +227,7 @@ class ParticipantsPasswordsPDF(PDFView):
""" """
Append PDF objects. Append PDF objects.
""" """
participants_passwords_to_pdf(pdf) users_passwords_to_pdf(pdf)
class UserCSVImportView(CSVImportView): class UserCSVImportView(CSVImportView):
@ -241,16 +235,16 @@ class UserCSVImportView(CSVImportView):
Import users via CSV. Import users via CSV.
""" """
import_function = staticmethod(import_users) import_function = staticmethod(import_users)
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
success_url_name = 'user_overview' success_url_name = 'user_list'
template_name = 'participant/user_form_csv_import.html' template_name = 'users/user_form_csv_import.html'
class ResetPasswordView(SingleObjectMixin, QuestionView): class ResetPasswordView(SingleObjectMixin, QuestionView):
""" """
Set the Passwort for a user to his default password. Set the Passwort for a user to his default password.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
model = User model = User
allow_ajax = True allow_ajax = True
question_message = ugettext_lazy('Do you really want to reset the password?') question_message = ugettext_lazy('Do you really want to reset the password?')
@ -264,17 +258,18 @@ class ResetPasswordView(SingleObjectMixin, QuestionView):
def on_clicked_yes(self): def on_clicked_yes(self):
self.object.reset_password() self.object.reset_password()
self.object.save()
def get_final_message(self): def get_final_message(self):
return _('The Password for %s was successfully reset.') % html_strong(self.object) return _('The Password for %s was successfully reset.') % html_strong(self.object)
class GroupOverview(ListView): class GroupListView(ListView):
""" """
Overview over all groups. Overview over all groups.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
template_name = 'participant/group_overview.html' template_name = 'users/group_list.html'
context_object_name = 'groups' context_object_name = 'groups'
model = Group model = Group
@ -283,19 +278,19 @@ class GroupDetailView(DetailView, PermissionMixin):
""" """
Classed based view to show a specific group in the interface. Classed based view to show a specific group in the interface.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
model = Group model = Group
template_name = 'participant/group_detail.html' template_name = 'users/group_detail.html'
context_object_name = 'group' context_object_name = 'group'
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
context = super(GroupDetailView, self).get_context_data(*args, **kwargs) context = super(GroupDetailView, self).get_context_data(*args, **kwargs)
query = User.objects 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') query = query.order_by('first_name')
else: else:
query = query.order_by('last_name') 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 return context
@ -303,57 +298,70 @@ class GroupCreateView(CreateView):
""" """
Create a new group. Create a new group.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
template_name = 'participant/group_edit.html' template_name = 'users/group_form.html'
context_object_name = 'group' context_object_name = 'group'
model = Group model = Group
form_class = GroupForm form_class = GroupForm
success_url_name = 'user_group_overview' success_url_name = 'group_list'
url_name_args = [] url_name_args = []
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
delete_default_permissions() delete_default_permissions()
return super(GroupCreateView, self).get(request, *args, **kwargs) 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): class GroupUpdateView(UpdateView):
""" """
Update an existing group. Update an existing group.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
template_name = 'participant/group_edit.html' template_name = 'users/group_form.html'
model = Group model = Group
context_object_name = 'group' context_object_name = 'group'
form_class = GroupForm form_class = GroupForm
success_url_name = 'user_group_overview'
url_name_args = [] url_name_args = []
success_url_name = 'group_list'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
delete_default_permissions() delete_default_permissions()
return super(GroupUpdateView, self).get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
def get_form_kwargs(self, *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}) form_kwargs.update({'request': self.request})
return form_kwargs 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): class GroupDeleteView(DeleteView):
""" """
Delete a group. Delete a group.
""" """
required_permission = 'participant.can_manage_participant' required_permission = 'users.can_manage'
model = Group model = Group
success_url_name = 'user_group_overview' success_url_name = 'group_list'
question_url_name = 'group_detail'
url_name_args = [] url_name_args = []
def pre_redirect(self, request, *args, **kwargs): def pre_redirect(self, request, *args, **kwargs):
if not self.is_protected_from_deleting(): 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): def pre_post_redirect(self, request, *args, **kwargs):
if not self.is_protected_from_deleting(): 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): def is_protected_from_deleting(self):
""" """
@ -370,10 +378,21 @@ class GroupDeleteView(DeleteView):
messages.error( messages.error(
self.request, self.request,
_('You can not delete the last group containing the permission ' _('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 True
return False 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): def login(request):
extra_content = {} extra_content = {}
@ -394,54 +413,41 @@ def login(request):
extra_content['next'] = reverse('password_change') extra_content['next'] = reverse('password_change')
except User.DoesNotExist: except User.DoesNotExist:
pass 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 class UserSettingsView(LoginMixin, UpdateView):
@template('participant/settings.html') model = User
def user_settings(request): form_class = UsersettingsForm
""" success_url_name = 'user_settings'
Edit own user account. url_name_args = []
""" template_name = 'users/settings.html'
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})
return { def get_initial(self):
'form': form_user, initial = super().get_initial()
'edituser': request.user, 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 class UserPasswordSettingsView(LoginMixin, FormView):
@template('participant/password_change.html') form_class = PasswordChangeForm
def user_settings_password(request): success_url_name = 'core_dashboard'
""" template_name = 'users/password_change.html'
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)
return { def form_valid(self, form):
'form': 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

View 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)

View File

@ -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'

View File

@ -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]

View File

@ -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)

View File

@ -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)

View File

@ -1,3 +0,0 @@
from django.dispatch import Signal
receive_persons = Signal(providing_args=['person_prefix_filter', 'id_filter'])

View File

@ -439,8 +439,8 @@ class FormView(PermissionMixin, ExtraContextMixin, FormMixin,
pass pass
class UpdateView(PermissionMixin, ExtraContextMixin, class UpdateView(PermissionMixin, ExtraContextMixin, ModelFormMixin,
ModelFormMixin, django_views.UpdateView): django_views.UpdateView):
""" """
View to update an model object. View to update an model object.
""" """
@ -471,13 +471,12 @@ class DeleteView(SingleObjectMixin, QuestionView):
""" """
View to delete an model object. View to delete an model object.
""" """
success_url = None success_url = None
success_url_name = None success_url_name = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() 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): def get_redirect_url(self, **kwargs):
""" """

View File

@ -1,7 +1,7 @@
from django.test.client import Client from django.test.client import Client
from openslides.config.api import config from openslides.config.api import config
from openslides.participant.models import User from openslides.users.models import User
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
@ -43,8 +43,7 @@ class PersonalInfoWidget(TestCase):
return assignment return assignment
def setUp(self): def setUp(self):
self.user = User.objects.create(username='HansMeiser') self.user = User.objects.create_user('HansMeiser', 'default')
self.user.reset_password('default')
self.client = Client() self.client = Client()
self.client.login(username='HansMeiser', password='default') self.client.login(username='HansMeiser', password='default')
@ -56,7 +55,7 @@ class PersonalInfoWidget(TestCase):
agenda = self.import_agenda() agenda = self.import_agenda()
if agenda: if agenda:
item_1 = agenda.models.Item.objects.create(title='My Item Title iw5ohNgee4eiYahb5Eiv') 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/') 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, 'I am on the list of speakers of the following items:', status_code=200)
self.assertContains(response, 'My Item Title iw5ohNgee4eiYahb5Eiv', status_code=200) self.assertContains(response, 'My Item Title iw5ohNgee4eiYahb5Eiv', status_code=200)

View File

@ -6,7 +6,7 @@ from django.test.client import Client
from openslides.agenda.models import Item, Speaker from openslides.agenda.models import Item, Speaker
from openslides.agenda.signals import agenda_list_of_speakers from openslides.agenda.signals import agenda_list_of_speakers
from openslides.config.api import config 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.projector.api import set_active_slide, register_slide_model
from openslides.utils.exceptions import OpenSlidesError from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
@ -24,15 +24,15 @@ class ListOfSpeakerModelTests(TestCase):
def test_append_speaker(self): def test_append_speaker(self):
# Append speaker1 to the list of item1 # Append speaker1 to the list of item1
speaker1_item1 = Speaker.objects.add(self.speaker1, self.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 # Append speaker1 to the list of item2
speaker1_item2 = Speaker.objects.add(self.speaker1, self.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 # Append speaker2 to the list of item1
speaker2_item1 = Speaker.objects.add(self.speaker2, self.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 # Try to append speaker 1 again to the list of item1
with self.assertRaises(OpenSlidesError): with self.assertRaises(OpenSlidesError):
@ -69,7 +69,7 @@ class ListOfSpeakerModelTests(TestCase):
self.assertIsNone(speaker1_item1.end_time) self.assertIsNone(speaker1_item1.end_time)
self.assertIsNone(speaker2_item1.begin_time) self.assertIsNone(speaker2_item1.begin_time)
speaker2_item1.begin_speach() 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) self.assertIsNotNone(speaker2_item1.begin_time)
@patch('openslides.agenda.models.update_projector_overlay') @patch('openslides.agenda.models.update_projector_overlay')
@ -109,12 +109,12 @@ class SpeakerViewTestCase(TestCase):
self.admin_client.login(username='admin', password='admin') self.admin_client.login(username='admin', password='admin')
# Speaker1 # 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 = Client()
self.speaker1_client.login(username='speaker1', password='speaker') self.speaker1_client.login(username='speaker1', password='speaker')
# Speaker2 # 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 = Client()
self.speaker2_client.login(username='speaker2', password='speaker') self.speaker2_client.login(username='speaker2', password='speaker')
@ -135,12 +135,12 @@ class SpeakerViewTestCase(TestCase):
class TestSpeakerAppendView(SpeakerViewTestCase): class TestSpeakerAppendView(SpeakerViewTestCase):
def test_get(self): 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) self.assertEqual(Speaker.objects.filter(item=self.item1).count(), 0)
# Set speaker1 to item1 # Set speaker1 to item1
response = self.check_url('/agenda/1/speaker/', self.speaker1_client, 302) 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.assertEqual(Speaker.objects.filter(item=self.item1).count(), 1)
self.assertMessage(response, 'You were successfully added to the list of speakers.') self.assertMessage(response, 'You were successfully added to the list of speakers.')
@ -162,23 +162,14 @@ class TestAgendaItemView(SpeakerViewTestCase):
def test_post(self): def test_post(self):
# Set speaker1 to item1 # Set speaker1 to item1
response = self.admin_client.post( response = self.admin_client.post(
'/agenda/1/', {'speaker': self.speaker1.person_id}) '/agenda/1/', {'speaker': self.speaker1.id})
self.assertTrue(Speaker.objects.filter(person=self.speaker1, item=self.item1).exists()) self.assertTrue(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists())
# Try it again # Try it again
response = self.admin_client.post( 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.') 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): class TestSpeakerDeleteView(SpeakerViewTestCase):
def test_get(self): def test_get(self):
@ -190,7 +181,7 @@ class TestSpeakerDeleteView(SpeakerViewTestCase):
response = self.admin_client.post( response = self.admin_client.post(
'/agenda/1/speaker/%d/del/' % speaker.pk, {'yes': 'yes'}) '/agenda/1/speaker/%d/del/' % speaker.pk, {'yes': 'yes'})
self.assertEqual(response.status_code, 302) 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): def test_post_as_user(self):
Speaker.objects.add(self.speaker1, self.item1) Speaker.objects.add(self.speaker1, self.item1)
@ -198,14 +189,14 @@ class TestSpeakerDeleteView(SpeakerViewTestCase):
response = self.speaker1_client.post( response = self.speaker1_client.post(
'/agenda/1/speaker/del/', {'yes': 'yes'}) '/agenda/1/speaker/del/', {'yes': 'yes'})
self.assertEqual(response.status_code, 302) 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): class TestSpeakerSpeakView(SpeakerViewTestCase):
def test_get(self): 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) 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) speaker = Speaker.objects.add(self.speaker1, self.item1)
response = self.check_url(url, self.admin_client, 302) response = self.check_url(url, self.admin_client, 302)
@ -257,7 +248,7 @@ class GlobalListOfSpeakersLinks(SpeakerViewTestCase):
set_active_slide('agenda', pk=1) set_active_slide('agenda', pk=1)
response = self.speaker1_client.get('/agenda/list_of_speakers/add/') response = self.speaker1_client.get('/agenda/list_of_speakers/add/')
self.assertRedirects(response, '/agenda/1/') 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.') self.assertMessage(response, 'You were successfully added to the list of speakers.')
perm = Permission.objects.filter(name='Can see agenda').get() 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/') response = self.speaker1_client.get('/agenda/list_of_speakers/add/')
self.assertRedirects(response, '/agenda/%d/' % agenda_item.pk) 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.') self.assertMessage(response, 'You were successfully added to the list of speakers.')
def test_global_next_speaker_url(self): def test_global_next_speaker_url(self):

View File

@ -7,7 +7,7 @@ from django.test.client import Client
from openslides.agenda.models import Item from openslides.agenda.models import Item
from openslides.agenda.slides import agenda_slide 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.projector.api import set_active_slide
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
@ -127,8 +127,7 @@ class ViewTest(TestCase):
self.refreshItems() self.refreshItems()
self.admin = User.objects.get(pk=1) self.admin = User.objects.get(pk=1)
self.anonym, created = User.objects.get_or_create(username='testanonym') self.anonym = User.objects.create_user('testanonym', 'default')
self.anonym.reset_password('default')
def refreshItems(self): def refreshItems(self):
self.item1 = Item.objects.get(pk=self.item1.id) self.item1 = Item.objects.get(pk=self.item1.id)
@ -268,8 +267,7 @@ class ViewTest(TestCase):
# Prepare # Prepare
self.item1.type = Item.ORGANIZATIONAL_ITEM self.item1.type = Item.ORGANIZATIONAL_ITEM
self.item1.save() self.item1.save()
user = User.objects.create(username='testuser_EeBoPh5uyookoowoodii') user = User.objects.create_user('testuser_EeBoPh5uyookoowoodii', 'default')
user.reset_password('default')
client = Client() client = Client()
client.login(username='testuser_EeBoPh5uyookoowoodii', password='default') client.login(username='testuser_EeBoPh5uyookoowoodii', password='default')
# Test view with permission # Test view with permission
@ -334,12 +332,8 @@ class ViewTest(TestCase):
class ConfigTest(TestCase): class ConfigTest(TestCase):
def setUp(self): 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 = Client()
self.client.login(username='config_test_admin', password='default') self.client.login(username='admin', password='admin')
def test_config_collection_css_javascript(self): def test_config_collection_css_javascript(self):
response = self.client.get('/config/agenda/') response = self.client.get('/config/agenda/')

View File

@ -2,7 +2,7 @@ from django.test.client import Client
from openslides.agenda.models import Item, Speaker from openslides.agenda.models import Item, Speaker
from openslides.assignment.models import Assignment from openslides.assignment.models import Assignment
from openslides.participant.models import User from openslides.users.models import User
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
@ -34,6 +34,6 @@ class AssignmentModelTest(TestCase):
self.assertEqual(item.speaker_set.count(), 1) self.assertEqual(item.speaker_set.count(), 1)
assignment.gen_poll() assignment.gen_poll()
self.assertTrue(item.speaker_set.filter(person=person_1).exists()) self.assertTrue(item.speaker_set.filter(user=person_1).exists())
self.assertTrue(item.speaker_set.filter(person=person_2).exists()) self.assertTrue(item.speaker_set.filter(user=person_2).exists())
self.assertTrue(item.speaker_set.filter(person=person_3).exists()) self.assertTrue(item.speaker_set.filter(user=person_3).exists())

View File

@ -1,7 +1,7 @@
from django.test.client import Client from django.test.client import Client
from openslides.assignment.models import Assignment from openslides.assignment.models import Assignment
from openslides.participant.models import User from openslides.users.models import User
from openslides.utils.test import TestCase from openslides.utils.test import TestCase

View File

@ -2,7 +2,7 @@ from django.test.client import Client
from openslides.assignment.models import Assignment, AssignmentPoll from openslides.assignment.models import Assignment, AssignmentPoll
from openslides.config.api import config 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 from openslides.utils.test import TestCase
@ -14,7 +14,7 @@ class AssignmentViewTestCase(TestCase):
self.admin_client.login(username='admin', password='admin') self.admin_client.login(username='admin', password='admin')
# Staff # 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') staff_group = Group.objects.get(name='Staff')
self.staff.groups.add(staff_group) self.staff.groups.add(staff_group)
self.staff.save() self.staff.save()
@ -22,7 +22,7 @@ class AssignmentViewTestCase(TestCase):
self.staff_client.login(username='staff', password='staff') self.staff_client.login(username='staff', password='staff')
# Delegate # 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') delegate_group = Group.objects.get(name='Delegates')
self.delegate.groups.add(delegate_group) self.delegate.groups.add(delegate_group)
self.delegate.save() self.delegate.save()
@ -30,7 +30,7 @@ class AssignmentViewTestCase(TestCase):
self.delegate_client.login(username='delegate', password='delegate') self.delegate_client.login(username='delegate', password='delegate')
# Registered # 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 = Client()
self.registered_client.login(username='registered', password='registered') self.registered_client.login(username='registered', password='registered')

View File

@ -12,7 +12,7 @@ from openslides.config.api import (config, ConfigCollection, ConfigGroup,
ConfigGroupedCollection, ConfigVariable) ConfigGroupedCollection, ConfigVariable)
from openslides.config.exceptions import ConfigError, ConfigNotFound from openslides.config.exceptions import ConfigError, ConfigNotFound
from openslides.config.signals import config_signal 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 from openslides.utils.test import TestCase
@ -105,12 +105,10 @@ class ConfigFormTest(TestCase):
perm = Permission.objects.get(content_type=ct, codename='can_manage') perm = Permission.objects.get(content_type=ct, codename='can_manage')
# Setup two users # Setup two users
self.manager = User.objects.create(username='config_test_manager') self.manager = User.objects.create_user('config_test_manager', 'default')
self.manager.reset_password('default')
self.manager.user_permissions.add(perm) self.manager.user_permissions.add(perm)
self.normal_user = User.objects.create(username='config_test_normal_user') self.normal_user = User.objects.create_user('config_test_normal_user', 'default')
self.normal_user.reset_password('default')
# Login # Login
self.client_manager = Client() self.client_manager = Client()
@ -254,8 +252,7 @@ class ConfigWeightTest(TestCase):
perm = Permission.objects.get(content_type=ct, codename='can_manage') perm = Permission.objects.get(content_type=ct, codename='can_manage')
# Setup two users # Setup two users
self.manager = User.objects.create(username='config_test_manager') self.manager = User.objects.create_user('config_test_manager', 'default')
self.manager.reset_password('default')
self.manager.user_permissions.add(perm) self.manager.user_permissions.add(perm)
# Login # Login

View File

@ -7,7 +7,7 @@ from openslides.agenda.models import Item
from openslides.config.api import config from openslides.config.api import config
from openslides.core import views from openslides.core import views
from openslides.core.models import CustomSlide from openslides.core.models import CustomSlide
from openslides.participant.models import User from openslides.users.models import User
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
@ -67,7 +67,7 @@ class SelectWidgetsViewTest(TestCase):
class VersionViewTest(TestCase): class VersionViewTest(TestCase):
def setUp(self): def setUp(self):
User.objects.create_user('CoreMaximilian', 'xxx@xx.xx', 'default') User.objects.create_user('CoreMaximilian', 'default')
self.client = Client() self.client = Client()
self.client.login(username='CoreMaximilian', password='default') self.client.login(username='CoreMaximilian', password='default')
@ -87,7 +87,7 @@ class VersionViewTest(TestCase):
class SearchViewTest(TestCase): class SearchViewTest(TestCase):
def test_simple_search(self): def test_simple_search(self):
Item.objects.create(title='agenda_item_bnghfdjkgndkjdfg') 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 = Client()
self.client.login(username='CoreMaximilian', password='default') self.client.login(username='CoreMaximilian', password='default')
response = self.client.get('/search/?q=agenda_item_bnghfd') response = self.client.get('/search/?q=agenda_item_bnghfd')

View File

@ -8,7 +8,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.test.client import Client from django.test.client import Client
from openslides.mediafile.models import Mediafile from openslides.mediafile.models import Mediafile
from openslides.participant.models import User from openslides.core.models import User
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
@ -24,11 +24,9 @@ class MediafileTest(TestCase):
# Setup three different users # Setup three different users
self.manager = User.objects.get(pk=1) self.manager = User.objects.get(pk=1)
self.vip_user = User.objects.create(username='mediafile_test_vip_user') self.vip_user = User.objects.create_user('mediafile_test_vip_user', 'default')
self.vip_user.reset_password('default')
self.vip_user.user_permissions.add(perm_1, perm_2) self.vip_user.user_permissions.add(perm_1, perm_2)
self.normal_user = User.objects.create(username='mediafile_test_normal_user') self.normal_user = User.objects.create_user('mediafile_test_normal_user', 'default')
self.normal_user.reset_password('default')
# Setup a mediafile object # Setup a mediafile object
self.tmp_dir = settings.MEDIA_ROOT self.tmp_dir = settings.MEDIA_ROOT
@ -74,13 +72,16 @@ class MediafileTest(TestCase):
clients = self.login_clients() clients = self.login_clients()
response = clients['client_manager'].get('/mediafile/new/') response = clients['client_manager'].get('/mediafile/new/')
self.assertContains(response, '---------', status_code=200) 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') self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
response = clients['client_vip_user'].get('/mediafile/new/') response = clients['client_vip_user'].get('/mediafile/new/')
self.assertNotContains(response, '<select id="id_uploader" name="uploader">', status_code=200) self.assertNotContains(response, '<select id="id_uploader" name="uploader">', status_code=200)
self.assertTemplateUsed(response, 'mediafile/mediafile_form.html') self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
response = clients['client_normal_user'].get('/mediafile/new/') response = clients['client_normal_user'].get('/mediafile/new/')
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
bad_client = Client() bad_client = Client()
response = bad_client.get('/mediafile/new/') response = bad_client.get('/mediafile/new/')
self.assertRedirects(response, expected_url='/login/?next=/mediafile/new/', status_code=302, target_status_code=200) 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/', response_1 = client_1.post('/mediafile/new/',
{'title': 'new_test_file_title_1', {'title': 'new_test_file_title_1',
'mediafile': new_file_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) self.assertRedirects(response_1, expected_url='/mediafile/', status_code=302, target_status_code=200)
object_1 = Mediafile.objects.latest('timestamp') object_1 = Mediafile.objects.latest('timestamp')
self.assertEqual(object_1.mediafile.url, '/media/file/new_test_file.txt') self.assertEqual(object_1.mediafile.url, '/media/file/new_test_file.txt')
@ -129,12 +130,15 @@ class MediafileTest(TestCase):
clients = self.login_clients() clients = self.login_clients()
response = clients['client_manager'].get('/mediafile/1/edit/') response = clients['client_manager'].get('/mediafile/1/edit/')
self.assertContains(response, '---------', status_code=200) 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') self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
response = clients['client_vip_user'].get('/mediafile/1/edit/') response = clients['client_vip_user'].get('/mediafile/1/edit/')
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
response = clients['client_normal_user'].get('/mediafile/1/edit/') response = clients['client_normal_user'].get('/mediafile/1/edit/')
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
bad_client = Client() bad_client = Client()
response = bad_client.get('/mediafile/1/edit/') response = bad_client.get('/mediafile/1/edit/')
self.assertRedirects(response, expected_url='/login/?next=/mediafile/1/edit/', status_code=302, target_status_code=200) 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() self.object.save()
response = clients['client_vip_user'].get('/mediafile/1/edit/') response = clients['client_vip_user'].get('/mediafile/1/edit/')
self.assertNotContains(response, '---------', status_code=200) 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') self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
def test_edit_mediafile_post_request(self): def test_edit_mediafile_post_request(self):
@ -158,7 +162,7 @@ class MediafileTest(TestCase):
response_1 = client_1.post('/mediafile/2/edit/', response_1 = client_1.post('/mediafile/2/edit/',
{'title': 'new_test_file_title_1', {'title': 'new_test_file_title_1',
'mediafile': new_file_1, 'mediafile': new_file_1,
'uploader': self.manager.person_id}) 'uploader': self.manager.pk})
self.assertEqual(response_1.status_code, 302) self.assertEqual(response_1.status_code, 302)
object_2 = Mediafile.objects.get(pk=2) object_2 = Mediafile.objects.get(pk=2)
self.assertEqual(object_2.mediafile.url, '/media/file/new_test_file.txt') self.assertEqual(object_2.mediafile.url, '/media/file/new_test_file.txt')

View File

@ -1,29 +1,23 @@
import os import os
from io import BytesIO from io import BytesIO
from django.test.client import Client
from openslides.motion.csv_import import import_motions from openslides.motion.csv_import import import_motions
from openslides.motion.models import Category, Motion 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 from openslides.utils.test import TestCase
class CSVImport(TestCase): class CSVImport(TestCase):
def setUp(self): def setUp(self):
# Admin # User1
self.admin = User.objects.create_superuser('Admin_ieY0Eereimeimeizuosh', 'admin@admin.admin', 'eHiK1aiRahxaix0Iequ2') self.user1 = User.objects.create_user('Admin_ieY0Eereimeimeizuosh', 'eHiK1aiRahxaix0Iequ2')
self.admin_client = Client()
self.admin_client.login(username='Admin_ieY0Eereimeimeizuosh', password='eHiK1aiRahxaix0Iequ2')
# Normal user # Normal user
self.normal_user = User.objects.create_user('User_CiuNgo4giqueeChie5oi', 'user@user.user', 'eihi1Eequaek4eagaiKu') self.normal_user = User.objects.create_user('User_CiuNgo4giqueeChie5oi', 'eihi1Eequaek4eagaiKu')
self.normal_client = Client()
self.normal_client.login(username='User_CiuNgo4giqueeChie5oi', password='eihi1Eequaek4eagaiKu')
# Category # Category
self.category1 = Category.objects.create(name='Bildung', prefix='B1') Category.objects.create(name='Bildung', prefix='B1')
self.category2 = Category.objects.create(name='Bildung', prefix='B2') Category.objects.create(name='Bildung', prefix='B2')
def test_example_file_de(self): def test_example_file_de(self):
special_user = User.objects.create_user(username='Harry_Holland', special_user = User.objects.create_user(username='Harry_Holland',
@ -41,7 +35,7 @@ class CSVImport(TestCase):
self.assertEqual(Motion.objects.count(), 0) self.assertEqual(Motion.objects.count(), 0)
with open(csv_dir + '/motions-demo_de.csv', 'rb') as f: with open(csv_dir + '/motions-demo_de.csv', 'rb') as f:
success_message, warning_message, error_message = import_motions( 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) self.assertEqual(Motion.objects.count(), 11)
motion1 = Motion.objects.get(pk=1) motion1 = Motion.objects.get(pk=1)
@ -74,7 +68,7 @@ class CSVImport(TestCase):
csv_file = BytesIO() csv_file = BytesIO()
csv_file.write(bytes('Header\nMalformed data,\n,Title,Text,,,\n', 'utf8')) csv_file.write(bytes('Header\nMalformed data,\n,Title,Text,,,\n', 'utf8'))
success_message, warning_message, error_message = import_motions( 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.assertEqual(success_message, '')
self.assertTrue('Line is malformed.' in error_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')) csv_file = BytesIO(bytes('Müller', 'iso-8859-15'))
success_message, warning_message, error_message = import_motions( success_message, warning_message, error_message = import_motions(
csvfile=csv_file, csvfile=csv_file,
default_submitter=self.normal_user.person_id, default_submitter=self.normal_user.id,
override=False) override=False)
self.assertEqual(success_message, '') self.assertEqual(success_message, '')
self.assertIn('Import file has wrong character encoding, only UTF-8 is supported!', error_message) self.assertIn('Import file has wrong character encoding, only UTF-8 is supported!', error_message)

View File

@ -1,7 +1,7 @@
from openslides.config.api import config from openslides.config.api import config
from openslides.motion.exceptions import WorkflowError from openslides.motion.exceptions import WorkflowError
from openslides.motion.models import Motion, State, Workflow 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 from openslides.utils.test import TestCase

View File

@ -1,6 +1,6 @@
from django.test.client import Client from django.test.client import Client
from openslides.motion.models import Motion from openslides.motion.models import Motion
from openslides.participant.models import User from openslides.users.models import User
from openslides.utils.test import TestCase from openslides.utils.test import TestCase

View File

@ -7,7 +7,7 @@ from django.test.client import Client
from openslides.config.api import config from openslides.config.api import config
from openslides.mediafile.models import Mediafile from openslides.mediafile.models import Mediafile
from openslides.motion.models import Category, Motion, MotionLog, State 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 from openslides.utils.test import TestCase
@ -19,7 +19,7 @@ class MotionViewTestCase(TestCase):
self.admin_client.login(username='admin', password='admin') self.admin_client.login(username='admin', password='admin')
# Staff # 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') staff_group = Group.objects.get(name='Staff')
self.staff.groups.add(staff_group) self.staff.groups.add(staff_group)
self.staff.save() self.staff.save()
@ -27,7 +27,7 @@ class MotionViewTestCase(TestCase):
self.staff_client.login(username='staff', password='staff') self.staff_client.login(username='staff', password='staff')
# Delegate # 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') delegate_group = Group.objects.get(name='Delegates')
self.delegate.groups.add(delegate_group) self.delegate.groups.add(delegate_group)
self.delegate.save() self.delegate.save()
@ -35,7 +35,7 @@ class MotionViewTestCase(TestCase):
self.delegate_client.login(username='delegate', password='delegate') self.delegate_client.login(username='delegate', password='delegate')
# Registered # 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 = Client()
self.registered_client.login(username='registered', password='registered') 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.assertContains(self.admin_client.get('/motion/1/'), 'registered')
self.registered.delete() self.registered.delete()
self.assertNotContains(self.admin_client.get('/motion/1/'), 'registered') self.assertNotContains(self.admin_client.get('/motion/1/'), 'registered')
self.assertContains(self.admin_client.get('/motion/1/'), 'empty')
class TestMotionDetailVersionView(MotionViewTestCase): class TestMotionDetailVersionView(MotionViewTestCase):
@ -126,7 +125,7 @@ class TestMotionCreateView(MotionViewTestCase):
response = self.delegate_client.post(self.url, {'title': 'delegate motion', response = self.delegate_client.post(self.url, {'title': 'delegate motion',
'text': 'motion text', 'text': 'motion text',
'reason': 'motion reason', 'reason': 'motion reason',
'submitter': self.admin.person_id}) 'submitter': self.admin.id})
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
motion = Motion.objects.get(versions__title='delegate motion') motion = Motion.objects.get(versions__title='delegate motion')
self.assertTrue(motion.is_submitter(self.delegate)) self.assertTrue(motion.is_submitter(self.delegate))
@ -135,7 +134,7 @@ class TestMotionCreateView(MotionViewTestCase):
response = self.registered_client.post(self.url, {'title': 'registered motion', response = self.registered_client.post(self.url, {'title': 'registered motion',
'text': 'motion text', 'text': 'motion text',
'reason': 'motion reason', 'reason': 'motion reason',
'submitter': self.admin.person_id}) 'submitter': self.admin.id})
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
self.assertFalse(Motion.objects.filter(versions__title='registered motion').exists()) self.assertFalse(Motion.objects.filter(versions__title='registered motion').exists())
@ -164,13 +163,13 @@ class TestMotionCreateView(MotionViewTestCase):
config['motion_identifier'] = 'manually' config['motion_identifier'] = 'manually'
response = self.admin_client.post(self.url, {'title': 'something', response = self.admin_client.post(self.url, {'title': 'something',
'text': 'bar', 'text': 'bar',
'submitter': self.admin.person_id, 'submitter': self.admin.id,
'identifier': 'uufag5faoX0thahBi8Fo'}) 'identifier': 'uufag5faoX0thahBi8Fo'})
self.assertFormError(response, 'form', 'identifier', 'Motion with this Identifier already exists.') self.assertFormError(response, 'form', 'identifier', 'Motion with this Identifier already exists.')
def test_empty_text_field(self): def test_empty_text_field(self):
response = self.admin_client.post(self.url, {'title': 'foo', 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.') self.assertFormError(response, 'form', 'text', 'This field is required.')
def test_identifier_with_category_prefix(self): 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', response = self.admin_client.post(self.url, {'title': 'new motion_title',
'text': 'motion text', 'text': 'motion text',
'reason': 'motion reason', 'reason': 'motion reason',
'submitter': self.admin.person_id, 'submitter': self.admin.id,
'workflow': 1}) 'workflow': 1})
self.assertRedirects(response, '/motion/1/') self.assertRedirects(response, '/motion/1/')
motion = Motion.objects.get(pk=1) motion = Motion.objects.get(pk=1)
@ -235,7 +234,7 @@ class TestMotionUpdateView(MotionViewTestCase):
'text': 'another motion text', 'text': 'another motion text',
'reason': 'another motion reason', 'reason': 'another motion reason',
'workflow': workflow.pk, 'workflow': workflow.pk,
'submitter': self.admin.person_id}) 'submitter': self.admin.id})
self.assertRedirects(response, '/motion/1/') self.assertRedirects(response, '/motion/1/')
motion = Motion.objects.get(pk=self.motion1.pk) motion = Motion.objects.get(pk=self.motion1.pk)
self.assertEqual(motion.versions.count(), 2) 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', response = self.admin_client.post(self.url, {'title': 'another new motion_title',
'text': 'another motion text', 'text': 'another motion text',
'reason': 'another motion reason', 'reason': 'another motion reason',
'submitter': self.admin.person_id, 'submitter': self.admin.id,
'workflow': workflow.pk, 'workflow': workflow.pk,
'disable_versioning': 'true'}) 'disable_versioning': 'true'})
self.assertRedirects(response, '/motion/1/') self.assertRedirects(response, '/motion/1/')
@ -278,7 +277,7 @@ class TestMotionUpdateView(MotionViewTestCase):
'text': 'eedieFoothae2iethuo3', 'text': 'eedieFoothae2iethuo3',
'reason': 'ier2laiy1veeGoo0mau2', 'reason': 'ier2laiy1veeGoo0mau2',
'workflow': workflow.pk, 'workflow': workflow.pk,
'submitter': self.admin.person_id}) 'submitter': self.admin.id})
self.assertRedirects(response, '/motion/1/') self.assertRedirects(response, '/motion/1/')
motion = Motion.objects.get(pk=self.motion1.pk) motion = Motion.objects.get(pk=self.motion1.pk)
self.assertEqual(motion.versions.count(), 1) self.assertEqual(motion.versions.count(), 1)
@ -287,11 +286,11 @@ class TestMotionUpdateView(MotionViewTestCase):
self.assertEqual(self.motion1.state.workflow.pk, 1) self.assertEqual(self.motion1.state.workflow.pk, 1)
response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2', response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2',
'text': 'eequei1Tee1aegeNgee0', 'text': 'eequei1Tee1aegeNgee0',
'submitter': self.admin.person_id}) 'submitter': self.admin.id})
self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 1) self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 1)
response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2', response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2',
'text': 'eequei1Tee1aegeNgee0', 'text': 'eequei1Tee1aegeNgee0',
'submitter': self.admin.person_id, 'submitter': self.admin.id,
'workflow': 2}) 'workflow': 2})
self.assertRedirects(response, '/motion/1/') self.assertRedirects(response, '/motion/1/')
self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 2) 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') motion = Motion.objects.create(title='cuoPhoX4Baifoxoothi3', text='zee7xei3taediR9loote')
response = self.staff_client.get('/motion/%s/' % motion.id) response = self.staff_client.get('/motion/%s/' % motion.id)
self.assertNotContains(response, 'aengeing3quair3fieGi') self.assertNotContains(response, 'aengeing3quair3fieGi')
motion.support(self.registered) motion.support(self.registered)
self.registered.last_name = 'aengeing3quair3fieGi' self.registered.last_name = 'aengeing3quair3fieGi'
self.registered.save() self.registered.save()
@ -313,7 +313,7 @@ class TestMotionUpdateView(MotionViewTestCase):
'/motion/%s/edit/' % motion.id, '/motion/%s/edit/' % motion.id,
{'title': 'oori4KiaghaeSeuzaim2', {'title': 'oori4KiaghaeSeuzaim2',
'text': 'eequei1Tee1aegeNgee0', 'text': 'eequei1Tee1aegeNgee0',
'submitter': self.delegate.person_id}) 'submitter': self.delegate.id})
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
motion.add_submitter(self.delegate) motion.add_submitter(self.delegate)

View File

@ -1 +0,0 @@
VERSION = (9999, 9999, 9999, 'alpha', 1)

View File

@ -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

View File

@ -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()

View File

@ -9,11 +9,11 @@ from openslides.global_settings import * # noqa
OPENSLIDES_USER_DATA_PATH = os.path.realpath(os.path.dirname(__file__)) 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 # Use 'DEBUG = True' to get more details for server errors. Default for releases: False
DEBUG = True DEBUG = False
TEMPLATE_DEBUG = DEBUG TEMPLATE_DEBUG = DEBUG
# Make this unique, and don't share it with anybody. # 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 # Database settings. Change this to use MySQL or PostgreSQL
DATABASES = { DATABASES = {
@ -25,13 +25,6 @@ DATABASES = {
'HOST': '', 'HOST': '',
'PORT': ''}} 'PORT': ''}}
# Add OpenSlides plugins to this list
INSTALLED_PLUGINS = (
'tests.person_api',
)
INSTALLED_APPS += INSTALLED_PLUGINS
# Some other settings # Some other settings
TIME_ZONE = 'Europe/Berlin' TIME_ZONE = 'Europe/Berlin'

View File

@ -2,7 +2,7 @@ from io import BytesIO
from textwrap import dedent from textwrap import dedent
from openslides.utils.test import TestCase 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): class TestCSVImport(TestCase):

View File

@ -1,31 +1,25 @@
from openslides.participant.api import gen_password, gen_username from openslides.users.api import gen_password, gen_username
from openslides.participant.models import Group, User from openslides.users.models import Group, User
from openslides.utils.person import get_person, Persons
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
class UserTest(TestCase): class UserTest(TestCase):
def setUp(self): def setUp(self):
self.user1 = User() self.user1 = User()
self.user1.first_name = u'Max' self.user1.first_name = 'Max'
self.user1.last_name = u'Mustermann' self.user1.last_name = 'Mustermann'
self.user1.username = gen_username( self.user1.username = gen_username(
self.user1.first_name, self.user1.last_name) self.user1.first_name, self.user1.last_name)
self.user1.default_password = gen_password() self.user1.default_password = gen_password()
self.user1.save() 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): def test_str(self):
self.assertEqual(str(self.user1), 'Max Mustermann') self.assertEqual(str(self.user1), 'Max Mustermann')
def test_name_suffix(self): def test_name_suffix(self):
self.user1.structure_level = u'München' self.user1.structure_level = 'München'
self.user1.save() 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): def test_reset_password(self):
self.assertIsInstance(self.user1.default_password, str) self.assertIsInstance(self.user1.default_password, str)
@ -35,39 +29,14 @@ class UserTest(TestCase):
self.user1.reset_password() self.user1.reset_password()
self.assertTrue(self.user1.check_password(self.user1.default_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): def test_get_absolute_url(self):
urls = (('detail', '/participant/2/'), urls = (('detail', '/user/2/'),
('update', '/participant/2/edit/'), ('update', '/user/2/edit/'),
('delete', '/participant/2/del/')) ('delete', '/user/2/del/'))
for link, url in urls: for link, url in urls:
self.assertEqual(self.user1.get_absolute_url(link), url) 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): class DefaultGroups(TestCase):
def test_pks_of_default_groups(self): def test_pks_of_default_groups(self):
@ -86,7 +55,7 @@ class DefaultGroups(TestCase):
'agenda.can_see_orga_items', 'agenda.can_see_orga_items',
'motion.can_see_motion', 'motion.can_see_motion',
'assignment.can_see_assignment', 'assignment.can_see_assignment',
'participant.can_see_participant', 'users.can_see',
'mediafile.can_see') 'mediafile.can_see')
for perm_string in default_perms: for perm_string in default_perms:
perm_string_list = [] perm_string_list = []

View File

@ -1,7 +1,7 @@
from django.test.client import Client from django.test.client import Client
from django.test.utils import override_settings from django.test.utils import override_settings
from openslides.participant.models import Group, User from openslides.users.models import Group, User
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
@ -12,8 +12,7 @@ class TestUmlautUser(TestCase):
""" """
def setUp(self): def setUp(self):
self.user = User.objects.create(username='äöü') self.user = User.objects.create_user('äöü', 'äöü')
self.user.reset_password('äöü')
self.client = Client() self.client = Client()
self.client.login(username='äöü', password='äöü') self.client.login(username='äöü', password='äöü')
@ -32,10 +31,10 @@ class TestUmlautUser(TestCase):
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
def test_permission(self): 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.assertEqual(response.status_code, 403)
self.user.groups.add(Group.objects.get(pk=4)) 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) self.assertEqual(response.status_code, 200)

View File

@ -1,5 +1,5 @@
from openslides.participant.api import gen_username from openslides.users.api import gen_username
from openslides.participant.models import User from openslides.users.models import User
from openslides.utils.test import TestCase from openslides.utils.test import TestCase

View File

@ -5,8 +5,8 @@ from django.contrib.contenttypes.models import ContentType
from django.test.client import Client from django.test.client import Client
from openslides.config.api import config from openslides.config.api import config
from openslides.participant.api import get_registered_group from openslides.users.api import get_registered_group
from openslides.participant.models import get_protected_perm, Group, User from openslides.users.models import Group, User
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
@ -20,37 +20,41 @@ class UserViews(TestCase):
self.client.login(username='admin', password='admin') self.client.login(username='admin', password='admin')
def test_create(self): def test_create(self):
response = self.client.get('/participant/new/') response = self.client.get('/user/new/')
self.assertTemplateUsed(response, 'participant/edit.html')
self.assertContains(response, 'New participant') self.assertTemplateUsed(response, 'users/user_form.html')
response = self.client.post('/participant/new/', {'first_name': 'test_name_ho8hui2niz4nohSupahb'}) self.assertContains(response, 'New user')
self.assertRedirects(response, '/participant/') response = self.client.post('/user/new/', {'first_name': 'test_name_ho8hui2niz4nohSupahb'})
self.assertRedirects(response, '/user/')
def test_create_multiple(self): def test_create_multiple(self):
response = self.client.get('/participant/new_multiple/') response = self.client.get('/user/new_multiple/')
self.assertTemplateUsed(response, 'participant/user_form_multiple.html') self.assertTemplateUsed(response, 'users/user_form_multiple.html')
self.assertContains(response, 'New multiple participants') self.assertContains(response, 'New multiple users')
self.assertEqual(User.objects.count(), 1) self.assertEqual(User.objects.count(), 1)
block = ('first_name_ksdjfhkjsdhf75utgeitrten last_name_khonizt958zh8fh\n' block = ('first_name_ksdjfhkjsdhf75utgeitrten last_name_khonizt958zh8fh\n'
'first_name_1_bmgnf7z8ru first_name_2_kjc98vivt last_name_dfg76kjkjuibv') 'first_name_1_bmgnf7z8ru first_name_2_kjc98vivt last_name_dfg76kjkjuibv')
response = self.client.post('/participant/new_multiple/', response = self.client.post('/user/new_multiple/',
{'participants_block': block}) {'users_block': block})
self.assertEqual(User.objects.count(), 3) self.assertEqual(User.objects.count(), 3)
def test_update(self): def test_update(self):
response = self.client.get('/participant/1/edit/') response = self.client.get('/user/1/edit/')
self.assertTemplateUsed(response, 'participant/edit.html')
self.assertContains(response, 'Edit participant') self.assertTemplateUsed(response, 'users/user_form.html')
self.assertContains(response, 'Edit user')
response = self.client.post( response = self.client.post(
'/participant/1/edit/', '/user/1/edit/',
{'user_name': 'test_name_unaewae5Ir0saijeac2I', {'username': 'test_name_unaewae5Ir0saijeac2I',
'first_name': 'test_name_aJi5jaizaVingaeF3Ohj', 'first_name': 'test_name_aJi5jaizaVingaeF3Ohj',
'groups': '4', 'groups': '4',
'is_active': 'yes'}) 'is_active': 'yes'})
self.assertRedirects(response, '/participant/')
self.assertRedirects(response, '/user/')
def test_activate(self): 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) self.assertEqual(response.status_code, 302)
@ -71,45 +75,49 @@ class GroupViews(TestCase):
self.user_2.groups.add(self.delegate) self.user_2.groups.add(self.delegate)
self.client = Client() self.client = Client()
login_user = User.objects.create(username='loginusername', is_superuser=True) self.client.login(username='admin', password='admin')
login_user.reset_password('default')
self.client.login(username='loginusername', password='default')
def test_detail(self): 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' pattern = r'admins_first_name Administrator|aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer'
match = re.findall(pattern, response.content.decode('utf8')) match = re.findall(pattern, response.content.decode('utf8'))
self.assertEqual(match[0], 'admins_first_name Administrator') self.assertEqual(match[0], 'admins_first_name Administrator')
self.assertEqual(match[1], 'aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer') self.assertEqual(match[1], 'aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer')
config['participant_sort_users_by_first_name'] = True config['users_sort_users_by_first_name'] = True
self.assertTrue(config['participant_sort_users_by_first_name']) self.assertTrue(config['users_sort_users_by_first_name'])
response = self.client.get('/participant/group/3/') response = self.client.get('/user/group/3/')
pattern = r'admins_first_name Administrator|aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer' pattern = r'admins_first_name Administrator|aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer'
match = re.findall(pattern, response.content.decode('utf8')) match = re.findall(pattern, response.content.decode('utf8'))
self.assertEqual(match[1], 'admins_first_name Administrator') self.assertEqual(match[1], 'admins_first_name Administrator')
self.assertEqual(match[0], 'aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer') self.assertEqual(match[0], 'aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer')
def test_create(self): def test_create(self):
response = self.client.get('/participant/group/new/') response = self.client.get('/user/group/new/')
self.assertTemplateUsed(response, 'participant/group_edit.html')
self.assertTemplateUsed(response, 'users/group_form.html')
self.assertContains(response, 'New group') 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): def test_update(self):
response = self.client.get('/participant/group/1/edit/') response = self.client.get('/user/group/1/edit/')
self.assertTemplateUsed(response, 'participant/group_edit.html')
self.assertTemplateUsed(response, 'users/group_form.html')
self.assertContains(response, 'Edit group') 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): class LockoutProtection(TestCase):
""" """
Tests that a manager user can not lockout himself by doing Tests that a manager user can not lockout himself by doing
something that removes his last permission to manage participants. Tests something that removes his last permission to manage users. Tests
also that he can see the participant app (although there is no absolute also that he can see the user app (although there is no absolute
protection). protection).
""" """
def setUp(self): def setUp(self):
@ -122,70 +130,69 @@ class LockoutProtection(TestCase):
self.assertFalse(self.user.is_superuser) self.assertFalse(self.user.is_superuser)
def test_delete_yourself(self): def test_delete_yourself(self):
response = self.client.get('/participant/1/del/') response = self.client.get('/user/1/del/')
self.assertRedirects(response, '/participant/1/') self.assertRedirects(response, '/user/1/')
self.assertTrue('You can not delete yourself.' in response.cookies['messages'].value) 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'}) {'yes': 'yes'})
self.assertTrue('You can not delete yourself.' in response.cookies['messages'].value) 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) self.assertEqual(User.objects.count(), 1)
def test_delete_last_manager_group(self): def test_delete_last_manager_group(self):
response = self.client.get('/participant/group/4/del/') response = self.client.get('/user/group/4/del/')
self.assertRedirects(response, '/participant/group/4/') self.assertRedirects(response, '/user/group/4/')
self.assertTrue('You can not delete the last group containing the permission ' self.assertTrue('You can not delete the last group containing the permission '
'to manage participants you are in.' in response.cookies['messages'].value) 'to manage users you are in.' in response.cookies['messages'].value)
response = self.client.post('/participant/group/4/del/', response = self.client.post('/user/group/4/del/',
{'yes': 'yes'}) {'yes': 'yes'})
self.assertTrue('You can not delete the last group containing the permission ' self.assertTrue('You can not delete the last group containing the permission '
'to manage participants you are in.' in response.cookies['messages'].value) 'to manage users you are in.' in response.cookies['messages'].value)
self.assertRedirects(response, '/participant/group/') self.assertRedirects(response, '/user/group/')
self.assertEqual(Group.objects.count(), 4) self.assertEqual(Group.objects.count(), 4)
def test_remove_user_from_last_manager_group_via_UserUpdateView(self): 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', {'username': 'arae0eQu8eeghoogeik0',
'groups': '3'}) 'groups': '3'})
self.assertFormError( self.assertFormError(
response=response, response=response,
form='form', form='form',
field=None, 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): def test_remove_user_from_last_manager_group_via_GroupUpdateView(self):
User.objects.get_or_create(username='foo', pk=2) 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', {'name': 'ChaeFaev4leephaiChae',
'users': '2'}) 'users': '2'})
self.assertFormError( self.assertFormError(
response=response, response=response,
form='form', form='form',
field=None, 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): def test_remove_perm_from_last_manager_group(self):
self.assertNotEqual(get_protected_perm().pk, 90) response = self.client.post('/user/group/4/edit/',
response = self.client.post('/participant/group/4/edit/',
{'name': 'ChaeFaev4leephaiChae', {'name': 'ChaeFaev4leephaiChae',
'users': '1', 'users': '1',
'permissions': '90'}) 'permissions': []})
self.assertFormError( self.assertFormError(
response=response, response=response,
form='form', form='form',
field=None, 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): def test_remove_permission_can_see_user_from_registered(self):
self.assertTrue(self.user.has_perm('participant.can_see_participant')) self.assertTrue(self.user.has_perm('users.can_see'))
# Remove perm from registered group # Remove perm from registered group
can_see_perm = Permission.objects.get( can_see_perm = Permission.objects.get(
content_type=ContentType.objects.get(app_label='participant', model='user'), content_type=ContentType.objects.get(app_label='users', model='user'),
codename='can_see_participant') codename='can_see')
get_registered_group().permissions.remove(can_see_perm) get_registered_group().permissions.remove(can_see_perm)
# Reload user # Reload user
self.user = User.objects.get(pk=1) 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): class TestUserSettings(TestCase):
@ -198,11 +205,13 @@ class TestUserSettings(TestCase):
response = self.admin_client.get('/usersettings/') response = self.admin_client.get('/usersettings/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_pst(self): def test_post(self):
response = self.admin_client.post('/usersettings/', { response = self.admin_client.post('/usersettings/', {
'user_name': 'new_name', 'username': 'new_name',
'language': 'de'}) 'language': 'de'})
self.assertEqual(response.status_code, 200)
self.assertRedirects(response, '/usersettings/')
admin = User.objects.get(pk=1) admin = User.objects.get(pk=1)
self.assertEqual(admin.username, 'new_name') self.assertEqual(admin.username, 'new_name')