Coverage for extras/templatetags/custom_links.py: 21%
43 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1from django import template
2from django.utils.html import escape
3from django.utils.safestring import mark_safe
5from core.models import ObjectType
6from extras.models import CustomLink
7from netbox.choices import ButtonColorChoices
8from utilities.request import get_safe_request_context
10register = template.Library()
12LINK_BUTTON = '<a href="{}"{} class="btn btn-sm btn-{}">{}</a>\n'
14GROUP_BUTTON = """
15<div class="dropdown">
16 <button
17 class="btn btn-sm btn-{} dropdown-toggle"
18 type="button"
19 data-bs-toggle="dropdown"
20 aria-expanded="false">
21 {}
22 </button>
23 <ul class="dropdown-menu dropdown-menu-end">
24 {}
25 </ul>
26</div>
27"""
29GROUP_LINK = '<li><a class="dropdown-item" href="{}"{}>{}</a></li>\n'
32@register.simple_tag(takes_context=True)
33def custom_links(context, obj):
34 """
35 Render all applicable links for the given object.
36 """
37 user = context['request'].user
38 object_type = ObjectType.objects.get_for_model(obj)
39 custom_links = CustomLink.objects.restrict(user, 'view').filter(object_types=object_type, enabled=True)
40 if not custom_links:
41 return ''
43 # Pass select context data when rendering the CustomLink
44 link_context = {
45 'object': obj,
46 'debug': context.get('debug', False), # django.template.context_processors.debug
47 'request': get_safe_request_context(context['request']), # Sanitized subset of the request
48 'user': context['user'], # django.contrib.auth.context_processors.auth
49 'perms': context['perms'], # django.contrib.auth.context_processors.auth
50 }
51 template_code = ''
52 group_names = {}
54 for cl in custom_links:
56 # Organize custom links by group
57 if cl.group_name and cl.group_name in group_names:
58 group_names[cl.group_name].append(cl)
59 elif cl.group_name:
60 group_names[cl.group_name] = [cl]
62 # Add non-grouped links
63 else:
64 button_class = 'outline-secondary' if cl.button_class == ButtonColorChoices.DEFAULT else cl.button_class
65 try:
66 if rendered := cl.render(link_context):
67 template_code += LINK_BUTTON.format(
68 rendered['link'], rendered['link_target'], button_class, rendered['text']
69 )
70 except Exception as e:
71 template_code += f'<a class="btn btn-sm btn-outline-secondary" disabled="disabled" ' \
72 f'title="{escape(e)}"><i class="mdi mdi-alert"></i> {escape(cl.name)}</a>\n'
74 # Add grouped links to template
75 for group, links in group_names.items():
77 links_rendered = []
79 for cl in links:
80 try:
81 if rendered := cl.render(link_context):
82 links_rendered.append(
83 GROUP_LINK.format(rendered['link'], rendered['link_target'], rendered['text'])
84 )
85 except Exception as e:
86 links_rendered.append(
87 f'<li><a class="dropdown-item" disabled="disabled" title="{escape(e)}"><span class="text-muted">'
88 f'<i class="mdi mdi-alert"></i> {escape(cl.name)}</span></a></li>'
89 )
91 if links_rendered:
92 template_code += GROUP_BUTTON.format(
93 links[0].button_class, escape(group), ''.join(links_rendered)
94 )
96 return mark_safe(template_code)