mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-09 00:58:16 -06:00
Merge branch 'netbox-community:develop' into script_rq_queue_name
This commit is contained in:
commit
e2f2799f73
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,3 +28,4 @@ netbox.pid
|
|||||||
.idea
|
.idea
|
||||||
.coverage
|
.coverage
|
||||||
.vscode
|
.vscode
|
||||||
|
.python-version
|
||||||
|
@ -24,7 +24,7 @@ If you believe you've uncovered a security vulnerability and wish to report it c
|
|||||||
|
|
||||||
Please note that we **DO NOT** accept reports generated by automated tooling which merely suggest that a file or file(s) _may_ be vulnerable under certain conditions, as these are most often innocuous.
|
Please note that we **DO NOT** accept reports generated by automated tooling which merely suggest that a file or file(s) _may_ be vulnerable under certain conditions, as these are most often innocuous.
|
||||||
|
|
||||||
If you believe that you've found a vulnerability which meets all of these conditions, please [submit a draft security advisory](https://github.com/netbox-community/netbox/security/advisories/new) on GitHub, or email a brief description of the suspected bug and instructions for reproduction to **security@netbox.dev**. For any security concerns regarding NetBox deployed via Docker, please see the [netbox-docker](https://github.com/netbox-community/netbox-docker) project.
|
If you believe that you've found a vulnerability which meets all of these conditions, please [submit a draft security advisory](https://github.com/netbox-community/netbox/security/advisories/new) on GitHub. For any security concerns regarding NetBox deployed via Docker, please see the [netbox-docker](https://github.com/netbox-community/netbox-docker) project.
|
||||||
|
|
||||||
### Bug Bounties
|
### Bug Bounties
|
||||||
|
|
||||||
|
@ -2,6 +2,26 @@
|
|||||||
|
|
||||||
## v4.0.6 (FUTURE)
|
## v4.0.6 (FUTURE)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [#15348](https://github.com/netbox-community/netbox/issues/15348) - Show saved filters alongside quick search on object list views
|
||||||
|
* [#15794](https://github.com/netbox-community/netbox/issues/15794) - Dynamically populate related objects in UI views
|
||||||
|
* [#16256](https://github.com/netbox-community/netbox/issues/16256) - Enable alphabetical ordering of bookmarks on dashboard
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#13925](https://github.com/netbox-community/netbox/issues/13925) - Fix support for "zulu" (UTC) timestamps for custom fields
|
||||||
|
* [#14829](https://github.com/netbox-community/netbox/issues/14829) - Fix support for simple conditions (without AND/OR) in event rules
|
||||||
|
* [#16143](https://github.com/netbox-community/netbox/issues/16143) - Display timestamps in tables in the configured timezone
|
||||||
|
* [#16416](https://github.com/netbox-community/netbox/issues/16416) - Retain dark/light mode toggle on mobile view
|
||||||
|
* [#16444](https://github.com/netbox-community/netbox/issues/16444) - Disable ordering circuits list by A/Z termination
|
||||||
|
* [#16450](https://github.com/netbox-community/netbox/issues/16450) - Searching for rack unit in form dropdown should be case-insensitive
|
||||||
|
* [#16452](https://github.com/netbox-community/netbox/issues/16452) - Fix sizing of buttons within object attribute panels
|
||||||
|
* [#16454](https://github.com/netbox-community/netbox/issues/16454) - Address DNS lookup bug in `django-debug-toolbar
|
||||||
|
* [#16460](https://github.com/netbox-community/netbox/issues/16460) - Omit spaces from telephone number URLs
|
||||||
|
* [#16512](https://github.com/netbox-community/netbox/issues/16512) - Restore a user's preferred language (if any) on login
|
||||||
|
* [#16542](https://github.com/netbox-community/netbox/issues/16542) - Fix bulk form operations when HTMX is enabled
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## v4.0.5 (2024-06-06)
|
## v4.0.5 (2024-06-06)
|
||||||
|
@ -104,10 +104,16 @@ class LoginView(View):
|
|||||||
# Ensure the user has a UserConfig defined. (This should normally be handled by
|
# Ensure the user has a UserConfig defined. (This should normally be handled by
|
||||||
# create_userconfig() on user creation.)
|
# create_userconfig() on user creation.)
|
||||||
if not hasattr(request.user, 'config'):
|
if not hasattr(request.user, 'config'):
|
||||||
config = get_config()
|
request.user.config = get_config()
|
||||||
UserConfig(user=request.user, data=config.DEFAULT_USER_PREFERENCES).save()
|
UserConfig(user=request.user, data=request.user.config.DEFAULT_USER_PREFERENCES).save()
|
||||||
|
|
||||||
return self.redirect_to_next(request, logger)
|
response = self.redirect_to_next(request, logger)
|
||||||
|
|
||||||
|
# Set the user's preferred language (if any)
|
||||||
|
if language := request.user.config.get('locale.language'):
|
||||||
|
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Login form validation failed for username: {form['username'].value()}")
|
logger.debug(f"Login form validation failed for username: {form['username'].value()}")
|
||||||
@ -145,9 +151,10 @@ class LogoutView(View):
|
|||||||
logger.info(f"User {username} has logged out")
|
logger.info(f"User {username} has logged out")
|
||||||
messages.info(request, "You have logged out.")
|
messages.info(request, "You have logged out.")
|
||||||
|
|
||||||
# Delete session key cookie (if set) upon logout
|
# Delete session key & language cookies (if set) upon logout
|
||||||
response = HttpResponseRedirect(resolve_url(settings.LOGOUT_REDIRECT_URL))
|
response = HttpResponseRedirect(resolve_url(settings.LOGOUT_REDIRECT_URL))
|
||||||
response.delete_cookie('session_key')
|
response.delete_cookie('session_key')
|
||||||
|
response.delete_cookie(settings.LANGUAGE_COOKIE_NAME)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -63,10 +63,12 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
|||||||
status = columns.ChoiceFieldColumn()
|
status = columns.ChoiceFieldColumn()
|
||||||
termination_a = tables.TemplateColumn(
|
termination_a = tables.TemplateColumn(
|
||||||
template_code=CIRCUITTERMINATION_LINK,
|
template_code=CIRCUITTERMINATION_LINK,
|
||||||
|
orderable=False,
|
||||||
verbose_name=_('Side A')
|
verbose_name=_('Side A')
|
||||||
)
|
)
|
||||||
termination_z = tables.TemplateColumn(
|
termination_z = tables.TemplateColumn(
|
||||||
template_code=CIRCUITTERMINATION_LINK,
|
template_code=CIRCUITTERMINATION_LINK,
|
||||||
|
orderable=False,
|
||||||
verbose_name=_('Side Z')
|
verbose_name=_('Side Z')
|
||||||
)
|
)
|
||||||
commit_rate = CommitRateColumn(
|
commit_rate = CommitRateColumn(
|
||||||
|
@ -219,9 +219,9 @@ class RackViewSet(NetBoxModelViewSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Enable filtering rack units by ID
|
# Enable filtering rack units by ID
|
||||||
q = data['q']
|
if q := data['q']:
|
||||||
if q:
|
q = q.lower()
|
||||||
elevation = [u for u in elevation if q in str(u['id']) or q in str(u['name'])]
|
elevation = [u for u in elevation if q in str(u['id']) or q in str(u['name']).lower()]
|
||||||
|
|
||||||
page = self.paginate_queryset(elevation)
|
page = self.paginate_queryset(elevation)
|
||||||
if page is not None:
|
if page is not None:
|
||||||
|
@ -465,7 +465,10 @@ class DeviceForm(TenancyForm, NetBoxModelForm):
|
|||||||
label=_('Cluster'),
|
label=_('Cluster'),
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
selector=True
|
selector=True,
|
||||||
|
query_params={
|
||||||
|
'site_id': ['$site', 'null']
|
||||||
|
},
|
||||||
)
|
)
|
||||||
comments = CommentField()
|
comments = CommentField()
|
||||||
local_context_data = JSONField(
|
local_context_data = JSONField(
|
||||||
|
@ -8,6 +8,7 @@ from dcim.models import *
|
|||||||
from extras.models import CustomField
|
from extras.models import CustomField
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.data import drange
|
from utilities.data import drange
|
||||||
|
from virtualization.models import Cluster, ClusterType
|
||||||
|
|
||||||
|
|
||||||
class LocationTestCase(TestCase):
|
class LocationTestCase(TestCase):
|
||||||
@ -533,6 +534,36 @@ class DeviceTestCase(TestCase):
|
|||||||
device2.full_clean()
|
device2.full_clean()
|
||||||
device2.save()
|
device2.save()
|
||||||
|
|
||||||
|
def test_device_mismatched_site_cluster(self):
|
||||||
|
cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
|
||||||
|
Cluster.objects.create(name='Cluster 1', type=cluster_type)
|
||||||
|
|
||||||
|
sites = (
|
||||||
|
Site(name='Site 1', slug='site-1'),
|
||||||
|
Site(name='Site 2', slug='site-2'),
|
||||||
|
)
|
||||||
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
|
clusters = (
|
||||||
|
Cluster(name='Cluster 1', type=cluster_type, site=sites[0]),
|
||||||
|
Cluster(name='Cluster 2', type=cluster_type, site=sites[1]),
|
||||||
|
Cluster(name='Cluster 3', type=cluster_type, site=None),
|
||||||
|
)
|
||||||
|
Cluster.objects.bulk_create(clusters)
|
||||||
|
|
||||||
|
device_type = DeviceType.objects.first()
|
||||||
|
device_role = DeviceRole.objects.first()
|
||||||
|
|
||||||
|
# Device with site only should pass
|
||||||
|
Device(name='device1', site=sites[0], device_type=device_type, role=device_role).full_clean()
|
||||||
|
|
||||||
|
# Device with site, cluster non-site should pass
|
||||||
|
Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[2]).full_clean()
|
||||||
|
|
||||||
|
# Device with mismatched site & cluster should fail
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[1]).full_clean()
|
||||||
|
|
||||||
|
|
||||||
class CableTestCase(TestCase):
|
class CableTestCase(TestCase):
|
||||||
|
|
||||||
|
@ -660,6 +660,10 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
|||||||
# Validate date & time
|
# Validate date & time
|
||||||
elif self.type == CustomFieldTypeChoices.TYPE_DATETIME:
|
elif self.type == CustomFieldTypeChoices.TYPE_DATETIME:
|
||||||
if type(value) is not datetime:
|
if type(value) is not datetime:
|
||||||
|
# Work around UTC issue for Python < 3.11; see
|
||||||
|
# https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat
|
||||||
|
if type(value) is str and value.endswith('Z'):
|
||||||
|
value = f'{value[:-1]}+00:00'
|
||||||
try:
|
try:
|
||||||
datetime.fromisoformat(value)
|
datetime.fromisoformat(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
BIN
netbox/project-static/dist/netbox.css
vendored
BIN
netbox/project-static/dist/netbox.css
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js
vendored
BIN
netbox/project-static/dist/netbox.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js.map
vendored
BIN
netbox/project-static/dist/netbox.js.map
vendored
Binary file not shown.
30
netbox/project-static/src/forms/savedFiltersSelect.ts
Normal file
30
netbox/project-static/src/forms/savedFiltersSelect.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { isTruthy } from '../util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle saved filter change event.
|
||||||
|
*
|
||||||
|
* @param event "change" event for the saved filter select
|
||||||
|
*/
|
||||||
|
function handleSavedFilterChange(event: Event): void {
|
||||||
|
const savedFilter = event.currentTarget as HTMLSelectElement;
|
||||||
|
let baseUrl = savedFilter.baseURI.split('?')[0];
|
||||||
|
const preFilter = '?';
|
||||||
|
|
||||||
|
const selectedOptions = Array.from(savedFilter.options)
|
||||||
|
.filter(option => option.selected)
|
||||||
|
.map(option => `filter_id=${option.value}`)
|
||||||
|
.join('&');
|
||||||
|
|
||||||
|
baseUrl += `${preFilter}${selectedOptions}`;
|
||||||
|
document.location.href = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initSavedFilterSelect(): void {
|
||||||
|
const divResults = document.getElementById('results');
|
||||||
|
if (isTruthy(divResults)) {
|
||||||
|
const savedFilterSelect = document.getElementById('id_filter_id');
|
||||||
|
if (isTruthy(savedFilterSelect)) {
|
||||||
|
savedFilterSelect.addEventListener('change', handleSavedFilterChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import { initSideNav } from './sidenav';
|
|||||||
import { initDashboard } from './dashboard';
|
import { initDashboard } from './dashboard';
|
||||||
import { initRackElevation } from './racks';
|
import { initRackElevation } from './racks';
|
||||||
import { initHtmx } from './htmx';
|
import { initHtmx } from './htmx';
|
||||||
|
import { initSavedFilterSelect } from './forms/savedFiltersSelect';
|
||||||
|
|
||||||
function initDocument(): void {
|
function initDocument(): void {
|
||||||
for (const init of [
|
for (const init of [
|
||||||
@ -31,6 +32,7 @@ function initDocument(): void {
|
|||||||
initDashboard,
|
initDashboard,
|
||||||
initRackElevation,
|
initRackElevation,
|
||||||
initHtmx,
|
initHtmx,
|
||||||
|
initSavedFilterSelect,
|
||||||
]) {
|
]) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
// Overrides of external libraries
|
// Overrides of external libraries
|
||||||
@import 'overrides/bootstrap';
|
@import 'overrides/bootstrap';
|
||||||
@import 'overrides/tabler';
|
@import 'overrides/tabler';
|
||||||
|
@import 'overrides/tomselect';
|
||||||
|
|
||||||
// Transitional styling to ease migration of templates from NetBox v3.x
|
// Transitional styling to ease migration of templates from NetBox v3.x
|
||||||
@import 'transitional/badges';
|
@import 'transitional/badges';
|
||||||
|
8
netbox/project-static/styles/overrides/_tomselect.scss
Normal file
8
netbox/project-static/styles/overrides/_tomselect.scss
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.ts-wrapper.multi {
|
||||||
|
.ts-control {
|
||||||
|
padding: 7px 7px 3px 7px;
|
||||||
|
div {
|
||||||
|
margin: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,7 @@ Blocks:
|
|||||||
|
|
||||||
{# User menu (mobile view) #}
|
{# User menu (mobile view) #}
|
||||||
<div class="navbar-nav flex-row d-lg-none">
|
<div class="navbar-nav flex-row d-lg-none">
|
||||||
|
{% include 'inc/light_toggle.html' %}
|
||||||
{% include 'inc/user_menu.html' %}
|
{% include 'inc/user_menu.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -52,14 +53,7 @@ Blocks:
|
|||||||
|
|
||||||
<div class="navbar-nav flex-row align-items-center order-md-last">
|
<div class="navbar-nav flex-row align-items-center order-md-last">
|
||||||
{# Dark/light mode toggle #}
|
{# Dark/light mode toggle #}
|
||||||
<div class="d-none d-md-flex">
|
{% include 'inc/light_toggle.html' %}
|
||||||
<button class="btn color-mode-toggle hide-theme-dark" title="{% trans "Enable dark mode" %}" data-bs-toggle="tooltip" data-bs-placement="bottom">
|
|
||||||
<i class="mdi mdi-lightbulb"></i>
|
|
||||||
</button>
|
|
||||||
<button class="btn color-mode-toggle hide-theme-light" title="{% trans "Enable light mode" %}" data-bs-toggle="tooltip" data-bs-placement="bottom">
|
|
||||||
<i class="mdi mdi-lightbulb-on"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# User menu #}
|
{# User menu #}
|
||||||
{% include 'inc/user_menu.html' %}
|
{% include 'inc/user_menu.html' %}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Rack" %}</th>
|
<th scope="row">{% trans "Rack" %}</th>
|
||||||
<td class="d-flex justify-content-between">
|
<td class="d-flex justify-content-between align-items-start">
|
||||||
{% if object.rack %}
|
{% if object.rack %}
|
||||||
{{ object.rack|linkify }}
|
{{ object.rack|linkify }}
|
||||||
<a href="{{ object.rack.get_absolute_url }}?device={{ object.pk }}" class="btn btn-primary btn-sm d-print-none" title="{% trans "Highlight device in rack" %}">
|
<a href="{{ object.rack.get_absolute_url }}?device={{ object.pk }}" class="btn btn-primary btn-sm d-print-none" title="{% trans "Highlight device in rack" %}">
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Physical Address" %}</th>
|
<th scope="row">{% trans "Physical Address" %}</th>
|
||||||
<td class="d-flex justify-content-between">
|
<td class="d-flex justify-content-between align-items-start">
|
||||||
{% if object.physical_address %}
|
{% if object.physical_address %}
|
||||||
<span>{{ object.physical_address|linebreaksbr }}</span>
|
<span>{{ object.physical_address|linebreaksbr }}</span>
|
||||||
{% if config.MAPS_URL %}
|
{% if config.MAPS_URL %}
|
||||||
|
10
netbox/templates/inc/light_toggle.html
Normal file
10
netbox/templates/inc/light_toggle.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<div class="d-flex">
|
||||||
|
<button class="btn color-mode-toggle hide-theme-dark" title="{% trans "Enable dark mode" %}" data-bs-toggle="tooltip" data-bs-placement="bottom">
|
||||||
|
<i class="mdi mdi-lightbulb"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn color-mode-toggle hide-theme-light" title="{% trans "Enable light mode" %}" data-bs-toggle="tooltip" data-bs-placement="bottom">
|
||||||
|
<i class="mdi mdi-lightbulb-on"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
@ -1,25 +1,37 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3" id="results">
|
||||||
<div class="col-auto d-print-none">
|
<div class="col-auto d-print-none">
|
||||||
<div class="input-group input-group-flat me-2 quicksearch" hx-disinherit="hx-select hx-swap">
|
<div class="input-group input-group-flat me-2 quicksearch" hx-disinherit="hx-select hx-swap">
|
||||||
<input type="search" results="5" name="q" id="quicksearch" class="form-control px-2 py-1" placeholder="Quick search"
|
<input type="search" results="5" name="q" id="quicksearch" class="form-control" placeholder="{% trans "Quick search" %}"
|
||||||
hx-get="{{ request.full_path }}" hx-target="#object_list" hx-trigger="keyup changed delay:500ms, search" />
|
hx-get="{{ request.full_path }}" hx-target="#object_list" hx-trigger="keyup changed delay:500ms, search"/>
|
||||||
<span class="input-group-text py-1">
|
<span class="input-group-text py-1">
|
||||||
<a href="#" id="quicksearch_clear" class="invisible text-secondary"><i class="mdi mdi-close-circle"></i></a>
|
<a href="#" id="quicksearch_clear" class="invisible text-secondary"><i class="mdi mdi-close-circle"></i></a>
|
||||||
</span>
|
</span>
|
||||||
{% block extra_table_controls %}{% endblock %}
|
{% block extra_table_controls %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-auto d-print-none">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<i class="mdi mdi-filter" title="{% trans "Saved filter" %}"></i>
|
||||||
|
</div>
|
||||||
|
{{ filter_form.filter_id }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-auto ms-auto d-print-none">
|
<div class="col-auto ms-auto d-print-none">
|
||||||
{% if request.user.is_authenticated and table_modal %}
|
{% if request.user.is_authenticated and table_modal %}
|
||||||
<div class="table-configure input-group">
|
<div class="table-configure input-group">
|
||||||
<button type="button" data-bs-toggle="modal" title="{% trans "Configure Table" %}" data-bs-target="#{{ table_modal }}"
|
<button type="button" data-bs-toggle="modal" title="{% trans "Configure Table" %}"
|
||||||
class="btn">
|
data-bs-target="#{{ table_modal }}"
|
||||||
|
class="btn">
|
||||||
<i class="mdi mdi-cog"></i> {% trans "Configure Table" %}
|
<i class="mdi mdi-cog"></i> {% trans "Configure Table" %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-06-12 05:01+0000\n"
|
"POT-Creation-Date: 2024-06-19 05:02+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -58,7 +58,7 @@ msgstr ""
|
|||||||
msgid "Allowed IPs"
|
msgid "Allowed IPs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/account/views.py:197
|
#: netbox/account/views.py:204
|
||||||
msgid "Your preferences have been updated."
|
msgid "Your preferences have been updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ msgstr ""
|
|||||||
#: netbox/circuits/forms/filtersets.py:207
|
#: netbox/circuits/forms/filtersets.py:207
|
||||||
#: netbox/circuits/forms/model_forms.py:136
|
#: netbox/circuits/forms/model_forms.py:136
|
||||||
#: netbox/circuits/forms/model_forms.py:152
|
#: netbox/circuits/forms/model_forms.py:152
|
||||||
#: netbox/circuits/tables/circuits.py:105 netbox/dcim/forms/bulk_edit.py:167
|
#: netbox/circuits/tables/circuits.py:107 netbox/dcim/forms/bulk_edit.py:167
|
||||||
#: netbox/dcim/forms/bulk_edit.py:239 netbox/dcim/forms/bulk_edit.py:575
|
#: netbox/dcim/forms/bulk_edit.py:239 netbox/dcim/forms/bulk_edit.py:575
|
||||||
#: netbox/dcim/forms/bulk_edit.py:771 netbox/dcim/forms/bulk_import.py:130
|
#: netbox/dcim/forms/bulk_edit.py:771 netbox/dcim/forms/bulk_import.py:130
|
||||||
#: netbox/dcim/forms/bulk_import.py:184 netbox/dcim/forms/bulk_import.py:257
|
#: netbox/dcim/forms/bulk_import.py:184 netbox/dcim/forms/bulk_import.py:257
|
||||||
@ -308,7 +308,7 @@ msgstr ""
|
|||||||
#: netbox/circuits/forms/filtersets.py:212
|
#: netbox/circuits/forms/filtersets.py:212
|
||||||
#: netbox/circuits/forms/model_forms.py:109
|
#: netbox/circuits/forms/model_forms.py:109
|
||||||
#: netbox/circuits/forms/model_forms.py:131
|
#: netbox/circuits/forms/model_forms.py:131
|
||||||
#: netbox/circuits/tables/circuits.py:96 netbox/dcim/forms/connections.py:71
|
#: netbox/circuits/tables/circuits.py:98 netbox/dcim/forms/connections.py:71
|
||||||
#: netbox/templates/circuits/circuit.html:15
|
#: netbox/templates/circuits/circuit.html:15
|
||||||
#: netbox/templates/circuits/circuittermination.html:19
|
#: netbox/templates/circuits/circuittermination.html:19
|
||||||
#: netbox/templates/dcim/inc/cable_termination.html:55
|
#: netbox/templates/dcim/inc/cable_termination.html:55
|
||||||
@ -325,7 +325,7 @@ msgstr ""
|
|||||||
#: netbox/circuits/tables/providers.py:33 netbox/dcim/forms/bulk_edit.py:127
|
#: netbox/circuits/tables/providers.py:33 netbox/dcim/forms/bulk_edit.py:127
|
||||||
#: netbox/dcim/forms/filtersets.py:188 netbox/dcim/forms/model_forms.py:122
|
#: netbox/dcim/forms/filtersets.py:188 netbox/dcim/forms/model_forms.py:122
|
||||||
#: netbox/dcim/tables/sites.py:94 netbox/ipam/models/asns.py:126
|
#: netbox/dcim/tables/sites.py:94 netbox/ipam/models/asns.py:126
|
||||||
#: netbox/ipam/tables/asn.py:27 netbox/ipam/views.py:219
|
#: netbox/ipam/tables/asn.py:27 netbox/ipam/views.py:210
|
||||||
#: netbox/netbox/navigation/menu.py:159 netbox/netbox/navigation/menu.py:162
|
#: netbox/netbox/navigation/menu.py:159 netbox/netbox/navigation/menu.py:162
|
||||||
#: netbox/templates/circuits/provider.html:23
|
#: netbox/templates/circuits/provider.html:23
|
||||||
msgid "ASNs"
|
msgid "ASNs"
|
||||||
@ -469,7 +469,7 @@ msgstr ""
|
|||||||
#: netbox/circuits/forms/model_forms.py:45
|
#: netbox/circuits/forms/model_forms.py:45
|
||||||
#: netbox/circuits/forms/model_forms.py:59
|
#: netbox/circuits/forms/model_forms.py:59
|
||||||
#: netbox/circuits/forms/model_forms.py:91
|
#: netbox/circuits/forms/model_forms.py:91
|
||||||
#: netbox/circuits/tables/circuits.py:56 netbox/circuits/tables/circuits.py:100
|
#: netbox/circuits/tables/circuits.py:56 netbox/circuits/tables/circuits.py:102
|
||||||
#: netbox/circuits/tables/providers.py:72
|
#: netbox/circuits/tables/providers.py:72
|
||||||
#: netbox/circuits/tables/providers.py:103
|
#: netbox/circuits/tables/providers.py:103
|
||||||
#: netbox/templates/circuits/circuit.html:18
|
#: netbox/templates/circuits/circuit.html:18
|
||||||
@ -748,7 +748,7 @@ msgstr ""
|
|||||||
#: netbox/circuits/forms/bulk_edit.py:191
|
#: netbox/circuits/forms/bulk_edit.py:191
|
||||||
#: netbox/circuits/forms/bulk_edit.py:215
|
#: netbox/circuits/forms/bulk_edit.py:215
|
||||||
#: netbox/circuits/forms/model_forms.py:153
|
#: netbox/circuits/forms/model_forms.py:153
|
||||||
#: netbox/circuits/tables/circuits.py:109
|
#: netbox/circuits/tables/circuits.py:111
|
||||||
#: netbox/templates/circuits/inc/circuit_termination_fields.html:62
|
#: netbox/templates/circuits/inc/circuit_termination_fields.html:62
|
||||||
#: netbox/templates/circuits/providernetwork.html:17
|
#: netbox/templates/circuits/providernetwork.html:17
|
||||||
msgid "Provider Network"
|
msgid "Provider Network"
|
||||||
@ -895,7 +895,7 @@ msgstr ""
|
|||||||
#: netbox/dcim/forms/filtersets.py:653 netbox/dcim/forms/filtersets.py:1010
|
#: netbox/dcim/forms/filtersets.py:653 netbox/dcim/forms/filtersets.py:1010
|
||||||
#: netbox/netbox/navigation/menu.py:44 netbox/netbox/navigation/menu.py:46
|
#: netbox/netbox/navigation/menu.py:44 netbox/netbox/navigation/menu.py:46
|
||||||
#: netbox/tenancy/forms/filtersets.py:42 netbox/tenancy/tables/columns.py:70
|
#: netbox/tenancy/forms/filtersets.py:42 netbox/tenancy/tables/columns.py:70
|
||||||
#: netbox/tenancy/tables/contacts.py:25 netbox/tenancy/views.py:19
|
#: netbox/tenancy/tables/contacts.py:25 netbox/tenancy/views.py:18
|
||||||
#: netbox/virtualization/forms/filtersets.py:37
|
#: netbox/virtualization/forms/filtersets.py:37
|
||||||
#: netbox/virtualization/forms/filtersets.py:48
|
#: netbox/virtualization/forms/filtersets.py:48
|
||||||
#: netbox/virtualization/forms/filtersets.py:106
|
#: netbox/virtualization/forms/filtersets.py:106
|
||||||
@ -1328,21 +1328,21 @@ msgstr ""
|
|||||||
msgid "Circuit ID"
|
msgid "Circuit ID"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/circuits/tables/circuits.py:66
|
#: netbox/circuits/tables/circuits.py:67
|
||||||
#: netbox/wireless/forms/model_forms.py:160
|
#: netbox/wireless/forms/model_forms.py:160
|
||||||
msgid "Side A"
|
msgid "Side A"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/circuits/tables/circuits.py:70
|
#: netbox/circuits/tables/circuits.py:72
|
||||||
msgid "Side Z"
|
msgid "Side Z"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/circuits/tables/circuits.py:73
|
#: netbox/circuits/tables/circuits.py:75
|
||||||
#: netbox/templates/circuits/circuit.html:55
|
#: netbox/templates/circuits/circuit.html:55
|
||||||
msgid "Commit Rate"
|
msgid "Commit Rate"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/circuits/tables/circuits.py:76 netbox/circuits/tables/providers.py:48
|
#: netbox/circuits/tables/circuits.py:78 netbox/circuits/tables/providers.py:48
|
||||||
#: netbox/circuits/tables/providers.py:82
|
#: netbox/circuits/tables/providers.py:82
|
||||||
#: netbox/circuits/tables/providers.py:107 netbox/dcim/tables/devices.py:1001
|
#: netbox/circuits/tables/providers.py:107 netbox/dcim/tables/devices.py:1001
|
||||||
#: netbox/dcim/tables/devicetypes.py:92 netbox/dcim/tables/modules.py:29
|
#: netbox/dcim/tables/devicetypes.py:92 netbox/dcim/tables/modules.py:29
|
||||||
@ -2067,8 +2067,8 @@ msgstr ""
|
|||||||
msgid "No workers found"
|
msgid "No workers found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/views.py:335 netbox/core/views.py:378 netbox/core/views.py:401
|
#: netbox/core/views.py:331 netbox/core/views.py:374 netbox/core/views.py:397
|
||||||
#: netbox/core/views.py:419 netbox/core/views.py:454
|
#: netbox/core/views.py:415 netbox/core/views.py:450
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Job {job_id} not found"
|
msgid "Job {job_id} not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2946,7 +2946,7 @@ msgstr ""
|
|||||||
#: netbox/dcim/forms/bulk_create.py:40 netbox/extras/forms/filtersets.py:410
|
#: netbox/dcim/forms/bulk_create.py:40 netbox/extras/forms/filtersets.py:410
|
||||||
#: netbox/extras/forms/model_forms.py:443
|
#: netbox/extras/forms/model_forms.py:443
|
||||||
#: netbox/extras/forms/model_forms.py:495 netbox/netbox/forms/base.py:84
|
#: netbox/extras/forms/model_forms.py:495 netbox/netbox/forms/base.py:84
|
||||||
#: netbox/netbox/forms/mixins.py:81 netbox/netbox/tables/columns.py:458
|
#: netbox/netbox/forms/mixins.py:81 netbox/netbox/tables/columns.py:461
|
||||||
#: netbox/templates/circuits/inc/circuit_termination.html:32
|
#: netbox/templates/circuits/inc/circuit_termination.html:32
|
||||||
#: netbox/templates/generic/bulk_edit.html:65
|
#: netbox/templates/generic/bulk_edit.html:65
|
||||||
#: netbox/templates/inc/panels/tags.html:5
|
#: netbox/templates/inc/panels/tags.html:5
|
||||||
@ -5974,7 +5974,7 @@ msgstr ""
|
|||||||
#: netbox/netbox/navigation/menu.py:60 netbox/netbox/navigation/menu.py:62
|
#: netbox/netbox/navigation/menu.py:60 netbox/netbox/navigation/menu.py:62
|
||||||
#: netbox/virtualization/forms/model_forms.py:122
|
#: netbox/virtualization/forms/model_forms.py:122
|
||||||
#: netbox/virtualization/tables/clusters.py:83
|
#: netbox/virtualization/tables/clusters.py:83
|
||||||
#: netbox/virtualization/views.py:210
|
#: netbox/virtualization/views.py:202
|
||||||
msgid "Devices"
|
msgid "Devices"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -6054,8 +6054,8 @@ msgid "Power outlets"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devices.py:243 netbox/dcim/tables/devices.py:1046
|
#: netbox/dcim/tables/devices.py:243 netbox/dcim/tables/devices.py:1046
|
||||||
#: netbox/dcim/tables/devicetypes.py:125 netbox/dcim/views.py:1006
|
#: netbox/dcim/tables/devicetypes.py:125 netbox/dcim/views.py:985
|
||||||
#: netbox/dcim/views.py:1245 netbox/dcim/views.py:1931
|
#: netbox/dcim/views.py:1224 netbox/dcim/views.py:1900
|
||||||
#: netbox/netbox/navigation/menu.py:81 netbox/netbox/navigation/menu.py:237
|
#: netbox/netbox/navigation/menu.py:81 netbox/netbox/navigation/menu.py:237
|
||||||
#: netbox/templates/dcim/device/base.html:37
|
#: netbox/templates/dcim/device/base.html:37
|
||||||
#: netbox/templates/dcim/device_list.html:43
|
#: netbox/templates/dcim/device_list.html:43
|
||||||
@ -6067,7 +6067,7 @@ msgstr ""
|
|||||||
#: netbox/templates/virtualization/virtualmachine/base.html:27
|
#: netbox/templates/virtualization/virtualmachine/base.html:27
|
||||||
#: netbox/templates/virtualization/virtualmachine_list.html:14
|
#: netbox/templates/virtualization/virtualmachine_list.html:14
|
||||||
#: netbox/virtualization/tables/virtualmachines.py:100
|
#: netbox/virtualization/tables/virtualmachines.py:100
|
||||||
#: netbox/virtualization/views.py:367 netbox/wireless/tables/wirelesslan.py:55
|
#: netbox/virtualization/views.py:359 netbox/wireless/tables/wirelesslan.py:55
|
||||||
msgid "Interfaces"
|
msgid "Interfaces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -6093,8 +6093,8 @@ msgid "Module Bay"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devices.py:310 netbox/dcim/tables/devicetypes.py:48
|
#: netbox/dcim/tables/devices.py:310 netbox/dcim/tables/devicetypes.py:48
|
||||||
#: netbox/dcim/tables/devicetypes.py:140 netbox/dcim/views.py:1081
|
#: netbox/dcim/tables/devicetypes.py:140 netbox/dcim/views.py:1060
|
||||||
#: netbox/dcim/views.py:2024 netbox/netbox/navigation/menu.py:90
|
#: netbox/dcim/views.py:1993 netbox/netbox/navigation/menu.py:90
|
||||||
#: netbox/templates/dcim/device/base.html:52
|
#: netbox/templates/dcim/device/base.html:52
|
||||||
#: netbox/templates/dcim/device_list.html:71
|
#: netbox/templates/dcim/device_list.html:71
|
||||||
#: netbox/templates/dcim/devicetype/base.html:49
|
#: netbox/templates/dcim/devicetype/base.html:49
|
||||||
@ -6124,8 +6124,8 @@ msgid "Allocated draw (W)"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devices.py:546 netbox/ipam/forms/model_forms.py:747
|
#: netbox/dcim/tables/devices.py:546 netbox/ipam/forms/model_forms.py:747
|
||||||
#: netbox/ipam/tables/fhrp.py:28 netbox/ipam/views.py:602
|
#: netbox/ipam/tables/fhrp.py:28 netbox/ipam/views.py:589
|
||||||
#: netbox/ipam/views.py:701 netbox/netbox/navigation/menu.py:145
|
#: netbox/ipam/views.py:688 netbox/netbox/navigation/menu.py:145
|
||||||
#: netbox/netbox/navigation/menu.py:147
|
#: netbox/netbox/navigation/menu.py:147
|
||||||
#: netbox/templates/dcim/interface.html:339
|
#: netbox/templates/dcim/interface.html:339
|
||||||
#: netbox/templates/ipam/ipaddress_bulk_add.html:15
|
#: netbox/templates/ipam/ipaddress_bulk_add.html:15
|
||||||
@ -6218,8 +6218,8 @@ msgstr ""
|
|||||||
msgid "Instances"
|
msgid "Instances"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devicetypes.py:113 netbox/dcim/views.py:946
|
#: netbox/dcim/tables/devicetypes.py:113 netbox/dcim/views.py:925
|
||||||
#: netbox/dcim/views.py:1185 netbox/dcim/views.py:1871
|
#: netbox/dcim/views.py:1164 netbox/dcim/views.py:1840
|
||||||
#: netbox/netbox/navigation/menu.py:84
|
#: netbox/netbox/navigation/menu.py:84
|
||||||
#: netbox/templates/dcim/device/base.html:25
|
#: netbox/templates/dcim/device/base.html:25
|
||||||
#: netbox/templates/dcim/device_list.html:15
|
#: netbox/templates/dcim/device_list.html:15
|
||||||
@ -6229,8 +6229,8 @@ msgstr ""
|
|||||||
msgid "Console Ports"
|
msgid "Console Ports"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devicetypes.py:116 netbox/dcim/views.py:961
|
#: netbox/dcim/tables/devicetypes.py:116 netbox/dcim/views.py:940
|
||||||
#: netbox/dcim/views.py:1200 netbox/dcim/views.py:1886
|
#: netbox/dcim/views.py:1179 netbox/dcim/views.py:1855
|
||||||
#: netbox/netbox/navigation/menu.py:85
|
#: netbox/netbox/navigation/menu.py:85
|
||||||
#: netbox/templates/dcim/device/base.html:28
|
#: netbox/templates/dcim/device/base.html:28
|
||||||
#: netbox/templates/dcim/device_list.html:22
|
#: netbox/templates/dcim/device_list.html:22
|
||||||
@ -6240,8 +6240,8 @@ msgstr ""
|
|||||||
msgid "Console Server Ports"
|
msgid "Console Server Ports"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devicetypes.py:119 netbox/dcim/views.py:976
|
#: netbox/dcim/tables/devicetypes.py:119 netbox/dcim/views.py:955
|
||||||
#: netbox/dcim/views.py:1215 netbox/dcim/views.py:1901
|
#: netbox/dcim/views.py:1194 netbox/dcim/views.py:1870
|
||||||
#: netbox/netbox/navigation/menu.py:86
|
#: netbox/netbox/navigation/menu.py:86
|
||||||
#: netbox/templates/dcim/device/base.html:31
|
#: netbox/templates/dcim/device/base.html:31
|
||||||
#: netbox/templates/dcim/device_list.html:29
|
#: netbox/templates/dcim/device_list.html:29
|
||||||
@ -6251,8 +6251,8 @@ msgstr ""
|
|||||||
msgid "Power Ports"
|
msgid "Power Ports"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devicetypes.py:122 netbox/dcim/views.py:991
|
#: netbox/dcim/tables/devicetypes.py:122 netbox/dcim/views.py:970
|
||||||
#: netbox/dcim/views.py:1230 netbox/dcim/views.py:1916
|
#: netbox/dcim/views.py:1209 netbox/dcim/views.py:1885
|
||||||
#: netbox/netbox/navigation/menu.py:87
|
#: netbox/netbox/navigation/menu.py:87
|
||||||
#: netbox/templates/dcim/device/base.html:34
|
#: netbox/templates/dcim/device/base.html:34
|
||||||
#: netbox/templates/dcim/device_list.html:36
|
#: netbox/templates/dcim/device_list.html:36
|
||||||
@ -6262,8 +6262,8 @@ msgstr ""
|
|||||||
msgid "Power Outlets"
|
msgid "Power Outlets"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devicetypes.py:128 netbox/dcim/views.py:1021
|
#: netbox/dcim/tables/devicetypes.py:128 netbox/dcim/views.py:1000
|
||||||
#: netbox/dcim/views.py:1260 netbox/dcim/views.py:1952
|
#: netbox/dcim/views.py:1239 netbox/dcim/views.py:1921
|
||||||
#: netbox/netbox/navigation/menu.py:82
|
#: netbox/netbox/navigation/menu.py:82
|
||||||
#: netbox/templates/dcim/device/base.html:40
|
#: netbox/templates/dcim/device/base.html:40
|
||||||
#: netbox/templates/dcim/devicetype/base.html:37
|
#: netbox/templates/dcim/devicetype/base.html:37
|
||||||
@ -6272,8 +6272,8 @@ msgstr ""
|
|||||||
msgid "Front Ports"
|
msgid "Front Ports"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devicetypes.py:131 netbox/dcim/views.py:1036
|
#: netbox/dcim/tables/devicetypes.py:131 netbox/dcim/views.py:1015
|
||||||
#: netbox/dcim/views.py:1275 netbox/dcim/views.py:1967
|
#: netbox/dcim/views.py:1254 netbox/dcim/views.py:1936
|
||||||
#: netbox/netbox/navigation/menu.py:83
|
#: netbox/netbox/navigation/menu.py:83
|
||||||
#: netbox/templates/dcim/device/base.html:43
|
#: netbox/templates/dcim/device/base.html:43
|
||||||
#: netbox/templates/dcim/device_list.html:50
|
#: netbox/templates/dcim/device_list.html:50
|
||||||
@ -6283,16 +6283,16 @@ msgstr ""
|
|||||||
msgid "Rear Ports"
|
msgid "Rear Ports"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devicetypes.py:134 netbox/dcim/views.py:1066
|
#: netbox/dcim/tables/devicetypes.py:134 netbox/dcim/views.py:1045
|
||||||
#: netbox/dcim/views.py:2005 netbox/netbox/navigation/menu.py:89
|
#: netbox/dcim/views.py:1974 netbox/netbox/navigation/menu.py:89
|
||||||
#: netbox/templates/dcim/device/base.html:49
|
#: netbox/templates/dcim/device/base.html:49
|
||||||
#: netbox/templates/dcim/device_list.html:57
|
#: netbox/templates/dcim/device_list.html:57
|
||||||
#: netbox/templates/dcim/devicetype/base.html:46
|
#: netbox/templates/dcim/devicetype/base.html:46
|
||||||
msgid "Device Bays"
|
msgid "Device Bays"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devicetypes.py:137 netbox/dcim/views.py:1051
|
#: netbox/dcim/tables/devicetypes.py:137 netbox/dcim/views.py:1030
|
||||||
#: netbox/dcim/views.py:1986 netbox/netbox/navigation/menu.py:88
|
#: netbox/dcim/views.py:1955 netbox/netbox/navigation/menu.py:88
|
||||||
#: netbox/templates/dcim/device/base.html:46
|
#: netbox/templates/dcim/device/base.html:46
|
||||||
#: netbox/templates/dcim/device_list.html:64
|
#: netbox/templates/dcim/device_list.html:64
|
||||||
#: netbox/templates/dcim/devicetype/base.html:43
|
#: netbox/templates/dcim/devicetype/base.html:43
|
||||||
@ -6350,38 +6350,38 @@ msgstr ""
|
|||||||
msgid "Test case must set peer_termination_type"
|
msgid "Test case must set peer_termination_type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/views.py:137
|
#: netbox/dcim/views.py:139
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Disconnected {count} {type}"
|
msgid "Disconnected {count} {type}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/views.py:698 netbox/netbox/navigation/menu.py:28
|
#: netbox/dcim/views.py:684 netbox/netbox/navigation/menu.py:28
|
||||||
msgid "Reservations"
|
msgid "Reservations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/views.py:716 netbox/templates/dcim/location.html:90
|
#: netbox/dcim/views.py:702 netbox/templates/dcim/location.html:90
|
||||||
#: netbox/templates/dcim/site.html:140
|
#: netbox/templates/dcim/site.html:140
|
||||||
msgid "Non-Racked Devices"
|
msgid "Non-Racked Devices"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/views.py:2037 netbox/extras/forms/model_forms.py:453
|
#: netbox/dcim/views.py:2006 netbox/extras/forms/model_forms.py:453
|
||||||
#: netbox/templates/extras/configcontext.html:10
|
#: netbox/templates/extras/configcontext.html:10
|
||||||
#: netbox/virtualization/forms/model_forms.py:225
|
#: netbox/virtualization/forms/model_forms.py:225
|
||||||
#: netbox/virtualization/views.py:407
|
#: netbox/virtualization/views.py:399
|
||||||
msgid "Config Context"
|
msgid "Config Context"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/views.py:2047 netbox/virtualization/views.py:417
|
#: netbox/dcim/views.py:2016 netbox/virtualization/views.py:409
|
||||||
msgid "Render Config"
|
msgid "Render Config"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/views.py:2097 netbox/extras/tables/tables.py:440
|
#: netbox/dcim/views.py:2066 netbox/extras/tables/tables.py:440
|
||||||
#: netbox/netbox/navigation/menu.py:234 netbox/netbox/navigation/menu.py:236
|
#: netbox/netbox/navigation/menu.py:234 netbox/netbox/navigation/menu.py:236
|
||||||
#: netbox/virtualization/views.py:185
|
#: netbox/virtualization/views.py:177
|
||||||
msgid "Virtual Machines"
|
msgid "Virtual Machines"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/views.py:2989 netbox/ipam/tables/ip.py:233
|
#: netbox/dcim/views.py:2948 netbox/ipam/tables/ip.py:233
|
||||||
msgid "Children"
|
msgid "Children"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -7686,56 +7686,56 @@ msgstr ""
|
|||||||
msgid "Date values must be in ISO 8601 format (YYYY-MM-DD)."
|
msgid "Date values must be in ISO 8601 format (YYYY-MM-DD)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:667
|
#: netbox/extras/models/customfields.py:671
|
||||||
msgid "Date and time values must be in ISO 8601 format (YYYY-MM-DD HH:MM:SS)."
|
msgid "Date and time values must be in ISO 8601 format (YYYY-MM-DD HH:MM:SS)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:674
|
#: netbox/extras/models/customfields.py:678
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid choice ({value}) for choice set {choiceset}."
|
msgid "Invalid choice ({value}) for choice set {choiceset}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:684
|
#: netbox/extras/models/customfields.py:688
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invalid choice(s) ({value}) for choice set {choiceset}."
|
msgid "Invalid choice(s) ({value}) for choice set {choiceset}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:693
|
#: netbox/extras/models/customfields.py:697
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Value must be an object ID, not {type}"
|
msgid "Value must be an object ID, not {type}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:699
|
#: netbox/extras/models/customfields.py:703
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Value must be a list of object IDs, not {type}"
|
msgid "Value must be a list of object IDs, not {type}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:703
|
#: netbox/extras/models/customfields.py:707
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Found invalid object ID: {id}"
|
msgid "Found invalid object ID: {id}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:706
|
#: netbox/extras/models/customfields.py:710
|
||||||
msgid "Required field cannot be empty."
|
msgid "Required field cannot be empty."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:725
|
#: netbox/extras/models/customfields.py:729
|
||||||
msgid "Base set of predefined choices (optional)"
|
msgid "Base set of predefined choices (optional)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:737
|
#: netbox/extras/models/customfields.py:741
|
||||||
msgid "Choices are automatically ordered alphabetically"
|
msgid "Choices are automatically ordered alphabetically"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:744
|
#: netbox/extras/models/customfields.py:748
|
||||||
msgid "custom field choice set"
|
msgid "custom field choice set"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:745
|
#: netbox/extras/models/customfields.py:749
|
||||||
msgid "custom field choice sets"
|
msgid "custom field choice sets"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:781
|
#: netbox/extras/models/customfields.py:785
|
||||||
msgid "Must define base or extra choices."
|
msgid "Must define base or extra choices."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -9415,7 +9415,7 @@ msgid "The primary function of this VLAN"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/models/vlans.py:215 netbox/ipam/tables/ip.py:175
|
#: netbox/ipam/models/vlans.py:215 netbox/ipam/tables/ip.py:175
|
||||||
#: netbox/ipam/tables/vlans.py:78 netbox/ipam/views.py:978
|
#: netbox/ipam/tables/vlans.py:78 netbox/ipam/views.py:961
|
||||||
#: netbox/netbox/navigation/menu.py:180 netbox/netbox/navigation/menu.py:182
|
#: netbox/netbox/navigation/menu.py:180 netbox/netbox/navigation/menu.py:182
|
||||||
msgid "VLANs"
|
msgid "VLANs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -9487,7 +9487,7 @@ msgid "Added"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/tables/ip.py:127 netbox/ipam/tables/ip.py:165
|
#: netbox/ipam/tables/ip.py:127 netbox/ipam/tables/ip.py:165
|
||||||
#: netbox/ipam/tables/vlans.py:138 netbox/ipam/views.py:349
|
#: netbox/ipam/tables/vlans.py:138 netbox/ipam/views.py:342
|
||||||
#: netbox/netbox/navigation/menu.py:152 netbox/netbox/navigation/menu.py:154
|
#: netbox/netbox/navigation/menu.py:152 netbox/netbox/navigation/menu.py:154
|
||||||
#: netbox/templates/ipam/vlan.html:84
|
#: netbox/templates/ipam/vlan.html:84
|
||||||
msgid "Prefixes"
|
msgid "Prefixes"
|
||||||
@ -9588,23 +9588,23 @@ msgid ""
|
|||||||
"are allowed in DNS names"
|
"are allowed in DNS names"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/views.py:541
|
#: netbox/ipam/views.py:528
|
||||||
msgid "Child Prefixes"
|
msgid "Child Prefixes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/views.py:576
|
#: netbox/ipam/views.py:563
|
||||||
msgid "Child Ranges"
|
msgid "Child Ranges"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/views.py:902
|
#: netbox/ipam/views.py:889
|
||||||
msgid "Related IPs"
|
msgid "Related IPs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/views.py:1133
|
#: netbox/ipam/views.py:1116
|
||||||
msgid "Device Interfaces"
|
msgid "Device Interfaces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/views.py:1150
|
#: netbox/ipam/views.py:1133
|
||||||
msgid "VM Interfaces"
|
msgid "VM Interfaces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -10159,7 +10159,7 @@ msgstr ""
|
|||||||
#: netbox/templates/virtualization/virtualmachine/base.html:32
|
#: netbox/templates/virtualization/virtualmachine/base.html:32
|
||||||
#: netbox/templates/virtualization/virtualmachine_list.html:21
|
#: netbox/templates/virtualization/virtualmachine_list.html:21
|
||||||
#: netbox/virtualization/tables/virtualmachines.py:103
|
#: netbox/virtualization/tables/virtualmachines.py:103
|
||||||
#: netbox/virtualization/views.py:388
|
#: netbox/virtualization/views.py:380
|
||||||
msgid "Virtual Disks"
|
msgid "Virtual Disks"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -10498,15 +10498,15 @@ msgstr ""
|
|||||||
msgid "Chinese"
|
msgid "Chinese"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/netbox/tables/columns.py:185
|
#: netbox/netbox/tables/columns.py:188
|
||||||
msgid "Toggle all"
|
msgid "Toggle all"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/netbox/tables/columns.py:287
|
#: netbox/netbox/tables/columns.py:290
|
||||||
msgid "Toggle Dropdown"
|
msgid "Toggle Dropdown"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/netbox/tables/columns.py:552 netbox/templates/core/job.html:35
|
#: netbox/netbox/tables/columns.py:555 netbox/templates/core/job.html:35
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -10790,36 +10790,28 @@ msgstr ""
|
|||||||
msgid "NetBox Logo"
|
msgid "NetBox Logo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/base/layout.html:56
|
#: netbox/templates/base/layout.html:139
|
||||||
msgid "Enable dark mode"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: netbox/templates/base/layout.html:59
|
|
||||||
msgid "Enable light mode"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: netbox/templates/base/layout.html:145
|
|
||||||
msgid "Docs"
|
msgid "Docs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/base/layout.html:151
|
#: netbox/templates/base/layout.html:145
|
||||||
#: netbox/templates/rest_framework/api.html:10
|
#: netbox/templates/rest_framework/api.html:10
|
||||||
msgid "REST API"
|
msgid "REST API"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/base/layout.html:157
|
#: netbox/templates/base/layout.html:151
|
||||||
msgid "REST API documentation"
|
msgid "REST API documentation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/base/layout.html:164
|
#: netbox/templates/base/layout.html:158
|
||||||
msgid "GraphQL API"
|
msgid "GraphQL API"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/base/layout.html:171
|
#: netbox/templates/base/layout.html:165
|
||||||
msgid "Source Code"
|
msgid "Source Code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/base/layout.html:177
|
#: netbox/templates/base/layout.html:171
|
||||||
msgid "Community"
|
msgid "Community"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -11112,8 +11104,8 @@ msgstr ""
|
|||||||
#: netbox/templates/core/rq_worker_list.html:45
|
#: netbox/templates/core/rq_worker_list.html:45
|
||||||
#: netbox/templates/extras/script_result.html:49
|
#: netbox/templates/extras/script_result.html:49
|
||||||
#: netbox/templates/extras/script_result.html:51
|
#: netbox/templates/extras/script_result.html:51
|
||||||
#: netbox/templates/inc/table_controls_htmx.html:18
|
#: netbox/templates/inc/table_controls_htmx.html:28
|
||||||
#: netbox/templates/inc/table_controls_htmx.html:20
|
#: netbox/templates/inc/table_controls_htmx.html:31
|
||||||
msgid "Configure Table"
|
msgid "Configure Table"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -12689,6 +12681,14 @@ msgstr ""
|
|||||||
msgid "Reset"
|
msgid "Reset"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: netbox/templates/inc/light_toggle.html:4
|
||||||
|
msgid "Enable dark mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: netbox/templates/inc/light_toggle.html:7
|
||||||
|
msgid "Enable light mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/inc/missing_prerequisites.html:8
|
#: netbox/templates/inc/missing_prerequisites.html:8
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -12729,6 +12729,14 @@ msgstr ""
|
|||||||
msgid "Data is out of sync with upstream file"
|
msgid "Data is out of sync with upstream file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: netbox/templates/inc/table_controls_htmx.html:7
|
||||||
|
msgid "Quick search"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: netbox/templates/inc/table_controls_htmx.html:19
|
||||||
|
msgid "Saved filter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/inc/user_menu.html:23
|
#: netbox/templates/inc/user_menu.html:23
|
||||||
msgid "Django Admin"
|
msgid "Django Admin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -14072,17 +14080,17 @@ msgstr ""
|
|||||||
msgid "{value} is not a valid regular expression."
|
msgid "{value} is not a valid regular expression."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/utilities/views.py:40
|
#: netbox/utilities/views.py:44
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{self.__class__.__name__} must implement get_required_permission()"
|
msgid "{self.__class__.__name__} must implement get_required_permission()"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/utilities/views.py:76
|
#: netbox/utilities/views.py:80
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{class_name} must implement get_required_permission()"
|
msgid "{class_name} must implement get_required_permission()"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/utilities/views.py:100
|
#: netbox/utilities/views.py:104
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"{class_name} has no queryset defined. ObjectPermissionRequiredMixin may only "
|
"{class_name} has no queryset defined. ObjectPermissionRequiredMixin may only "
|
||||||
|
@ -29,7 +29,7 @@ def linkify_phone(value):
|
|||||||
"""
|
"""
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
return f"tel:{value}"
|
return f"tel:{value.replace(' ', '')}"
|
||||||
|
|
||||||
|
|
||||||
def register_table_column(column, name, *tables):
|
def register_table_column(column, name, *tables):
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
{% elif customfield.type == 'date' and value %}
|
{% elif customfield.type == 'date' and value %}
|
||||||
{{ value|isodate }}
|
{{ value|isodate }}
|
||||||
{% elif customfield.type == 'datetime' and value %}
|
{% elif customfield.type == 'datetime' and value %}
|
||||||
{{ value|isodate }} {{ value|isodatetime }}
|
{{ value|isodatetime }}
|
||||||
{% elif customfield.type == 'url' and value %}
|
{% elif customfield.type == 'url' and value %}
|
||||||
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
||||||
{% elif customfield.type == 'json' and value %}
|
{% elif customfield.type == 'json' and value %}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from django import template
|
from django import template
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from extras.choices import CustomFieldTypeChoices
|
from extras.choices import CustomFieldTypeChoices
|
||||||
from utilities.querydict import dict_to_querydict
|
from utilities.querydict import dict_to_querydict
|
||||||
@ -124,5 +125,5 @@ def formaction(context):
|
|||||||
if HTMX navigation is enabled (per the user's preferences).
|
if HTMX navigation is enabled (per the user's preferences).
|
||||||
"""
|
"""
|
||||||
if context.get('htmx_navigation', False):
|
if context.get('htmx_navigation', False):
|
||||||
return 'hx-push-url="true" hx-post'
|
return mark_safe('hx-push-url="true" hx-post')
|
||||||
return 'formaction'
|
return 'formaction'
|
||||||
|
@ -281,6 +281,10 @@ def applied_filters(context, model, form, query_params):
|
|||||||
if filter_name not in querydict:
|
if filter_name not in querydict:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Skip saved filters, as they're displayed alongside the quick search widget
|
||||||
|
if filter_name == 'filter_id':
|
||||||
|
continue
|
||||||
|
|
||||||
bound_field = form.fields[filter_name].get_bound_field(form, filter_name)
|
bound_field = form.fields[filter_name].get_bound_field(form, filter_name)
|
||||||
querydict.pop(filter_name)
|
querydict.pop(filter_name)
|
||||||
display_value = ', '.join([str(v) for v in get_selected_values(form, filter_name)])
|
display_value = ', '.join([str(v) for v in get_selected_values(form, filter_name)])
|
||||||
|
@ -178,8 +178,8 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
|
|||||||
required=False,
|
required=False,
|
||||||
selector=True,
|
selector=True,
|
||||||
query_params={
|
query_params={
|
||||||
'site_id': '$site',
|
'site_id': ['$site', 'null']
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
device = DynamicModelChoiceField(
|
device = DynamicModelChoiceField(
|
||||||
label=_('Device'),
|
label=_('Device'),
|
||||||
|
@ -180,7 +180,7 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
|
|||||||
})
|
})
|
||||||
|
|
||||||
# Validate site for cluster & device
|
# Validate site for cluster & device
|
||||||
if self.cluster and self.site and self.cluster.site != self.site:
|
if self.cluster and self.cluster.site is not None and self.cluster.site != self.site:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'cluster': _(
|
'cluster': _(
|
||||||
'The selected cluster ({cluster}) is not assigned to this site ({site}).'
|
'The selected cluster ({cluster}) is not assigned to this site ({site}).'
|
||||||
|
@ -63,6 +63,9 @@ class VirtualMachineTestCase(TestCase):
|
|||||||
# VM with site only should pass
|
# VM with site only should pass
|
||||||
VirtualMachine(name='vm1', site=sites[0]).full_clean()
|
VirtualMachine(name='vm1', site=sites[0]).full_clean()
|
||||||
|
|
||||||
|
# VM with site, cluster non-site should pass
|
||||||
|
VirtualMachine(name='vm1', site=sites[0], cluster=clusters[2]).full_clean()
|
||||||
|
|
||||||
# VM with non-site cluster only should pass
|
# VM with non-site cluster only should pass
|
||||||
VirtualMachine(name='vm1', cluster=clusters[2]).full_clean()
|
VirtualMachine(name='vm1', cluster=clusters[2]).full_clean()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user