diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py
index e5f4faee1e0..f1cfdd1d5d3 100644
--- a/netbox/circuits/views.py
+++ b/netbox/circuits/views.py
@@ -1,10 +1,10 @@
from django.contrib import messages
from django.db import transaction
-from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect, render
from dcim.views import PathTraceView
from netbox.views import generic
+from tenancy.views import ObjectContactsView
from utilities.forms import ConfirmationForm
from utilities.utils import count_related
from utilities.views import register_model_view
@@ -73,6 +73,11 @@ class ProviderBulkDeleteView(generic.BulkDeleteView):
table = tables.ProviderTable
+@register_model_view(Provider, 'contacts')
+class ProviderContactsView(ObjectContactsView):
+ queryset = Provider.objects.all()
+
+
#
# ProviderAccounts
#
@@ -134,6 +139,11 @@ class ProviderAccountBulkDeleteView(generic.BulkDeleteView):
table = tables.ProviderAccountTable
+@register_model_view(ProviderAccount, 'contacts')
+class ProviderAccountContactsView(ObjectContactsView):
+ queryset = ProviderAccount.objects.all()
+
+
#
# Provider networks
#
@@ -389,6 +399,11 @@ def post(self, request, pk):
})
+@register_model_view(Circuit, 'contacts')
+class CircuitContactsView(ObjectContactsView):
+ queryset = Circuit.objects.all()
+
+
#
# Circuit terminations
#
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
index bcbbf17395b..0def4f4a8ae 100644
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -20,6 +20,7 @@
from ipam.models import ASN, IPAddress, Prefix, VLAN, VLANGroup
from ipam.tables import InterfaceVLANTable
from netbox.views import generic
+from tenancy.views import ObjectContactsView
from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.permissions import get_permission_for_model
@@ -267,6 +268,11 @@ class RegionBulkDeleteView(generic.BulkDeleteView):
table = tables.RegionTable
+@register_model_view(Region, 'contacts')
+class RegionContactsView(ObjectContactsView):
+ queryset = Region.objects.all()
+
+
#
# Site groups
#
@@ -342,6 +348,11 @@ class SiteGroupBulkDeleteView(generic.BulkDeleteView):
table = tables.SiteGroupTable
+@register_model_view(SiteGroup, 'contacts')
+class SiteGroupContactsView(ObjectContactsView):
+ queryset = SiteGroup.objects.all()
+
+
#
# Sites
#
@@ -435,6 +446,11 @@ class SiteBulkDeleteView(generic.BulkDeleteView):
table = tables.SiteTable
+@register_model_view(Site, 'contacts')
+class SiteContactsView(ObjectContactsView):
+ queryset = Site.objects.all()
+
+
#
# Locations
#
@@ -523,6 +539,11 @@ class LocationBulkDeleteView(generic.BulkDeleteView):
table = tables.LocationTable
+@register_model_view(Location, 'contacts')
+class LocationContactsView(ObjectContactsView):
+ queryset = Location.objects.all()
+
+
#
# Rack roles
#
@@ -740,6 +761,11 @@ class RackBulkDeleteView(generic.BulkDeleteView):
table = tables.RackTable
+@register_model_view(Rack, 'contacts')
+class RackContactsView(ObjectContactsView):
+ queryset = Rack.objects.all()
+
+
#
# Rack reservations
#
@@ -874,6 +900,11 @@ class ManufacturerBulkDeleteView(generic.BulkDeleteView):
table = tables.ManufacturerTable
+@register_model_view(Manufacturer, 'contacts')
+class ManufacturerContactsView(ObjectContactsView):
+ queryset = Manufacturer.objects.all()
+
+
#
# Device types
#
@@ -2088,6 +2119,11 @@ class DeviceBulkRenameView(generic.BulkRenameView):
table = tables.DeviceTable
+@register_model_view(Device, 'contacts')
+class DeviceContactsView(ObjectContactsView):
+ queryset = Device.objects.all()
+
+
#
# Modules
#
@@ -3469,6 +3505,11 @@ class PowerPanelBulkDeleteView(generic.BulkDeleteView):
table = tables.PowerPanelTable
+@register_model_view(PowerPanel, 'contacts')
+class PowerPanelContactsView(ObjectContactsView):
+ queryset = PowerPanel.objects.all()
+
+
#
# Power feeds
#
diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py
index a49c4aab3de..f26d00bb9d4 100644
--- a/netbox/ipam/views.py
+++ b/netbox/ipam/views.py
@@ -9,6 +9,7 @@
from dcim.filtersets import InterfaceFilterSet
from dcim.models import Interface, Site
from netbox.views import generic
+from tenancy.views import ObjectContactsView
from utilities.utils import count_related
from utilities.views import ViewTab, register_model_view
from virtualization.filtersets import VMInterfaceFilterSet
@@ -1291,6 +1292,11 @@ class L2VPNBulkDeleteView(generic.BulkDeleteView):
table = tables.L2VPNTable
+@register_model_view(L2VPN, 'contacts')
+class L2VPNContactsView(ObjectContactsView):
+ queryset = L2VPN.objects.all()
+
+
#
# L2VPN terminations
#
diff --git a/netbox/templates/circuits/circuit.html b/netbox/templates/circuits/circuit.html
index ee994e95997..a5913e2ad1a 100644
--- a/netbox/templates/circuits/circuit.html
+++ b/netbox/templates/circuits/circuit.html
@@ -70,7 +70,6 @@
{% include 'circuits/inc/circuit_termination.html' with termination=object.termination_a side='A' %}
{% include 'circuits/inc/circuit_termination.html' with termination=object.termination_z side='Z' %}
- {% include 'inc/panels/contacts.html' %}
{% include 'inc/panels/image_attachments.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/circuits/provider.html b/netbox/templates/circuits/provider.html
index 69520217640..58e62ab07cd 100644
--- a/netbox/templates/circuits/provider.html
+++ b/netbox/templates/circuits/provider.html
@@ -54,7 +54,6 @@
{% include 'inc/panels/related_objects.html' %}
{% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/contacts.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/circuits/provideraccount.html b/netbox/templates/circuits/provideraccount.html
index 63344ada1dd..c55663b4a09 100644
--- a/netbox/templates/circuits/provideraccount.html
+++ b/netbox/templates/circuits/provideraccount.html
@@ -38,7 +38,6 @@
{% include 'inc/panels/related_objects.html' %}
{% include 'inc/panels/comments.html' %}
{% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/contacts.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html
index 2c79ab006f7..df6397a38d8 100644
--- a/netbox/templates/dcim/device.html
+++ b/netbox/templates/dcim/device.html
@@ -298,7 +298,6 @@
{% endif %}
- {% include 'inc/panels/contacts.html' %}
{% include 'inc/panels/image_attachments.html' %}
{% if object.rack and object.position %}
diff --git a/netbox/templates/dcim/location.html b/netbox/templates/dcim/location.html
index 193d93f9a85..795aeb35f20 100644
--- a/netbox/templates/dcim/location.html
+++ b/netbox/templates/dcim/location.html
@@ -65,7 +65,6 @@
{% include 'inc/panels/related_objects.html' %}
- {% include 'inc/panels/contacts.html' %}
{% include 'dcim/inc/nonracked_devices.html' %}
{% include 'inc/panels/image_attachments.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/dcim/manufacturer.html b/netbox/templates/dcim/manufacturer.html
index a60b3503c8d..8233b6fc8c6 100644
--- a/netbox/templates/dcim/manufacturer.html
+++ b/netbox/templates/dcim/manufacturer.html
@@ -51,7 +51,6 @@
diff --git a/netbox/templates/dcim/powerpanel.html b/netbox/templates/dcim/powerpanel.html
index af08f30230a..ea9210ba7ec 100644
--- a/netbox/templates/dcim/powerpanel.html
+++ b/netbox/templates/dcim/powerpanel.html
@@ -40,7 +40,6 @@
{% include 'inc/panels/related_objects.html' %}
{% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/contacts.html' %}
{% include 'inc/panels/image_attachments.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html
index 9cb046b4e4b..52b5d4bfec6 100644
--- a/netbox/templates/dcim/rack.html
+++ b/netbox/templates/dcim/rack.html
@@ -191,7 +191,6 @@ Rear
{% include 'inc/panels/related_objects.html' %}
{% include 'dcim/inc/nonracked_devices.html' %}
- {% include 'inc/panels/contacts.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/dcim/region.html b/netbox/templates/dcim/region.html
index 85587e4b5ed..05cc424d7c9 100644
--- a/netbox/templates/dcim/region.html
+++ b/netbox/templates/dcim/region.html
@@ -46,7 +46,6 @@
{% include 'inc/panels/related_objects.html' %}
- {% include 'inc/panels/contacts.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/dcim/site.html b/netbox/templates/dcim/site.html
index d6de8f3cbda..6b802c56dd7 100644
--- a/netbox/templates/dcim/site.html
+++ b/netbox/templates/dcim/site.html
@@ -127,7 +127,6 @@
{% include 'inc/panels/related_objects.html' with filter_name='site_id' %}
- {% include 'inc/panels/contacts.html' %}
diff --git a/netbox/templates/dcim/sitegroup.html b/netbox/templates/dcim/sitegroup.html
index 2cf8e71684a..819022a34de 100644
--- a/netbox/templates/dcim/sitegroup.html
+++ b/netbox/templates/dcim/sitegroup.html
@@ -42,7 +42,6 @@
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/custom_fields.html' %}
- {% include 'inc/panels/contacts.html' %}
{% plugin_left_page object %}
diff --git a/netbox/templates/inc/panels/contacts.html b/netbox/templates/inc/panels/contacts.html
deleted file mode 100644
index 359ad8d7edb..00000000000
--- a/netbox/templates/inc/panels/contacts.html
+++ /dev/null
@@ -1,63 +0,0 @@
-{% load helpers %}
-
-
-
-
- {% with contacts=object.contacts.all %}
- {% if contacts.exists %}
-
-
- | Name |
- Role |
- Priority |
- Phone |
- Email |
- |
-
- {% for contact in contacts %}
-
- | {{ contact.contact|linkify }} |
- {{ contact.role|placeholder }} |
- {{ contact.get_priority_display|placeholder }} |
-
- {% if contact.contact.phone %}
- {{ contact.contact.phone }}
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
- {% if contact.contact.email %}
- {{ contact.contact.email }}
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
-
- {% if perms.tenancy.change_contactassignment %}
-
-
-
- {% endif %}
- {% if perms.tenancy.delete_contactassignment %}
-
-
-
- {% endif %}
- |
-
- {% endfor %}
-
- {% else %}
-
None
- {% endif %}
- {% endwith %}
-
- {% if perms.tenancy.add_contactassignment %}
-
- {% endif %}
-
diff --git a/netbox/templates/ipam/l2vpn.html b/netbox/templates/ipam/l2vpn.html
index 87050eb2686..8896dd6c250 100644
--- a/netbox/templates/ipam/l2vpn.html
+++ b/netbox/templates/ipam/l2vpn.html
@@ -37,7 +37,6 @@
{% plugin_left_page object %}
- {% include 'inc/panels/contacts.html' %}
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/comments.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/tenancy/object_contacts.html b/netbox/templates/tenancy/object_contacts.html
new file mode 100644
index 00000000000..aca63a3793c
--- /dev/null
+++ b/netbox/templates/tenancy/object_contacts.html
@@ -0,0 +1,27 @@
+{% extends base_template %}
+{% load helpers %}
+
+{% block extra_controls %}
+ {% if perms.tenancy.add_contactassignment %}
+
+ Add a contact
+
+ {% endif %}
+{% endblock %}
+
+{% block content %}
+ {% include 'inc/table_controls_htmx.html' with table_modal="ContactTable_config" %}
+
+{% endblock content %}
+
+{% block modals %}
+ {{ block.super }}
+ {% table_config_form table %}
+{% endblock modals %}
diff --git a/netbox/templates/tenancy/tenant.html b/netbox/templates/tenancy/tenant.html
index da48f1ef590..34abe5c01b7 100644
--- a/netbox/templates/tenancy/tenant.html
+++ b/netbox/templates/tenancy/tenant.html
@@ -30,7 +30,6 @@
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
- {% include 'inc/panels/contacts.html' %}
{% plugin_left_page object %}
diff --git a/netbox/templates/virtualization/cluster.html b/netbox/templates/virtualization/cluster.html
index 3dfef108be3..508bca547a9 100644
--- a/netbox/templates/virtualization/cluster.html
+++ b/netbox/templates/virtualization/cluster.html
@@ -84,7 +84,6 @@
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
- {% include 'inc/panels/contacts.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/virtualization/clustergroup.html b/netbox/templates/virtualization/clustergroup.html
index 5104330689b..2496ad08522 100644
--- a/netbox/templates/virtualization/clustergroup.html
+++ b/netbox/templates/virtualization/clustergroup.html
@@ -37,7 +37,6 @@