Files
TimeTracker/app/utils/module_helpers.py
T
Dries Peeters 3b6bcc890b fix(nav): hide sidebar folder when all modules in category are disabled
Add has_enabled_modules(category) helper and wrap Finance & Expenses
folder so it only renders when at least one FINANCE module is enabled.
Fixes empty folder visible after disabling all modules in a category.

Closes #481

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 17:58:55 +01:00

146 lines
4.6 KiB
Python

"""
Module Helper Utilities
Provides decorators and helper functions for checking module availability
and protecting routes based on module flags.
"""
from functools import wraps
from flask import abort, redirect, url_for, flash, current_app
from flask_login import current_user
from flask_babel import gettext as _
from app.models import Settings
from app.utils.module_registry import ModuleRegistry
def module_enabled(module_id: str, redirect_to: str = None):
"""
Decorator to require a module to be enabled for a route.
Args:
module_id: The module ID to check
redirect_to: Optional route name to redirect to if module is disabled
Usage:
@module_enabled("calendar")
def view_calendar():
return render_template("calendar/view.html")
"""
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated:
if redirect_to:
return redirect(url_for(redirect_to))
abort(403)
settings = Settings.get_settings()
if not ModuleRegistry.is_enabled(module_id, settings, current_user):
if current_user.is_admin:
module = ModuleRegistry.get(module_id)
module_name = module.name if module else module_id
flash(
_("Module '%(module)s' is disabled. Enable it in Settings.", module=module_name),
"warning"
)
if redirect_to:
return redirect(url_for(redirect_to))
return redirect(url_for('admin.settings'))
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
def is_module_enabled(module_id: str) -> bool:
"""
Check if a module is enabled for the current user.
Args:
module_id: The module ID to check
Returns:
True if module is enabled, False otherwise
"""
if not current_user.is_authenticated:
return False
try:
settings = Settings.get_settings()
return ModuleRegistry.is_enabled(module_id, settings, current_user)
except Exception:
# If we can't check, default to False for safety
return False
def get_enabled_modules(category=None):
"""
Get all enabled modules, optionally filtered by category.
Args:
category: Optional ModuleCategory to filter by
Returns:
List of enabled ModuleDefinition objects
"""
if not current_user.is_authenticated:
return []
try:
settings = Settings.get_settings()
modules = ModuleRegistry.get_enabled_modules(settings, current_user)
if category:
from app.utils.module_registry import ModuleCategory
if isinstance(category, str):
try:
category = ModuleCategory(category)
except ValueError:
return []
modules = [m for m in modules if m.category == category]
return modules
except Exception:
return []
def has_enabled_modules(category=None) -> bool:
"""
Check whether a category has any enabled modules for the current user.
Args:
category: Optional ModuleCategory (or its value as string). If omitted/invalid, returns False.
Returns:
True if at least one module in the category is enabled for the current user.
"""
return bool(get_enabled_modules(category))
def init_module_helpers(app):
"""
Initialize module helper functions for use in templates and routes.
This should be called during app initialization.
"""
# Initialize module registry
ModuleRegistry.initialize_defaults()
@app.context_processor
def inject_module_helpers():
"""Make module helpers available in templates"""
from app.utils.module_registry import ModuleCategory
return {
"is_module_enabled": is_module_enabled,
"get_enabled_modules": get_enabled_modules,
"has_enabled_modules": has_enabled_modules,
"get_modules_by_category": lambda cat: ModuleRegistry.get_by_category(cat),
"ModuleCategory": ModuleCategory,
}
# Also make it available as a global function
app.jinja_env.globals['is_module_enabled'] = is_module_enabled
app.jinja_env.globals['get_enabled_modules'] = get_enabled_modules
app.jinja_env.globals['has_enabled_modules'] = has_enabled_modules