diff --git a/aleksis/core/apps.py b/aleksis/core/apps.py index b373367efe01bd5e1d470aaeba02eabe92b9b5e5..13e3b3ddcf1083e1cff0f8c5e3461103bb12f4b2 100644 --- a/aleksis/core/apps.py +++ b/aleksis/core/apps.py @@ -1,9 +1,12 @@ from typing import Any, List, Optional, Tuple import django.apps +from django.contrib.auth.signals import user_logged_in +from django.http import HttpRequest from .signals import clean_scss from .util.apps import AppConfig +from .util.core_helpers import has_person class CoreConfig(AppConfig): @@ -29,3 +32,10 @@ class CoreConfig(AppConfig): apps.get_model("otp_yubikey", "ValidationService").objects.using(using).update_or_create( name="default", defaults={"use_ssl": True, "param_sl": "", "param_timeout": ""} ) + + def user_logged_in( + self, sender: type, request: Optional[HttpRequest], user: "User", **kwargs + ) -> None: + if has_person(user): + # Save the associated person to pick up defaults + user.person.save() diff --git a/aleksis/core/models.py b/aleksis/core/models.py index 490d797755be220237985972d96fbf876041c99d..ffb60ae67fe9799d8faf041a44c423ae07f5308f 100644 --- a/aleksis/core/models.py +++ b/aleksis/core/models.py @@ -176,8 +176,6 @@ class Person(ExtensibleModel): return f"{self.first_name} {self.last_name}" def save(self, *args, **kwargs): - super().save(*args, **kwargs) - # Synchronise user fields to linked User object to keep it up to date if self.user: self.user.first_name = self.first_name @@ -185,6 +183,9 @@ class Person(ExtensibleModel): self.user.email = self.email self.user.save() + self.auto_select_primary_group() + super().save(*args, **kwargs) + def __str__(self) -> str: return self.full_name @@ -204,6 +205,21 @@ class Person(ExtensibleModel): person = Person(user=admin) person.save() + def auto_select_primary_group(self, pattern: Optional[str] = None, force: bool = False) -> None: + """ Auto-select the primary group among the groups the person is member of + + Uses either the pattern passed as argument, or the pattern configured system-wide. + + Does not do anything if either no pattern is defined or the user already has + a primary group, unless force is True. + """ + + pattern = pattern or config.PRIMARY_GROUP_PATTERN + + if pattern: + if force or not self.primary_group: + self.primary_group = self.member_of.filter(name__regex=pattern).first() + class Group(ExtensibleModel): """Any kind of group of persons in a school, including, but not limited diff --git a/aleksis/core/util/apps.py b/aleksis/core/util/apps.py index 2e4ab1d0a860ac8bceb26d8d14db28136af2f483..7eef538ae05265d3492bda331eaf75875229a4ae 100644 --- a/aleksis/core/util/apps.py +++ b/aleksis/core/util/apps.py @@ -2,7 +2,9 @@ from importlib import import_module from typing import Any, List, Optional, Tuple import django.apps +from django.contrib.auth.signals import user_logged_in, user_logged_out from django.db.models.signals import post_migrate, pre_migrate +from django.http import HttpRequest from constance.signals import config_updated @@ -26,6 +28,8 @@ class AppConfig(django.apps.AppConfig): pre_migrate.connect(self.pre_migrate, sender=self) post_migrate.connect(self.post_migrate, sender=self) config_updated.connect(self.config_updated) + user_logged_in.connect(self.user_logged_in) + user_logged_out.connect(self.user_logged_out) # Getting an app ready means it should look at its config once self.config_updated() @@ -82,6 +86,24 @@ class AppConfig(django.apps.AppConfig): """ self._maintain_default_data() + def user_logged_in( + self, sender: type, request: Optional[HttpRequest], user: "User", **kwargs + ) -> None: + """ Called after a user logged in + + By default, it does nothing. + """ + pass + + def user_logged_out( + self, sender: type, request: Optional[HttpRequest], user: "User", **kwargs + ) -> None: + """ Called after a user logged out + + By default, it does nothing. + """ + pass + def _maintain_default_data(self): if not self.models_module: # This app does not have any models, so bail out early