Skip to content

Commit

Permalink
Add BS_ICONS_BASE_PATH and MD_ICONS_BASE_PATH to use local copies of …
Browse files Browse the repository at this point in the history
…icon sets. (#26)

This removes the need to call out to CDNs to download the icons.
  • Loading branch information
Kagee committed Jul 16, 2024
1 parent e660a43 commit 6393d7b
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 33 deletions.
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ BS_ICONS_BASE_URL = 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/'
BS_ICONS_BASE_URL defaults to the latest boostrap-icons CDN that was available
when releasing this package. Change the URL to use an older or newer one.

You can also load icons from a local path. This will disable downloading icons
from BS_ICONS_BASE_URL:

```
BS_ICONS_BASE_PATH = 'node_modules/bootstrap-icons/'
```

To add custom icons to your app you need to set the path where these can be found.
The default setting is *custom-icons*, so you would add your icons
to */your-app/static/custom-icons/*.
Expand All @@ -167,7 +174,14 @@ Material Desing Icons are loaded from the default URL:
MD_ICONS_BASE_URL = 'https://cdn.jsdelivr.net/npm/@mdi/svg@7.2.96/'
```

You can change it to your desired location by overriding this setting.
You can change it to your desired location by overriding this setting.

You can also load icons from a local path. This will disable downloading icons
from MD_ICONS_BASE_URL:

```
MD_ICONS_BASE_PATH = 'node_modules/@mdi/svg'
```

### Configure icon cache

Expand All @@ -185,7 +199,7 @@ and stored to a local file. On each subsequent use the icon will be simply loade
In case icons are not found you can configure, what to display:

```
BS_ICONS_NOT_FOUND = f"Icon <{icon_path}> does not exist"
BS_ICONS_NOT_FOUND = f"Icon `{icon_path}` does not exist"
```

This shows the error message if you for example misspelled an icon name.
Expand Down
91 changes: 60 additions & 31 deletions django_bootstrap_icons/templatetags/bootstrap_icons.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
""" django bootstrap icons templatetags """
import os
from pathlib import Path
import requests
from defusedxml.minidom import parse, parseString

Expand Down Expand Up @@ -87,18 +88,19 @@ def custom_icon(icon_name, size=None, color=None, extra_classes=None):
try:
content = parse(icon_path)
except FileNotFoundError:
return f"Icon <{icon_path}> does not exist"
return f"Icon `{icon_path}` does not exist"
return mark_safe(render_svg(content, size, color, extra_classes))


def get_icon(icon_path, icon_name, size=None, color=None, extra_classes=None):
"""
Manage caching of bootstrap icons
:param str icon_path: icon path given by CDN
:param icon_path icon_path: icon path given by CDN or local path
:param str icon_name: Name of custom icon to render
:param str size: size of custom icon to render
:param str color: color of custom icon to render
:param str extra_classes: String of classes to add to icon
:type icon_path: str or Path
"""
cache_path = getattr(
settings,
Expand All @@ -119,26 +121,43 @@ def get_icon(icon_path, icon_name, size=None, color=None, extra_classes=None):

# cached icon doesn't exist or no cache configured, create and return icon
try:
resp = requests.get(icon_path, timeout=20)
if resp.status_code >= 400:
# return f"Icon <{icon_path}> does not exist"
return getattr(
settings,
'BS_ICONS_NOT_FOUND',
f"Icon <{icon_path}> does not exist"
)
content = parseString(resp.text)
svg = render_svg(content, size, color, extra_classes)
# if cache configured write icon to cache
if cache_path and cache_file:
with open(cache_file, 'w', encoding="utf-8") as icon_file:
icon_file.write(svg)
except requests.ConnectionError:
if isinstance(icon_path, Path):
# icon_path is local path
with icon_path.open("r") as icon_file:
svg_string = icon_file.read()
else:
# icon_path is URL
resp = requests.get(icon_path, timeout=20)
if resp.status_code == 404:
# co-opt FileNotFoundError for HTTP 404
msg = "Got HTTP 404 when downloading icon"
raise FileNotFoundError(msg)
resp.raise_for_status()
svg_string = resp.text

except FileNotFoundError:
# The icon was not found (on disk, or on the web)
return getattr(
settings,
'BS_ICONS_NOT_FOUND',
f"Icon <{icon_path}> does not exist"
"BS_ICONS_NOT_FOUND",
f"Icon `{icon_path}` does not exist",
)

# RequestException includes ConnectionError, Timeout, HTTPError and TooManyRedirects
except (requests.exceptions.RequestException, OSError):
# We failed to get the icon, but it might exist
return getattr(
settings, "BS_ICONS_NOT_FOUND", f"Failed to read icon `{icon_path}`"
)

content = parseString(svg_string)
svg = render_svg(content, size, color, extra_classes)

# if cache configured write icon to cache
if cache_path and cache_file:
with open(cache_file, "w", encoding="utf-8") as icon_file:
icon_file.write(svg)

return svg


Expand All @@ -154,12 +173,17 @@ def bs_icon(icon_name, size=None, color=None, extra_classes=None):
if icon_name is None:
return ''

base_url = getattr(
settings,
'BS_ICONS_BASE_URL',
'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/',
)
icon_path = f'{base_url}icons/{icon_name}.svg'
base_path = getattr(settings, "BS_ICONS_BASE_PATH", None)

if base_path:
icon_path = Path(base_path, "icons", f"{icon_name}.svg")
else:
base_url = getattr(
settings,
"BS_ICONS_BASE_URL",
"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/",
)
icon_path = f"{base_url}icons/{icon_name}.svg"

svg = get_icon(icon_path, icon_name, size, color, extra_classes)
return mark_safe(svg)
Expand Down Expand Up @@ -187,12 +211,17 @@ def md_icon(icon_name, size=None, color=None, extra_classes=None):
if size is None:
size = '20'

base_url = getattr(
settings,
'MD_ICONS_BASE_URL',
'https://cdn.jsdelivr.net/npm/@mdi/svg@7.2.96/'
)
icon_path = f'{base_url}svg/{icon_name}.svg'
base_path = getattr(settings, "MD_ICONS_BASE_PATH", None)

if base_path:
icon_path = Path(base_path, "svg", f"{icon_name}.svg")
else:
base_url = getattr(
settings,
"MD_ICONS_BASE_URL",
"https://cdn.jsdelivr.net/npm/@mdi/svg@7.2.96/",
)
icon_path = f"{base_url}svg/{icon_name}.svg"

svg = get_icon(icon_path, icon_name, size, color, extra_classes)
return mark_safe(svg)

0 comments on commit 6393d7b

Please sign in to comment.