diff --git a/app/models/settings.py b/app/models/settings.py index 3cc2f5f2..4934b05b 100644 --- a/app/models/settings.py +++ b/app/models/settings.py @@ -55,6 +55,9 @@ class Settings(db.Model): # Privacy and analytics settings allow_analytics = db.Column(db.Boolean, default=True, nullable=False) # Controls system info sharing for analytics + # Module visibility: admin-disabled module IDs (e.g. ["gantt", "leads"]). Empty/None = all enabled. + disabled_module_ids = db.Column(db.JSON, default=list, nullable=True) + # Kiosk mode settings kiosk_mode_enabled = db.Column(db.Boolean, default=False, nullable=False) kiosk_auto_logout_minutes = db.Column(db.Integer, default=15, nullable=False) @@ -374,6 +377,7 @@ class Settings(db.Model): "invoice_pdf_template_css": self.invoice_pdf_template_css, "invoice_pdf_design_json": self.invoice_pdf_design_json, "allow_analytics": self.allow_analytics, + "disabled_module_ids": (self.disabled_module_ids if self.disabled_module_ids is not None else []), "mail_enabled": self.mail_enabled, "mail_server": self.mail_server, "mail_port": self.mail_port, diff --git a/app/routes/admin.py b/app/routes/admin.py index 2b4406c5..b08ee630 100644 --- a/app/routes/admin.py +++ b/app/routes/admin.py @@ -949,6 +949,16 @@ def settings(): "kiosk_default_movement_type": getattr(settings_obj, "kiosk_default_movement_type", "adjustment"), } + # Module visibility: non-CORE modules for admin toggles + from app.utils.module_registry import ModuleRegistry, ModuleCategory + + ModuleRegistry.initialize_defaults() + modules_by_category = {} + for cat in ModuleCategory: + mods = [m for m in ModuleRegistry.get_by_category(cat) if m.category != ModuleCategory.CORE] + if mods: + modules_by_category[cat] = mods + if request.method == "POST": # Validate timezone timezone = request.form.get("timezone") or settings_obj.timezone @@ -964,6 +974,8 @@ def settings(): timezones=timezones, kiosk_settings=kiosk_settings, peppol_env_enabled=peppol_env_enabled, + modules_by_category=modules_by_category, + ModuleCategory=ModuleCategory, ) # Update basic settings @@ -1059,6 +1071,15 @@ def settings(): app_module.log_event("admin.analytics_toggled", user_id=current_user.id, new_state=allow_analytics) app_module.track_event(current_user.id, "admin.analytics_toggled", {"enabled": allow_analytics}) + # Module visibility: build disabled_module_ids from unchecked module_enabled_* checkboxes + if hasattr(settings_obj, "disabled_module_ids"): + disabled = [] + for mods in modules_by_category.values(): + for m in mods: + if ("module_enabled_" + m.id) not in request.form: + disabled.append(m.id) + settings_obj.disabled_module_ids = disabled + # Ensure settings object is in the session (important for new instances) if settings_obj not in db.session: db.session.add(settings_obj) @@ -1071,6 +1092,8 @@ def settings(): timezones=timezones, kiosk_settings=kiosk_settings, peppol_env_enabled=peppol_env_enabled, + modules_by_category=modules_by_category, + ModuleCategory=ModuleCategory, ) # #region agent log try: @@ -1099,6 +1122,8 @@ def settings(): timezones=timezones, kiosk_settings=kiosk_settings, peppol_env_enabled=peppol_env_enabled, + modules_by_category=modules_by_category, + ModuleCategory=ModuleCategory, ) diff --git a/app/templates/admin/settings.html b/app/templates/admin/settings.html index ff35e473..68b159fe 100644 --- a/app/templates/admin/settings.html +++ b/app/templates/admin/settings.html @@ -263,6 +263,49 @@ + + {% if modules_by_category is defined and modules_by_category %} +
+ {{ _('Disable modules to hide them from all users. Disabled modules will not appear in the sidebar. Core modules (Dashboard, Projects, Timer, etc.) are always enabled.') }} +
+ {% set _d = (settings.disabled_module_ids or []) %} ++ {{ _('Disabling "Reports" hides the whole Reports submenu including Report Builder and Scheduled Reports.') }} +
+