mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-20 11:29:57 -06:00
- Add centralized module registry system (ModuleRegistry) for managing module metadata, dependencies, and visibility across the application - Create module helper utilities with decorators (@module_enabled) and helper functions for route protection and template access - Add database migration (092) to add missing module visibility flags to settings and users tables for granular control - Extend Settings and User models with additional module visibility flags for CRM, Finance, Tools, and Advanced features - Implement admin module management UI for system-wide module enable/disable controls - Add module checks to routes (calendar, contacts, deals, expenses, invoices, leads, custom_reports) to enforce visibility rules - Update scheduled report service and report templates to respect module visibility settings - Bump version to 4.8.0 in setup.py - Add comprehensive documentation for module integration planning and implementation analysis
131 lines
4.1 KiB
Python
131 lines
4.1 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 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,
|
|
"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
|
|
|