mirror of
https://github.com/netbox-community/netbox.git
synced 2026-03-27 11:51:50 +01:00
Closes #8296: Allow disabling custom links
This commit is contained in:
@@ -101,7 +101,7 @@ class CustomLinkSerializer(ValidatedModelSerializer):
|
||||
class Meta:
|
||||
model = CustomLink
|
||||
fields = [
|
||||
'id', 'url', 'display', 'content_type', 'name', 'link_text', 'link_url', 'weight', 'group_name',
|
||||
'id', 'url', 'display', 'content_type', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
|
||||
'button_class', 'new_window',
|
||||
]
|
||||
|
||||
|
||||
@@ -82,7 +82,9 @@ class CustomLinkFilterSet(BaseFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = CustomLink
|
||||
fields = ['id', 'content_type', 'name', 'link_text', 'link_url', 'weight', 'group_name', 'new_window']
|
||||
fields = [
|
||||
'id', 'content_type', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name', 'new_window',
|
||||
]
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
|
||||
@@ -47,6 +47,10 @@ class CustomLinkBulkEditForm(BulkEditForm):
|
||||
limit_choices_to=FeatureQuery('custom_fields'),
|
||||
required=False
|
||||
)
|
||||
enabled = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect()
|
||||
)
|
||||
new_window = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect()
|
||||
|
||||
@@ -51,7 +51,8 @@ class CustomLinkCSVForm(CSVModelForm):
|
||||
class Meta:
|
||||
model = CustomLink
|
||||
fields = (
|
||||
'name', 'content_type', 'weight', 'group_name', 'button_class', 'new_window', 'link_text', 'link_url',
|
||||
'name', 'content_type', 'enabled', 'weight', 'group_name', 'button_class', 'new_window', 'link_text',
|
||||
'link_url',
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -58,15 +58,18 @@ class CustomFieldFilterForm(FilterForm):
|
||||
class CustomLinkFilterForm(FilterForm):
|
||||
field_groups = [
|
||||
['q'],
|
||||
['content_type', 'weight', 'new_window'],
|
||||
['content_type', 'enabled', 'new_window', 'weight'],
|
||||
]
|
||||
content_type = ContentTypeChoiceField(
|
||||
queryset=ContentType.objects.all(),
|
||||
limit_choices_to=FeatureQuery('custom_fields'),
|
||||
required=False
|
||||
)
|
||||
weight = forms.IntegerField(
|
||||
required=False
|
||||
enabled = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=StaticSelect(
|
||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||
)
|
||||
)
|
||||
new_window = forms.NullBooleanField(
|
||||
required=False,
|
||||
@@ -74,6 +77,9 @@ class CustomLinkFilterForm(FilterForm):
|
||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||
)
|
||||
)
|
||||
weight = forms.IntegerField(
|
||||
required=False
|
||||
)
|
||||
|
||||
|
||||
class ExportTemplateFilterForm(FilterForm):
|
||||
|
||||
@@ -53,7 +53,7 @@ class CustomLinkForm(BootstrapMixin, forms.ModelForm):
|
||||
model = CustomLink
|
||||
fields = '__all__'
|
||||
fieldsets = (
|
||||
('Custom Link', ('name', 'content_type', 'weight', 'group_name', 'button_class', 'new_window')),
|
||||
('Custom Link', ('name', 'content_type', 'weight', 'group_name', 'button_class', 'enabled', 'new_window')),
|
||||
('Templates', ('link_text', 'link_url')),
|
||||
)
|
||||
widgets = {
|
||||
|
||||
18
netbox/extras/migrations/0070_customlink_enabled.py
Normal file
18
netbox/extras/migrations/0070_customlink_enabled.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.11 on 2022-01-10 16:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('extras', '0069_custom_object_field'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='customlink',
|
||||
name='enabled',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
||||
@@ -192,6 +192,9 @@ class CustomLink(ChangeLoggedModel):
|
||||
max_length=100,
|
||||
unique=True
|
||||
)
|
||||
enabled = models.BooleanField(
|
||||
default=True
|
||||
)
|
||||
link_text = models.CharField(
|
||||
max_length=500,
|
||||
help_text="Jinja2 template code for link text"
|
||||
|
||||
@@ -73,15 +73,16 @@ class CustomLinkTable(BaseTable):
|
||||
linkify=True
|
||||
)
|
||||
content_type = ContentTypeColumn()
|
||||
enabled = BooleanColumn()
|
||||
new_window = BooleanColumn()
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = CustomLink
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'content_type', 'link_text', 'link_url', 'weight', 'group_name',
|
||||
'pk', 'id', 'name', 'content_type', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
|
||||
'button_class', 'new_window',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'content_type', 'group_name', 'button_class', 'new_window')
|
||||
default_columns = ('pk', 'name', 'content_type', 'enabled', 'group_name', 'button_class', 'new_window')
|
||||
|
||||
|
||||
#
|
||||
|
||||
@@ -36,7 +36,7 @@ def custom_links(context, obj):
|
||||
Render all applicable links for the given object.
|
||||
"""
|
||||
content_type = ContentType.objects.get_for_model(obj)
|
||||
custom_links = CustomLink.objects.filter(content_type=content_type)
|
||||
custom_links = CustomLink.objects.filter(content_type=content_type, enabled=True)
|
||||
if not custom_links:
|
||||
return ''
|
||||
|
||||
|
||||
@@ -139,24 +139,28 @@ class CustomLinkTest(APIViewTestCases.APIViewTestCase):
|
||||
{
|
||||
'content_type': 'dcim.site',
|
||||
'name': 'Custom Link 4',
|
||||
'enabled': True,
|
||||
'link_text': 'Link 4',
|
||||
'link_url': 'http://example.com/?4',
|
||||
},
|
||||
{
|
||||
'content_type': 'dcim.site',
|
||||
'name': 'Custom Link 5',
|
||||
'enabled': True,
|
||||
'link_text': 'Link 5',
|
||||
'link_url': 'http://example.com/?5',
|
||||
},
|
||||
{
|
||||
'content_type': 'dcim.site',
|
||||
'name': 'Custom Link 6',
|
||||
'enabled': False,
|
||||
'link_text': 'Link 6',
|
||||
'link_url': 'http://example.com/?6',
|
||||
},
|
||||
]
|
||||
bulk_update_data = {
|
||||
'new_window': True,
|
||||
'enabled': False,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -167,18 +171,21 @@ class CustomLinkTest(APIViewTestCases.APIViewTestCase):
|
||||
CustomLink(
|
||||
content_type=site_ct,
|
||||
name='Custom Link 1',
|
||||
enabled=True,
|
||||
link_text='Link 1',
|
||||
link_url='http://example.com/?1',
|
||||
),
|
||||
CustomLink(
|
||||
content_type=site_ct,
|
||||
name='Custom Link 2',
|
||||
enabled=True,
|
||||
link_text='Link 2',
|
||||
link_url='http://example.com/?2',
|
||||
),
|
||||
CustomLink(
|
||||
content_type=site_ct,
|
||||
name='Custom Link 3',
|
||||
enabled=False,
|
||||
link_text='Link 3',
|
||||
link_url='http://example.com/?3',
|
||||
),
|
||||
|
||||
@@ -100,6 +100,7 @@ class CustomLinkTestCase(TestCase, BaseFilterSetTests):
|
||||
CustomLink(
|
||||
name='Custom Link 1',
|
||||
content_type=content_types[0],
|
||||
enabled=True,
|
||||
weight=100,
|
||||
new_window=False,
|
||||
link_text='Link 1',
|
||||
@@ -108,6 +109,7 @@ class CustomLinkTestCase(TestCase, BaseFilterSetTests):
|
||||
CustomLink(
|
||||
name='Custom Link 2',
|
||||
content_type=content_types[1],
|
||||
enabled=True,
|
||||
weight=200,
|
||||
new_window=False,
|
||||
link_text='Link 1',
|
||||
@@ -116,6 +118,7 @@ class CustomLinkTestCase(TestCase, BaseFilterSetTests):
|
||||
CustomLink(
|
||||
name='Custom Link 3',
|
||||
content_type=content_types[2],
|
||||
enabled=False,
|
||||
weight=300,
|
||||
new_window=True,
|
||||
link_text='Link 1',
|
||||
@@ -136,6 +139,12 @@ class CustomLinkTestCase(TestCase, BaseFilterSetTests):
|
||||
params = {'weight': [100, 200]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_enabled(self):
|
||||
params = {'enabled': True}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'enabled': False}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
def test_new_window(self):
|
||||
params = {'new_window': False}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
@@ -59,14 +59,15 @@ class CustomLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
|
||||
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'),
|
||||
CustomLink(name='Custom Link 1', content_type=site_ct, enabled=True, link_text='Link 1', link_url='http://example.com/?1'),
|
||||
CustomLink(name='Custom Link 2', content_type=site_ct, enabled=True, link_text='Link 2', link_url='http://example.com/?2'),
|
||||
CustomLink(name='Custom Link 3', content_type=site_ct, enabled=False, link_text='Link 3', link_url='http://example.com/?3'),
|
||||
))
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'Custom Link X',
|
||||
'content_type': site_ct.pk,
|
||||
'enabled': False,
|
||||
'weight': 100,
|
||||
'button_class': CustomLinkButtonClassChoices.DEFAULT,
|
||||
'link_text': 'Link X',
|
||||
@@ -74,14 +75,15 @@ class CustomLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"name,content_type,weight,button_class,link_text,link_url",
|
||||
"Custom Link 4,dcim.site,100,blue,Link 4,http://exmaple.com/?4",
|
||||
"Custom Link 5,dcim.site,100,blue,Link 5,http://exmaple.com/?5",
|
||||
"Custom Link 6,dcim.site,100,blue,Link 6,http://exmaple.com/?6",
|
||||
"name,content_type,enabled,weight,button_class,link_text,link_url",
|
||||
"Custom Link 4,dcim.site,True,100,blue,Link 4,http://exmaple.com/?4",
|
||||
"Custom Link 5,dcim.site,True,100,blue,Link 5,http://exmaple.com/?5",
|
||||
"Custom Link 6,dcim.site,False,100,blue,Link 6,http://exmaple.com/?6",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'button_class': CustomLinkButtonClassChoices.CYAN,
|
||||
'enabled': False,
|
||||
'weight': 200,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
<th scope="row">Content Type</th>
|
||||
<td>{{ object.content_type }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Enabled</th>
|
||||
<td>{% checkmark object.enabled %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Group Name</th>
|
||||
<td>{{ object.group_name|placeholder }}</td>
|
||||
|
||||
@@ -35,15 +35,16 @@ class BaseTable(tables.Table):
|
||||
if extra_columns is None:
|
||||
extra_columns = []
|
||||
|
||||
# Add custom field columns
|
||||
obj_type = ContentType.objects.get_for_model(self._meta.model)
|
||||
cf_columns = [
|
||||
(f'cf_{cf.name}', columns.CustomFieldColumn(cf)) for cf in CustomField.objects.filter(content_types=obj_type)
|
||||
]
|
||||
cl_columns = [
|
||||
(f'cl_{cl.name}', columns.CustomLinkColumn(cl)) for cl in CustomLink.objects.filter(content_type=obj_type)
|
||||
]
|
||||
extra_columns.extend([*cf_columns, *cl_columns])
|
||||
# Add custom field & custom link columns
|
||||
content_type = ContentType.objects.get_for_model(self._meta.model)
|
||||
custom_fields = CustomField.objects.filter(content_types=content_type)
|
||||
extra_columns.extend([
|
||||
(f'cf_{cf.name}', columns.CustomFieldColumn(cf)) for cf in custom_fields
|
||||
])
|
||||
custom_links = CustomLink.objects.filter(content_type=content_type, enabled=True)
|
||||
extra_columns.extend([
|
||||
(f'cl_{cl.name}', columns.CustomLinkColumn(cl)) for cl in custom_links
|
||||
])
|
||||
|
||||
super().__init__(*args, extra_columns=extra_columns, **kwargs)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user