From 295361a541a4efb06ca49f6ca61ad01592b9122a Mon Sep 17 00:00:00 2001 From: Peter Pfeufer Date: Wed, 15 Oct 2025 01:13:42 +0200 Subject: [PATCH] [ADD] User setting to keep the sidebar menu minimized --- allianceauth/authentication/middleware.py | 6 +++++ .../0025_userprofile_minimize_sidebar.py | 22 ++++++++++++++++ allianceauth/authentication/models.py | 9 ++++++- .../authentication/tests/test_middleware.py | 24 ++++++++++++++++++ .../menu/templates/menu/menu-user.html | 25 +++++++++++++++++++ .../allianceauth/js/sidebar-collapse.js | 6 ++++- .../templates/allianceauth/base-bs5.html | 5 ++++ allianceauth/urls.py | 5 +++- allianceauth/views.py | 23 +++++++++++++++++ 9 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 allianceauth/authentication/migrations/0025_userprofile_minimize_sidebar.py diff --git a/allianceauth/authentication/middleware.py b/allianceauth/authentication/middleware.py index ca28d92f..e4bfb10f 100644 --- a/allianceauth/authentication/middleware.py +++ b/allianceauth/authentication/middleware.py @@ -52,4 +52,10 @@ class UserSettingsMiddleware(MiddlewareMixin): except Exception as e: logger.exception(e) + # Minimize Menu + try: + request.session["MINIMIZE_SIDEBAR"] = request.user.profile.minimize_sidebar + except Exception as e: + pass # We don't care that an anonymous user has no profile (not logged in) + return response diff --git a/allianceauth/authentication/migrations/0025_userprofile_minimize_sidebar.py b/allianceauth/authentication/migrations/0025_userprofile_minimize_sidebar.py new file mode 100644 index 00000000..bbd28732 --- /dev/null +++ b/allianceauth/authentication/migrations/0025_userprofile_minimize_sidebar.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.25 on 2025-10-14 22:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentication", "0024_alter_userprofile_language"), + ] + + operations = [ + migrations.AddField( + model_name="userprofile", + name="minimize_sidebar", + field=models.BooleanField( + default=False, + help_text="Keep the sidebar menu minimized", + verbose_name="Minimize Sidebar Menu", + ), + ), + ] diff --git a/allianceauth/authentication/models.py b/allianceauth/authentication/models.py index 8871e7ee..074c10a5 100644 --- a/allianceauth/authentication/models.py +++ b/allianceauth/authentication/models.py @@ -97,7 +97,8 @@ class UserProfile(models.Model): on_delete=models.SET_DEFAULT, default=get_guest_state_pk) language = models.CharField( - _("Language"), max_length=10, + _("Language"), + max_length=10, choices=Language.choices, blank=True, default='') @@ -112,6 +113,12 @@ class UserProfile(models.Model): null=True, help_text="Bootstrap 5 Themes from https://bootswatch.com/ or Community Apps" ) + minimize_sidebar = models.BooleanField( + _("Minimize Sidebar Menu"), + default=False, + help_text=_("Keep the sidebar menu minimized") + ) + def assign_state(self, state=None, commit=True): if not state: diff --git a/allianceauth/authentication/tests/test_middleware.py b/allianceauth/authentication/tests/test_middleware.py index 70335e58..d5dd8667 100644 --- a/allianceauth/authentication/tests/test_middleware.py +++ b/allianceauth/authentication/tests/test_middleware.py @@ -88,6 +88,7 @@ class TestUserSettingsMiddlewareLoginFlow(TestCase): self.request.LANGUAGE_CODE = 'en' self.request.user.profile.language = 'de' self.request.user.profile.night_mode = True + self.request.user.profile.minimize_sidebar = False self.request.user.is_anonymous = False self.response = Mock() self.response.content = 'hello world' @@ -173,3 +174,26 @@ class TestUserSettingsMiddlewareLoginFlow(TestCase): self.response ) self.assertEqual(self.request.session["NIGHT_MODE"], True) + + def test_middleware_set_mimimize_sidebar(self): + """ + tests the middleware will always set minimize_sidebar to False (default) + """ + + response = self.middleware.process_response( + self.request, + self.response + ) + self.assertEqual(self.request.session["MINIMIZE_SIDEBAR"], False) + + def test_middleware_minimize_sidebar_when_set(self): + """ + tests the middleware will set mimimize_sidebar to True from DB + """ + + self.request.user.profile.minimize_sidebar = True + response = self.middleware.process_response( + self.request, + self.response + ) + self.assertEqual(self.request.session["MINIMIZE_SIDEBAR"], True) diff --git a/allianceauth/menu/templates/menu/menu-user.html b/allianceauth/menu/templates/menu/menu-user.html index fd2bc31f..ad715888 100644 --- a/allianceauth/menu/templates/menu/menu-user.html +++ b/allianceauth/menu/templates/menu/menu-user.html @@ -72,6 +72,31 @@ {% theme_select %} + {% if user.is_authenticated %} +
  • +
  • + +
  • + +
  • + {% endif %} + {% if user.is_superuser %}
  • diff --git a/allianceauth/static/allianceauth/js/sidebar-collapse.js b/allianceauth/static/allianceauth/js/sidebar-collapse.js index 436dfdaa..967de96c 100644 --- a/allianceauth/static/allianceauth/js/sidebar-collapse.js +++ b/allianceauth/static/allianceauth/js/sidebar-collapse.js @@ -1,3 +1,5 @@ +/* global sidebarSettings */ + $(document).ready(() => { 'use strict'; @@ -16,7 +18,9 @@ $(document).ready(() => { } }); - sidebar.classList.toggle('show', localStorage.getItem(sidebarKey) !== 'closed'); + if (!sidebarSettings.minimizeSidebar) { + sidebar.classList.toggle('show', localStorage.getItem(sidebarKey) !== 'closed'); + } const activeChildMenuItem = document.querySelector('ul#sidebar-menu ul.collapse a.active'); diff --git a/allianceauth/templates/allianceauth/base-bs5.html b/allianceauth/templates/allianceauth/base-bs5.html index b21da87a..9209fb7c 100644 --- a/allianceauth/templates/allianceauth/base-bs5.html +++ b/allianceauth/templates/allianceauth/base-bs5.html @@ -102,6 +102,11 @@ + {% include "bundles/auth-sidebar-collapse-js.html" %} {% theme_js %} diff --git a/allianceauth/urls.py b/allianceauth/urls.py index 71a43b26..6be0890d 100644 --- a/allianceauth/urls.py +++ b/allianceauth/urls.py @@ -80,7 +80,10 @@ urlpatterns = [ path('night/', views.NightModeRedirectView.as_view(), name='nightmode'), # Theme Change - path('theme/', views.ThemeRedirectView.as_view(), name='theme') + path('theme/', views.ThemeRedirectView.as_view(), name='theme'), + + # Minimize Menu + path('minimize-sidebar/', views.MinimizeSidebarRedirectView.as_view(), name='minimize_sidebar') ] url_hooks = get_hooks("url_hook") diff --git a/allianceauth/views.py b/allianceauth/views.py index 95550ed1..27f076cf 100644 --- a/allianceauth/views.py +++ b/allianceauth/views.py @@ -48,6 +48,29 @@ class ThemeRedirectView(View): return HttpResponseRedirect(request.GET.get("next", "/")) +class MinimizeSidebarRedirectView(View): + SESSION_VAR = "MINIMIZE_SIDEBAR" + + def post(self, request, *args, **kwargs): + request.session[self.SESSION_VAR] = not self.minimize_sidebar_state(request) + if not request.user.is_anonymous: + try: + request.user.profile.minimize_sidebar = request.session[self.SESSION_VAR] + request.user.profile.save() + except Exception as e: + logger.exception(e) + + return HttpResponseRedirect(request.GET.get("next", "/")) + + @classmethod + def minimize_sidebar_state(cls, request): + try: + return request.session.get(cls.SESSION_VAR, False) + except AttributeError: + # Session is middleware + # Sometimes request wont have a session attribute + return False + # TODO: error views should be renamed to a proper function name when possible