mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-19 12:50:11 -05:00
8b6e61873b
Display formats for dates and times now follow the system settings (Admin settings) by default. Users can override in their profile (User settings) or choose "Use system default" so their view matches the rest of the system. Backend: - User.date_format and User.time_format are nullable; null means use system. - Migration 120 makes these columns nullable (existing rows unchanged). - get_resolved_date_format_key() and get_resolved_time_format_key() in timezone utils return the effective key (user or system) for templates and API. - Context processor injects resolved_date_format_key and resolved_time_format_key so base.html and JS (window.userPrefs) always see the resolved format. - User settings form: "Use system default" option and save logic for null. - User.to_dict() includes resolved date_format, time_format, and timezone for API clients (e.g. mobile). Web: - base.html uses resolved keys for window.userPrefs (no hardcoded fallback). - Replaced display-only strftime() in templates with |user_date, |user_datetime, |user_time, and |format_date so all visible dates/times respect settings. Left <input type="date"> values and URL/API params as YYYY-MM-DD where required. Mobile: - ApiClient.getCurrentUser() and user prefs provider load resolved prefs from /api/v1/users/me. - date_format_utils maps API keys to intl patterns; formatDate, formatTime, formatDateTime, formatDateRange used for display. - Time entries screen (filter dialog), time entry form, time entry card, and home dashboard use user prefs for formatting; API requests still send ISO dates. Co-authored-by: Cursor <cursoragent@cursor.com>
164 lines
6.0 KiB
Python
164 lines
6.0 KiB
Python
from flask import g, request, current_app
|
|
from flask_babel import get_locale
|
|
from flask_login import current_user
|
|
from app.models import Settings
|
|
from app.utils.timezone import (
|
|
get_timezone_offset_for_timezone,
|
|
get_resolved_date_format_key,
|
|
get_resolved_time_format_key,
|
|
)
|
|
|
|
|
|
def register_context_processors(app):
|
|
"""Register context processors for the application"""
|
|
|
|
# Register permission helpers for templates
|
|
from app.utils.permissions import init_permission_helpers
|
|
|
|
init_permission_helpers(app)
|
|
|
|
@app.context_processor
|
|
def inject_settings():
|
|
"""Inject settings into all templates"""
|
|
try:
|
|
from app import db
|
|
|
|
# Check if we have an active database session
|
|
if db.session.is_active:
|
|
settings = Settings.get_settings()
|
|
resolved_date = get_resolved_date_format_key()
|
|
resolved_time = get_resolved_time_format_key()
|
|
return {
|
|
"settings": settings,
|
|
"currency": settings.currency,
|
|
"timezone": settings.timezone,
|
|
"resolved_date_format_key": resolved_date,
|
|
"resolved_time_format_key": resolved_time,
|
|
}
|
|
except Exception as e:
|
|
# Log the error but continue with defaults
|
|
print(f"Warning: Could not inject settings: {e}")
|
|
# Rollback the failed transaction
|
|
try:
|
|
from app import db
|
|
|
|
db.session.rollback()
|
|
except Exception:
|
|
pass
|
|
pass
|
|
|
|
# Return defaults if settings not available (resolved keys still work without db)
|
|
try:
|
|
resolved_date = get_resolved_date_format_key()
|
|
resolved_time = get_resolved_time_format_key()
|
|
except Exception:
|
|
resolved_date = "YYYY-MM-DD"
|
|
resolved_time = "24h"
|
|
return {
|
|
"settings": None,
|
|
"currency": "EUR",
|
|
"timezone": "Europe/Rome",
|
|
"resolved_date_format_key": resolved_date,
|
|
"resolved_time_format_key": resolved_time,
|
|
}
|
|
|
|
@app.context_processor
|
|
def inject_globals():
|
|
"""Inject global variables into all templates"""
|
|
try:
|
|
from app import db
|
|
|
|
# Check if we have an active database session
|
|
if db.session.is_active:
|
|
settings = Settings.get_settings()
|
|
timezone_name = settings.timezone if settings else "Europe/Rome"
|
|
else:
|
|
timezone_name = "Europe/Rome"
|
|
except Exception as e:
|
|
# Log the error but continue with defaults
|
|
print(f"Warning: Could not inject globals: {e}")
|
|
# Rollback the failed transaction
|
|
try:
|
|
from app import db
|
|
|
|
db.session.rollback()
|
|
except Exception:
|
|
pass
|
|
timezone_name = "Europe/Rome"
|
|
|
|
# Resolve user-specific timezone, falling back to application timezone
|
|
user_timezone = timezone_name
|
|
try:
|
|
if (
|
|
current_user
|
|
and getattr(current_user, "is_authenticated", False)
|
|
and getattr(current_user, "timezone", None)
|
|
):
|
|
user_timezone = current_user.timezone
|
|
except Exception:
|
|
pass
|
|
|
|
# Determine app version from setup.py (single source of truth)
|
|
try:
|
|
from app.config.analytics_defaults import get_version_from_setup
|
|
import os
|
|
|
|
# Get version from setup.py
|
|
version_value = get_version_from_setup()
|
|
|
|
# If version is "unknown", fall back to environment variable for dev mode
|
|
if version_value == "unknown":
|
|
env_version = os.getenv("APP_VERSION")
|
|
if env_version:
|
|
version_value = env_version
|
|
else:
|
|
# Last resort: use "dev-0" for development
|
|
version_value = "dev-0"
|
|
|
|
# Strip any leading 'v' prefix to avoid double 'v' in template (e.g., vv3.5.0)
|
|
if version_value and version_value.startswith("v"):
|
|
version_value = version_value[1:]
|
|
except Exception:
|
|
# Fallback if anything goes wrong
|
|
version_value = "dev-0"
|
|
|
|
# Current locale code (e.g., 'en', 'de')
|
|
try:
|
|
current_locale = str(get_locale())
|
|
except Exception:
|
|
current_locale = "en"
|
|
# Normalize to short code for comparisons (e.g., 'en' from 'en_US')
|
|
short_locale = current_locale.split("_", 1)[0] if current_locale else "en"
|
|
|
|
# Reverse-map normalized locale codes back to config keys for label lookup
|
|
# 'nb' (used by Flask-Babel) should map back to 'no' (used in LANGUAGES config)
|
|
display_locale = short_locale
|
|
if short_locale == "nb":
|
|
display_locale = "no"
|
|
|
|
available_languages = current_app.config.get("LANGUAGES", {}) or {}
|
|
current_language_label = available_languages.get(display_locale, short_locale.upper())
|
|
|
|
# Check if current language is RTL
|
|
rtl_languages = current_app.config.get("RTL_LANGUAGES", set())
|
|
is_rtl = short_locale in rtl_languages
|
|
|
|
return {
|
|
"app_name": "Time Tracker",
|
|
"app_version": version_value,
|
|
"timezone": timezone_name,
|
|
"timezone_offset": get_timezone_offset_for_timezone(timezone_name),
|
|
"user_timezone": user_timezone,
|
|
"current_locale": current_locale,
|
|
"current_language_code": display_locale, # Use display locale (e.g., 'no' not 'nb')
|
|
"current_language_label": current_language_label,
|
|
"is_rtl": is_rtl,
|
|
"available_languages": available_languages,
|
|
"config": current_app.config,
|
|
}
|
|
|
|
@app.before_request
|
|
def before_request():
|
|
"""Set up request-specific data"""
|
|
g.request_start_time = request.start_time if hasattr(request, "start_time") else None
|