Skip to content

Commit

Permalink
Store language choice in user preferences
Browse files Browse the repository at this point in the history
  • Loading branch information
monsieurswag committed Oct 10, 2024
1 parent 1e2850f commit 1fe2ddc
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 3 deletions.
1 change: 1 addition & 0 deletions backend/core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
path("get_metrics/", get_metrics_view, name="get_metrics_view"),
path("agg_data/", get_agg_data, name="get_agg_data"),
path("composer_data/", get_composer_data, name="get_composer_data"),
path("preferences/", UpdatePreferences.as_view(), name="preferences"),
path("i18n/", include("django.conf.urls.i18n")),
path(
"accounts/saml/", include("iam.sso.saml.urls")
Expand Down
9 changes: 9 additions & 0 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,15 @@ def get_composer_data(request):
return Response({"result": data})


class UpdatePreferences(APIView):
def get(self, request):
return Response(request.user.preferences)

def patch(self, request):
request.user.update_preferences(request.data)
return Response(status=status.HTTP_200_OK)


# Compliance Assessment


Expand Down
17 changes: 17 additions & 0 deletions backend/iam/migrations/0009_user_preferences.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.1 on 2024-10-10 11:46

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("iam", "0008_user_is_third_party"),
]

operations = [
migrations.AddField(
model_name="user",
name="preferences",
field=models.JSONField(default=dict),
),
]
13 changes: 12 additions & 1 deletion backend/iam/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
Inspired from Azure IAM model"""

from collections import defaultdict
from typing import Any, List, Self, Tuple
from typing import Any, Dict, List, Self, Tuple
import uuid
from django.forms import JSONField
from django.utils import timezone
from django.db import models
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
Expand Down Expand Up @@ -358,6 +359,7 @@ class User(AbstractBaseUser, AbstractBaseModel, FolderMixin):
"granted to each of their user groups."
),
)
preferences = models.JSONField(default=dict)
objects = CaseInsensitiveUserManager()

# USERNAME_FIELD is used as the unique identifier for the user
Expand All @@ -366,6 +368,9 @@ class User(AbstractBaseUser, AbstractBaseModel, FolderMixin):
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []

# This is the set of of keys allowed in the preferences JSONField
PREFERENCE_SET = {"lang"}

class Meta:
"""for Model"""

Expand Down Expand Up @@ -474,6 +479,12 @@ def get_user_groups(self):
"""get the list of user groups containing the user in the form (group_name, builtin)"""
return [(x.__str__(), x.builtin) for x in self.user_groups.all()]

def update_preferences(self, new_preferences: Dict[str, Any]):
for key, value in new_preferences.items():
if key in self.PREFERENCE_SET:
self.preferences[key] = value
self.save()

@property
def has_backup_permission(self) -> bool:
return RoleAssignment.is_access_allowed(
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const handle: Handle = async ({ event, resolve }) => {

const errorId = new URL(event.request.url).searchParams.get('error');
if (errorId) {
setLanguageTag(event.cookies.get('ciso_lang') || generalSettings.lang || 'en');
setLanguageTag(event.cookies.get('ciso_lang') || 'en');
setFlash({ type: 'error', message: safeTranslate(errorId) }, event);
redirect(302, '/login');
}
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/lib/components/SideBar/SideBarFooter.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
value = event?.target?.value;
setLanguageTag(value);
// sessionStorage.setItem('lang', value);
fetch('/api/preferences', {
method: 'PATCH',
body: JSON.stringify({
lang: value
})
});
setCookie('ciso_lang', value);
window.location.reload();
}
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/routes/(app)/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@
import { pageTitle, clientSideToast } from '$lib/utils/stores';
import { getCookie, deleteCookie } from '$lib/utils/cookies';
import { browser } from '$app/environment';
import { page } from '$app/stores';
import { onMount } from 'svelte';
import * as m from '$paraglide/messages';
onMount(() => {
if ($page.url.searchParams.has('refresh')) {
$page.url.searchParams.delete('refresh');
window.location.href = $page.url.href;
}
});
let sidebarOpen = true;
$: classesSidebarOpen = (open: boolean) => (open ? 'ml-64' : 'ml-7');
Expand Down
17 changes: 16 additions & 1 deletion frontend/src/routes/(authentication)/login/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@ export const actions: Actions = {
secure: true
});

redirect(302, getSecureRedirect(url.searchParams.get('next')) || '/analytics');
const preferencesRes = await fetch(`${BASE_API_URL}/preferences/`);
const preferences = await preferencesRes.json();

const currentLang = cookies.get('ciso_lang') || 'en';
const preferedLang = preferences.lang;

if (preferedLang && currentLang !== preferedLang) {
cookies.set('ciso_lang', preferedLang, {
httpOnly: false,
sameSite: 'lax',
path: '/',
secure: true
});
}

redirect(302, getSecureRedirect(url.searchParams.get('next') || '/analytics') + '?refresh=1');
}
};
14 changes: 14 additions & 0 deletions frontend/src/routes/api/preferences/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { BASE_API_URL } from '$lib/utils/constants';
import type { RequestHandler } from './$types';

export const PATCH: RequestHandler = async ({ fetch, request }) => {
const preferences = await request.json();
const requestInitOptions: RequestInit = {
method: 'PATCH',
body: JSON.stringify(preferences)
};

const endpoint = `${BASE_API_URL}/preferences/`;
await fetch(endpoint, requestInitOptions);
return new Response();
};

0 comments on commit 1fe2ddc

Please sign in to comment.