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

1# Copyright © Michal Čihař <michal@weblate.org> 

2# 

3# SPDX-License-Identifier: GPL-3.0-or-later 

4 

5from __future__ import annotations 

6 

7from typing import TYPE_CHECKING 

8 

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 

15 

16from weblate.utils.hash import calculate_checksum 

17 

18from .models import Setting, SettingCategory 

19 

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 

22 

23 

24@method_decorator(cache_control(max_age=7200), name="get") 

25class CustomCSSView(TemplateView): 

26 template_name = "configuration/custom.css" 

27 cache_key = "css:custom" 

28 

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] 

39 

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 

45 

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 

71 

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 

76 

77 def get(self, request: AuthenticatedHttpRequest, *args, **kwargs): # type: ignore[override] 

78 return HttpResponse(content_type="text/css", content=self.get_css(request)) 

79 

80 @classmethod 

81 def drop_cache(cls) -> None: 

82 cache.delete(cls.cache_key) 

83 

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)