diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py
index 86d1a3775..aff090f3a 100644
--- a/netbox/ipam/tables/ip.py
+++ b/netbox/ipam/tables/ip.py
@@ -19,14 +19,22 @@ __all__ = (
AVAILABLE_LABEL = mark_safe('Available')
+AGGREGATE_COPY_BUTTON = """
+{% copy_content record.pk prefix="aggregate_" %}
+"""
+
PREFIX_LINK = """
{% if record.pk %}
- {{ record.prefix }}
+ {{ record.prefix }}
{% else %}
{{ record.prefix }}
{% endif %}
"""
+PREFIX_COPY_BUTTON = """
+{% copy_content record.pk prefix="prefix_" %}
+"""
+
PREFIX_LINK_WITH_DEPTH = """
{% load helpers %}
{% if record.depth %}
@@ -40,7 +48,7 @@ PREFIX_LINK_WITH_DEPTH = """
IPADDRESS_LINK = """
{% if record.pk %}
- {{ record.address }}
+ {{ record.address }}
{% elif perms.ipam.add_ipaddress %}
{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available
{% else %}
@@ -48,6 +56,10 @@ IPADDRESS_LINK = """
{% endif %}
"""
+IPADDRESS_COPY_BUTTON = """
+{% copy_content record.pk prefix="ipaddress_" %}
+"""
+
IPADDRESS_ASSIGN_LINK = """
{{ record }}
"""
@@ -99,7 +111,11 @@ class RIRTable(NetBoxTable):
class AggregateTable(TenancyColumnsMixin, NetBoxTable):
prefix = tables.Column(
linkify=True,
- verbose_name='Aggregate'
+ verbose_name='Aggregate',
+ attrs={
+ # Allow the aggregate to be copied to the clipboard
+ 'a': {'id': lambda record: f"aggregate_{record.pk}"}
+ }
)
date_added = tables.DateColumn(
format="Y-m-d",
@@ -116,6 +132,9 @@ class AggregateTable(TenancyColumnsMixin, NetBoxTable):
tags = columns.TagColumn(
url_name='ipam:aggregate_list'
)
+ actions = columns.ActionsColumn(
+ extra_buttons=AGGREGATE_COPY_BUTTON
+ )
class Meta(NetBoxTable.Meta):
model = Aggregate
@@ -242,6 +261,9 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable):
tags = columns.TagColumn(
url_name='ipam:prefix_list'
)
+ actions = columns.ActionsColumn(
+ extra_buttons=PREFIX_COPY_BUTTON
+ )
class Meta(NetBoxTable.Meta):
model = Prefix
@@ -348,6 +370,9 @@ class IPAddressTable(TenancyColumnsMixin, NetBoxTable):
tags = columns.TagColumn(
url_name='ipam:ipaddress_list'
)
+ actions = columns.ActionsColumn(
+ extra_buttons=IPADDRESS_COPY_BUTTON
+ )
class Meta(NetBoxTable.Meta):
model = IPAddress
diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js
index 9642d1585..b62436d75 100644
Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ
diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map
index f86d50148..ed3833f98 100644
Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ
diff --git a/netbox/project-static/src/clipboard.ts b/netbox/project-static/src/clipboard.ts
index a04acba39..46ca5e36c 100644
--- a/netbox/project-static/src/clipboard.ts
+++ b/netbox/project-static/src/clipboard.ts
@@ -2,7 +2,7 @@ import Clipboard from 'clipboard';
import { getElements } from './util';
export function initClipboard(): void {
- for (const element of getElements('a.copy-token', 'button.copy-secret')) {
+ for (const element of getElements('a.copy-content')) {
new Clipboard(element);
}
}
diff --git a/netbox/templates/core/datafile.html b/netbox/templates/core/datafile.html
index 3d79d17e2..785617ae5 100644
--- a/netbox/templates/core/datafile.html
+++ b/netbox/templates/core/datafile.html
@@ -39,9 +39,7 @@
Path |
{{ object.path }}
-
-
-
+ {% copy_content "datafile_path" %}
|
@@ -56,9 +54,7 @@
SHA256 Hash |
{{ object.hash }}
-
-
-
+ {% copy_content "datafile_hash" %}
|
diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html
index b0e67269c..df5209add 100644
--- a/netbox/templates/dcim/device.html
+++ b/netbox/templates/dcim/device.html
@@ -194,12 +194,13 @@
Primary IPv4 |
{% if object.primary_ip4 %}
- {{ object.primary_ip4.address.ip }}
+ {{ object.primary_ip4.address.ip }}
{% if object.primary_ip4.nat_inside %}
(NAT for {{ object.primary_ip4.nat_inside.address.ip }})
{% elif object.primary_ip4.nat_outside.exists %}
(NAT: {% for nat in object.primary_ip4.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %})
{% endif %}
+ {% copy_content "primary_ip4" %}
{% else %}
{{ ''|placeholder }}
{% endif %}
@@ -209,12 +210,13 @@
| Primary IPv6 |
{% if object.primary_ip6 %}
- {{ object.primary_ip6.address.ip }}
+ {{ object.primary_ip6.address.ip }}
{% if object.primary_ip6.nat_inside %}
(NAT for {{ object.primary_ip6.nat_inside.address.ip }})
{% elif object.primary_ip6.nat_outside.exists %}
(NAT: {% for nat in object.primary_ip6.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %})
{% endif %}
+ {% copy_content "primary_ip6" %}
{% else %}
{{ ''|placeholder }}
{% endif %}
diff --git a/netbox/templates/dcim/virtualdevicecontext.html b/netbox/templates/dcim/virtualdevicecontext.html
index d6e3e0c63..1caf05bd2 100644
--- a/netbox/templates/dcim/virtualdevicecontext.html
+++ b/netbox/templates/dcim/virtualdevicecontext.html
@@ -31,13 +31,23 @@
|
Primary IPv4 |
- {{ object.primary_ip4|linkify|placeholder }}
+ {% if object.primary_ip4 %}
+ {{ object.primary_ip4 }}
+ {% copy_content "primary_ip4" %}
+ {% else %}
+ —
+ {% endif %}
|
Primary IPv6 |
- {{ object.primary_ip6|linkify|placeholder }}
+ {% if object.primary_ip6 %}
+ {{ object.primary_ip6 }}
+ {% copy_content "primary_ip6" %}
+ {% else %}
+ —
+ {% endif %}
|
diff --git a/netbox/templates/users/api_token.html b/netbox/templates/users/api_token.html
index 1a9296704..7fd6f064d 100644
--- a/netbox/templates/users/api_token.html
+++ b/netbox/templates/users/api_token.html
@@ -8,7 +8,7 @@
{% if not settings.ALLOW_TOKEN_RETRIEVAL %}
-
Tokens cannot be retrieved at a later time. You must
copy the token value below and store it securely.
+
Tokens cannot be retrieved at a later time. You must
copy the token value below and store it securely.
{% endif %}
@@ -19,9 +19,7 @@
Key |
-
-
-
+ {% copy_content "token_id" %}
{{ key }}
|
diff --git a/netbox/templates/virtualization/virtualmachine.html b/netbox/templates/virtualization/virtualmachine.html
index 51fd8aa80..3d3b498ad 100644
--- a/netbox/templates/virtualization/virtualmachine.html
+++ b/netbox/templates/virtualization/virtualmachine.html
@@ -46,12 +46,13 @@
Primary IPv4 |
{% if object.primary_ip4 %}
- {{ object.primary_ip4.address.ip }}
+ {{ object.primary_ip4.address.ip }}
{% if object.primary_ip4.nat_inside %}
(NAT for {{ object.primary_ip4.nat_inside.address.ip }})
{% elif object.primary_ip4.nat_outside.exists %}
(NAT: {% for nat in object.primary_ip4.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %})
{% endif %}
+ {% copy_content "primary_ip4" %}
{% else %}
{{ ''|placeholder }}
{% endif %}
@@ -61,12 +62,13 @@
| Primary IPv6 |
{% if object.primary_ip6 %}
- {{ object.primary_ip6.address.ip }}
+ {{ object.primary_ip6.address.ip }}
{% if object.primary_ip6.nat_inside %}
(NAT for {{ object.primary_ip6.nat_inside.address.ip }})
{% elif object.primary_ip6.nat_outside.exists %}
(NAT: {% for nat in object.primary_ip6.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %})
{% endif %}
+ {% copy_content "primary_ip6" %}
{% else %}
{{ ''|placeholder }}
{% endif %}
diff --git a/netbox/users/tables.py b/netbox/users/tables.py
index 0f1484887..cea50b10f 100644
--- a/netbox/users/tables.py
+++ b/netbox/users/tables.py
@@ -12,9 +12,7 @@ ALLOWED_IPS = """{{ value|join:", " }}"""
COPY_BUTTON = """
{% if settings.ALLOW_TOKEN_RETRIEVAL %}
-
-
-
+ {% copy_content record.pk prefix="token_" color="success" %}
{% endif %}
"""
diff --git a/netbox/utilities/templates/builtins/copy_content.html b/netbox/utilities/templates/builtins/copy_content.html
new file mode 100644
index 000000000..9025a71a1
--- /dev/null
+++ b/netbox/utilities/templates/builtins/copy_content.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/netbox/utilities/templatetags/builtins/tags.py b/netbox/utilities/templatetags/builtins/tags.py
index f9fe5f4e3..35aec1000 100644
--- a/netbox/utilities/templatetags/builtins/tags.py
+++ b/netbox/utilities/templatetags/builtins/tags.py
@@ -6,6 +6,7 @@ from utilities.utils import dict_to_querydict
__all__ = (
'badge',
'checkmark',
+ 'copy_content',
'customfield_value',
'tag',
)
@@ -79,6 +80,17 @@ def checkmark(value, show_false=True, true='Yes', false='No'):
}
+@register.inclusion_tag('builtins/copy_content.html')
+def copy_content(target, prefix=None, color='primary'):
+ """
+ Display a copy button to copy the content of a field.
+ """
+ return {
+ 'target': f'#{prefix or ""}{target}',
+ 'color': f'btn-{color}'
+ }
+
+
@register.inclusion_tag('builtins/htmx_table.html', takes_context=True)
def htmx_table(context, viewname, return_url=None, **kwargs):
"""
|