Commit 80202e60 authored by Thom Wiggers's avatar Thom Wiggers 📐
Browse files

Merge branch 'feature/activemembers-refactor' into 'master'

Added Society model

Closes #635

See merge request thalia/concrexit!933
parents 931ceaec ce7f1c89
......@@ -3,65 +3,48 @@ import csv
import datetime
from django import forms
from django.db.models import Q
from django.contrib import admin, messages
from django.contrib.auth.models import Permission
from django.db.models import Q
from django.http import HttpResponse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from activemembers import models
from activemembers.forms import CommitteeMembershipForm
from utils.translation import TranslatedModelAdmin
from activemembers.forms import MemberGroupMembershipForm, MemberGroupForm
from utils.snippets import datetime_to_lectureyear
from utils.translation import TranslatedModelAdmin
class CommitteeMembershipInlineFormSet(forms.BaseInlineFormSet):
class MemberGroupMembershipInlineFormSet(forms.BaseInlineFormSet):
"""
Solely here for performance reasons.
Needed because the `__str__()` of `CommitteeMembership` (which is displayed
Needed because the `__str__()` of `MemberGroupMembership` (which is displayed
above each inline form) uses the username, name of the member and name of
the committee.
the group.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.queryset = self.queryset.select_related(
'member', 'committee').filter(until=None)
'member', 'group').filter(until=None)
class CommitteeMembershipInline(admin.StackedInline):
"""Inline for committee memberships"""
model = models.CommitteeMembership
formset = CommitteeMembershipInlineFormSet
class MemberGroupMembershipInline(admin.StackedInline):
"""Inline for group memberships"""
model = models.MemberGroupMembership
formset = MemberGroupMembershipInlineFormSet
can_delete = False
ordering = ('since',)
extra = 0
autocomplete_fields = ('member',)
class CommitteeForm(forms.ModelForm):
"""
Solely here for performance reasons.
Needed because the `__str__()` of `Permission` (which is displayed in the
permissions selection box) also prints the corresponding app and
`content_type` for each permission.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['permissions'].queryset = (Permission
.objects
.select_related('content_type'))
@admin.register(models.Committee)
class CommitteeAdmin(TranslatedModelAdmin):
"""Manage the committees"""
inlines = (CommitteeMembershipInline,)
form = CommitteeForm
inlines = (MemberGroupMembershipInline,)
form = MemberGroupForm
list_display = ('name', 'since', 'until', 'active', 'email')
list_filter = ('until', 'active',)
search_fields = ('name', 'description')
......@@ -78,16 +61,33 @@ class CommitteeAdmin(TranslatedModelAdmin):
return instance.contact_mailinglist.name + '@thalia.nu'
return None
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.exclude(board__is_board=True)
@admin.register(models.Society)
class CommitteeAdmin(TranslatedModelAdmin):
"""Manage the societies"""
inlines = (MemberGroupMembershipInline,)
form = MemberGroupForm
list_display = ('name', 'since', 'until', 'active', 'email')
list_filter = ('until', 'active',)
search_fields = ('name', 'description')
filter_horizontal = ('permissions',)
fields = ('name', 'description', 'photo', 'permissions', 'since',
'until', 'contact_mailinglist', 'contact_email', 'active')
def email(self, instance):
if instance.contact_email:
return instance.contact_email
elif instance.contact_mailinglist:
return instance.contact_mailinglist.name + '@thalia.nu'
return None
@admin.register(models.Board)
class BoardAdmin(TranslatedModelAdmin):
"""Manage the board"""
inlines = (CommitteeMembershipInline,)
form = CommitteeForm
inlines = (MemberGroupMembershipInline,)
form = MemberGroupForm
exclude = ('is_board',)
filter_horizontal = ('permissions',)
......@@ -95,22 +95,25 @@ class BoardAdmin(TranslatedModelAdmin):
'contact_mailinglist', 'contact_email', 'since', 'until',)
class BoardFilter(admin.SimpleListFilter):
class TypeFilter(admin.SimpleListFilter):
"""Filter memberships on board-only"""
title = _('board memberships')
parameter_name = 'board'
title = _('group memberships')
parameter_name = 'group_type'
def lookups(self, request, model_admin):
return [
('only', _('Only board memberships')),
('none', _('No board memberships')),
('boards', _('Only boards')),
('committees', _('Only committees')),
('societies', _('Only societies')),
]
def queryset(self, request, queryset):
if self.value() == 'only':
return queryset.filter(committee__board__is_board=True)
elif self.value() == 'none':
return queryset.exclude(committee__board__is_board=True)
if self.value() == 'boards':
return queryset.exclude(group__board=None)
elif self.value() == 'committees':
return queryset.exclude(group__committee=None)
elif self.value() == 'societies':
return queryset.exclude(group__society=None)
return queryset
......@@ -123,7 +126,7 @@ class LectureYearFilter(admin.SimpleListFilter):
def lookups(self, request, model_admin):
current_year = datetime_to_lectureyear(timezone.now())
first_year = datetime_to_lectureyear(
models.CommitteeMembership.objects.earliest('since').since
models.MemberGroupMembership.objects.earliest('since').since
)
return [(year, '{}-{}'.format(year, year+1))
......@@ -161,14 +164,14 @@ class ActiveMembershipsFilter(admin.SimpleListFilter):
return queryset.filter(until__lt=now)
@admin.register(models.CommitteeMembership)
class CommitteeMembershipAdmin(TranslatedModelAdmin):
"""Manage the committee memberships"""
form = CommitteeMembershipForm
list_display = ('member', 'committee', 'since', 'until', 'chair', 'role')
list_filter = ('committee', BoardFilter, LectureYearFilter,
@admin.register(models.MemberGroupMembership)
class MemberGroupMembershipAdmin(TranslatedModelAdmin):
"""Manage the group memberships"""
form = MemberGroupMembershipForm
list_display = ('member', 'group', 'since', 'until', 'chair', 'role')
list_filter = ('group', TypeFilter, LectureYearFilter,
ActiveMembershipsFilter)
list_select_related = ('member', 'committee',)
list_select_related = ('member', 'group',)
search_fields = ('member__first_name', 'member__last_name',
'member__email')
......@@ -176,8 +179,8 @@ class CommitteeMembershipAdmin(TranslatedModelAdmin):
def changelist_view(self, request, extra_context=None):
self.message_user(request, _('Do not edit existing memberships if the '
'chair of a committee has changed, add a '
'new committeemembership instead.'),
'chair of a group has changed, add a '
'new membership instead.'),
messages.WARNING)
return super().changelist_view(request, extra_context)
......@@ -185,16 +188,16 @@ class CommitteeMembershipAdmin(TranslatedModelAdmin):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = ('attachment;'
'filename='
'"committee_memberships.csv"')
'"group_memberships.csv"')
writer = csv.writer(response)
writer.writerow([
_('First name'),
_('Last name'),
_('Email'),
_('Committee'),
_('Committee member since'),
_('Committee member until'),
_('Chair of the committee'),
_('Group'),
_('Member since'),
_('Member until'),
_('Chair of the group'),
_('Role'),
])
......@@ -203,7 +206,7 @@ class CommitteeMembershipAdmin(TranslatedModelAdmin):
membership.member.first_name,
membership.member.last_name,
membership.member.email,
membership.committee,
membership.group,
membership.since,
membership.until,
membership.chair,
......
......@@ -8,8 +8,8 @@ from django.utils import timezone
from members.models import Member
class CommitteeBackend(object):
"""Check permissions against committees"""
class MemberGroupBackend(object):
"""Check permissions against MemberGroups"""
def authenticate(self, *args, **kwargs):
"""Not implemented in this backend"""
......@@ -27,15 +27,15 @@ class CommitteeBackend(object):
except Member.DoesNotExist:
return set()
committees = member.committee_set.filter(
Q(committeemembership__until=None) |
Q(committeemembership__until__gte=timezone.now())
groups = member.membergroup_set.filter(
Q(membergroupmembership__until=None) |
Q(membergroupmembership__until__gte=timezone.now())
)
perm_cache_name = '_committee_perm_cache'
perm_cache_name = '_membergroup_perm_cache'
if not hasattr(user, perm_cache_name):
perms = (Permission.objects
.filter(committee__in=committees)
.filter(membergroup__in=groups)
.values_list('content_type__app_label', 'codename')
.order_by())
setattr(user, perm_cache_name,
......
[
{
"model": "activemembers.committee",
"model": "activemembers.membergroup",
"pk": 1,
"fields": {
"name_nl": "testcie1",
......@@ -17,7 +17,7 @@
}
},
{
"model": "activemembers.committee",
"model": "activemembers.membergroup",
"pk": 2,
"fields": {
"name_nl": "testcie2",
......@@ -30,7 +30,7 @@
}
},
{
"model": "activemembers.committee",
"model": "activemembers.membergroup",
"pk": 3,
"fields": {
"name_nl": "testbestuur1",
......@@ -42,11 +42,41 @@
"until": "1991-09-01"
}
},
{
"model": "activemembers.membergroup",
"pk": 4,
"fields": {
"name_nl": "testsoc1",
"name_en": "testsoc1",
"active": "True",
"description_nl": "testdesc1",
"description_en": "testdesc1",
"since": "1990-09-01",
"until": "1991-09-01"
}
},
{
"model": "activemembers.committee",
"pk": 1,
"fields": {
}
},
{
"model": "activemembers.committee",
"pk": 2,
"fields": {
}
},
{
"model": "activemembers.board",
"pk": 3,
"fields": {
"is_board": true
}
},
{
"model": "activemembers.society",
"pk": 4,
"fields": {
}
}
]
"""The forms defined by the activemembers module"""
from django import forms
from django.contrib.auth.models import Permission
from activemembers.models import CommitteeMembership
from activemembers.models import MemberGroupMembership
from members.models import Member
class CommitteeMembershipForm(forms.ModelForm):
"""Custom form for committee memberships that orders the members"""
class MemberGroupMembershipForm(forms.ModelForm):
"""Custom form for group memberships that orders the members"""
member = forms.ModelChoiceField(
queryset=Member.objects.order_by('first_name',
'last_name'))
class Meta:
model = CommitteeMembership
model = MemberGroupMembership
exclude = ()
class MemberGroupForm(forms.ModelForm):
"""
Solely here for performance reasons.
Needed because the `__str__()` of `Permission` (which is displayed in the
permissions selection box) also prints the corresponding app and
`content_type` for each permission.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['permissions'].queryset = (
Permission.objects.select_related('content_type'))
......@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-07-11 20:07+0200\n"
"PO-Revision-Date: 2018-06-13 20:08+0200\n"
"POT-Creation-Date: 2018-09-06 16:13+0200\n"
"PO-Revision-Date: 2018-09-06 16:12+0200\n"
"Last-Translator: Sébastiaan Versteeg <se_bastiaan@outlook.com>\n"
"Language-Team: \n"
"Language: nl\n"
......@@ -16,245 +16,274 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.0.6\n"
"X-Generator: Poedit 2.0.4\n"
#: activemembers/admin.py
msgid "board memberships"
msgstr "bestuurslidmaatschappen"
#: admin.py models.py
msgid "group memberships"
msgstr "groepslidmaatschappen"
#: activemembers/admin.py
msgid "Only board memberships"
msgstr "Alleen bestuurslidmaatschappen"
#: admin.py
msgid "Only boards"
msgstr "Alleen besturen"
#: activemembers/admin.py
msgid "No board memberships"
msgstr "Geen bestuurslidmaatschappen"
#: admin.py
msgid "Only committees"
msgstr "Alleen commissies"
#: activemembers/admin.py
#: admin.py
msgid "Only societies"
msgstr "Alleen gezelschappen"
#: admin.py
msgid "lecture year"
msgstr "collegejaar"
#: activemembers/admin.py
#: admin.py
msgid "active memberships"
msgstr "actieve lidmaatschappen"
#: activemembers/admin.py
#: admin.py
msgid "Active"
msgstr "Actief"
#: activemembers/admin.py
#: admin.py
msgid "Inactive"
msgstr "Inactief"
#: activemembers/admin.py
#: admin.py
msgid ""
"Do not edit existing memberships if the chair of a committee has changed, "
"add a new committeemembership instead."
"Do not edit existing memberships if the chair of a group has changed, add a "
"new membership instead."
msgstr ""
"Pas geen bestaande lidmaatschappen aan als de voorzitter van een commissie "
"is veranderd. Voeg in de plaats daarvan een nieuw commissielidmaatschap toe."
"Pas geen bestaande lidmaatschappen aan als de voorzitter van een groep is "
"veranderd. Voeg in de plaats daarvan een nieuw commissielidmaatschap toe."
#: activemembers/admin.py
#: admin.py
msgid "First name"
msgstr "Voornaam"
#: activemembers/admin.py
#: admin.py
msgid "Last name"
msgstr "Achernaam"
#: activemembers/admin.py
#: admin.py
msgid "Email"
msgstr "Email"
#: activemembers/admin.py activemembers/models.py
msgid "Committee"
msgstr "Commissie"
#: admin.py
msgid "Group"
msgstr "Groep"
#: activemembers/admin.py activemembers/models.py
msgid "Committee member since"
msgstr "Commissielid sinds"
#: admin.py models.py
msgid "Member since"
msgstr "Lid sinds"
#: activemembers/admin.py activemembers/models.py
msgid "Committee member until"
msgstr "Commissielid tot"
#: admin.py models.py
msgid "Member until"
msgstr "Lid tot"
#: activemembers/admin.py activemembers/models.py
msgid "Chair of the committee"
msgstr "Voorzitter van de commissie"
#: admin.py
msgid "Chair of the group"
msgstr "Voorzitter van de groep"
#: activemembers/admin.py
#: admin.py
msgid "Role"
msgstr "Rol"
#: activemembers/admin.py
#: admin.py
msgid "Export selected memberships"
msgstr "Exporteer de geselecteerde lidmaatschappen"
#: activemembers/apps.py
#: apps.py
msgid "Active members"
msgstr "Actieve leden"
#: activemembers/models.py
#: models.py
msgid "Committee name"
msgstr "Commissienaam"
#: activemembers/models.py
#: models.py
msgid "Description"
msgstr "Beschrijving"
#: activemembers/models.py
#: models.py
msgid "Image"
msgstr "Afbeelding"
#: activemembers/models.py
#: models.py
msgid "permissions"
msgstr "permissies"
#: activemembers/models.py
#: models.py
msgid "founded in"
msgstr "opgericht in"
#: activemembers/models.py
#: models.py
msgid "existed until"
msgstr "bestond tot"
#: activemembers/models.py
#: models.py
msgid "contact email address"
msgstr "contact e-mailadres"
#: activemembers/models.py
#: models.py
msgid "contact mailing list"
msgstr "contact mailinglijst"
#: activemembers/models.py
msgid "Wiki namespace"
msgstr "Wiki namespace"
#: activemembers/models.py
#: models.py
msgid "Please use either the mailing list or email address option."
msgstr "Selecteer een mailinglijst óf vul een e-mailadres in."
#: activemembers/models.py
#: models.py
msgid "member group"
msgstr "ledengroep"
#: models.py
msgid "member groups"
msgstr "ledengroepen"
#: models.py
msgid "Wiki namespace"
msgstr "Wiki namespace"
#: models.py
msgid "committee"
msgstr "commissie"
#: activemembers/models.py
#: activemembers/templates/activemembers/committee_index.html
#: models.py templates/activemembers/committee_index.html
msgid "committees"
msgstr "commissies"
#: activemembers/models.py
msgid "Is this a board"
msgstr "Is dit een bestuur"
#: models.py
msgid "society"
msgstr "gezelschap"
#: models.py templates/activemembers/society_detail.html
#: templates/activemembers/society_index.html
msgid "societies"
msgstr "gezelschappen"
#: models.py
msgid "board"
msgstr "bestuur"
#: activemembers/models.py
#: models.py
msgid "boards"
msgstr "besturen"
#: models.py
msgid "Access the board wiki"
msgstr "Toegang tot bestuurswiki"
#: activemembers/models.py
#: models.py
msgid "A board already exists for those years"
msgstr "Er bestaat al een bestuur voor die jaren"
#: activemembers/models.py
#: models.py
msgid "Member"
msgstr "Lid"
#: activemembers/models.py
msgid "The date this member joined the committee in this role"
msgstr "De datum waarop deze persoon lid werd deze commissie in deze rol"
#: models.py
msgid "Committee"
msgstr "Commissie"
#: models.py
msgid "The date this member joined in this role"
msgstr "De datum waarop deze persoon lid werd in deze rol"
#: activemembers/models.py
msgid "A member of this committee until this time (can't be in the future)."
#: models.py
msgid "A member until this time (can't be in the future)."