1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00

fix: every icon of the templates comes from the local icon cache

django-bootstrap-icons serves icons from static/icon_cache and fetches a
missing one from the CDN while the page renders; a server without
internet access shows an error text instead of the icon.

- copy (Clone on the instance lists), envelope-open ("Lost OTP?" page)
  and save (VM settings, network page) were not cached; they are now,
  fetched with django-bootstrap-icons itself.
- The user and group lists asked for 'exclamation-triangle ' with a
  trailing space, an icon that does not exist; the warning icon never
  showed.

A test checks that every {% bs_icon %} in the templates has its cache
file; it fails on the previous files.
This commit is contained in:
catborise 2026-10-05 20:10:58 +03:00
parent da63e570e5
commit 2295673203
6 changed files with 58 additions and 2 deletions

View file

@ -17,7 +17,7 @@
{% if not groups %}
<div class="col-lg-12">
<div class="alert alert-warning shadow-sm">
{% bs_icon 'exclamation-triangle '%} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any groups" %}
{% bs_icon 'exclamation-triangle' %} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any groups" %}
</div>
</div>
{% else %}

View file

@ -20,7 +20,7 @@
{% if not users %}
<div class="col-lg-12">
<div class="alert alert-warning shadow-sm">
{% bs_icon 'exclamation-triangle '%} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any user" %}
{% bs_icon 'exclamation-triangle' %} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any user" %}
</div>
</div>
{% else %}

View file

@ -0,0 +1,8 @@
<?xml version="1.0" ?>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-copy" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z"/>
</svg>

After

Width:  |  Height:  |  Size: 437 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" ?>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-envelope-open" viewBox="0 0 16 16">
<path d="M8.47 1.318a1 1 0 0 0-.94 0l-6 3.2A1 1 0 0 0 1 5.4v.817l5.75 3.45L8 8.917l1.25.75L15 6.217V5.4a1 1 0 0 0-.53-.882l-6-3.2ZM15 7.383l-4.778 2.867L15 13.117zm-.035 6.88L8 10.082l-6.965 4.18A1 1 0 0 0 2 15h12a1 1 0 0 0 .965-.738ZM1 13.116l4.778-2.867L1 7.383v5.734ZM7.059.435a2 2 0 0 1 1.882 0l6 3.2A2 2 0 0 1 16 5.4V14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V5.4a2 2 0 0 1 1.059-1.765l6-3.2"/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" ?>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-save" viewBox="0 0 16 16">
<path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H9.5a1 1 0 0 0-1 1v7.293l2.646-2.647a.5.5 0 0 1 .708.708l-3.5 3.5a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L7.5 9.293V2a2 2 0 0 1 2-2H14a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h2.5a.5.5 0 0 1 0 1z"/>
</svg>

After

Width:  |  Height:  |  Size: 468 B

View file

@ -0,0 +1,32 @@
"""Every {% bs_icon %} of the templates is in the committed icon cache
(BS_ICONS_CACHE). A missing one is fetched from the CDN when a page is
rendered, or shown as an error where the server has no internet access."""
import re
import unittest
from pathlib import Path
from django.conf import settings
BASE = Path(settings.BASE_DIR)
CALL = re.compile(r"\{%\s*bs_icon\s+(.*?)\s*%\}")
NAME = re.compile(r"""^['"]([^'"]+)['"]""")
KWARG = re.compile(r"""(\w+)=['"]([^'"]*)['"]""")
class IconCacheTestCase(unittest.TestCase):
def test_every_template_icon_is_cached(self):
cache = Path(settings.BS_ICONS_CACHE)
missing = []
templates = [*BASE.glob("templates/**/*.html"), *BASE.glob("*/templates/**/*.html")]
self.assertGreater(len(templates), 50) # the globs still find the templates
for template in templates:
for args in CALL.findall(template.read_text(encoding="utf-8")):
name = NAME.match(args)
self.assertIsNotNone(name, f"{template}: icon name is not a literal: {args}")
kw = dict(KWARG.findall(args))
# the file name django-bootstrap-icons uses for its cache
cached = f"{name.group(1)}_{kw.get('size')}_{kw.get('color')}_{kw.get('extra_classes')}.svg".replace(" ", "_")
if not (cache / cached).exists():
missing.append(f"{template.relative_to(BASE)}: {cached}")
self.assertEqual(missing, [])