Files
orion/app/templates_config.py
Samir Boulahtit 2ce19e66b1 feat: implement self-contained module architecture (Phase 1 & 2)
Phase 1 - Foundation:
- Add app/modules/contracts/ with Protocol definitions for cross-module
  communication (ServiceProtocol, ContentServiceProtocol, MediaServiceProtocol)
- Enhance app/modules/base.py ModuleDefinition with self-contained module
  support (is_self_contained, services_path, models_path, etc.)
- Update app/templates_config.py with multi-directory template loading
  using Jinja2 ChoiceLoader for module templates

Phase 2 - CMS Pilot Module:
- Migrate CMS service to app/modules/cms/services/content_page_service.py
- Create app/modules/cms/exceptions.py with CMS-specific exceptions
- Configure app/modules/cms/models/ to re-export ContentPage from canonical
  location (models.database) to avoid circular imports
- Update cms_module definition with is_self_contained=True and paths
- Add backwards compatibility shims with deprecation warnings:
  - app/services/content_page_service.py -> app.modules.cms.services
  - app/exceptions/content_page.py -> app.modules.cms.exceptions

Note: SQLAlchemy models remain in models/database/ as the canonical location
to avoid circular imports at startup time. Module model packages re-export
from the canonical location.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 21:35:36 +01:00

95 lines
3.1 KiB
Python

# app/templates_config.py
"""
Shared Jinja2 templates configuration.
All route modules should import `templates` from here to ensure
consistent globals (like translation function) are available.
Template Loading Strategy:
- Core templates from app/templates/ (highest priority)
- Module templates from app/modules/<module>/templates/ (namespaced)
Module templates should use namespace prefix to avoid collisions:
app/modules/cms/templates/cms/admin/pages.html
-> Rendered as: templates.TemplateResponse("cms/admin/pages.html", ...)
"""
import logging
from pathlib import Path
from fastapi.templating import Jinja2Templates
from jinja2 import ChoiceLoader, FileSystemLoader
from app.utils.i18n import (
LANGUAGE_FLAGS,
LANGUAGE_NAMES,
SUPPORTED_LANGUAGES,
DEFAULT_LANGUAGE,
create_translation_context,
)
logger = logging.getLogger(__name__)
# Core templates directory
TEMPLATES_DIR = Path(__file__).parent / "templates"
MODULES_DIR = Path(__file__).parent / "modules"
def create_template_loaders() -> ChoiceLoader:
"""
Create a ChoiceLoader that searches multiple template directories.
Search order:
1. Core templates (app/templates/) - highest priority
2. Module templates (app/modules/<module>/templates/) - in alphabetical order
Returns:
ChoiceLoader configured with all template directories
"""
loaders = [FileSystemLoader(str(TEMPLATES_DIR))] # Core templates first
# Add module template directories
if MODULES_DIR.exists():
for module_dir in sorted(MODULES_DIR.iterdir()):
if module_dir.is_dir():
templates_path = module_dir / "templates"
if templates_path.exists() and templates_path.is_dir():
loaders.append(FileSystemLoader(str(templates_path)))
logger.debug(f"[Templates] Added module templates: {module_dir.name}")
return ChoiceLoader(loaders)
def get_module_template_dirs() -> list[Path]:
"""
Get list of all module template directories.
Useful for debugging and introspection.
Returns:
List of Path objects for module template directories
"""
dirs = []
if MODULES_DIR.exists():
for module_dir in sorted(MODULES_DIR.iterdir()):
if module_dir.is_dir():
templates_path = module_dir / "templates"
if templates_path.exists() and templates_path.is_dir():
dirs.append(templates_path)
return dirs
# Create shared templates instance with multi-directory loader
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
templates.env.loader = create_template_loaders()
# Add translation function to Jinja2 environment globals
# This makes _() available in all templates AND macros
_default_translator = create_translation_context(DEFAULT_LANGUAGE)
templates.env.globals["_"] = _default_translator
templates.env.globals["t"] = _default_translator # Alias
templates.env.globals["SUPPORTED_LANGUAGES"] = SUPPORTED_LANGUAGES
templates.env.globals["DEFAULT_LANGUAGE"] = DEFAULT_LANGUAGE
templates.env.globals["LANGUAGE_NAMES"] = LANGUAGE_NAMES
templates.env.globals["LANGUAGE_FLAGS"] = LANGUAGE_FLAGS