Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions netbox/templates/base/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
{# Initialize color mode #}
<script
type="text/javascript"
src="{% static 'setmode.js' %}?v={{ settings.RELEASE.version }}"
src="{% static_with_params 'setmode.js' v=settings.RELEASE.version %}"
onerror="window.location='{% url 'media_failure' %}?filename=setmode.js'">
</script>
<script type="text/javascript">
Expand All @@ -39,12 +39,12 @@
{# Static resources #}
<link
rel="stylesheet"
href="{% static 'netbox-external.css'%}?v={{ settings.RELEASE.version }}"
href="{% static_with_params 'netbox-external.css' v=settings.RELEASE.version %}"
onerror="window.location='{% url 'media_failure' %}?filename=netbox-external.css'"
/>
<link
rel="stylesheet"
href="{% static 'netbox.css'%}?v={{ settings.RELEASE.version }}"
href="{% static_with_params 'netbox.css' v=settings.RELEASE.version %}"
onerror="window.location='{% url 'media_failure' %}?filename=netbox.css'"
/>
<link rel="icon" type="image/png" href="{% static 'netbox.ico' %}" />
Expand All @@ -53,7 +53,7 @@
{# Javascript #}
<script
type="text/javascript"
src="{% static 'netbox.js' %}?v={{ settings.RELEASE.version }}"
src="{% static_with_params 'netbox.js' v=settings.RELEASE.version %}"
onerror="window.location='{% url 'media_failure' %}?filename=netbox.js'">
</script>
{% django_htmx_script %}
Expand Down
46 changes: 46 additions & 0 deletions netbox/utilities/templatetags/builtins/tags.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import logging

from django import template
from django.templatetags.static import static
from django.utils.safestring import mark_safe
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode

from extras.choices import CustomFieldTypeChoices
from utilities.querydict import dict_to_querydict
Expand All @@ -11,6 +15,7 @@
'customfield_value',
'htmx_table',
'formaction',
'static_with_params',
'tag',
)

Expand Down Expand Up @@ -127,3 +132,44 @@ def formaction(context):
if context.get('htmx_navigation', False):
return mark_safe('hx-push-url="true" hx-post')
return 'formaction'


@register.simple_tag
def static_with_params(path, **params):
"""
Generate a static URL with properly appended query parameters.

This template tag handles the case where static files are served from AWS S3 or other
CDNs that already include query parameters in the URL. It properly appends additional
query parameters without creating double question marks.

Args:
path: The static file path (e.g., 'setmode.js')
**params: Query parameters to append (e.g., v='4.3.1')

Returns:
A properly formatted URL with query parameters.
"""
# Get the base static URL
static_url = static(path)

# Parse the URL to extract existing query parameters
parsed = urlparse(static_url)
existing_params = parse_qs(parsed.query)

# Check for duplicate parameters and log warnings
logger = logging.getLogger('netbox.utilities.templatetags.tags')
for key, value in params.items():
if key in existing_params:
logger.warning(
f"Parameter '{key}' already exists in static URL '{static_url}' "
f"with value(s) {existing_params[key]}, overwriting with '{value}'"
)
existing_params[key] = [str(value)]

# Rebuild the query string
new_query = urlencode(existing_params, doseq=True)

# Reconstruct the URL with the new query string
new_parsed = parsed._replace(query=new_query)
return urlunparse(new_parsed)