mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #8600: Document built-in template tags & filters
This commit is contained in:
@ -193,3 +193,43 @@ This template is used by the `BulkDeleteView` generic view to delete multiple ob
|
||||
| `form` | Yes | The bulk delete form class |
|
||||
| `table` | Yes | The table class used for rendering the list of objects |
|
||||
| `return_url` | Yes | The URL to which the user is redirect after submitting the form |
|
||||
|
||||
## Tags
|
||||
|
||||
The following custom template tags are available in NetBox.
|
||||
|
||||
!!! info
|
||||
These are loaded automatically by the template backend: You do _not_ need to include a `{% load %}` tag in your template to activate them.
|
||||
|
||||
::: utilities.templatetags.builtins.tags.badge
|
||||
|
||||
::: utilities.templatetags.builtins.tags.checkmark
|
||||
|
||||
::: utilities.templatetags.builtins.tags.tag
|
||||
|
||||
## Filters
|
||||
|
||||
The following custom template filters are available in NetBox.
|
||||
|
||||
!!! info
|
||||
These are loaded automatically by the template backend: You do _not_ need to include a `{% load %}` tag in your template to activate them.
|
||||
|
||||
::: utilities.templatetags.builtins.filters.bettertitle
|
||||
|
||||
::: utilities.templatetags.builtins.filters.content_type
|
||||
|
||||
::: utilities.templatetags.builtins.filters.content_type_id
|
||||
|
||||
::: utilities.templatetags.builtins.filters.meta
|
||||
|
||||
::: utilities.templatetags.builtins.filters.placeholder
|
||||
|
||||
::: utilities.templatetags.builtins.filters.render_json
|
||||
|
||||
::: utilities.templatetags.builtins.filters.render_markdown
|
||||
|
||||
::: utilities.templatetags.builtins.filters.render_yaml
|
||||
|
||||
::: utilities.templatetags.builtins.filters.split
|
||||
|
||||
::: utilities.templatetags.builtins.filters.tzoffset
|
||||
|
@ -225,7 +225,7 @@ class ObjectJournalTable(NetBoxTable):
|
||||
)
|
||||
kind = columns.ChoiceFieldColumn()
|
||||
comments = tables.TemplateColumn(
|
||||
template_code='{% load helpers %}{{ value|render_markdown|truncatewords_html:50 }}'
|
||||
template_code='{{ value|markdown|truncatewords_html:50 }}'
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
|
@ -352,6 +352,10 @@ TEMPLATES = [
|
||||
'DIRS': [TEMPLATES_DIR],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'builtins': [
|
||||
'utilities.templatetags.builtins.filters',
|
||||
'utilities.templatetags.builtins.tags',
|
||||
],
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
|
@ -290,7 +290,7 @@ class TagColumn(tables.TemplateColumn):
|
||||
template_code = """
|
||||
{% load helpers %}
|
||||
{% for tag in value.all %}
|
||||
{% tag tag url_name=url_name %}
|
||||
{% tag tag url_name %}
|
||||
{% empty %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endfor %}
|
||||
@ -414,9 +414,8 @@ class MarkdownColumn(tables.TemplateColumn):
|
||||
Render a Markdown string.
|
||||
"""
|
||||
template_code = """
|
||||
{% load helpers %}
|
||||
{% if value %}
|
||||
{{ value|render_markdown }}
|
||||
{{ value|markdown }}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
|
@ -41,11 +41,11 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">NOC Contact</th>
|
||||
<td>{{ object.noc_contact|render_markdown|placeholder }}</td>
|
||||
<td>{{ object.noc_contact|markdown|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Admin Contact</th>
|
||||
<td>{{ object.admin_contact|render_markdown|placeholder }}</td>
|
||||
<td>{{ object.admin_contact|markdown|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Circuits</th>
|
||||
|
@ -73,7 +73,7 @@
|
||||
NAPALM Arguments
|
||||
</h5>
|
||||
<div class="card-body">
|
||||
<pre>{{ object.napalm_args|render_json }}</pre>
|
||||
<pre>{{ object.napalm_args|json }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
|
@ -60,7 +60,7 @@
|
||||
<span class="muted">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="rendered-markdown">{{ message|render_markdown }}</td>
|
||||
<td class="rendered-markdown">{{ message|markdown }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
@ -22,7 +22,7 @@
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{% log_level log.status %}</td>
|
||||
<td class="rendered-markdown">{{ log.message|render_markdown }}</td>
|
||||
<td class="rendered-markdown">{{ log.message|markdown }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% load helpers %}
|
||||
|
||||
<div class="rendered-context-data">
|
||||
<pre class="block">{% if format == 'json' %}{{ data|render_json }}{% elif format == 'yaml' %}{{ data|render_yaml }}{% else %}{{ data }}{% endif %}</pre>
|
||||
<pre class="block">{% if format == 'json' %}{{ data|json }}{% elif format == 'yaml' %}{{ data|yaml }}{% else %}{{ data }}{% endif %}</pre>
|
||||
</div>
|
||||
|
@ -98,8 +98,8 @@
|
||||
{% endif %}
|
||||
</span>
|
||||
{% else %}
|
||||
<pre class="change-diff change-removed">{{ diff_removed|render_json }}</pre>
|
||||
<pre class="change-diff change-added">{{ diff_added|render_json }}</pre>
|
||||
<pre class="change-diff change-removed">{{ diff_removed|json }}</pre>
|
||||
<pre class="change-diff change-added">{{ diff_added|json }}</pre>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@ -114,7 +114,7 @@
|
||||
<div class="card-body">
|
||||
{% if object.prechange_data %}
|
||||
<pre class="change-data">{% for k, v in object.prechange_data.items %}{% spaceless %}
|
||||
<span{% if k in diff_removed %} class="removed"{% endif %}>{{ k }}: {{ v|render_json }}</span>
|
||||
<span{% if k in diff_removed %} class="removed"{% endif %}>{{ k }}: {{ v|json }}</span>
|
||||
{% endspaceless %}{% endfor %}
|
||||
</pre>
|
||||
{% elif non_atomic_change %}
|
||||
@ -133,7 +133,7 @@
|
||||
<div class="card-body">
|
||||
{% if object.postchange_data %}
|
||||
<pre class="change-data">{% for k, v in object.postchange_data.items %}{% spaceless %}
|
||||
<span{% if k in diff_added %} class="added"{% endif %}>{{ k }}: {{ v|render_json }}</span>
|
||||
<span{% if k in diff_added %} class="added"{% endif %}>{{ k }}: {{ v|json }}</span>
|
||||
{% endspaceless %}{% endfor %}
|
||||
</pre>
|
||||
{% else %}
|
||||
|
@ -15,7 +15,7 @@
|
||||
{% block subtitle %}
|
||||
{% if report.description %}
|
||||
<div class="object-subtitle">
|
||||
<div class="text-muted">{{ report.description|render_markdown }}</div>
|
||||
<div class="text-muted">{{ report.description|markdown }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock subtitle %}
|
||||
|
@ -40,7 +40,7 @@
|
||||
<td>
|
||||
{% include 'extras/inc/job_label.html' with result=report.result %}
|
||||
</td>
|
||||
<td>{{ report.description|render_markdown|placeholder }}</td>
|
||||
<td>{{ report.description|markdown|placeholder }}</td>
|
||||
<td class="text-end">
|
||||
{% if report.result %}
|
||||
<a href="{% url 'extras:report_result' job_result_pk=report.result.pk %}">{{ report.result.created|annotated_date }}</a>
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
{% block subtitle %}
|
||||
<div class="object-subtitle">
|
||||
<div class="text-muted">{{ script.Meta.description|render_markdown }}</div>
|
||||
<div class="text-muted">{{ script.Meta.description|markdown }}</div>
|
||||
</div>
|
||||
{% endblock subtitle %}
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
||||
{% include 'extras/inc/job_label.html' with result=script.result %}
|
||||
</td>
|
||||
<td>
|
||||
{{ script.Meta.description|render_markdown|placeholder }}
|
||||
{{ script.Meta.description|markdown|placeholder }}
|
||||
</td>
|
||||
{% if script.result %}
|
||||
<td class="text-end">
|
||||
|
@ -4,7 +4,7 @@
|
||||
{% block title %}{{ script }}{% endblock %}
|
||||
|
||||
{% block subtitle %}
|
||||
{{ script.Meta.description|render_markdown }}
|
||||
{{ script.Meta.description|markdown }}
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
|
@ -108,7 +108,7 @@
|
||||
</h5>
|
||||
<div class="card-body">
|
||||
{% if object.conditions %}
|
||||
<pre>{{ object.conditions|render_json }}</pre>
|
||||
<pre>{{ object.conditions|json }}</pre>
|
||||
{% else %}
|
||||
<p class="text-muted">None</p>
|
||||
{% endif %}
|
||||
|
@ -6,7 +6,7 @@
|
||||
</h5>
|
||||
<div class="card-body">
|
||||
{% if object.comments %}
|
||||
{{ object.comments|render_markdown }}
|
||||
{{ object.comments|markdown }}
|
||||
{% else %}
|
||||
<span class="text-muted">None</span>
|
||||
{% endif %}
|
||||
|
@ -13,7 +13,7 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if field.type == 'longtext' and value %}
|
||||
{{ value|render_markdown }}
|
||||
{{ value|markdown }}
|
||||
{% elif field.type == 'boolean' and value == True %}
|
||||
{% checkmark value true="True" %}
|
||||
{% elif field.type == 'boolean' and value == False %}
|
||||
@ -21,7 +21,7 @@
|
||||
{% elif field.type == 'url' and value %}
|
||||
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
||||
{% elif field.type == 'json' and value %}
|
||||
<pre>{{ value|render_json }}</pre>
|
||||
<pre>{{ value|json }}</pre>
|
||||
{% elif field.type == 'multiselect' and value %}
|
||||
{{ value|join:", " }}
|
||||
{% elif field.type == 'object' and value %}
|
||||
|
@ -1,3 +1,3 @@
|
||||
{% load helpers %}
|
||||
|
||||
{% if url_name %}<a href="{% url url_name %}?tag={{ tag.slug }}">{% endif %}<span class="badge" style="color: {{ tag.color|fgcolor }}; background-color: #{{ tag.color }}">{{ tag }}</span>{% if url_name %}</a>{% endif %}
|
||||
{% if viewname %}<a href="{% url viewname %}?tag={{ tag.slug }}">{% endif %}<span class="badge" style="color: {{ tag.color|fgcolor }}; background-color: #{{ tag.color }}">{{ tag }}</span>{% if viewname %}</a>{% endif %}
|
0
netbox/utilities/templatetags/builtins/__init__.py
Normal file
0
netbox/utilities/templatetags/builtins/__init__.py
Normal file
167
netbox/utilities/templatetags/builtins/filters.py
Normal file
167
netbox/utilities/templatetags/builtins/filters.py
Normal file
@ -0,0 +1,167 @@
|
||||
import datetime
|
||||
import json
|
||||
import re
|
||||
|
||||
import yaml
|
||||
from django import template
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.html import strip_tags
|
||||
from django.utils.safestring import mark_safe
|
||||
from markdown import markdown
|
||||
|
||||
from netbox.config import get_config
|
||||
from utilities.markdown import StrikethroughExtension
|
||||
from utilities.utils import foreground_color
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
#
|
||||
# General
|
||||
#
|
||||
|
||||
@register.filter()
|
||||
def bettertitle(value):
|
||||
"""
|
||||
Alternative to the builtin title(). Ensures that the first letter of each word is uppercase but retains the
|
||||
original case of all others.
|
||||
"""
|
||||
return ' '.join([w[0].upper() + w[1:] for w in value.split()])
|
||||
|
||||
|
||||
@register.filter()
|
||||
def fgcolor(value, dark='000000', light='ffffff'):
|
||||
"""
|
||||
Return black (#000000) or white (#ffffff) given an arbitrary background color in RRGGBB format. The foreground
|
||||
color with the better contrast is returned.
|
||||
|
||||
Args:
|
||||
value: The background color
|
||||
dark: The foreground color to use for light backgrounds
|
||||
light: The foreground color to use for dark backgrounds
|
||||
"""
|
||||
value = value.lower().strip('#')
|
||||
if not re.match('^[0-9a-f]{6}$', value):
|
||||
return ''
|
||||
return f'#{foreground_color(value, dark, light)}'
|
||||
|
||||
|
||||
@register.filter()
|
||||
def meta(model, attr):
|
||||
"""
|
||||
Return the specified Meta attribute of a model. This is needed because Django does not permit templates
|
||||
to access attributes which begin with an underscore (e.g. _meta).
|
||||
|
||||
Args:
|
||||
model: A Django model class or instance
|
||||
attr: The attribute name
|
||||
"""
|
||||
return getattr(model._meta, attr, '')
|
||||
|
||||
|
||||
@register.filter()
|
||||
def placeholder(value):
|
||||
"""
|
||||
Render a muted placeholder if the value equates to False.
|
||||
"""
|
||||
if value not in ('', None):
|
||||
return value
|
||||
placeholder = '<span class="text-muted">—</span>'
|
||||
return mark_safe(placeholder)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def split(value, separator=','):
|
||||
"""
|
||||
Wrapper for Python's `split()` string method.
|
||||
|
||||
Args:
|
||||
value: A string
|
||||
separator: String on which the value will be split
|
||||
"""
|
||||
return value.split(separator)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def tzoffset(value):
|
||||
"""
|
||||
Returns the hour offset of a given time zone using the current time.
|
||||
"""
|
||||
return datetime.datetime.now(value).strftime('%z')
|
||||
|
||||
|
||||
#
|
||||
# Content types
|
||||
#
|
||||
|
||||
@register.filter()
|
||||
def content_type(model):
|
||||
"""
|
||||
Return the ContentType for the given object.
|
||||
"""
|
||||
return ContentType.objects.get_for_model(model)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def content_type_id(model):
|
||||
"""
|
||||
Return the ContentType ID for the given object.
|
||||
"""
|
||||
content_type = ContentType.objects.get_for_model(model)
|
||||
if content_type:
|
||||
return content_type.pk
|
||||
return None
|
||||
|
||||
|
||||
#
|
||||
# Rendering
|
||||
#
|
||||
|
||||
@register.filter('markdown', is_safe=True)
|
||||
def render_markdown(value):
|
||||
"""
|
||||
Render a string as Markdown. This filter is invoked as "markdown":
|
||||
|
||||
{{ md_source_text|markdown }}
|
||||
"""
|
||||
schemes = '|'.join(get_config().ALLOWED_URL_SCHEMES)
|
||||
|
||||
# Strip HTML tags
|
||||
value = strip_tags(value)
|
||||
|
||||
# Sanitize Markdown links
|
||||
pattern = fr'\[([^\]]+)\]\((?!({schemes})).*:(.+)\)'
|
||||
value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE)
|
||||
|
||||
# Sanitize Markdown reference links
|
||||
pattern = fr'\[(.+)\]:\s*(?!({schemes}))\w*:(.+)'
|
||||
value = re.sub(pattern, '[\\1]: \\3', value, flags=re.IGNORECASE)
|
||||
|
||||
# Render Markdown
|
||||
html = markdown(value, extensions=['fenced_code', 'tables', StrikethroughExtension()])
|
||||
|
||||
# If the string is not empty wrap it in rendered-markdown to style tables
|
||||
if html:
|
||||
html = f'<div class="rendered-markdown">{html}</div>'
|
||||
|
||||
return mark_safe(html)
|
||||
|
||||
|
||||
@register.filter('json')
|
||||
def render_json(value):
|
||||
"""
|
||||
Render a dictionary as formatted JSON. This filter is invoked as "json":
|
||||
|
||||
{{ data_dict|json }}
|
||||
"""
|
||||
return json.dumps(value, ensure_ascii=False, indent=4, sort_keys=True)
|
||||
|
||||
|
||||
@register.filter('yaml')
|
||||
def render_yaml(value):
|
||||
"""
|
||||
Render a dictionary as formatted YAML. This filter is invoked as "yaml":
|
||||
|
||||
{{ data_dict|yaml }}
|
||||
"""
|
||||
return yaml.dump(json.loads(json.dumps(value)))
|
54
netbox/utilities/templatetags/builtins/tags.py
Normal file
54
netbox/utilities/templatetags/builtins/tags.py
Normal file
@ -0,0 +1,54 @@
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.inclusion_tag('builtins/tag.html')
|
||||
def tag(value, viewname=None):
|
||||
"""
|
||||
Display a tag, optionally linked to a filtered list of objects.
|
||||
|
||||
Args:
|
||||
value: A Tag instance
|
||||
viewname: If provided, the tag will be a hyperlink to the specified view's URL
|
||||
"""
|
||||
return {
|
||||
'tag': value,
|
||||
'viewname': viewname,
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('builtins/badge.html')
|
||||
def badge(value, bg_class='secondary', show_empty=False):
|
||||
"""
|
||||
Display the specified number as a badge.
|
||||
|
||||
Args:
|
||||
value: The value to be displayed within the badge
|
||||
bg_class: Bootstrap 5 background CSS name
|
||||
show_empty: If true, display the badge even if value is None or zero
|
||||
"""
|
||||
return {
|
||||
'value': value,
|
||||
'bg_class': bg_class,
|
||||
'show_empty': show_empty,
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('builtins/checkmark.html')
|
||||
def checkmark(value, show_false=True, true='Yes', false='No'):
|
||||
"""
|
||||
Display either a green checkmark or red X to indicate a boolean value.
|
||||
|
||||
Args:
|
||||
value: True or False
|
||||
show_false: Show false values
|
||||
true: Text label for true values
|
||||
false: Text label for false values
|
||||
"""
|
||||
return {
|
||||
'value': bool(value),
|
||||
'show_false': show_false,
|
||||
'true_label': true,
|
||||
'false_label': false,
|
||||
}
|
@ -1,24 +1,17 @@
|
||||
import datetime
|
||||
import decimal
|
||||
import json
|
||||
import re
|
||||
from typing import Dict, Any
|
||||
|
||||
import yaml
|
||||
from django import template
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.template.defaultfilters import date
|
||||
from django.urls import NoReverseMatch, reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.html import strip_tags
|
||||
from django.utils.safestring import mark_safe
|
||||
from markdown import markdown
|
||||
|
||||
from netbox.config import get_config
|
||||
from utilities.forms import get_selected_values, TableConfigForm
|
||||
from utilities.markdown import StrikethroughExtension
|
||||
from utilities.utils import foreground_color, get_viewname
|
||||
from utilities.utils import get_viewname
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@ -27,88 +20,6 @@ register = template.Library()
|
||||
# Filters
|
||||
#
|
||||
|
||||
@register.filter()
|
||||
def placeholder(value):
|
||||
"""
|
||||
Render a muted placeholder if value equates to False.
|
||||
"""
|
||||
if value not in ('', None):
|
||||
return value
|
||||
placeholder = '<span class="text-muted">—</span>'
|
||||
return mark_safe(placeholder)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def render_markdown(value):
|
||||
"""
|
||||
Render text as Markdown
|
||||
"""
|
||||
schemes = '|'.join(get_config().ALLOWED_URL_SCHEMES)
|
||||
|
||||
# Strip HTML tags
|
||||
value = strip_tags(value)
|
||||
|
||||
# Sanitize Markdown links
|
||||
pattern = fr'\[([^\]]+)\]\((?!({schemes})).*:(.+)\)'
|
||||
value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE)
|
||||
|
||||
# Sanitize Markdown reference links
|
||||
pattern = fr'\[(.+)\]:\s*(?!({schemes}))\w*:(.+)'
|
||||
value = re.sub(pattern, '[\\1]: \\3', value, flags=re.IGNORECASE)
|
||||
|
||||
# Render Markdown
|
||||
html = markdown(value, extensions=['fenced_code', 'tables', StrikethroughExtension()])
|
||||
|
||||
# If the string is not empty wrap it in rendered-markdown to style tables
|
||||
if html:
|
||||
html = f'<div class="rendered-markdown">{html}</div>'
|
||||
|
||||
return mark_safe(html)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def render_json(value):
|
||||
"""
|
||||
Render a dictionary as formatted JSON.
|
||||
"""
|
||||
return json.dumps(value, ensure_ascii=False, indent=4, sort_keys=True)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def render_yaml(value):
|
||||
"""
|
||||
Render a dictionary as formatted YAML.
|
||||
"""
|
||||
return yaml.dump(json.loads(json.dumps(value)))
|
||||
|
||||
|
||||
@register.filter()
|
||||
def meta(obj, attr):
|
||||
"""
|
||||
Return the specified Meta attribute of a model. This is needed because Django does not permit templates
|
||||
to access attributes which begin with an underscore (e.g. _meta).
|
||||
"""
|
||||
return getattr(obj._meta, attr, '')
|
||||
|
||||
|
||||
@register.filter()
|
||||
def content_type(obj):
|
||||
"""
|
||||
Return the ContentType for the given object.
|
||||
"""
|
||||
return ContentType.objects.get_for_model(obj)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def content_type_id(obj):
|
||||
"""
|
||||
Return the ContentType ID for the given object.
|
||||
"""
|
||||
content_type = ContentType.objects.get_for_model(obj)
|
||||
if content_type:
|
||||
return content_type.pk
|
||||
return None
|
||||
|
||||
|
||||
@register.filter()
|
||||
def viewname(model, action):
|
||||
@ -133,14 +44,6 @@ def validated_viewname(model, action):
|
||||
return None
|
||||
|
||||
|
||||
@register.filter()
|
||||
def bettertitle(value):
|
||||
"""
|
||||
Alternative to the builtin title(); uppercases words without replacing letters that are already uppercase.
|
||||
"""
|
||||
return ' '.join([w[0].upper() + w[1:] for w in value.split()])
|
||||
|
||||
|
||||
@register.filter()
|
||||
def humanize_speed(speed):
|
||||
"""
|
||||
@ -191,14 +94,6 @@ def simplify_decimal(value):
|
||||
return str(value).rstrip('0').rstrip('.')
|
||||
|
||||
|
||||
@register.filter()
|
||||
def tzoffset(value):
|
||||
"""
|
||||
Returns the hour offset of a given time zone using the current time.
|
||||
"""
|
||||
return datetime.datetime.now(value).strftime('%z')
|
||||
|
||||
|
||||
@register.filter(expects_localtime=True)
|
||||
def annotated_date(date_value):
|
||||
"""
|
||||
@ -229,17 +124,6 @@ def annotated_now():
|
||||
return annotated_date(datetime.datetime.now(tz=tzinfo))
|
||||
|
||||
|
||||
@register.filter()
|
||||
def fgcolor(value):
|
||||
"""
|
||||
Return black (#000000) or white (#ffffff) given an arbitrary background color in RRGGBB format.
|
||||
"""
|
||||
value = value.lower().strip('#')
|
||||
if not re.match('^[0-9a-f]{6}$', value):
|
||||
return ''
|
||||
return f'#{foreground_color(value)}'
|
||||
|
||||
|
||||
@register.filter()
|
||||
def divide(x, y):
|
||||
"""
|
||||
@ -276,14 +160,6 @@ def has_perms(user, permissions_list):
|
||||
return user.has_perms(permissions_list)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def split(string, sep=','):
|
||||
"""
|
||||
Split a string by the given value (default: comma)
|
||||
"""
|
||||
return string.split(sep)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def as_range(n):
|
||||
"""
|
||||
@ -403,46 +279,6 @@ def utilization_graph(utilization, warning_threshold=75, danger_threshold=90):
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('helpers/tag.html')
|
||||
def tag(tag, url_name=None):
|
||||
"""
|
||||
Display a tag, optionally linked to a filtered list of objects.
|
||||
"""
|
||||
return {
|
||||
'tag': tag,
|
||||
'url_name': url_name,
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('helpers/badge.html')
|
||||
def badge(value, bg_class='secondary', show_empty=False):
|
||||
"""
|
||||
Display the specified number as a badge.
|
||||
"""
|
||||
return {
|
||||
'value': value,
|
||||
'bg_class': bg_class,
|
||||
'show_empty': show_empty,
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('helpers/checkmark.html')
|
||||
def checkmark(value, show_false=True, true='Yes', false='No'):
|
||||
"""
|
||||
Display either a green checkmark or red X to indicate a boolean value.
|
||||
|
||||
:param show_false: Display a red X if the value is False
|
||||
:param true: Text label for true value
|
||||
:param false: Text label for false value
|
||||
"""
|
||||
return {
|
||||
'value': bool(value),
|
||||
'show_false': show_false,
|
||||
'true_label': true,
|
||||
'false_label': false,
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('helpers/table_config_form.html')
|
||||
def table_config_form(table, table_name=None):
|
||||
return {
|
||||
|
Reference in New Issue
Block a user