diff --git a/bookmarks/middlewares.py b/bookmarks/middlewares.py index 981137f..af7ff12 100644 --- a/bookmarks/middlewares.py +++ b/bookmarks/middlewares.py @@ -1,13 +1,17 @@ from django.conf import settings from django.contrib.auth.middleware import RemoteUserMiddleware -from bookmarks.models import UserProfile +from bookmarks.models import UserProfile, GlobalSettings class CustomRemoteUserMiddleware(RemoteUserMiddleware): header = settings.LD_AUTH_PROXY_USERNAME_HEADER +standard_profile = UserProfile() +standard_profile.enable_favicons = True + + class UserProfileMiddleware: def __init__(self, get_response): self.get_response = get_response @@ -16,8 +20,16 @@ class UserProfileMiddleware: if request.user.is_authenticated: request.user_profile = request.user.profile else: - request.user_profile = UserProfile() - request.user_profile.enable_favicons = True + # check if a custom profile for guests exists, otherwise use standard profile + guest_profile = None + try: + global_settings = GlobalSettings.get() + if global_settings.guest_profile_user: + guest_profile = global_settings.guest_profile_user.profile + except: + pass + + request.user_profile = guest_profile or standard_profile response = self.get_response(request) diff --git a/bookmarks/migrations/0038_globalsettings_guest_profile_user.py b/bookmarks/migrations/0038_globalsettings_guest_profile_user.py new file mode 100644 index 0000000..6950e9c --- /dev/null +++ b/bookmarks/migrations/0038_globalsettings_guest_profile_user.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0.8 on 2024-08-31 17:54 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookmarks", "0037_globalsettings"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name="globalsettings", + name="guest_profile_user", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/bookmarks/models.py b/bookmarks/models.py index 71dda82..f4db7d5 100644 --- a/bookmarks/models.py +++ b/bookmarks/models.py @@ -508,6 +508,9 @@ class GlobalSettings(models.Model): blank=False, default=LANDING_PAGE_LOGIN, ) + guest_profile_user = models.ForeignKey( + get_user_model(), on_delete=models.SET_NULL, null=True, blank=True + ) @classmethod def get(cls): @@ -526,6 +529,8 @@ class GlobalSettings(models.Model): class GlobalSettingsForm(forms.ModelForm): class Meta: model = GlobalSettings - fields = [ - "landing_page", - ] + fields = ["landing_page", "guest_profile_user"] + + def __init__(self, *args, **kwargs): + super(GlobalSettingsForm, self).__init__(*args, **kwargs) + self.fields["guest_profile_user"].empty_label = "Standard profile" diff --git a/bookmarks/templates/settings/general.html b/bookmarks/templates/settings/general.html index eae24bf..741d8c0 100644 --- a/bookmarks/templates/settings/general.html +++ b/bookmarks/templates/settings/general.html @@ -249,7 +249,17 @@ reddit.com/r/Music music reddit {{ global_settings_form.landing_page|add_class:"form-select width-25 width-sm-100" }}
- The page that unauthorized users are redirected to when accessing the root URL. + The page that unauthenticated users are redirected to when accessing the root URL. +
+ +
+ + {{ global_settings_form.guest_profile_user|add_class:"form-select width-25 width-sm-100" }} +
+ The user profile to use for users that are not logged in. This will affect how publicly shared bookmarks + are displayed regarding theme, bookmark list settings, etc. You can either use your own profile or create + a dedicated user for this purpose. By default, a standard profile with fixed settings is used.
diff --git a/bookmarks/tests/test_root_view.py b/bookmarks/tests/test_root_view.py index a02b598..d9f978b 100644 --- a/bookmarks/tests/test_root_view.py +++ b/bookmarks/tests/test_root_view.py @@ -5,7 +5,7 @@ from bookmarks.models import GlobalSettings from bookmarks.tests.helpers import BookmarkFactoryMixin -class AnonymousViewTestCase(TestCase, BookmarkFactoryMixin): +class RootViewTestCase(TestCase, BookmarkFactoryMixin): def test_unauthenticated_user_redirect_to_login_by_default(self): response = self.client.get(reverse("bookmarks:root")) self.assertRedirects(response, reverse("login")) diff --git a/bookmarks/tests/test_settings_general_view.py b/bookmarks/tests/test_settings_general_view.py index 568a7c7..0f82726 100644 --- a/bookmarks/tests/test_settings_general_view.py +++ b/bookmarks/tests/test_settings_general_view.py @@ -469,10 +469,13 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin): def test_update_global_settings(self): superuser = self.setup_superuser() self.client.force_login(superuser) + selectable_user = self.setup_user() + # Update global settings form_data = { "update_global_settings": "", "landing_page": GlobalSettings.LANDING_PAGE_SHARED_BOOKMARKS, + "guest_profile_user": selectable_user.id, } response = self.client.post(reverse("bookmarks:settings.general"), form_data) self.assertEqual(response.status_code, 200) @@ -480,6 +483,22 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin): global_settings = GlobalSettings.get() self.assertEqual(global_settings.landing_page, form_data["landing_page"]) + self.assertEqual(global_settings.guest_profile_user, selectable_user) + + # Revert settings + form_data = { + "update_global_settings": "", + "landing_page": GlobalSettings.LANDING_PAGE_LOGIN, + "guest_profile_user": "", + } + response = self.client.post(reverse("bookmarks:settings.general"), form_data) + self.assertEqual(response.status_code, 200) + self.assertSuccessMessage(response.content.decode(), "Global settings updated") + + global_settings = GlobalSettings.get() + global_settings.refresh_from_db() + self.assertEqual(global_settings.landing_page, form_data["landing_page"]) + self.assertIsNone(global_settings.guest_profile_user) def test_update_global_settings_should_not_be_called_without_respective_form_action( self, diff --git a/bookmarks/tests/test_user_profile_middleware.py b/bookmarks/tests/test_user_profile_middleware.py new file mode 100644 index 0000000..1713df0 --- /dev/null +++ b/bookmarks/tests/test_user_profile_middleware.py @@ -0,0 +1,47 @@ +from django.test import TestCase +from django.urls import reverse + +from bookmarks.models import UserProfile, GlobalSettings +from bookmarks.tests.helpers import BookmarkFactoryMixin +from bookmarks.middlewares import standard_profile + + +class UserProfileMiddlewareTestCase(TestCase, BookmarkFactoryMixin): + def test_unauthenticated_user_should_use_standard_profile_by_default(self): + response = self.client.get(reverse("login")) + + self.assertEqual(standard_profile, response.wsgi_request.user_profile) + + def test_unauthenticated_user_should_use_custom_configured_profile(self): + guest_user = self.setup_user() + guest_user_profile = guest_user.profile + guest_user_profile.theme = UserProfile.THEME_DARK + guest_user_profile.save() + + global_settings = GlobalSettings.get() + global_settings.guest_profile_user = guest_user + global_settings.save() + + response = self.client.get(reverse("login")) + + self.assertEqual(guest_user_profile, response.wsgi_request.user_profile) + + def test_authenticated_user_should_use_own_profile(self): + guest_user = self.setup_user() + guest_user_profile = guest_user.profile + guest_user_profile.theme = UserProfile.THEME_DARK + guest_user_profile.save() + + global_settings = GlobalSettings.get() + global_settings.guest_profile_user = guest_user + global_settings.save() + + user = self.get_or_create_test_user() + user_profile = user.profile + user_profile.theme = UserProfile.THEME_LIGHT + user_profile.save() + self.client.force_login(user) + + response = self.client.get(reverse("login"), follow=True) + + self.assertEqual(user_profile, response.wsgi_request.user_profile)