Coverage for app/venv/lib/python3.14/site-packages/weblate/configuration/views.py: 70%
50 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
1# Copyright © Michal Čihař <michal@weblate.org>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
5from __future__ import annotations
7from typing import TYPE_CHECKING
9from django.core.cache import cache
10from django.http import HttpResponse
11from django.template.loader import render_to_string
12from django.utils.decorators import method_decorator
13from django.views.decorators.cache import cache_control
14from django.views.generic import TemplateView
16from weblate.utils.hash import calculate_checksum
18from .models import Setting, SettingCategory
20if TYPE_CHECKING: 20 ↛ 21line 20 didn't jump to line 21 because the condition on line 20 was never true
21 from weblate.auth.models import AuthenticatedHttpRequest
24@method_decorator(cache_control(max_age=7200), name="get")
25class CustomCSSView(TemplateView):
26 template_name = "configuration/custom.css"
27 cache_key = "css:custom"
29 @classmethod
30 def split_colors(cls, hex_color_string):
31 if hex_color_string: 31 ↛ 32line 31 didn't jump to line 32 because the condition on line 31 was never true
32 colors = hex_color_string.split(",")
33 if len(colors) == 1:
34 return [colors[0], colors[0]]
35 if len(colors) == 2:
36 return colors
37 return [None, None]
38 return [None, None]
40 @classmethod
41 def get_css(cls, request: AuthenticatedHttpRequest):
42 # Request level caching
43 if hasattr(request, "weblate_custom_css"): 43 ↛ 44line 43 didn't jump to line 44 because the condition on line 43 was never true
44 return request.weblate_custom_css
46 # Site level caching
47 css = cache.get(cls.cache_key)
48 if css is None:
49 settings = Setting.objects.get_settings_dict(SettingCategory.UI)
50 split_colors = cls.split_colors
51 # fmt: off
52 custom_theme_settings = {
53 "header_color_light": split_colors(settings.get("header_color"))[0],
54 "header_color_dark": split_colors(settings.get("header_color"))[1],
55 "header_text_color_light": split_colors(settings.get("header_text_color"))[0],
56 "header_text_color_dark": split_colors(settings.get("header_text_color"))[1],
57 "navi_color_light": split_colors(settings.get("navi_color"))[0],
58 "navi_color_dark": split_colors(settings.get("navi_color"))[1],
59 "navi_text_color_light": split_colors(settings.get("navi_text_color"))[0],
60 "navi_text_color_dark": split_colors(settings.get("navi_text_color"))[1],
61 "focus_color_light": split_colors(settings.get("focus_color"))[0],
62 "focus_color_dark": split_colors(settings.get("focus_color"))[1],
63 "hover_color_light": split_colors(settings.get("hover_color"))[0],
64 "hover_color_dark": split_colors(settings.get("hover_color"))[1],
65 "hide_footer": settings.get("hide_footer"),
66 "page_font": settings.get("page_font"),
67 "brand_font": settings.get("brand_font"),
68 "enforce_hamburger": settings.get("enforce_hamburger"),
69 }
70 # fmt: on
72 css = render_to_string(cls.template_name, custom_theme_settings).strip()
73 cache.set(cls.cache_key, css, 24 * 3600)
74 request.weblate_custom_css = css
75 return css
77 def get(self, request: AuthenticatedHttpRequest, *args, **kwargs): # type: ignore[override]
78 return HttpResponse(content_type="text/css", content=self.get_css(request))
80 @classmethod
81 def drop_cache(cls) -> None:
82 cache.delete(cls.cache_key)
84 @classmethod
85 def get_hash(cls, request: AuthenticatedHttpRequest) -> str | None:
86 css = cls.get_css(request)
87 if not css: 87 ↛ 89line 87 didn't jump to line 89 because the condition on line 87 was always true
88 return None
89 return calculate_checksum(css)