mirror of
https://github.com/ae-utbm/sith.git
synced 2025-07-09 19:40:19 +00:00
replace MetaGroups by proper group management
This commit is contained in:
@ -23,8 +23,8 @@ admin.site.unregister(AuthGroup)
|
||||
|
||||
@admin.register(Group)
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "description", "is_meta")
|
||||
list_filter = ("is_meta",)
|
||||
list_display = ("name", "description", "is_manually_manageable")
|
||||
list_filter = ("is_manually_manageable",)
|
||||
search_fields = ("name",)
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@ from model_bakery import seq
|
||||
from model_bakery.recipe import Recipe, related
|
||||
|
||||
from club.models import Membership
|
||||
from core.models import User
|
||||
from core.models import Group, User
|
||||
from subscription.models import Subscription
|
||||
|
||||
active_subscription = Recipe(
|
||||
@ -60,5 +60,6 @@ board_user = Recipe(
|
||||
first_name="AE",
|
||||
last_name=seq("member "),
|
||||
memberships=related(ae_board_membership),
|
||||
groups=lambda: [Group.objects.get(club_board=settings.SITH_MAIN_CLUB_ID)],
|
||||
)
|
||||
"""A user which is in the board of the AE."""
|
||||
|
@ -47,7 +47,7 @@ from accounting.models import (
|
||||
)
|
||||
from club.models import Club, Membership
|
||||
from com.models import News, NewsDate, Sith, Weekmail
|
||||
from core.models import Group, Page, PageRev, RealGroup, SithFile, User
|
||||
from core.models import Group, Page, PageRev, SithFile, User
|
||||
from core.utils import resize_image
|
||||
from counter.models import Counter, Product, ProductType, StudentCard
|
||||
from election.models import Candidature, Election, ElectionList, Role
|
||||
@ -143,7 +143,9 @@ class Command(BaseCommand):
|
||||
Counter.objects.bulk_create(counters)
|
||||
bar_groups = []
|
||||
for bar_id, bar_name in settings.SITH_COUNTER_BARS:
|
||||
group = RealGroup.objects.create(name=f"{bar_name} admin")
|
||||
group = Group.objects.create(
|
||||
name=f"{bar_name} admin", is_manually_manageable=True
|
||||
)
|
||||
bar_groups.append(
|
||||
Counter.edit_groups.through(counter_id=bar_id, group=group)
|
||||
)
|
||||
@ -366,46 +368,42 @@ Welcome to the wiki page!
|
||||
parent=main_club,
|
||||
)
|
||||
|
||||
Membership.objects.bulk_create(
|
||||
[
|
||||
Membership(user=skia, club=main_club, role=3),
|
||||
Membership(
|
||||
user=comunity,
|
||||
club=bar_club,
|
||||
start_date=localdate(),
|
||||
role=settings.SITH_CLUB_ROLES_ID["Board member"],
|
||||
),
|
||||
Membership(
|
||||
user=sli,
|
||||
club=troll,
|
||||
role=9,
|
||||
description="Padawan Troll",
|
||||
start_date=localdate() - timedelta(days=17),
|
||||
),
|
||||
Membership(
|
||||
user=krophil,
|
||||
club=troll,
|
||||
role=10,
|
||||
description="Maitre Troll",
|
||||
start_date=localdate() - timedelta(days=200),
|
||||
),
|
||||
Membership(
|
||||
user=skia,
|
||||
club=troll,
|
||||
role=2,
|
||||
description="Grand Ancien Troll",
|
||||
start_date=localdate() - timedelta(days=400),
|
||||
end_date=localdate() - timedelta(days=86),
|
||||
),
|
||||
Membership(
|
||||
user=richard,
|
||||
club=troll,
|
||||
role=2,
|
||||
description="",
|
||||
start_date=localdate() - timedelta(days=200),
|
||||
end_date=localdate() - timedelta(days=100),
|
||||
),
|
||||
]
|
||||
Membership.objects.create(user=skia, club=main_club, role=3)
|
||||
Membership.objects.create(
|
||||
user=comunity,
|
||||
club=bar_club,
|
||||
start_date=localdate(),
|
||||
role=settings.SITH_CLUB_ROLES_ID["Board member"],
|
||||
)
|
||||
Membership.objects.create(
|
||||
user=sli,
|
||||
club=troll,
|
||||
role=9,
|
||||
description="Padawan Troll",
|
||||
start_date=localdate() - timedelta(days=17),
|
||||
)
|
||||
Membership.objects.create(
|
||||
user=krophil,
|
||||
club=troll,
|
||||
role=10,
|
||||
description="Maitre Troll",
|
||||
start_date=localdate() - timedelta(days=200),
|
||||
)
|
||||
Membership.objects.create(
|
||||
user=skia,
|
||||
club=troll,
|
||||
role=2,
|
||||
description="Grand Ancien Troll",
|
||||
start_date=localdate() - timedelta(days=400),
|
||||
end_date=localdate() - timedelta(days=86),
|
||||
)
|
||||
Membership.objects.create(
|
||||
user=richard,
|
||||
club=troll,
|
||||
role=2,
|
||||
description="",
|
||||
start_date=localdate() - timedelta(days=200),
|
||||
end_date=localdate() - timedelta(days=100),
|
||||
)
|
||||
|
||||
p = ProductType.objects.create(name="Bières bouteilles")
|
||||
@ -594,7 +592,6 @@ Welcome to the wiki page!
|
||||
)
|
||||
|
||||
# Create an election
|
||||
ae_board_group = Group.objects.get(name=settings.SITH_MAIN_BOARD_GROUP)
|
||||
el = Election.objects.create(
|
||||
title="Élection 2017",
|
||||
description="La roue tourne",
|
||||
@ -604,7 +601,7 @@ Welcome to the wiki page!
|
||||
end_date="7942-06-12 10:28:45+01",
|
||||
)
|
||||
el.view_groups.add(groups.public)
|
||||
el.edit_groups.add(ae_board_group)
|
||||
el.edit_groups.add(main_club.board_group)
|
||||
el.candidature_groups.add(groups.subscribers)
|
||||
el.vote_groups.add(groups.subscribers)
|
||||
liste = ElectionList.objects.create(title="Candidature Libre", election=el)
|
||||
@ -889,7 +886,7 @@ Welcome to the wiki page!
|
||||
def _create_groups(self) -> PopulatedGroups:
|
||||
perms = Permission.objects.all()
|
||||
|
||||
root_group = Group.objects.create(name="Root")
|
||||
root_group = Group.objects.create(name="Root", is_manually_manageable=True)
|
||||
root_group.permissions.add(*list(perms.values_list("pk", flat=True)))
|
||||
# public has no permission.
|
||||
# Its purpose is not to link users to permissions,
|
||||
@ -911,7 +908,9 @@ Welcome to the wiki page!
|
||||
)
|
||||
)
|
||||
)
|
||||
accounting_admin = Group.objects.create(name="Accounting admin")
|
||||
accounting_admin = Group.objects.create(
|
||||
name="Accounting admin", is_manually_manageable=True
|
||||
)
|
||||
accounting_admin.permissions.add(
|
||||
*list(
|
||||
perms.filter(
|
||||
@ -931,13 +930,17 @@ Welcome to the wiki page!
|
||||
).values_list("pk", flat=True)
|
||||
)
|
||||
)
|
||||
com_admin = Group.objects.create(name="Communication admin")
|
||||
com_admin = Group.objects.create(
|
||||
name="Communication admin", is_manually_manageable=True
|
||||
)
|
||||
com_admin.permissions.add(
|
||||
*list(
|
||||
perms.filter(content_type__app_label="com").values_list("pk", flat=True)
|
||||
)
|
||||
)
|
||||
counter_admin = Group.objects.create(name="Counter admin")
|
||||
counter_admin = Group.objects.create(
|
||||
name="Counter admin", is_manually_manageable=True
|
||||
)
|
||||
counter_admin.permissions.add(
|
||||
*list(
|
||||
perms.filter(
|
||||
@ -946,16 +949,20 @@ Welcome to the wiki page!
|
||||
)
|
||||
)
|
||||
)
|
||||
Group.objects.create(name="Banned from buying alcohol")
|
||||
Group.objects.create(name="Banned from counters")
|
||||
Group.objects.create(name="Banned to subscribe")
|
||||
sas_admin = Group.objects.create(name="SAS admin")
|
||||
Group.objects.create(
|
||||
name="Banned from buying alcohol", is_manually_manageable=True
|
||||
)
|
||||
Group.objects.create(name="Banned from counters", is_manually_manageable=True)
|
||||
Group.objects.create(name="Banned to subscribe", is_manually_manageable=True)
|
||||
sas_admin = Group.objects.create(name="SAS admin", is_manually_manageable=True)
|
||||
sas_admin.permissions.add(
|
||||
*list(
|
||||
perms.filter(content_type__app_label="sas").values_list("pk", flat=True)
|
||||
)
|
||||
)
|
||||
forum_admin = Group.objects.create(name="Forum admin")
|
||||
forum_admin = Group.objects.create(
|
||||
name="Forum admin", is_manually_manageable=True
|
||||
)
|
||||
forum_admin.permissions.add(
|
||||
*list(
|
||||
perms.filter(content_type__app_label="forum").values_list(
|
||||
@ -963,7 +970,9 @@ Welcome to the wiki page!
|
||||
)
|
||||
)
|
||||
)
|
||||
pedagogy_admin = Group.objects.create(name="Pedagogy admin")
|
||||
pedagogy_admin = Group.objects.create(
|
||||
name="Pedagogy admin", is_manually_manageable=True
|
||||
)
|
||||
pedagogy_admin.permissions.add(
|
||||
*list(
|
||||
perms.filter(content_type__app_label="pedagogy").values_list(
|
||||
|
@ -563,14 +563,21 @@ class Migration(migrations.Migration):
|
||||
fields=[],
|
||||
options={"proxy": True},
|
||||
bases=("core.group",),
|
||||
managers=[("objects", core.models.MetaGroupManager())],
|
||||
managers=[("objects", django.contrib.auth.models.GroupManager())],
|
||||
),
|
||||
# at first, there existed a RealGroupManager and a RealGroupManager,
|
||||
# which have been since been removed.
|
||||
# However, this removal broke the migrations because it caused an ImportError.
|
||||
# Thus, the managers MetaGroupManager (above) and RealGroupManager (below)
|
||||
# have been replaced by the base django GroupManager to fix the import.
|
||||
# As those managers aren't actually used in migrations,
|
||||
# this replacement doesn't break anything.
|
||||
migrations.CreateModel(
|
||||
name="RealGroup",
|
||||
fields=[],
|
||||
options={"proxy": True},
|
||||
bases=("core.group",),
|
||||
managers=[("objects", core.models.RealGroupManager())],
|
||||
managers=[("objects", django.contrib.auth.models.GroupManager())],
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="page", unique_together={("name", "parent")}
|
||||
|
@ -0,0 +1,51 @@
|
||||
# Generated by Django 4.2.16 on 2024-11-30 13:16
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.db.migrations.state import StateApps
|
||||
from django.db.models import F
|
||||
|
||||
|
||||
def invert_is_manually_manageable(apps: StateApps, schema_editor):
|
||||
"""Invert `is_manually_manageable`.
|
||||
|
||||
This field is a renaming of `is_meta`.
|
||||
However, the meaning has been inverted : the groups
|
||||
which were meta are not manually manageable and vice versa.
|
||||
Thus, the value must be inverted.
|
||||
"""
|
||||
Group = apps.get_model("core", "Group")
|
||||
Group.objects.all().update(is_manually_manageable=~F("is_manually_manageable"))
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [("core", "0040_alter_user_options_user_user_permissions_and_more")]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name="MetaGroup",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name="RealGroup",
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name="group",
|
||||
options={},
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="group",
|
||||
old_name="is_meta",
|
||||
new_name="is_manually_manageable",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="group",
|
||||
name="is_manually_manageable",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="If False, this shouldn't be shown on group management pages",
|
||||
verbose_name="Is manually manageable",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(
|
||||
invert_is_manually_manageable, reverse_code=invert_is_manually_manageable
|
||||
),
|
||||
]
|
112
core/models.py
112
core/models.py
@ -36,7 +36,6 @@ from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractUser, UserManager
|
||||
from django.contrib.auth.models import AnonymousUser as AuthAnonymousUser
|
||||
from django.contrib.auth.models import Group as AuthGroup
|
||||
from django.contrib.auth.models import GroupManager as AuthGroupManager
|
||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||
from django.core import validators
|
||||
from django.core.cache import cache
|
||||
@ -58,34 +57,17 @@ if TYPE_CHECKING:
|
||||
from club.models import Club
|
||||
|
||||
|
||||
class RealGroupManager(AuthGroupManager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(is_meta=False)
|
||||
|
||||
|
||||
class MetaGroupManager(AuthGroupManager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(is_meta=True)
|
||||
|
||||
|
||||
class Group(AuthGroup):
|
||||
"""Implement both RealGroups and Meta groups.
|
||||
"""Wrapper around django.auth.Group"""
|
||||
|
||||
Groups are sorted by their is_meta property
|
||||
"""
|
||||
|
||||
#: If False, this is a RealGroup
|
||||
is_meta = models.BooleanField(
|
||||
_("meta group status"),
|
||||
is_manually_manageable = models.BooleanField(
|
||||
_("Is manually manageable"),
|
||||
default=False,
|
||||
help_text=_("Whether a group is a meta group or not"),
|
||||
help_text=_("If False, this shouldn't be shown on group management pages"),
|
||||
)
|
||||
#: Description of the group
|
||||
description = models.CharField(_("description"), max_length=60)
|
||||
|
||||
class Meta:
|
||||
ordering = ["name"]
|
||||
|
||||
def get_absolute_url(self) -> str:
|
||||
return reverse("core:group_list")
|
||||
|
||||
@ -100,65 +82,6 @@ class Group(AuthGroup):
|
||||
cache.delete(f"sith_group_{self.name.replace(' ', '_')}")
|
||||
|
||||
|
||||
class MetaGroup(Group):
|
||||
"""MetaGroups are dynamically created groups.
|
||||
|
||||
Generally used with clubs where creating a club creates two groups:
|
||||
|
||||
* club-SITH_BOARD_SUFFIX
|
||||
* club-SITH_MEMBER_SUFFIX
|
||||
"""
|
||||
|
||||
#: Assign a manager in a way that MetaGroup.objects only return groups with is_meta=False
|
||||
objects = MetaGroupManager()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.is_meta = True
|
||||
|
||||
@cached_property
|
||||
def associated_club(self) -> Club | None:
|
||||
"""Return the group associated with this meta group.
|
||||
|
||||
The result of this function is cached
|
||||
|
||||
|
||||
Returns:
|
||||
The associated club if it exists, else None
|
||||
"""
|
||||
from club.models import Club
|
||||
|
||||
if self.name.endswith(settings.SITH_BOARD_SUFFIX):
|
||||
# replace this with str.removesuffix as soon as Python
|
||||
# is upgraded to 3.10
|
||||
club_name = self.name[: -len(settings.SITH_BOARD_SUFFIX)]
|
||||
elif self.name.endswith(settings.SITH_MEMBER_SUFFIX):
|
||||
club_name = self.name[: -len(settings.SITH_MEMBER_SUFFIX)]
|
||||
else:
|
||||
return None
|
||||
club = cache.get(f"sith_club_{club_name}")
|
||||
if club is None:
|
||||
club = Club.objects.filter(unix_name=club_name).first()
|
||||
cache.set(f"sith_club_{club_name}", club)
|
||||
return club
|
||||
|
||||
|
||||
class RealGroup(Group):
|
||||
"""RealGroups are created by the developer.
|
||||
|
||||
Most of the time they match a number in settings to be easily used for permissions.
|
||||
"""
|
||||
|
||||
#: Assign a manager in a way that MetaGroup.objects only return groups with is_meta=True
|
||||
objects = RealGroupManager()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
|
||||
def validate_promo(value: int) -> None:
|
||||
start_year = settings.SITH_SCHOOL_START_YEAR
|
||||
delta = (localdate() + timedelta(days=180)).year - start_year
|
||||
@ -204,8 +127,8 @@ def get_group(*, pk: int | None = None, name: str | None = None) -> Group | None
|
||||
else:
|
||||
group = Group.objects.filter(name=name).first()
|
||||
if group is not None:
|
||||
cache.set(f"sith_group_{group.id}", group)
|
||||
cache.set(f"sith_group_{group.name.replace(' ', '_')}", group)
|
||||
name = group.name.replace(" ", "_")
|
||||
cache.set_many({f"sith_group_{group.id}": group, f"sith_group_{name}": group})
|
||||
else:
|
||||
cache.set(f"sith_group_{pk_or_name}", "not_found")
|
||||
return group
|
||||
@ -438,18 +361,6 @@ class User(AbstractUser):
|
||||
return self.was_subscribed
|
||||
if group.id == settings.SITH_GROUP_ROOT_ID:
|
||||
return self.is_root
|
||||
if group.is_meta:
|
||||
# check if this group is associated with a club
|
||||
group.__class__ = MetaGroup
|
||||
club = group.associated_club
|
||||
if club is None:
|
||||
return False
|
||||
membership = club.get_membership_for(self)
|
||||
if membership is None:
|
||||
return False
|
||||
if group.name.endswith(settings.SITH_MEMBER_SUFFIX):
|
||||
return True
|
||||
return membership.role > settings.SITH_MAXIMUM_FREE_ROLE
|
||||
return group in self.cached_groups
|
||||
|
||||
@property
|
||||
@ -474,12 +385,11 @@ class User(AbstractUser):
|
||||
return any(g.id == root_id for g in self.cached_groups)
|
||||
|
||||
@cached_property
|
||||
def is_board_member(self):
|
||||
main_club = settings.SITH_MAIN_CLUB["unix_name"]
|
||||
return self.is_in_group(name=main_club + settings.SITH_BOARD_SUFFIX)
|
||||
def is_board_member(self) -> bool:
|
||||
return self.groups.filter(club_board=settings.SITH_MAIN_CLUB_ID).exists()
|
||||
|
||||
@cached_property
|
||||
def can_read_subscription_history(self):
|
||||
def can_read_subscription_history(self) -> bool:
|
||||
if self.is_root or self.is_board_member:
|
||||
return True
|
||||
|
||||
@ -943,7 +853,7 @@ class SithFile(models.Model):
|
||||
param="1",
|
||||
).save()
|
||||
|
||||
def is_owned_by(self, user):
|
||||
def is_owned_by(self, user: User) -> bool:
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if user.is_root:
|
||||
@ -958,7 +868,7 @@ class SithFile(models.Model):
|
||||
return True
|
||||
return user.id == self.owner_id
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
def can_be_viewed_by(self, user: User) -> bool:
|
||||
if hasattr(self, "profile_of"):
|
||||
return user.can_view(self.profile_of)
|
||||
if hasattr(self, "avatar_of"):
|
||||
|
@ -18,6 +18,7 @@ from smtplib import SMTPException
|
||||
|
||||
import freezegun
|
||||
import pytest
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.core import mail
|
||||
from django.core.cache import cache
|
||||
from django.core.mail import EmailMessage
|
||||
@ -30,7 +31,7 @@ from model_bakery import baker
|
||||
from pytest_django.asserts import assertInHTML, assertRedirects
|
||||
|
||||
from antispam.models import ToxicDomain
|
||||
from club.models import Membership
|
||||
from club.models import Club, Membership
|
||||
from core.markdown import markdown
|
||||
from core.models import AnonymousUser, Group, Page, User
|
||||
from core.utils import get_semester_code, get_start_of_semester
|
||||
@ -145,7 +146,7 @@ class TestUserRegistration:
|
||||
class TestUserLogin:
|
||||
@pytest.fixture()
|
||||
def user(self) -> User:
|
||||
return User.objects.first()
|
||||
return baker.make(User, password=make_password("plop"))
|
||||
|
||||
def test_login_fail(self, client, user):
|
||||
"""Should not login a user correctly."""
|
||||
@ -349,14 +350,9 @@ class TestUserIsInGroup(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
from club.models import Club
|
||||
|
||||
cls.root_group = Group.objects.get(name="Root")
|
||||
cls.public = Group.objects.get(name="Public")
|
||||
cls.skia = User.objects.get(username="skia")
|
||||
cls.toto = User.objects.create(
|
||||
username="toto", first_name="a", last_name="b", email="a.b@toto.fr"
|
||||
)
|
||||
cls.public_group = Group.objects.get(name="Public")
|
||||
cls.public_user = baker.make(User)
|
||||
cls.subscribers = Group.objects.get(name="Subscribers")
|
||||
cls.old_subscribers = Group.objects.get(name="Old subscribers")
|
||||
cls.accounting_admin = Group.objects.get(name="Accounting admin")
|
||||
@ -366,22 +362,12 @@ class TestUserIsInGroup(TestCase):
|
||||
cls.banned_counters = Group.objects.get(name="Banned from counters")
|
||||
cls.banned_subscription = Group.objects.get(name="Banned to subscribe")
|
||||
cls.sas_admin = Group.objects.get(name="SAS admin")
|
||||
cls.club = Club.objects.create(
|
||||
name="Fake Club",
|
||||
unix_name="fake-club",
|
||||
address="Fake address",
|
||||
)
|
||||
cls.club = baker.make(Club)
|
||||
cls.main_club = Club.objects.get(id=1)
|
||||
|
||||
def assert_in_public_group(self, user):
|
||||
assert user.is_in_group(pk=self.public.id)
|
||||
assert user.is_in_group(name=self.public.name)
|
||||
|
||||
def assert_in_club_metagroups(self, user, club):
|
||||
meta_groups_board = club.unix_name + settings.SITH_BOARD_SUFFIX
|
||||
meta_groups_members = club.unix_name + settings.SITH_MEMBER_SUFFIX
|
||||
assert user.is_in_group(name=meta_groups_board) is False
|
||||
assert user.is_in_group(name=meta_groups_members) is False
|
||||
assert user.is_in_group(pk=self.public_group.id)
|
||||
assert user.is_in_group(name=self.public_group.name)
|
||||
|
||||
def assert_only_in_public_group(self, user):
|
||||
self.assert_in_public_group(user)
|
||||
@ -392,13 +378,11 @@ class TestUserIsInGroup(TestCase):
|
||||
self.sas_admin,
|
||||
self.subscribers,
|
||||
self.old_subscribers,
|
||||
self.club.members_group,
|
||||
self.club.board_group,
|
||||
):
|
||||
assert not user.is_in_group(pk=group.pk)
|
||||
assert not user.is_in_group(name=group.name)
|
||||
meta_groups_board = self.club.unix_name + settings.SITH_BOARD_SUFFIX
|
||||
meta_groups_members = self.club.unix_name + settings.SITH_MEMBER_SUFFIX
|
||||
assert user.is_in_group(name=meta_groups_board) is False
|
||||
assert user.is_in_group(name=meta_groups_members) is False
|
||||
|
||||
def test_anonymous_user(self):
|
||||
"""Test that anonymous users are only in the public group."""
|
||||
@ -407,80 +391,80 @@ class TestUserIsInGroup(TestCase):
|
||||
|
||||
def test_not_subscribed_user(self):
|
||||
"""Test that users who never subscribed are only in the public group."""
|
||||
self.assert_only_in_public_group(self.toto)
|
||||
self.assert_only_in_public_group(self.public_user)
|
||||
|
||||
def test_wrong_parameter_fail(self):
|
||||
"""Test that when neither the pk nor the name argument is given,
|
||||
the function raises a ValueError.
|
||||
"""
|
||||
with self.assertRaises(ValueError):
|
||||
self.toto.is_in_group()
|
||||
self.public_user.is_in_group()
|
||||
|
||||
def test_number_queries(self):
|
||||
"""Test that the number of db queries is stable
|
||||
and that less queries are made when making a new call.
|
||||
"""
|
||||
# make sure Skia is in at least one group
|
||||
self.skia.groups.add(Group.objects.first().pk)
|
||||
skia_groups = self.skia.groups.all()
|
||||
group_in = baker.make(Group)
|
||||
self.public_user.groups.add(group_in)
|
||||
|
||||
group_in = skia_groups.first()
|
||||
cache.clear()
|
||||
# Test when the user is in the group
|
||||
with self.assertNumQueries(2):
|
||||
self.skia.is_in_group(pk=group_in.id)
|
||||
self.public_user.is_in_group(pk=group_in.id)
|
||||
with self.assertNumQueries(0):
|
||||
self.skia.is_in_group(pk=group_in.id)
|
||||
self.public_user.is_in_group(pk=group_in.id)
|
||||
|
||||
ids = skia_groups.values_list("pk", flat=True)
|
||||
group_not_in = Group.objects.exclude(pk__in=ids).first()
|
||||
group_not_in = baker.make(Group)
|
||||
cache.clear()
|
||||
# Test when the user is not in the group
|
||||
with self.assertNumQueries(2):
|
||||
self.skia.is_in_group(pk=group_not_in.id)
|
||||
self.public_user.is_in_group(pk=group_not_in.id)
|
||||
with self.assertNumQueries(0):
|
||||
self.skia.is_in_group(pk=group_not_in.id)
|
||||
self.public_user.is_in_group(pk=group_not_in.id)
|
||||
|
||||
def test_cache_properly_cleared_membership(self):
|
||||
"""Test that when the membership of a user end,
|
||||
the cache is properly invalidated.
|
||||
"""
|
||||
membership = Membership.objects.create(
|
||||
club=self.club, user=self.toto, end_date=None
|
||||
)
|
||||
meta_groups_members = self.club.unix_name + settings.SITH_MEMBER_SUFFIX
|
||||
membership = baker.make(Membership, club=self.club, user=self.public_user)
|
||||
cache.clear()
|
||||
assert self.toto.is_in_group(name=meta_groups_members) is True
|
||||
assert membership == cache.get(f"membership_{self.club.id}_{self.toto.id}")
|
||||
self.club.get_membership_for(self.public_user) # this should populate the cache
|
||||
assert membership == cache.get(
|
||||
f"membership_{self.club.id}_{self.public_user.id}"
|
||||
)
|
||||
membership.end_date = now() - timedelta(minutes=5)
|
||||
membership.save()
|
||||
cached_membership = cache.get(f"membership_{self.club.id}_{self.toto.id}")
|
||||
cached_membership = cache.get(
|
||||
f"membership_{self.club.id}_{self.public_user.id}"
|
||||
)
|
||||
assert cached_membership == "not_member"
|
||||
assert self.toto.is_in_group(name=meta_groups_members) is False
|
||||
|
||||
def test_cache_properly_cleared_group(self):
|
||||
"""Test that when a user is removed from a group,
|
||||
the is_in_group_method return False when calling it again.
|
||||
"""
|
||||
# testing with pk
|
||||
self.toto.groups.add(self.com_admin.pk)
|
||||
assert self.toto.is_in_group(pk=self.com_admin.pk) is True
|
||||
self.public_user.groups.add(self.com_admin.pk)
|
||||
assert self.public_user.is_in_group(pk=self.com_admin.pk) is True
|
||||
|
||||
self.toto.groups.remove(self.com_admin.pk)
|
||||
assert self.toto.is_in_group(pk=self.com_admin.pk) is False
|
||||
self.public_user.groups.remove(self.com_admin.pk)
|
||||
assert self.public_user.is_in_group(pk=self.com_admin.pk) is False
|
||||
|
||||
# testing with name
|
||||
self.toto.groups.add(self.sas_admin.pk)
|
||||
assert self.toto.is_in_group(name="SAS admin") is True
|
||||
self.public_user.groups.add(self.sas_admin.pk)
|
||||
assert self.public_user.is_in_group(name="SAS admin") is True
|
||||
|
||||
self.toto.groups.remove(self.sas_admin.pk)
|
||||
assert self.toto.is_in_group(name="SAS admin") is False
|
||||
self.public_user.groups.remove(self.sas_admin.pk)
|
||||
assert self.public_user.is_in_group(name="SAS admin") is False
|
||||
|
||||
def test_not_existing_group(self):
|
||||
"""Test that searching for a not existing group
|
||||
returns False.
|
||||
"""
|
||||
assert self.skia.is_in_group(name="This doesn't exist") is False
|
||||
user = baker.make(User)
|
||||
user.groups.set(list(Group.objects.all()))
|
||||
assert not user.is_in_group(name="This doesn't exist")
|
||||
|
||||
|
||||
class TestDateUtils(TestCase):
|
||||
|
@ -32,19 +32,14 @@ from django.contrib.staticfiles.management.commands.collectstatic import (
|
||||
)
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction
|
||||
from django.forms import (
|
||||
CheckboxSelectMultiple,
|
||||
DateInput,
|
||||
DateTimeInput,
|
||||
TextInput,
|
||||
)
|
||||
from django.forms import CheckboxSelectMultiple, DateInput, DateTimeInput, TextInput
|
||||
from django.utils.translation import gettext
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from phonenumber_field.widgets import RegionalPhoneNumberWidget
|
||||
from PIL import Image
|
||||
|
||||
from antispam.forms import AntiSpamEmailField
|
||||
from core.models import Gift, Page, RealGroup, SithFile, User
|
||||
from core.models import Gift, Group, Page, SithFile, User
|
||||
from core.utils import resize_image
|
||||
from core.views.widgets.select import (
|
||||
AutoCompleteSelect,
|
||||
@ -293,7 +288,7 @@ class UserGroupsForm(forms.ModelForm):
|
||||
required_css_class = "required"
|
||||
|
||||
groups = forms.ModelMultipleChoiceField(
|
||||
queryset=RealGroup.objects.all(),
|
||||
queryset=Group.objects.filter(is_manually_manageable=True),
|
||||
widget=CheckboxSelectMultiple,
|
||||
label=_("Groups"),
|
||||
required=False,
|
||||
|
@ -21,7 +21,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
|
||||
from core.models import RealGroup, User
|
||||
from core.models import Group, User
|
||||
from core.views import CanCreateMixin, CanEditMixin, DetailFormView
|
||||
from core.views.widgets.select import AutoCompleteSelectMultipleUser
|
||||
|
||||
@ -57,7 +57,8 @@ class EditMembersForm(forms.Form):
|
||||
class GroupListView(CanEditMixin, ListView):
|
||||
"""Displays the Group list."""
|
||||
|
||||
model = RealGroup
|
||||
model = Group
|
||||
queryset = Group.objects.filter(is_manually_manageable=True)
|
||||
ordering = ["name"]
|
||||
template_name = "core/group_list.jinja"
|
||||
|
||||
@ -65,7 +66,8 @@ class GroupListView(CanEditMixin, ListView):
|
||||
class GroupEditView(CanEditMixin, UpdateView):
|
||||
"""Edit infos of a Group."""
|
||||
|
||||
model = RealGroup
|
||||
model = Group
|
||||
queryset = Group.objects.filter(is_manually_manageable=True)
|
||||
pk_url_kwarg = "group_id"
|
||||
template_name = "core/group_edit.jinja"
|
||||
fields = ["name", "description"]
|
||||
@ -74,7 +76,8 @@ class GroupEditView(CanEditMixin, UpdateView):
|
||||
class GroupCreateView(CanCreateMixin, CreateView):
|
||||
"""Add a new Group."""
|
||||
|
||||
model = RealGroup
|
||||
model = Group
|
||||
queryset = Group.objects.filter(is_manually_manageable=True)
|
||||
template_name = "core/create.jinja"
|
||||
fields = ["name", "description"]
|
||||
|
||||
@ -84,7 +87,8 @@ class GroupTemplateView(CanEditMixin, DetailFormView):
|
||||
Allow adding and removing users from it.
|
||||
"""
|
||||
|
||||
model = RealGroup
|
||||
model = Group
|
||||
queryset = Group.objects.filter(is_manually_manageable=True)
|
||||
form_class = EditMembersForm
|
||||
pk_url_kwarg = "group_id"
|
||||
template_name = "core/group_detail.jinja"
|
||||
@ -118,7 +122,8 @@ class GroupTemplateView(CanEditMixin, DetailFormView):
|
||||
class GroupDeleteView(CanEditMixin, DeleteView):
|
||||
"""Delete a Group."""
|
||||
|
||||
model = RealGroup
|
||||
model = Group
|
||||
queryset = Group.objects.filter(is_manually_manageable=True)
|
||||
pk_url_kwarg = "group_id"
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
success_url = reverse_lazy("core:group_list")
|
||||
|
Reference in New Issue
Block a user