Skip to content

Commit

Permalink
feat: favicons (#560)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasvinclav committed Jul 10, 2024
1 parent 159ba43 commit 1233c96
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 1 deletion.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
- **Environment label**: distinguish between environments by displaying a label
- **Nonrelated inlines**: displays nonrelated model as inline in changeform
- **Parallel admin**: support for default admin in parallel with Unfold. [Admin migration guide](https://unfoldadmin.com/blog/migrating-django-admin-unfold/?utm_medium=github&utm_source=unfold)
- **Favicons**: built-in support for configuring various site favicons
- **VS Code**: project configuration and development container is included

## Table of contents <!-- omit from toc -->
Expand Down Expand Up @@ -186,6 +187,14 @@ UNFOLD = {
"dark": lambda request: static("logo-dark.svg"), # dark mode
},
"SITE_SYMBOL": "speed", # symbol from icon set
"SITE_FAVICONS": [
{
"rel": "icon",
"sizes": "32x32",
"type": "image/svg+xml",
"href": lambda request: static("favicon.svg"),
},
],
"SHOW_HISTORY": True, # show/hide "History" button, default: True
"SHOW_VIEW_ON_SITE": True, # show/hide "View on site" button, default: True
"ENVIRONMENT": "sample_app.environment_callback",
Expand Down
10 changes: 9 additions & 1 deletion src/unfold/dataclasses.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass
from typing import Dict, Optional, Union
from typing import Callable, Dict, Optional, Union

from .typing import ActionFunction

Expand All @@ -12,3 +12,11 @@ class UnfoldAction:
path: str
attrs: Optional[Dict] = None
object_id: Optional[Union[int, str]] = None


@dataclass
class Favicon:
href: Union[str, Callable]
rel: Optional[str] = None
type: Optional[str] = None
sizes: Optional[str] = None
1 change: 1 addition & 0 deletions src/unfold/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"SITE_ICON": None,
"SITE_SYMBOL": None,
"SITE_LOGO": None,
"SITE_FAVICONS": [],
"SHOW_HISTORY": True,
"SHOW_VIEW_ON_SITE": True,
"COLORS": {
Expand Down
17 changes: 17 additions & 0 deletions src/unfold/sites.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.utils.functional import lazy
from django.utils.module_loading import import_string

from .dataclasses import Favicon
from .settings import get_config
from .utils import hex_to_rgb
from .widgets import CHECKBOX_CLASSES, INPUT_CLASSES
Expand Down Expand Up @@ -66,6 +67,9 @@ def each_context(self, request: HttpRequest) -> Dict[str, Any]:
"site_symbol": self._get_value(
get_config(self.settings_name)["SITE_SYMBOL"], request
),
"site_favicons": self._process_favicons(
request, get_config(self.settings_name)["SITE_FAVICONS"]
),
"show_history": get_config(self.settings_name)["SHOW_HISTORY"],
"show_view_on_site": get_config(self.settings_name)[
"SHOW_VIEW_ON_SITE"
Expand Down Expand Up @@ -350,6 +354,19 @@ def _replace_values(self, target: Dict, source: Dict, request: HttpRequest):

return target

def _process_favicons(
self, request: HttpRequest, favicons: List[Dict]
) -> List[Favicon]:
return [
Favicon(
href=self._get_value(item["href"], request),
rel=item.get("rel"),
sizes=item.get("sizes"),
type=item.get("type"),
)
for item in favicons
]

def _process_colors(
self, colors: Dict[str, Dict[str, str]]
) -> Dict[str, Dict[str, str]]:
Expand Down
4 changes: 4 additions & 0 deletions src/unfold/templates/unfold/layouts/skeleton.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
<link href="{{ style }}" rel="stylesheet">
{% endfor %}

{% for favicon in site_favicons %}
<link {% if favicon.rel %}rel="{{ favicon.rel }}"{% endif %} {% if favicon.href %}href="{{ favicon.href }}"{% endif %} {% if favicon.type %}type="{{ favicon.type }}"{% endif %} {% if favicon.sizes %}sizes="{{ favicon.sizes }}"{% endif %}>
{% endfor %}

<link href="{% static 'unfold/css/styles.css' %}" rel="stylesheet">
<link href="{% static 'unfold/css/simplebar.css' %}" rel="stylesheet">

Expand Down
26 changes: 26 additions & 0 deletions tests/test_site_branding.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,29 @@ def test_incorrect_mode_site_logo(self):
request.user = AnonymousUser()
context = admin_site.each_context(request)
self.assertIsNone(context["site_logo"])

@override_settings(
UNFOLD={
**CONFIG_DEFAULTS,
**{
"SITE_FAVICONS": [
{
"rel": "icon",
"sizes": "32x32",
"type": "image/svg+xml",
"href": lambda request: static("favicon.svg"),
}
]
},
}
)
def test_favicons(self):
admin_site = UnfoldAdminSite()
request = RequestFactory().get("/rand")
request.user = AnonymousUser()
context = admin_site.each_context(request)

self.assertEqual(context["site_favicons"][0].rel, "icon")
self.assertEqual(context["site_favicons"][0].sizes, "32x32")
self.assertEqual(context["site_favicons"][0].type, "image/svg+xml")
self.assertEqual(context["site_favicons"][0].href, "/static/favicon.svg")

0 comments on commit 1233c96

Please sign in to comment.