Skip to content

Commit

Permalink
save language preference for user (#1080)
Browse files Browse the repository at this point in the history
* save language preference for user

* respect language for notification

* fix loading logentries for models that dont exist

---------

Co-authored-by: Felix Rindt <[email protected]>
  • Loading branch information
jeriox and felixrindt authored Sep 30, 2023
1 parent 57ba281 commit 8964bc9
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 21 deletions.
6 changes: 6 additions & 0 deletions ephios/core/forms/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,9 @@ def __init__(self, *args, **kwargs):

def update_preferences(self):
self.user.preferences["notifications__notifications"] = self.cleaned_data


class UserOwnDataForm(ModelForm):
class Meta:
model = UserProfile
fields = ["preferred_language"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2.4 on 2023-09-30 13:03

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("core", "0020_qualificationcategory_show_with_user_and_more"),
]

operations = [
migrations.AddField(
model_name="userprofile",
name="preferred_language",
field=models.CharField(
choices=[("de", "German"), ("en", "English")],
default="de",
max_length=10,
verbose_name="preferred language",
),
),
migrations.AlterField(
model_name="qualificationcategory",
name="show_with_user",
field=models.BooleanField(
default=True,
verbose_name="Show qualifications of this category everywhere a user is presented",
),
),
]
7 changes: 7 additions & 0 deletions ephios/core/models/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import Optional

import guardian.mixins
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.models import Group, PermissionsMixin
Expand Down Expand Up @@ -110,6 +111,12 @@ class UserProfile(guardian.mixins.GuardianUserMixin, PermissionsMixin, AbstractB
date_of_birth = DateField(_("date of birth"), null=True, blank=False)
phone = CharField(_("phone number"), max_length=254, blank=True, null=True)
calendar_token = CharField(_("calendar token"), max_length=254, default=secrets.token_urlsafe)
preferred_language = CharField(
_("preferred language"),
max_length=10,
default=settings.LANGUAGE_CODE,
choices=settings.LANGUAGES,
)

USERNAME_FIELD = "email"
REQUIRED_FIELDS = [
Expand Down
32 changes: 17 additions & 15 deletions ephios/core/services/notifications/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from ephios.core.models.users import Notification
from ephios.core.services.mail.send import send_mail
from ephios.extra.i18n import language

logger = logging.getLogger(__name__)

Expand All @@ -31,23 +32,24 @@ def send_all_notifications():
for backend in installed_notification_backends():
for notification in Notification.objects.filter(failed=False):
if backend.can_send(notification) and backend.user_prefers_sending(notification):
try:
backend.send(notification)
except Exception as e: # pylint: disable=broad-except
if settings.DEBUG:
raise e
notification.failed = True
notification.save()
with language((notification.user and notification.user.preferred_language) or None):
try:
mail_admins(
"Notification sending failed",
f"Notification: {notification}\nException: {e}\n{traceback.format_exc()}",
backend.send(notification)
except Exception as e: # pylint: disable=broad-except
if settings.DEBUG:
raise e
notification.failed = True
notification.save()
try:
mail_admins(
"Notification sending failed",
f"Notification: {notification}\nException: {e}\n{traceback.format_exc()}",
)
except smtplib.SMTPConnectError:
pass # if the mail backend threw this, mail admin will probably throw this as well
logger.warning(
f"Notification sending failed for notification object #{notification.pk} ({notification}) for backend {backend} with {e}"
)
except smtplib.SMTPConnectError:
pass # if the mail backend threw this, mail admin will probably throw this as well
logger.warning(
f"Notification sending failed for notification object #{notification.pk} ({notification}) for backend {backend} with {e}"
)
Notification.objects.filter(failed=False).delete()


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,10 @@ <h3>{% translate "Qualifications" %}</h3>
<i>{% translate "You have not been assigned any qualificiations." %}</i>
{% endfor %}
</ul>
<h3>{% translate "Language" %}</h3>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">{% translate "Save" %}</button>
</form>
{% endblock %}
18 changes: 13 additions & 5 deletions ephios/core/views/settings.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import PasswordChangeView
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import FormView, TemplateView
from django.views.generic.edit import UpdateView
from dynamic_preferences.forms import global_preference_form_builder

from ephios.core.forms.users import UserNotificationPreferenceForm
from ephios.core.forms.users import UserNotificationPreferenceForm, UserOwnDataForm
from ephios.core.services.health.healthchecks import run_healthchecks
from ephios.core.signals import management_settings_sections
from ephios.extra.mixins import StaffRequiredMixin
Expand Down Expand Up @@ -62,12 +64,18 @@ def get_context_data(self, **kwargs):
return super().get_context_data(**kwargs)


class PersonalDataSettingsView(LoginRequiredMixin, TemplateView):
class PersonalDataSettingsView(LoginRequiredMixin, UpdateView):
template_name = "core/settings/settings_personal_data.html"
form_class = UserOwnDataForm
success_url = reverse_lazy("core:settings_personal_data")

def get_context_data(self, **kwargs):
kwargs["userprofile"] = self.request.user
return super().get_context_data(**kwargs)
def get_object(self, queryset=None):
return self.request.user

def form_valid(self, form):
response = super().form_valid(form)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, form.cleaned_data["preferred_language"])
return response


class CalendarSettingsView(LoginRequiredMixin, TemplateView):
Expand Down
15 changes: 15 additions & 0 deletions ephios/extra/i18n.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from contextlib import contextmanager

from django.conf import settings
from django.utils import translation


@contextmanager
def language(lang):
previous_language = translation.get_language()
lang = lang or settings.LANGUAGE_CODE
translation.activate(lang)
try:
yield
finally:
translation.activate(previous_language)
15 changes: 15 additions & 0 deletions ephios/extra/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.conf import settings


class EphiosLocaleMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
response = self.get_response(request)
if settings.LANGUAGE_COOKIE_NAME not in request.COOKIES:
try:
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, request.user.preferred_language)
except (KeyError, AttributeError):
pass
return response
2 changes: 1 addition & 1 deletion ephios/modellogging/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def custom_hook(self, d):
return ContentType.objects.get_for_id(d["contenttype_id"]).get_object_for_this_type(
pk=d["pk"]
)
except ObjectDoesNotExist:
except (ObjectDoesNotExist, AttributeError):
return d["str"]
for k, v in d.items():
if isinstance(v, str):
Expand Down
6 changes: 6 additions & 0 deletions ephios/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"ephios.extra.middleware.EphiosLocaleMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
Expand Down Expand Up @@ -206,6 +208,10 @@
# https://docs.djangoproject.com/en/3.0/topics/i18n/

LANGUAGE_CODE = "de"
LANGUAGES = [
("de", gettext_lazy("German")),
("en", gettext_lazy("English")),
]

TIME_ZONE = "Europe/Berlin"

Expand Down

0 comments on commit 8964bc9

Please sign in to comment.