mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 01:48:38 -06:00
Add UI views for custom links
This commit is contained in:
parent
b017927c69
commit
276ded0119
@ -57,52 +57,6 @@ class WebhookAdmin(admin.ModelAdmin):
|
|||||||
return ', '.join([ct.name for ct in obj.content_types.all()])
|
return ', '.join([ct.name for ct in obj.content_types.all()])
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Custom links
|
|
||||||
#
|
|
||||||
|
|
||||||
class CustomLinkForm(forms.ModelForm):
|
|
||||||
content_type = ContentTypeChoiceField(
|
|
||||||
queryset=ContentType.objects.all(),
|
|
||||||
limit_choices_to=FeatureQuery('custom_links')
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = CustomLink
|
|
||||||
exclude = []
|
|
||||||
widgets = {
|
|
||||||
'link_text': forms.Textarea,
|
|
||||||
'link_url': forms.Textarea,
|
|
||||||
}
|
|
||||||
help_texts = {
|
|
||||||
'weight': 'A numeric weight to influence the ordering of this link among its peers. Lower weights appear '
|
|
||||||
'first in a list.',
|
|
||||||
'link_text': 'Jinja2 template code for the link text. Reference the object as <code>{{ obj }}</code>. '
|
|
||||||
'Links which render as empty text will not be displayed.',
|
|
||||||
'link_url': 'Jinja2 template code for the link URL. Reference the object as <code>{{ obj }}</code>.',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(CustomLink)
|
|
||||||
class CustomLinkAdmin(admin.ModelAdmin):
|
|
||||||
fieldsets = (
|
|
||||||
('Custom Link', {
|
|
||||||
'fields': ('content_type', 'name', 'group_name', 'weight', 'button_class', 'new_window')
|
|
||||||
}),
|
|
||||||
('Templates', {
|
|
||||||
'fields': ('link_text', 'link_url'),
|
|
||||||
'classes': ('monospace',)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
list_display = [
|
|
||||||
'name', 'content_type', 'group_name', 'weight',
|
|
||||||
]
|
|
||||||
list_filter = [
|
|
||||||
'content_type',
|
|
||||||
]
|
|
||||||
form = CustomLinkForm
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Export templates
|
# Export templates
|
||||||
#
|
#
|
||||||
|
@ -8,13 +8,13 @@ from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGrou
|
|||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorField,
|
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorField,
|
||||||
CommentField, ContentTypeMultipleChoiceField, CSVModelForm, CSVMultipleContentTypeField, DateTimePicker,
|
CommentField, ContentTypeChoiceField, ContentTypeMultipleChoiceField, CSVContentTypeField, CSVModelForm,
|
||||||
DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, StaticSelect2Multiple,
|
CSVMultipleContentTypeField, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2,
|
||||||
BOOLEAN_WITH_BLANK_CHOICES,
|
StaticSelect2Multiple, BOOLEAN_WITH_BLANK_CHOICES,
|
||||||
)
|
)
|
||||||
from virtualization.models import Cluster, ClusterGroup
|
from virtualization.models import Cluster, ClusterGroup
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .models import ConfigContext, CustomField, ImageAttachment, JournalEntry, ObjectChange, Tag
|
from .models import *
|
||||||
from .utils import FeatureQuery
|
from .utils import FeatureQuery
|
||||||
|
|
||||||
|
|
||||||
@ -100,6 +100,86 @@ class CustomFieldFilterForm(BootstrapMixin, forms.Form):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Custom links
|
||||||
|
#
|
||||||
|
|
||||||
|
class CustomLinkForm(BootstrapMixin, forms.ModelForm):
|
||||||
|
content_type = ContentTypeChoiceField(
|
||||||
|
queryset=ContentType.objects.all(),
|
||||||
|
limit_choices_to=FeatureQuery('custom_links')
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CustomLink
|
||||||
|
fields = '__all__'
|
||||||
|
fieldsets = (
|
||||||
|
('Custom Link', ('name', 'content_type', 'weight', 'group_name', 'button_class', 'new_window')),
|
||||||
|
('Templates', ('link_text', 'link_url')),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkCSVForm(CSVModelForm):
|
||||||
|
content_type = CSVContentTypeField(
|
||||||
|
queryset=ContentType.objects.all(),
|
||||||
|
limit_choices_to=FeatureQuery('custom_links'),
|
||||||
|
help_text="One or more assigned object types"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CustomLink
|
||||||
|
fields = (
|
||||||
|
'name', 'content_type', 'weight', 'group_name', 'button_class', 'new_window', 'link_text', 'link_url',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||||
|
pk = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=CustomLink.objects.all(),
|
||||||
|
widget=forms.MultipleHiddenInput
|
||||||
|
)
|
||||||
|
content_type = ContentTypeChoiceField(
|
||||||
|
queryset=ContentType.objects.all(),
|
||||||
|
limit_choices_to=FeatureQuery('custom_fields'),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
new_window = forms.NullBooleanField(
|
||||||
|
required=False,
|
||||||
|
widget=BulkEditNullBooleanSelect()
|
||||||
|
)
|
||||||
|
weight = forms.IntegerField(
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
button_class = forms.ChoiceField(
|
||||||
|
choices=CustomLinkButtonClassChoices,
|
||||||
|
required=False,
|
||||||
|
widget=StaticSelect2()
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
nullable_fields = []
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkFilterForm(BootstrapMixin, forms.Form):
|
||||||
|
field_groups = [
|
||||||
|
['content_type'],
|
||||||
|
['weight', 'new_window'],
|
||||||
|
]
|
||||||
|
content_type = ContentTypeChoiceField(
|
||||||
|
queryset=ContentType.objects.all(),
|
||||||
|
limit_choices_to=FeatureQuery('custom_fields')
|
||||||
|
)
|
||||||
|
weight = forms.IntegerField(
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
new_window = forms.NullBooleanField(
|
||||||
|
required=False,
|
||||||
|
widget=StaticSelect2(
|
||||||
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Custom field models
|
# Custom field models
|
||||||
#
|
#
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# Generated by Django 3.2.4 on 2021-06-23 17:37
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
@ -20,4 +18,14 @@ class Migration(migrations.Migration):
|
|||||||
name='last_updated',
|
name='last_updated',
|
||||||
field=models.DateTimeField(auto_now=True, null=True),
|
field=models.DateTimeField(auto_now=True, null=True),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customlink',
|
||||||
|
name='created',
|
||||||
|
field=models.DateField(auto_now_add=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customlink',
|
||||||
|
name='last_updated',
|
||||||
|
field=models.DateTimeField(auto_now=True, null=True),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -171,7 +171,7 @@ class Webhook(BigIDModel):
|
|||||||
# Custom links
|
# Custom links
|
||||||
#
|
#
|
||||||
|
|
||||||
class CustomLink(BigIDModel):
|
class CustomLink(ChangeLoggedModel):
|
||||||
"""
|
"""
|
||||||
A custom link to an external representation of a NetBox object. The link text and URL fields accept Jinja2 template
|
A custom link to an external representation of a NetBox object. The link text and URL fields accept Jinja2 template
|
||||||
code to be rendered with an object as context.
|
code to be rendered with an object as context.
|
||||||
@ -221,6 +221,9 @@ class CustomLink(BigIDModel):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('extras:customlink', args=[self.pk])
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Export templates
|
# Export templates
|
||||||
|
@ -46,6 +46,24 @@ class CustomFieldTable(BaseTable):
|
|||||||
default_columns = ('pk', 'name', 'label', 'type', 'required', 'description')
|
default_columns = ('pk', 'name', 'label', 'type', 'required', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Custom links
|
||||||
|
#
|
||||||
|
|
||||||
|
class CustomLinkTable(BaseTable):
|
||||||
|
pk = ToggleColumn()
|
||||||
|
name = tables.Column(
|
||||||
|
linkify=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
model = CustomLink
|
||||||
|
fields = (
|
||||||
|
'pk', 'name', 'content_type', 'link_text', 'link_url', 'weight', 'group_name', 'button_class', 'new_window',
|
||||||
|
)
|
||||||
|
default_columns = ('pk', 'name', 'content_type', 'group_name', 'button_class', 'new_window')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Tags
|
# Tags
|
||||||
#
|
#
|
||||||
|
@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
from extras.choices import CustomFieldFilterLogicChoices, CustomFieldTypeChoices, ObjectChangeActionChoices
|
from extras.choices import *
|
||||||
from extras.models import *
|
from extras.models import *
|
||||||
from utilities.testing import ViewTestCases, TestCase
|
from utilities.testing import ViewTestCases, TestCase
|
||||||
|
|
||||||
@ -51,6 +51,41 @@ class CustomFieldTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
|
model = CustomLink
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
|
||||||
|
site_ct = ContentType.objects.get_for_model(Site)
|
||||||
|
CustomLink.objects.bulk_create((
|
||||||
|
CustomLink(name='Custom Link 1', content_type=site_ct, link_text='Link 1', link_url='http://example.com/?1'),
|
||||||
|
CustomLink(name='Custom Link 2', content_type=site_ct, link_text='Link 2', link_url='http://example.com/?2'),
|
||||||
|
CustomLink(name='Custom Link 3', content_type=site_ct, link_text='Link 3', link_url='http://example.com/?3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'name': 'Custom Link X',
|
||||||
|
'content_type': site_ct.pk,
|
||||||
|
'weight': 100,
|
||||||
|
'button_class': CustomLinkButtonClassChoices.CLASS_DEFAULT,
|
||||||
|
'link_text': 'Link X',
|
||||||
|
'link_url': 'http://example.com/?x'
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.csv_data = (
|
||||||
|
"name,content_type,weight,button_class,link_text,link_url",
|
||||||
|
"Custom Link 4,dcim.site,100,primary,Link 4,http://exmaple.com/?4",
|
||||||
|
"Custom Link 5,dcim.site,100,primary,Link 5,http://exmaple.com/?5",
|
||||||
|
"Custom Link 6,dcim.site,100,primary,Link 6,http://exmaple.com/?6",
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'button_class': CustomLinkButtonClassChoices.CLASS_INFO,
|
||||||
|
'weight': 200,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = Tag
|
model = Tag
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from extras import views
|
from extras import models, views
|
||||||
from extras.models import ConfigContext, CustomField, JournalEntry, Tag
|
|
||||||
|
|
||||||
|
|
||||||
app_name = 'extras'
|
app_name = 'extras'
|
||||||
@ -16,7 +15,20 @@ urlpatterns = [
|
|||||||
path('custom-fields/<int:pk>/', views.CustomFieldView.as_view(), name='customfield'),
|
path('custom-fields/<int:pk>/', views.CustomFieldView.as_view(), name='customfield'),
|
||||||
path('custom-fields/<int:pk>/edit/', views.CustomFieldEditView.as_view(), name='customfield_edit'),
|
path('custom-fields/<int:pk>/edit/', views.CustomFieldEditView.as_view(), name='customfield_edit'),
|
||||||
path('custom-fields/<int:pk>/delete/', views.CustomFieldDeleteView.as_view(), name='customfield_delete'),
|
path('custom-fields/<int:pk>/delete/', views.CustomFieldDeleteView.as_view(), name='customfield_delete'),
|
||||||
path('custom-fields/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='customfield_changelog', kwargs={'model': CustomField}),
|
path('custom-fields/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='customfield_changelog',
|
||||||
|
kwargs={'model': models.CustomField}),
|
||||||
|
|
||||||
|
# Custom links
|
||||||
|
path('custom-links/', views.CustomLinkListView.as_view(), name='customlink_list'),
|
||||||
|
path('custom-links/add/', views.CustomLinkEditView.as_view(), name='customlink_add'),
|
||||||
|
path('custom-links/import/', views.CustomLinkBulkImportView.as_view(), name='customlink_import'),
|
||||||
|
path('custom-links/edit/', views.CustomLinkBulkEditView.as_view(), name='customlink_bulk_edit'),
|
||||||
|
path('custom-links/delete/', views.CustomLinkBulkDeleteView.as_view(), name='customlink_bulk_delete'),
|
||||||
|
path('custom-links/<int:pk>/', views.CustomLinkView.as_view(), name='customlink'),
|
||||||
|
path('custom-links/<int:pk>/edit/', views.CustomLinkEditView.as_view(), name='customlink_edit'),
|
||||||
|
path('custom-links/<int:pk>/delete/', views.CustomLinkDeleteView.as_view(), name='customlink_delete'),
|
||||||
|
path('custom-links/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='customlink_changelog',
|
||||||
|
kwargs={'model': models.CustomLink}),
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
path('tags/', views.TagListView.as_view(), name='tag_list'),
|
path('tags/', views.TagListView.as_view(), name='tag_list'),
|
||||||
@ -27,7 +39,8 @@ urlpatterns = [
|
|||||||
path('tags/<int:pk>/', views.TagView.as_view(), name='tag'),
|
path('tags/<int:pk>/', views.TagView.as_view(), name='tag'),
|
||||||
path('tags/<int:pk>/edit/', views.TagEditView.as_view(), name='tag_edit'),
|
path('tags/<int:pk>/edit/', views.TagEditView.as_view(), name='tag_edit'),
|
||||||
path('tags/<int:pk>/delete/', views.TagDeleteView.as_view(), name='tag_delete'),
|
path('tags/<int:pk>/delete/', views.TagDeleteView.as_view(), name='tag_delete'),
|
||||||
path('tags/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='tag_changelog', kwargs={'model': Tag}),
|
path('tags/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='tag_changelog',
|
||||||
|
kwargs={'model': models.Tag}),
|
||||||
|
|
||||||
# Config contexts
|
# Config contexts
|
||||||
path('config-contexts/', views.ConfigContextListView.as_view(), name='configcontext_list'),
|
path('config-contexts/', views.ConfigContextListView.as_view(), name='configcontext_list'),
|
||||||
@ -37,7 +50,8 @@ urlpatterns = [
|
|||||||
path('config-contexts/<int:pk>/', views.ConfigContextView.as_view(), name='configcontext'),
|
path('config-contexts/<int:pk>/', views.ConfigContextView.as_view(), name='configcontext'),
|
||||||
path('config-contexts/<int:pk>/edit/', views.ConfigContextEditView.as_view(), name='configcontext_edit'),
|
path('config-contexts/<int:pk>/edit/', views.ConfigContextEditView.as_view(), name='configcontext_edit'),
|
||||||
path('config-contexts/<int:pk>/delete/', views.ConfigContextDeleteView.as_view(), name='configcontext_delete'),
|
path('config-contexts/<int:pk>/delete/', views.ConfigContextDeleteView.as_view(), name='configcontext_delete'),
|
||||||
path('config-contexts/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='configcontext_changelog', kwargs={'model': ConfigContext}),
|
path('config-contexts/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='configcontext_changelog',
|
||||||
|
kwargs={'model': models.ConfigContext}),
|
||||||
|
|
||||||
# Image attachments
|
# Image attachments
|
||||||
path('image-attachments/<int:pk>/edit/', views.ImageAttachmentEditView.as_view(), name='imageattachment_edit'),
|
path('image-attachments/<int:pk>/edit/', views.ImageAttachmentEditView.as_view(), name='imageattachment_edit'),
|
||||||
@ -51,7 +65,8 @@ urlpatterns = [
|
|||||||
path('journal-entries/<int:pk>/', views.JournalEntryView.as_view(), name='journalentry'),
|
path('journal-entries/<int:pk>/', views.JournalEntryView.as_view(), name='journalentry'),
|
||||||
path('journal-entries/<int:pk>/edit/', views.JournalEntryEditView.as_view(), name='journalentry_edit'),
|
path('journal-entries/<int:pk>/edit/', views.JournalEntryEditView.as_view(), name='journalentry_edit'),
|
||||||
path('journal-entries/<int:pk>/delete/', views.JournalEntryDeleteView.as_view(), name='journalentry_delete'),
|
path('journal-entries/<int:pk>/delete/', views.JournalEntryDeleteView.as_view(), name='journalentry_delete'),
|
||||||
path('journal-entries/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='journalentry_changelog', kwargs={'model': JournalEntry}),
|
path('journal-entries/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='journalentry_changelog',
|
||||||
|
kwargs={'model': models.JournalEntry}),
|
||||||
|
|
||||||
# Change logging
|
# Change logging
|
||||||
path('changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
|
path('changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
|
||||||
|
@ -63,6 +63,49 @@ class CustomFieldBulkDeleteView(generic.BulkDeleteView):
|
|||||||
table = tables.CustomFieldTable
|
table = tables.CustomFieldTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Custom links
|
||||||
|
#
|
||||||
|
|
||||||
|
class CustomLinkListView(generic.ObjectListView):
|
||||||
|
queryset = CustomLink.objects.all()
|
||||||
|
filterset = filtersets.CustomLinkFilterSet
|
||||||
|
filterset_form = forms.CustomLinkFilterForm
|
||||||
|
table = tables.CustomLinkTable
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkView(generic.ObjectView):
|
||||||
|
queryset = CustomLink.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkEditView(generic.ObjectEditView):
|
||||||
|
queryset = CustomLink.objects.all()
|
||||||
|
model_form = forms.CustomLinkForm
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkDeleteView(generic.ObjectDeleteView):
|
||||||
|
queryset = CustomLink.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkBulkImportView(generic.BulkImportView):
|
||||||
|
queryset = CustomLink.objects.all()
|
||||||
|
model_form = forms.CustomLinkCSVForm
|
||||||
|
table = tables.CustomLinkTable
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkBulkEditView(generic.BulkEditView):
|
||||||
|
queryset = CustomLink.objects.all()
|
||||||
|
filterset = filtersets.CustomLinkFilterSet
|
||||||
|
table = tables.CustomLinkTable
|
||||||
|
form = forms.CustomLinkBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLinkBulkDeleteView(generic.BulkDeleteView):
|
||||||
|
queryset = CustomLink.objects.all()
|
||||||
|
filterset = filtersets.CustomLinkFilterSet
|
||||||
|
table = tables.CustomLinkTable
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Tags
|
# Tags
|
||||||
#
|
#
|
||||||
|
74
netbox/templates/extras/customlink.html
Normal file
74
netbox/templates/extras/customlink.html
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{% extends 'generic/object.html' %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load plugins %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'extras:customlink_list' %}">Cusotm Links</a></li>
|
||||||
|
<li class="breadcrumb-item">{{ object }}</li>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<h5 class="card-header">
|
||||||
|
Custom Link
|
||||||
|
</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table table-hover attr-table">
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Content Type</th>
|
||||||
|
<td>{{ object.content_type }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Name</th>
|
||||||
|
<td>{{ object.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Group Name</th>
|
||||||
|
<td>{{ object.group_name|placeholder }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Weight</th>
|
||||||
|
<td>{{ object.weight }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Button Class</th>
|
||||||
|
<td>{{ object.get_button_class_display }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">New Window</th>
|
||||||
|
<td>
|
||||||
|
{% if object.new_window %}
|
||||||
|
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="mdi mdi-close-thick text-danger" title="No"></i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% plugin_left_page object %}
|
||||||
|
</div>
|
||||||
|
<div class="col col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<h5 class="card-header">
|
||||||
|
Link Text
|
||||||
|
</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<pre>{{ object.link_text }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h5 class="card-header">
|
||||||
|
Link URL
|
||||||
|
</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<pre>{{ object.link_url }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% plugin_right_page object %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -294,6 +294,8 @@ OTHER_MENU = Menu(
|
|||||||
items=(
|
items=(
|
||||||
MenuItem(label="Custom Fields", url="extras:customfield_list",
|
MenuItem(label="Custom Fields", url="extras:customfield_list",
|
||||||
add_url="extras:customfield_add", import_url="extras:customfield_import"),
|
add_url="extras:customfield_add", import_url="extras:customfield_import"),
|
||||||
|
MenuItem(label="Custom Links", url="extras:customlink_list",
|
||||||
|
add_url="extras:customlink_add", import_url="extras:customlink_import"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
MenuGroup(
|
MenuGroup(
|
||||||
|
Loading…
Reference in New Issue
Block a user