Merge branch 'develop' into feature

This commit is contained in:
Jeremy Stretch 2024-07-26 16:39:58 -04:00
commit a12fdd6e2d
62 changed files with 96214 additions and 16724 deletions

View File

@ -26,7 +26,7 @@ body:
attributes: attributes:
label: NetBox Version label: NetBox Version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.0.7 placeholder: v4.0.8
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.0.7 placeholder: v4.0.8
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -16,6 +16,6 @@ jobs:
if: "contains(github.event.issue.labels.*.name, 'status: needs triage')" if: "contains(github.event.issue.labels.*.name, 'status: needs triage')"
with: with:
# Weighted assignments # Weighted assignments
assignees: arthanson:3, jeffgdotorg:3, jeremystretch:3, DanSheps assignees: arthanson:3, jeremystretch:3, DanSheps
numOfAssignee: 1 numOfAssignee: 1
abortIfPreviousAssignees: true abortIfPreviousAssignees: true

View File

@ -40,7 +40,7 @@ NetBox users are welcome to participate in either role, on stage or in the crowd
* First, ensure that you're running the [latest stable version](https://github.com/netbox-community/netbox/releases) of NetBox. If you're running an older version, it's likely that the bug has already been fixed. * First, ensure that you're running the [latest stable version](https://github.com/netbox-community/netbox/releases) of NetBox. If you're running an older version, it's likely that the bug has already been fixed.
* Next, search our [issues list](https://github.com/netbox-community/netbox/issues?q=is%3Aissue) to see if the bug you've found has already been reported. If you come across a bug report that seems to match, please click "add a reaction" in the top right corner of the issue and add a thumbs up (:thumbsup:). This will help draw more attention to it. Any comments you can add to provide additional information or context would also be much appreciated. * Next, search our [issues list](https://github.com/netbox-community/netbox/issues?q=is%3Aissue) to see if the bug you've found has already been reported. If you come across a bug report that seems to match, please click "add a reaction" in the bottom left corner of the issue and add a thumbs up (:thumbsup:). This will help draw more attention to it. Any comments you can add to provide additional information or context would also be much appreciated.
* If you can't find any existing issues (open or closed) that seem to match yours, you're welcome to [submit a new bug report](https://github.com/netbox-community/netbox/issues/new?label=type%3A+bug&template=bug_report.yaml). Be sure to complete the entire report template, including detailed steps that someone triaging your issue can follow to confirm the reported behavior. (If we're not able to replicate the bug based on the information provided, we'll ask for additional detail.) * If you can't find any existing issues (open or closed) that seem to match yours, you're welcome to [submit a new bug report](https://github.com/netbox-community/netbox/issues/new?label=type%3A+bug&template=bug_report.yaml). Be sure to complete the entire report template, including detailed steps that someone triaging your issue can follow to confirm the reported behavior. (If we're not able to replicate the bug based on the information provided, we'll ask for additional detail.)

View File

@ -5,7 +5,7 @@
<a href="https://github.com/netbox-community/netbox/blob/master/LICENSE.txt"><img src="https://img.shields.io/badge/license-Apache_2.0-blue.svg" alt="License" /></a> <a href="https://github.com/netbox-community/netbox/blob/master/LICENSE.txt"><img src="https://img.shields.io/badge/license-Apache_2.0-blue.svg" alt="License" /></a>
<a href="https://github.com/netbox-community/netbox/graphs/contributors"><img src="https://img.shields.io/github/contributors/netbox-community/netbox?color=blue" alt="Contributors" /></a> <a href="https://github.com/netbox-community/netbox/graphs/contributors"><img src="https://img.shields.io/github/contributors/netbox-community/netbox?color=blue" alt="Contributors" /></a>
<a href="https://github.com/netbox-community/netbox/stargazers"><img src="https://img.shields.io/github/stars/netbox-community/netbox?style=flat" alt="GitHub stars" /></a> <a href="https://github.com/netbox-community/netbox/stargazers"><img src="https://img.shields.io/github/stars/netbox-community/netbox?style=flat" alt="GitHub stars" /></a>
<a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-10-blue" alt="Languages supported" /></a> <a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-15-blue" alt="Languages supported" /></a>
<a href="https://github.com/netbox-community/netbox/actions/workflows/ci.yml"><img src="https://github.com/netbox-community/netbox/workflows/CI/badge.svg?branch=master" alt="CI status" /></a> <a href="https://github.com/netbox-community/netbox/actions/workflows/ci.yml"><img src="https://github.com/netbox-community/netbox/workflows/CI/badge.svg?branch=master" alt="CI status" /></a>
<p></p> <p></p>
</div> </div>

View File

@ -16,7 +16,7 @@ Administrators are encouraged to adhere to industry best practices concerning th
## Reporting a Suspected Vulnerability ## Reporting a Suspected Vulnerability
If you believe you've uncovered a security vulnerability and wish to report it confidentially, you may do so via email. Please note that any reported vulnerabilities **MUST** meet all the following conditions: If you believe you've uncovered a security vulnerability and wish to report it confidentially, you may do so by emailing `security@netboxlabs.com`. Please ensure that your report meets all the following conditions:
* Affects the most recent stable release of NetBox, or a current beta release * Affects the most recent stable release of NetBox, or a current beta release
* Affects a NetBox instance installed and configured per the official documentation * Affects a NetBox instance installed and configured per the official documentation
@ -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. For any security concerns regarding NetBox deployed via Docker, please see the [netbox-docker](https://github.com/netbox-community/netbox-docker) project. For any security concerns regarding the community-maintained Docker image for NetBox, please see the [netbox-docker](https://github.com/netbox-community/netbox-docker) project.
### Bug Bounties ### Bug Bounties

View File

@ -40,3 +40,22 @@ REMOTE_AUTH_BACKEND = 'social_core.backends.google.GoogleOAuth2'
NetBox supports single sign-on authentication via the [python-social-auth](https://github.com/python-social-auth) library. To enable SSO, specify the path to the desired authentication backend within the `social_core` Python package. Please see the complete list of [supported authentication backends](https://github.com/python-social-auth/social-core/tree/master/social_core/backends) for the available options. NetBox supports single sign-on authentication via the [python-social-auth](https://github.com/python-social-auth) library. To enable SSO, specify the path to the desired authentication backend within the `social_core` Python package. Please see the complete list of [supported authentication backends](https://github.com/python-social-auth/social-core/tree/master/social_core/backends) for the available options.
Most remote authentication backends require some additional configuration through settings prefixed with `SOCIAL_AUTH_`. These will be automatically imported from NetBox's `configuration.py` file. Additionally, the [authentication pipeline](https://python-social-auth.readthedocs.io/en/latest/pipeline.html) can be customized via the `SOCIAL_AUTH_PIPELINE` parameter. (NetBox's default pipeline is defined in `netbox/settings.py` for your reference.) Most remote authentication backends require some additional configuration through settings prefixed with `SOCIAL_AUTH_`. These will be automatically imported from NetBox's `configuration.py` file. Additionally, the [authentication pipeline](https://python-social-auth.readthedocs.io/en/latest/pipeline.html) can be customized via the `SOCIAL_AUTH_PIPELINE` parameter. (NetBox's default pipeline is defined in `netbox/settings.py` for your reference.)
#### Configuring the SSO module's appearance
The way a remote authentication backend is displayed to the user on the login
page may be adjusted via the `SOCIAL_AUTH_BACKEND_ATTRS` parameter, defaulting
to an empty dictionary. This dictionary maps a `social_core` module's name (ie.
`REMOTE_AUTH_BACKEND.name`) to a couple of parameters, `(display_name, icon)`.
The `display_name` is the name displayed to the user on the login page. The
icon may either be the URL of an icon; refer to a [Material Design
Icons](https://github.com/google/material-design-icons) icon's name; or be
`None` for no icon.
For instance, the OIDC backend may be customized with
```python
SOCIAL_AUTH_BACKEND_ATTRS = {
'oidc': ("My awesome SSO", "login"),
}
```

View File

@ -113,7 +113,7 @@ Create a [new release](https://github.com/netbox-community/netbox/releases/new)
* **Tag:** Current version (e.g. `v3.3.1`) * **Tag:** Current version (e.g. `v3.3.1`)
* **Target:** `master` * **Target:** `master`
* **Title:** Version and date (e.g. `v3.3.1 - 2022-08-25`) * **Title:** Version and date (e.g. `v3.3.1 - 2022-08-25`)
* **Description:** Copy from the pull request body * **Description:** Copy from the pull request body, then promote the `###` headers to `##` ones
Once created, the release will become available for users to install. Once created, the release will become available for users to install.
@ -135,6 +135,6 @@ First, run the `build-site` action, by navigating to Actions > build-site > Run
Once the documentation files have been compiled, they must be published by running the `deploy-kinsta` action. Select the desired deployment environment (staging or production) and specify `latest` as the deploy tag. Once the documentation files have been compiled, they must be published by running the `deploy-kinsta` action. Select the desired deployment environment (staging or production) and specify `latest` as the deploy tag.
Clear the CDN cache from the [Kinsta](https://my.kinsta.com/) portal. Navigate to _Sites_ / _NetBox Labs_ / _Live_, select _CDN_ in the left-nav, click the _Clear CDN cache_ button, and confirm the clear operation. Clear the CDN cache from the [Kinsta](https://my.kinsta.com/) portal. Navigate to _Sites_ / _NetBox Labs_ / _Live_, select _Cache_ in the left-nav, click the _Clear Cache_ button, and confirm the clear operation.
Finally, verify that the documentation at <https://netboxlabs.com/docs/netbox/en/stable/> has been updated. Finally, verify that the documentation at <https://netboxlabs.com/docs/netbox/en/stable/> has been updated.

View File

@ -20,6 +20,8 @@ Then, commit the change and push to the `develop` branch on GitHub. Any new stri
Typically, translated strings need to be updated only as part of the NetBox [release process](./release-checklist.md). Typically, translated strings need to be updated only as part of the NetBox [release process](./release-checklist.md).
Check the Transifex dashboard for languages that are not marked _ready for use_, being sure to click _Show all languages_ if it appears at the bottom of the list. Use machine translation to round out any not-ready languages. It's not necessary to review the machine translation immediately as the translation teams will handle that aspect; the goal at this stage is to get translations included in the Transifex pull request.
To update translated strings, start by initiating a sync from Transifex. From the Transifex dashboard, navigate to Settings > Integrations > GitHub > Manage, and click the **Manual Sync** button at top right. To update translated strings, start by initiating a sync from Transifex. From the Transifex dashboard, navigate to Settings > Integrations > GitHub > Manage, and click the **Manual Sync** button at top right.
![Transifex manual sync](../media/development/transifex_sync.png) ![Transifex manual sync](../media/development/transifex_sync.png)

View File

@ -80,11 +80,11 @@ To create a viewset for a plugin model, subclass `NetBoxModelViewSet` in `api/vi
```python ```python
# api/views.py # api/views.py
from netbox.api.viewsets import ModelViewSet from netbox.api.viewsets import NetBoxModelViewSet
from my_plugin.models import MyModel from my_plugin.models import MyModel
from .serializers import MyModelSerializer from .serializers import MyModelSerializer
class MyModelViewSet(ModelViewSet): class MyModelViewSet(NetBoxModelViewSet):
queryset = MyModel.objects.all() queryset = MyModel.objects.all()
serializer_class = MyModelSerializer serializer_class = MyModelSerializer
``` ```

View File

@ -1,6 +1,34 @@
# NetBox v4.0 # NetBox v4.0
## v4.0.8 (FUTURE) ## v4.0.9 (FUTURE)
---
## v4.0.8 (2024-07-26)
### Enhancements
* [#14640](https://github.com/netbox-community/netbox/issues/14640) - Add Dutch language support
* [#14792](https://github.com/netbox-community/netbox/issues/14792) - Add Polish language support
* [#15375](https://github.com/netbox-community/netbox/issues/15375) - Enable customization of SSO backend name & icon
* [#15660](https://github.com/netbox-community/netbox/issues/15660) - Add Czech language support
* [#15696](https://github.com/netbox-community/netbox/issues/15696) - Add Danish language support
* [#16793](https://github.com/netbox-community/netbox/issues/16793) - Add Italian language support
* [#16933](https://github.com/netbox-community/netbox/issues/16933) - Enable toggling true/false marks on BooleanColumn
* [#16943](https://github.com/netbox-community/netbox/issues/16943) - Expand navigation breadcrumbs on job view to include the parent object
### Bug Fixes
* [#16357](https://github.com/netbox-community/netbox/issues/16357) - Replicate assigned type & tenant for cable when clicking "create an add another"
* [#16402](https://github.com/netbox-community/netbox/issues/16402) - Remove inoperative links from report result view
* [#16536](https://github.com/netbox-community/netbox/issues/16536) - Revert `role` & `role_id` filters for device components to `device_role` & `device_role_id` to avoid conflict with inventory item `role` field
* [#16624](https://github.com/netbox-community/netbox/issues/16624) - Correct OpenAPI schema definitions for several fields
* [#16760](https://github.com/netbox-community/netbox/issues/16760) - Fix data source syncing using git via a local path
* [#16819](https://github.com/netbox-community/netbox/issues/16819) - Highlight parent device in rack when viewing child device
* [#16838](https://github.com/netbox-community/netbox/issues/16838) - ActionsColumn should render extra buttons even when no stock actions are enabled
* [#16867](https://github.com/netbox-community/netbox/issues/16867) - Fix exception when a dashboard list widget references a model which has been removed
* [#16963](https://github.com/netbox-community/netbox/issues/16963) - Fix filtering of "accounts" link under providers list
* [#16964](https://github.com/netbox-community/netbox/issues/16964) - Ensure configured password validators are enforced
--- ---

View File

@ -46,10 +46,20 @@ class LoginView(View):
return super().dispatch(*args, **kwargs) return super().dispatch(*args, **kwargs)
def gen_auth_data(self, name, url, params): def gen_auth_data(self, name, url, params):
display_name, icon_name = get_auth_backend_display(name) display_name, icon_source = get_auth_backend_display(name)
icon_name = None
icon_img = None
if icon_source:
if '://' in icon_source:
icon_img = icon_source
else:
icon_name = icon_source
return { return {
'display_name': display_name, 'display_name': display_name,
'icon_name': icon_name, 'icon_name': icon_name,
'icon_img': icon_img,
'url': f'{url}?{urlencode(params)}', 'url': f'{url}?{urlencode(params)}',
} }

View File

@ -25,7 +25,7 @@ class ProviderTable(ContactsColumnMixin, NetBoxTable):
account_count = columns.LinkedCountColumn( account_count = columns.LinkedCountColumn(
accessor=tables.A('accounts__count'), accessor=tables.A('accounts__count'),
viewname='circuits:provideraccount_list', viewname='circuits:provideraccount_list',
url_params={'account_id': 'pk'}, url_params={'provider_id': 'pk'},
verbose_name=_('Account Count') verbose_name=_('Account Count')
) )
asns = columns.ManyToManyColumn( asns = columns.ManyToManyColumn(

View File

@ -84,9 +84,7 @@ class GitBackend(DataBackend):
clone_args = { clone_args = {
"branch": self.params.get('branch'), "branch": self.params.get('branch'),
"config": self.config, "config": self.config,
"depth": 1,
"errstream": porcelain.NoneStream(), "errstream": porcelain.NoneStream(),
"quiet": True,
} }
if self.url_scheme in ('http', 'https'): if self.url_scheme in ('http', 'https'):
@ -97,6 +95,9 @@ class GitBackend(DataBackend):
"password": self.params.get('password'), "password": self.params.get('password'),
} }
) )
if self.url_scheme:
clone_args["quiet"] = True
clone_args["depth"] = 1
logger.debug(f"Cloning git repo: {self.url}") logger.debug(f"Cloning git repo: {self.url}")
try: try:

View File

@ -19,6 +19,7 @@ REVISION_BUTTONS = """
class ConfigRevisionTable(NetBoxTable): class ConfigRevisionTable(NetBoxTable):
is_active = columns.BooleanColumn( is_active = columns.BooleanColumn(
verbose_name=_('Is Active'), verbose_name=_('Is Active'),
false_mark=None
) )
actions = columns.ActionsColumn( actions = columns.ActionsColumn(
actions=('delete',), actions=('delete',),

View File

@ -13,7 +13,7 @@ class ConnectedEndpointsSerializer(serializers.ModelSerializer):
""" """
Legacy serializer for pre-v3.3 connections Legacy serializer for pre-v3.3 connections
""" """
connected_endpoints_type = serializers.SerializerMethodField(read_only=True) connected_endpoints_type = serializers.SerializerMethodField(read_only=True, allow_null=True)
connected_endpoints = serializers.SerializerMethodField(read_only=True) connected_endpoints = serializers.SerializerMethodField(read_only=True)
connected_endpoints_reachable = serializers.SerializerMethodField(read_only=True) connected_endpoints_reachable = serializers.SerializerMethodField(read_only=True)
@ -22,7 +22,7 @@ class ConnectedEndpointsSerializer(serializers.ModelSerializer):
if endpoints := obj.connected_endpoints: if endpoints := obj.connected_endpoints:
return f'{endpoints[0]._meta.app_label}.{endpoints[0]._meta.model_name}' return f'{endpoints[0]._meta.app_label}.{endpoints[0]._meta.model_name}'
@extend_schema_field(serializers.ListField) @extend_schema_field(serializers.ListField(allow_null=True))
def get_connected_endpoints(self, obj): def get_connected_endpoints(self, obj):
""" """
Return the appropriate serializer for the type of connected object. Return the appropriate serializer for the type of connected object.

View File

@ -89,7 +89,7 @@ class CablePathSerializer(serializers.ModelSerializer):
class CabledObjectSerializer(serializers.ModelSerializer): class CabledObjectSerializer(serializers.ModelSerializer):
cable = CableSerializer(nested=True, read_only=True, allow_null=True) cable = CableSerializer(nested=True, read_only=True, allow_null=True)
cable_end = serializers.CharField(read_only=True) cable_end = serializers.CharField(read_only=True)
link_peers_type = serializers.SerializerMethodField(read_only=True) link_peers_type = serializers.SerializerMethodField(read_only=True, allow_null=True)
link_peers = serializers.SerializerMethodField(read_only=True) link_peers = serializers.SerializerMethodField(read_only=True)
_occupied = serializers.SerializerMethodField(read_only=True) _occupied = serializers.SerializerMethodField(read_only=True)

View File

@ -87,7 +87,7 @@ class DeviceSerializer(NetBoxModelSerializer):
] ]
brief_fields = ('id', 'url', 'display', 'name', 'description') brief_fields = ('id', 'url', 'display', 'name', 'description')
@extend_schema_field(NestedDeviceSerializer) @extend_schema_field(NestedDeviceSerializer(allow_null=True))
def get_parent_device(self, obj): def get_parent_device(self, obj):
try: try:
device_bay = obj.parent_bay device_bay = obj.parent_bay

View File

@ -1430,12 +1430,12 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
to_field_name='model', to_field_name='model',
label=_('Device type (model)'), label=_('Device type (model)'),
) )
role_id = django_filters.ModelMultipleChoiceFilter( device_role_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__role', field_name='device__role',
queryset=DeviceRole.objects.all(), queryset=DeviceRole.objects.all(),
label=_('Device role (ID)'), label=_('Device role (ID)'),
) )
role = django_filters.ModelMultipleChoiceFilter( device_role = django_filters.ModelMultipleChoiceFilter(
field_name='device__role__slug', field_name='device__role__slug',
queryset=DeviceRole.objects.all(), queryset=DeviceRole.objects.all(),
to_field_name='slug', to_field_name='slug',

View File

@ -88,6 +88,8 @@ class Cable(PrimaryModel):
null=True null=True
) )
clone_fields = ('tenant', 'type',)
class Meta: class Meta:
ordering = ('pk',) ordering = ('pk',)
verbose_name = _('cable') verbose_name = _('cable')

View File

@ -63,7 +63,10 @@ class DeviceRoleTable(NetBoxTable):
verbose_name=_('VMs') verbose_name=_('VMs')
) )
color = columns.ColorColumn() color = columns.ColorColumn()
vm_role = columns.BooleanColumn() vm_role = columns.BooleanColumn(
verbose_name=_('VM role'),
false_mark=None
)
config_template = tables.Column( config_template = tables.Column(
linkify=True linkify=True
) )
@ -329,6 +332,7 @@ class CableTerminationTable(NetBoxTable):
) )
mark_connected = columns.BooleanColumn( mark_connected = columns.BooleanColumn(
verbose_name=_('Mark Connected'), verbose_name=_('Mark Connected'),
false_mark=None
) )
class Meta: class Meta:
@ -586,7 +590,8 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
} }
) )
mgmt_only = columns.BooleanColumn( mgmt_only = columns.BooleanColumn(
verbose_name=_('Management Only') verbose_name=_('Management Only'),
false_mark=None
) )
speed_formatted = columns.TemplateColumn( speed_formatted = columns.TemplateColumn(
template_code='{% load helpers %}{{ value|humanize_speed }}', template_code='{% load helpers %}{{ value|humanize_speed }}',
@ -913,6 +918,7 @@ class InventoryItemTable(DeviceComponentTable):
) )
discovered = columns.BooleanColumn( discovered = columns.BooleanColumn(
verbose_name=_('Discovered'), verbose_name=_('Discovered'),
false_mark=None
) )
parent = tables.Column( parent = tables.Column(
linkify=True, linkify=True,

View File

@ -86,7 +86,8 @@ class DeviceTypeTable(NetBoxTable):
linkify=True linkify=True
) )
is_full_depth = columns.BooleanColumn( is_full_depth = columns.BooleanColumn(
verbose_name=_('Full Depth') verbose_name=_('Full Depth'),
false_mark=None
) )
comments = columns.MarkdownColumn( comments = columns.MarkdownColumn(
verbose_name=_('Comments'), verbose_name=_('Comments'),
@ -98,7 +99,10 @@ class DeviceTypeTable(NetBoxTable):
verbose_name=_('U Height'), verbose_name=_('U Height'),
template_code='{{ value|floatformat }}' template_code='{{ value|floatformat }}'
) )
exclude_from_utilization = columns.BooleanColumn() exclude_from_utilization = columns.BooleanColumn(
verbose_name=_('Exclude from utilization'),
false_mark=None
)
weight = columns.TemplateColumn( weight = columns.TemplateColumn(
verbose_name=_('Weight'), verbose_name=_('Weight'),
template_code=WEIGHT, template_code=WEIGHT,
@ -221,7 +225,8 @@ class InterfaceTemplateTable(ComponentTemplateTable):
verbose_name=_('Enabled'), verbose_name=_('Enabled'),
) )
mgmt_only = columns.BooleanColumn( mgmt_only = columns.BooleanColumn(
verbose_name=_('Management Only') verbose_name=_('Management Only'),
false_mark=None
) )
actions = columns.ActionsColumn( actions = columns.ActionsColumn(
actions=('edit', 'delete'), actions=('edit', 'delete'),

View File

@ -32,11 +32,11 @@ class DeviceComponentFilterSetTests:
params = {'device_type': [device_types[0].model, device_types[1].model]} params = {'device_type': [device_types[0].model, device_types[1].model]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_role(self): def test_device_role(self):
role = DeviceRole.objects.all()[:2] role = DeviceRole.objects.all()[:2]
params = {'role_id': [role[0].pk, role[1].pk]} params = {'device_role_id': [role[0].pk, role[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'role': [role[0].slug, role[1].slug]} params = {'device_role': [role[0].slug, role[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -4693,6 +4693,13 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'device_type': [device_types[0].model, device_types[1].model]} params = {'device_type': [device_types[0].model, device_types[1].model]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
def test_device_role(self):
role = DeviceRole.objects.all()[:2]
params = {'device_role_id': [role[0].pk, role[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
params = {'device_role': [role[0].slug, role[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
def test_role(self): def test_role(self):
role = DeviceRole.objects.all()[:2] role = DeviceRole.objects.all()[:2]
params = {'role_id': [role[0].pk, role[1].pk]} params = {'role_id': [role[0].pk, role[1].pk]}

View File

@ -38,7 +38,7 @@ class ScriptSerializer(ValidatedModelSerializer):
def get_display(self, obj): def get_display(self, obj):
return f'{obj.name} ({obj.module})' return f'{obj.name} ({obj.module})'
@extend_schema_field(serializers.CharField()) @extend_schema_field(serializers.CharField(allow_null=True))
def get_description(self, obj): def get_description(self, obj):
if obj.python_class: if obj.python_class:
return obj.python_class().description return obj.python_class().description

View File

@ -251,6 +251,10 @@ class ObjectListWidget(DashboardWidget):
def render(self, request): def render(self, request):
app_label, model_name = self.config['model'].split('.') app_label, model_name = self.config['model'].split('.')
model = ObjectType.objects.get_by_natural_key(app_label, model_name).model_class() model = ObjectType.objects.get_by_natural_key(app_label, model_name).model_class()
if not model:
logger.debug(f"Dashboard Widget model_class not found: {app_label}:{model_name}")
return
viewname = get_viewname(model, action='list') viewname = get_viewname(model, action='list')
# Evaluate user's permission. Note that this controls only whether the HTMX element is # Evaluate user's permission. Note that this controls only whether the HTMX element is

View File

@ -54,7 +54,8 @@ class CustomFieldTable(NetBoxTable):
verbose_name=_('Object Types') verbose_name=_('Object Types')
) )
required = columns.BooleanColumn( required = columns.BooleanColumn(
verbose_name=_('Required') verbose_name=_('Required'),
false_mark=None
) )
ui_visible = columns.ChoiceFieldColumn( ui_visible = columns.ChoiceFieldColumn(
verbose_name=_('Visible') verbose_name=_('Visible')
@ -79,6 +80,7 @@ class CustomFieldTable(NetBoxTable):
) )
is_cloneable = columns.BooleanColumn( is_cloneable = columns.BooleanColumn(
verbose_name=_('Is Cloneable'), verbose_name=_('Is Cloneable'),
false_mark=None
) )
validation_minimum = tables.Column( validation_minimum = tables.Column(
verbose_name=_('Minimum Value'), verbose_name=_('Minimum Value'),
@ -125,6 +127,7 @@ class CustomFieldChoiceSetTable(NetBoxTable):
) )
order_alphabetically = columns.BooleanColumn( order_alphabetically = columns.BooleanColumn(
verbose_name=_('Order Alphabetically'), verbose_name=_('Order Alphabetically'),
false_mark=None
) )
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
@ -149,6 +152,7 @@ class CustomLinkTable(NetBoxTable):
) )
new_window = columns.BooleanColumn( new_window = columns.BooleanColumn(
verbose_name=_('New Window'), verbose_name=_('New Window'),
false_mark=None
) )
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
@ -170,6 +174,7 @@ class ExportTemplateTable(NetBoxTable):
) )
as_attachment = columns.BooleanColumn( as_attachment = columns.BooleanColumn(
verbose_name=_('As Attachment'), verbose_name=_('As Attachment'),
false_mark=None
) )
data_source = tables.Column( data_source = tables.Column(
verbose_name=_('Data Source'), verbose_name=_('Data Source'),
@ -238,6 +243,7 @@ class SavedFilterTable(NetBoxTable):
) )
shared = columns.BooleanColumn( shared = columns.BooleanColumn(
verbose_name=_('Shared'), verbose_name=_('Shared'),
false_mark=None
) )
def value_parameters(self, value): def value_parameters(self, value):

View File

@ -86,7 +86,8 @@ class RIRTable(NetBoxTable):
linkify=True linkify=True
) )
is_private = columns.BooleanColumn( is_private = columns.BooleanColumn(
verbose_name=_('Private') verbose_name=_('Private'),
false_mark=None
) )
aggregate_count = columns.LinkedCountColumn( aggregate_count = columns.LinkedCountColumn(
viewname='ipam:aggregate_list', viewname='ipam:aggregate_list',
@ -258,10 +259,12 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable):
linkify=True linkify=True
) )
is_pool = columns.BooleanColumn( is_pool = columns.BooleanColumn(
verbose_name=_('Pool') verbose_name=_('Pool'),
false_mark=None
) )
mark_utilized = columns.BooleanColumn( mark_utilized = columns.BooleanColumn(
verbose_name=_('Marked Utilized') verbose_name=_('Marked Utilized'),
false_mark=None
) )
utilization = PrefixUtilizationColumn( utilization = PrefixUtilizationColumn(
verbose_name=_('Utilization'), verbose_name=_('Utilization'),
@ -314,7 +317,8 @@ class IPRangeTable(TenancyColumnsMixin, NetBoxTable):
linkify=True linkify=True
) )
mark_utilized = columns.BooleanColumn( mark_utilized = columns.BooleanColumn(
verbose_name=_('Marked Utilized') verbose_name=_('Marked Utilized'),
false_mark=None
) )
utilization = columns.UtilizationColumn( utilization = columns.UtilizationColumn(
verbose_name=_('Utilization'), verbose_name=_('Utilization'),
@ -386,7 +390,8 @@ class IPAddressTable(TenancyColumnsMixin, NetBoxTable):
assigned = columns.BooleanColumn( assigned = columns.BooleanColumn(
accessor='assigned_object_id', accessor='assigned_object_id',
linkify=lambda record: record.assigned_object.get_absolute_url(), linkify=lambda record: record.assigned_object.get_absolute_url(),
verbose_name=_('Assigned') verbose_name=_('Assigned'),
false_mark=None
) )
comments = columns.MarkdownColumn( comments = columns.MarkdownColumn(
verbose_name=_('Comments'), verbose_name=_('Comments'),

View File

@ -215,6 +215,7 @@ class InterfaceVLANTable(NetBoxTable):
) )
tagged = columns.BooleanColumn( tagged = columns.BooleanColumn(
verbose_name=_('Tagged'), verbose_name=_('Tagged'),
false_mark=None
) )
site = tables.Column( site = tables.Column(
verbose_name=_('Site'), verbose_name=_('Site'),

View File

@ -30,7 +30,8 @@ class VRFTable(TenancyColumnsMixin, NetBoxTable):
verbose_name=_('RD') verbose_name=_('RD')
) )
enforce_unique = columns.BooleanColumn( enforce_unique = columns.BooleanColumn(
verbose_name=_('Unique') verbose_name=_('Unique'),
false_mark=None
) )
import_targets = columns.TemplateColumn( import_targets = columns.TemplateColumn(
verbose_name=_('Import Targets'), verbose_name=_('Import Targets'),

View File

@ -49,12 +49,15 @@ AUTH_BACKEND_ATTRS = {
'okta-openidconnect': ('Okta (OIDC)', None), 'okta-openidconnect': ('Okta (OIDC)', None),
'salesforce-oauth2': ('Salesforce', 'salesforce'), 'salesforce-oauth2': ('Salesforce', 'salesforce'),
} }
# Override with potential user configuration
AUTH_BACKEND_ATTRS.update(getattr(settings, 'SOCIAL_AUTH_BACKEND_ATTRS', {}))
def get_auth_backend_display(name): def get_auth_backend_display(name):
""" """
Return the user-friendly name and icon name for a remote authentication backend, if known. Defaults to the Return the user-friendly name and icon name for a remote authentication backend, if
raw backend name and no icon. known. Obtained from the defaults dictionary AUTH_BACKEND_ATTRS, overridden by the
setting `SOCIAL_AUTH_BACKEND_ATTRS`. Defaults to the raw backend name and no icon.
""" """
return AUTH_BACKEND_ATTRS.get(name, (name, None)) return AUTH_BACKEND_ATTRS.get(name, (name, None))

View File

@ -742,11 +742,16 @@ RQ_QUEUES.update({
# Supported translation languages # Supported translation languages
LANGUAGES = ( LANGUAGES = (
('cs', _('Czech')),
('da', _('Danish')),
('de', _('German')), ('de', _('German')),
('en', _('English')), ('en', _('English')),
('es', _('Spanish')), ('es', _('Spanish')),
('fr', _('French')), ('fr', _('French')),
('it', _('Italian')),
('ja', _('Japanese')), ('ja', _('Japanese')),
('nl', _('Dutch')),
('pl', _('Polish')),
('pt', _('Portuguese')), ('pt', _('Portuguese')),
('ru', _('Russian')), ('ru', _('Russian')),
('tr', _('Turkish')), ('tr', _('Turkish')),

View File

@ -194,14 +194,23 @@ class BooleanColumn(tables.Column):
Custom implementation of BooleanColumn to render a nicely-formatted checkmark or X icon instead of a Unicode Custom implementation of BooleanColumn to render a nicely-formatted checkmark or X icon instead of a Unicode
character. character.
""" """
TRUE_MARK = mark_safe('<span class="text-success"><i class="mdi mdi-check-bold"></i></span>')
FALSE_MARK = mark_safe('<span class="text-danger"><i class="mdi mdi-close-thick"></i></span>')
EMPTY_MARK = mark_safe('<span class="text-muted">&mdash;</span>') # Placeholder
def __init__(self, *args, true_mark=TRUE_MARK, false_mark=FALSE_MARK, **kwargs):
self.true_mark = true_mark
self.false_mark = false_mark
super().__init__(*args, **kwargs)
def render(self, value): def render(self, value):
if value: if value is None:
rendered = '<span class="text-success"><i class="mdi mdi-check-bold"></i></span>' return self.EMPTY_MARK
elif value is None: if value and self.true_mark:
rendered = '<span class="text-muted">&mdash;</span>' return self.true_mark
else: if not value and self.false_mark:
rendered = '<span class="text-danger"><i class="mdi mdi-close-thick"></i></span>' return self.false_mark
return mark_safe(rendered) return self.EMPTY_MARK
def value(self, value): def value(self, value):
return str(value) return str(value)
@ -249,7 +258,7 @@ class ActionsColumn(tables.Column):
def render(self, record, table, **kwargs): def render(self, record, table, **kwargs):
# Skip dummy records (e.g. available VLANs) or those with no actions # Skip dummy records (e.g. available VLANs) or those with no actions
if not getattr(record, 'pk', None) or not self.actions: if not getattr(record, 'pk', None) or not (self.actions or self.extra_buttons):
return '' return ''
model = table.Meta.model model = table.Meta.model

Binary file not shown.

Binary file not shown.

View File

@ -27,10 +27,10 @@
"bootstrap": "5.3.3", "bootstrap": "5.3.3",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"flatpickr": "4.6.13", "flatpickr": "4.6.13",
"gridstack": "10.3.0", "gridstack": "10.3.1",
"htmx.org": "1.9.12", "htmx.org": "1.9.12",
"query-string": "9.0.0", "query-string": "9.1.0",
"sass": "1.77.6", "sass": "1.77.8",
"tom-select": "2.3.1", "tom-select": "2.3.1",
"typeface-inter": "3.18.1", "typeface-inter": "3.18.1",
"typeface-roboto-mono": "1.1.13" "typeface-roboto-mono": "1.1.13"

View File

@ -1754,10 +1754,10 @@ graphql@16.8.1:
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07"
integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==
gridstack@10.3.0: gridstack@10.3.1:
version "10.3.0" version "10.3.1"
resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-10.3.0.tgz#8fa065f896d0a880c5c54c24d189f3197184488a" resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-10.3.1.tgz#4ed704279c40094fc1b9e3318f20b573f2fe9f40"
integrity sha512-eGKsmU2TppV4coyDu9IIdIkm4qjgLLdjlEOFwQyQMuSwfOpzSfLdPc8du0HuebGr7CvAIrJxN4lBOmGrWSBg9g== integrity sha512-Ra82k/88gdeiu3ZP40COS4bI4sGhNQlZAaAQ6szfPfr68zVpsXxiyLKr5zYcTpKX4jjcwyNsNNdcV1tDJc71fA==
has-bigints@^1.0.1, has-bigints@^1.0.2: has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2" version "1.0.2"
@ -2346,10 +2346,10 @@ punycode@^2.1.0:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
query-string@9.0.0: query-string@9.1.0:
version "9.0.0" version "9.1.0"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.0.0.tgz#1fe177cd95545600f0deab93f5fb02fd4e3e7273" resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.1.0.tgz#5f12a4653a4ba56021e113b5cf58e56581823e7a"
integrity sha512-4EWwcRGsO2H+yzq6ddHcVqkCQ2EFUSfDMEjF8ryp8ReymyZhIuaFRGLomeOQLkrzacMHoyky2HW0Qe30UbzkKw== integrity sha512-t6dqMECpCkqfyv2FfwVS1xcB6lgXW/0XZSaKdsCNGYkqMO76AFiJEg4vINzoDKcZa6MS7JX+OHIjwh06K5vczw==
dependencies: dependencies:
decode-uri-component "^0.4.1" decode-uri-component "^0.4.1"
filter-obj "^5.1.0" filter-obj "^5.1.0"
@ -2482,10 +2482,10 @@ safe-regex-test@^1.0.3:
es-errors "^1.3.0" es-errors "^1.3.0"
is-regex "^1.1.4" is-regex "^1.1.4"
sass@1.77.6: sass@1.77.8:
version "1.77.6" version "1.77.8"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.6.tgz#898845c1348078c2e6d1b64f9ee06b3f8bd489e4" resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.8.tgz#9f18b449ea401759ef7ec1752a16373e296b52bd"
integrity sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q== integrity sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==
dependencies: dependencies:
chokidar ">=3.0.0 <4.0.0" chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0" immutable "^4.0.0"

View File

@ -7,7 +7,11 @@
<html <html
lang="en" lang="en"
data-netbox-url-name="{{ request.resolver_match.url_name }}" data-netbox-url-name="{{ request.resolver_match.url_name }}"
data-netbox-base-path="{{ settings.BASE_PATH }}" data-netbox-version="{{ settings.VERSION }}"
{% if request.user.is_authenticated %}
data-netbox-user-name="{{ request.user.username }}"
data-netbox-user-id="{{ request.user.pk }}"
{% endif %}
> >
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />

View File

@ -4,6 +4,18 @@
{% load perms %} {% load perms %}
{% load i18n %} {% load i18n %}
{% block breadcrumbs %}
{{ block.super }}
<li class="breadcrumb-item">
<a href="{% url 'core:job_list' %}?object_type={{ object.object_type_id }}">{{ object.object|meta:"verbose_name_plural"|bettertitle }}</a>
</li>
{% with parent_jobs_viewname=object.object|viewname:"jobs" %}
<li class="breadcrumb-item">
<a href="{% url parent_jobs_viewname pk=object.object.pk %}">{{ object.object }}</a>
</li>
{% endwith %}
{% endblock breadcrumbs %}
{% block control-buttons %} {% block control-buttons %}
{% if request.user|can_delete:object %} {% if request.user|can_delete:object %}
{% delete_button object %} {% delete_button object %}

View File

@ -31,7 +31,7 @@
<td class="d-flex justify-content-between align-items-start"> <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={% firstof object.parent_bay.device.pk object.pk %}" class="btn btn-primary btn-sm d-print-none" title="{% trans "Highlight device in rack" %}">
<i class="mdi mdi-view-day-outline"></i> <i class="mdi mdi-view-day-outline"></i>
</a> </a>
{% else %} {% else %}

View File

@ -24,7 +24,7 @@
<table class="table table-hover"> <table class="table table-hover">
{% for test, data in tests.items %} {% for test, data in tests.items %}
<tr> <tr>
<td class="font-monospace"><a href="#{{ test }}">{{ test }}</a></td> <td class="font-monospace">{{ test }}</td>
<td class="text-end report-stats"> <td class="text-end report-stats">
<span class="badge text-bg-success">{{ data.success }}</span> <span class="badge text-bg-success">{{ data.success }}</span>
<span class="badge text-bg-info">{{ data.info }}</span> <span class="badge text-bg-info">{{ data.info }}</span>

View File

@ -78,7 +78,8 @@
{% for backend in auth_backends %} {% for backend in auth_backends %}
<div class="col"> <div class="col">
<a href="{{ backend.url }}" class="btn w-100"> <a href="{{ backend.url }}" class="btn w-100">
{% if backend.icon_name %}<i class="mdi mdi-{{ backend.icon_name }}"></i>{% endif %} {% if backend.icon_name %}<i class="mdi mdi-{{ backend.icon_name }}"></i>
{% elif backend.icon_img %}<img src="{{ backend.icon_img }}" height="24" class="me-2" />{% endif %}
{{ backend.display_name }} {{ backend.display_name }}
</a> </a>
</div> </div>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,21 +6,21 @@
# Translators: # Translators:
# Quentin Laurent, 2024 # Quentin Laurent, 2024
# Xavier W, 2024 # Xavier W, 2024
# Jeremy Stretch, 2024
# Jonathan Senecal, 2024 # Jonathan Senecal, 2024
# Lou Lecrivain, 2024 # Lou Lecrivain, 2024
# Jean Benoit <jean@unistra.fr>, 2024 # Jean Benoit <jean@unistra.fr>, 2024
# thomas rivemale, 2024 # thomas rivemale, 2024
# Jeff Gehlbach, 2024 # Jeff Gehlbach, 2024
# Jeremy Stretch, 2024
# #
#, fuzzy #, fuzzy
msgid "" 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-07-09 05:02+0000\n" "POT-Creation-Date: 2024-07-11 05:01+0000\n"
"PO-Revision-Date: 2023-10-30 17:48+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n"
"Last-Translator: Jeff Gehlbach, 2024\n" "Last-Translator: Jeremy Stretch, 2024\n"
"Language-Team: French (https://app.transifex.com/netbox-community/teams/178115/fr/)\n" "Language-Team: French (https://app.transifex.com/netbox-community/teams/178115/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -69,7 +69,7 @@ msgstr "Dernière utilisation"
msgid "Allowed IPs" msgid "Allowed IPs"
msgstr "IP autorisées" msgstr "IP autorisées"
#: netbox/account/views.py:204 #: netbox/account/views.py:214
msgid "Your preferences have been updated." msgid "Your preferences have been updated."
msgstr "Vos préférences ont été mises à jour." msgstr "Vos préférences ont été mises à jour."
@ -1519,16 +1519,16 @@ msgstr "Mot de passe"
msgid "Branch" msgid "Branch"
msgstr "Branche" msgstr "Branche"
#: netbox/core/data_backends.py:105 #: netbox/core/data_backends.py:106
#, python-brace-format #, python-brace-format
msgid "Fetching remote data failed ({name}): {error}" msgid "Fetching remote data failed ({name}): {error}"
msgstr "La récupération des données distantes a échoué ({name}) : {error}" msgstr "La récupération des données distantes a échoué ({name}) : {error}"
#: netbox/core/data_backends.py:118 #: netbox/core/data_backends.py:119
msgid "AWS access key ID" msgid "AWS access key ID"
msgstr "ID de clé d'accès AWS" msgstr "ID de clé d'accès AWS"
#: netbox/core/data_backends.py:122 #: netbox/core/data_backends.py:123
msgid "AWS secret access key" msgid "AWS secret access key"
msgstr "Clé d'accès secrète AWS" msgstr "Clé d'accès secrète AWS"
@ -1877,7 +1877,7 @@ msgstr ""
msgid "last updated" msgid "last updated"
msgstr "dernière mise à jour" msgstr "dernière mise à jour"
#: netbox/core/models/data.py:296 netbox/dcim/models/cables.py:442 #: netbox/core/models/data.py:296 netbox/dcim/models/cables.py:444
msgid "path" msgid "path"
msgstr "chemin" msgstr "chemin"
@ -4656,53 +4656,53 @@ msgstr "longueur"
msgid "length unit" msgid "length unit"
msgstr "unité de longueur" msgstr "unité de longueur"
#: netbox/dcim/models/cables.py:93 #: netbox/dcim/models/cables.py:95
msgid "cable" msgid "cable"
msgstr "câble" msgstr "câble"
#: netbox/dcim/models/cables.py:94 #: netbox/dcim/models/cables.py:96
msgid "cables" msgid "cables"
msgstr "câbles" msgstr "câbles"
#: netbox/dcim/models/cables.py:163 #: netbox/dcim/models/cables.py:165
msgid "Must specify a unit when setting a cable length" msgid "Must specify a unit when setting a cable length"
msgstr "" msgstr ""
"Vous devez spécifier une unité lors du réglage de la longueur du câble" "Vous devez spécifier une unité lors du réglage de la longueur du câble"
#: netbox/dcim/models/cables.py:166 #: netbox/dcim/models/cables.py:168
msgid "Must define A and B terminations when creating a new cable." msgid "Must define A and B terminations when creating a new cable."
msgstr "" msgstr ""
"Vous devez définir les terminaisons A et B lors de la création d'un nouveau " "Vous devez définir les terminaisons A et B lors de la création d'un nouveau "
"câble." "câble."
#: netbox/dcim/models/cables.py:173 #: netbox/dcim/models/cables.py:175
msgid "Cannot connect different termination types to same end of cable." msgid "Cannot connect different termination types to same end of cable."
msgstr "" msgstr ""
"Impossible de connecter différents types de terminaisons à la même extrémité" "Impossible de connecter différents types de terminaisons à la même extrémité"
" du câble." " du câble."
#: netbox/dcim/models/cables.py:181 #: netbox/dcim/models/cables.py:183
#, python-brace-format #, python-brace-format
msgid "Incompatible termination types: {type_a} and {type_b}" msgid "Incompatible termination types: {type_a} and {type_b}"
msgstr "Types de terminaison incompatibles : {type_a} et {type_b}" msgstr "Types de terminaison incompatibles : {type_a} et {type_b}"
#: netbox/dcim/models/cables.py:191 #: netbox/dcim/models/cables.py:193
msgid "A and B terminations cannot connect to the same object." msgid "A and B terminations cannot connect to the same object."
msgstr "Les terminaisons A et B ne peuvent pas se connecter au même objet." msgstr "Les terminaisons A et B ne peuvent pas se connecter au même objet."
#: netbox/dcim/models/cables.py:258 netbox/ipam/models/asns.py:37 #: netbox/dcim/models/cables.py:260 netbox/ipam/models/asns.py:37
msgid "end" msgid "end"
msgstr "fin" msgstr "fin"
#: netbox/dcim/models/cables.py:311 #: netbox/dcim/models/cables.py:313
msgid "cable termination" msgid "cable termination"
msgstr "terminaison de câble" msgstr "terminaison de câble"
#: netbox/dcim/models/cables.py:312 #: netbox/dcim/models/cables.py:314
msgid "cable terminations" msgid "cable terminations"
msgstr "terminaisons de câble" msgstr "terminaisons de câble"
#: netbox/dcim/models/cables.py:331 #: netbox/dcim/models/cables.py:333
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"Duplicate termination found for {app_label}.{model} {termination_id}: cable " "Duplicate termination found for {app_label}.{model} {termination_id}: cable "
@ -4711,34 +4711,34 @@ msgstr ""
"Un doublon de terminaison a été trouvé pour {app_label}.{model} " "Un doublon de terminaison a été trouvé pour {app_label}.{model} "
"{termination_id}: câble {cable_pk}" "{termination_id}: câble {cable_pk}"
#: netbox/dcim/models/cables.py:341 #: netbox/dcim/models/cables.py:343
#, python-brace-format #, python-brace-format
msgid "Cables cannot be terminated to {type_display} interfaces" msgid "Cables cannot be terminated to {type_display} interfaces"
msgstr "Les câbles ne peuvent pas être raccordés à {type_display} interfaces" msgstr "Les câbles ne peuvent pas être raccordés à {type_display} interfaces"
#: netbox/dcim/models/cables.py:348 #: netbox/dcim/models/cables.py:350
msgid "Circuit terminations attached to a provider network may not be cabled." msgid "Circuit terminations attached to a provider network may not be cabled."
msgstr "" msgstr ""
"Les terminaisons de circuit connectées au réseau d'un fournisseur peuvent ne" "Les terminaisons de circuit connectées au réseau d'un fournisseur peuvent ne"
" pas être câblées." " pas être câblées."
#: netbox/dcim/models/cables.py:446 netbox/extras/models/configs.py:50 #: netbox/dcim/models/cables.py:448 netbox/extras/models/configs.py:50
msgid "is active" msgid "is active"
msgstr "est actif" msgstr "est actif"
#: netbox/dcim/models/cables.py:450 #: netbox/dcim/models/cables.py:452
msgid "is complete" msgid "is complete"
msgstr "est terminé" msgstr "est terminé"
#: netbox/dcim/models/cables.py:454 #: netbox/dcim/models/cables.py:456
msgid "is split" msgid "is split"
msgstr "est divisé" msgstr "est divisé"
#: netbox/dcim/models/cables.py:462 #: netbox/dcim/models/cables.py:464
msgid "cable path" msgid "cable path"
msgstr "chemin de câble" msgstr "chemin de câble"
#: netbox/dcim/models/cables.py:463 #: netbox/dcim/models/cables.py:465
msgid "cable paths" msgid "cable paths"
msgstr "chemins de câbles" msgstr "chemins de câbles"
@ -6983,43 +6983,43 @@ msgstr ""
"Format non valide. Les paramètres d'URL doivent être transmis sous forme de " "Format non valide. Les paramètres d'URL doivent être transmis sous forme de "
"dictionnaire." "dictionnaire."
#: netbox/extras/dashboard/widgets.py:284 #: netbox/extras/dashboard/widgets.py:288
msgid "RSS Feed" msgid "RSS Feed"
msgstr "Fil RSS" msgstr "Fil RSS"
#: netbox/extras/dashboard/widgets.py:289 #: netbox/extras/dashboard/widgets.py:293
msgid "Embed an RSS feed from an external website." msgid "Embed an RSS feed from an external website."
msgstr "Intégrez un flux RSS provenant d'un site Web externe." msgstr "Intégrez un flux RSS provenant d'un site Web externe."
#: netbox/extras/dashboard/widgets.py:296 #: netbox/extras/dashboard/widgets.py:300
msgid "Feed URL" msgid "Feed URL"
msgstr "URL du flux" msgstr "URL du flux"
#: netbox/extras/dashboard/widgets.py:301 #: netbox/extras/dashboard/widgets.py:305
msgid "The maximum number of objects to display" msgid "The maximum number of objects to display"
msgstr "Le nombre maximum d'objets à afficher" msgstr "Le nombre maximum d'objets à afficher"
#: netbox/extras/dashboard/widgets.py:306 #: netbox/extras/dashboard/widgets.py:310
msgid "How long to stored the cached content (in seconds)" msgid "How long to stored the cached content (in seconds)"
msgstr "Durée de conservation du contenu mis en cache (en secondes)" msgstr "Durée de conservation du contenu mis en cache (en secondes)"
#: netbox/extras/dashboard/widgets.py:358 #: netbox/extras/dashboard/widgets.py:362
#: netbox/templates/account/base.html:10 #: netbox/templates/account/base.html:10
#: netbox/templates/account/bookmarks.html:7 #: netbox/templates/account/bookmarks.html:7
#: netbox/templates/inc/user_menu.html:30 #: netbox/templates/inc/user_menu.html:30
msgid "Bookmarks" msgid "Bookmarks"
msgstr "Signets" msgstr "Signets"
#: netbox/extras/dashboard/widgets.py:362 #: netbox/extras/dashboard/widgets.py:366
msgid "Show your personal bookmarks" msgid "Show your personal bookmarks"
msgstr "Afficher vos favoris personnels" msgstr "Afficher vos favoris personnels"
#: netbox/extras/events.py:134 #: netbox/extras/events.py:137
#, python-brace-format #, python-brace-format
msgid "Unknown action type for an event rule: {action_type}" msgid "Unknown action type for an event rule: {action_type}"
msgstr "Type d'action inconnu pour une règle d'événement : {action_type}" msgstr "Type d'action inconnu pour une règle d'événement : {action_type}"
#: netbox/extras/events.py:182 #: netbox/extras/events.py:185
#, python-brace-format #, python-brace-format
msgid "Cannot import events pipeline {name} error: {error}" msgid "Cannot import events pipeline {name} error: {error}"
msgstr "" msgstr ""
@ -10048,7 +10048,7 @@ msgstr ""
"Valeur non valide. Spécifiez un type de contenu comme " "Valeur non valide. Spécifiez un type de contenu comme "
"«<app_label>.<model_name>'." "«<app_label>.<model_name>'."
#: netbox/netbox/authentication/__init__.py:138 #: netbox/netbox/authentication/__init__.py:141
#, python-brace-format #, python-brace-format
msgid "Invalid permission {permission} for model {model}" msgid "Invalid permission {permission} for model {model}"
msgstr "Autorisation non valide {permission} pour modèle {model}" msgstr "Autorisation non valide {permission} pour modèle {model}"
@ -10357,7 +10357,7 @@ msgstr "Type (s) d'objet"
#: netbox/netbox/forms/__init__.py:40 #: netbox/netbox/forms/__init__.py:40
msgid "Lookup" msgid "Lookup"
msgstr "" msgstr "Chercher"
#: netbox/netbox/forms/base.py:88 #: netbox/netbox/forms/base.py:88
msgid "" msgid ""
@ -10906,43 +10906,43 @@ msgstr "Impossible d'ajouter des magasins au registre après l'initialisation"
msgid "Cannot delete stores from registry" msgid "Cannot delete stores from registry"
msgstr "Impossible de supprimer des magasins du registre" msgstr "Impossible de supprimer des magasins du registre"
#: netbox/netbox/settings.py:741 #: netbox/netbox/settings.py:742
msgid "German" msgid "German"
msgstr "allemand" msgstr "allemand"
#: netbox/netbox/settings.py:742 #: netbox/netbox/settings.py:743
msgid "English" msgid "English"
msgstr "Anglais" msgstr "Anglais"
#: netbox/netbox/settings.py:743 #: netbox/netbox/settings.py:744
msgid "Spanish" msgid "Spanish"
msgstr "espagnol" msgstr "espagnol"
#: netbox/netbox/settings.py:744 #: netbox/netbox/settings.py:745
msgid "French" msgid "French"
msgstr "français" msgstr "français"
#: netbox/netbox/settings.py:745 #: netbox/netbox/settings.py:746
msgid "Japanese" msgid "Japanese"
msgstr "japonais" msgstr "japonais"
#: netbox/netbox/settings.py:746 #: netbox/netbox/settings.py:747
msgid "Portuguese" msgid "Portuguese"
msgstr "portugais" msgstr "portugais"
#: netbox/netbox/settings.py:747 #: netbox/netbox/settings.py:748
msgid "Russian" msgid "Russian"
msgstr "russe" msgstr "russe"
#: netbox/netbox/settings.py:748 #: netbox/netbox/settings.py:749
msgid "Turkish" msgid "Turkish"
msgstr "Turc" msgstr "Turc"
#: netbox/netbox/settings.py:749 #: netbox/netbox/settings.py:750
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukrainien" msgstr "Ukrainien"
#: netbox/netbox/settings.py:750 #: netbox/netbox/settings.py:751
msgid "Chinese" msgid "Chinese"
msgstr "chinois" msgstr "chinois"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,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-07-09 05:02+0000\n" "POT-Creation-Date: 2024-07-11 05:01+0000\n"
"PO-Revision-Date: 2023-10-30 17:48+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n"
"Last-Translator: Artem Kotik, 2024\n" "Last-Translator: Artem Kotik, 2024\n"
"Language-Team: Russian (https://app.transifex.com/netbox-community/teams/178115/ru/)\n" "Language-Team: Russian (https://app.transifex.com/netbox-community/teams/178115/ru/)\n"
@ -70,7 +70,7 @@ msgstr "Последнее использование"
msgid "Allowed IPs" msgid "Allowed IPs"
msgstr "Разрешенные IP-адреса" msgstr "Разрешенные IP-адреса"
#: netbox/account/views.py:204 #: netbox/account/views.py:214
msgid "Your preferences have been updated." msgid "Your preferences have been updated."
msgstr "Ваши настройки были обновлены." msgstr "Ваши настройки были обновлены."
@ -263,7 +263,7 @@ msgstr "Аккаунт провайдера (ID)"
#: netbox/circuits/filtersets.py:171 #: netbox/circuits/filtersets.py:171
msgid "Provider account (account)" msgid "Provider account (account)"
msgstr "Учетная запись провайдера (учетная запись)" msgstr "Учетная запись провайдера"
#: netbox/circuits/filtersets.py:176 #: netbox/circuits/filtersets.py:176
msgid "Provider network (ID)" msgid "Provider network (ID)"
@ -1520,16 +1520,16 @@ msgstr "Пароль"
msgid "Branch" msgid "Branch"
msgstr "Ветка" msgstr "Ветка"
#: netbox/core/data_backends.py:105 #: netbox/core/data_backends.py:106
#, python-brace-format #, python-brace-format
msgid "Fetching remote data failed ({name}): {error}" msgid "Fetching remote data failed ({name}): {error}"
msgstr "Не удалось получить удаленные данные ({name}): {error}" msgstr "Не удалось получить удаленные данные ({name}): {error}"
#: netbox/core/data_backends.py:118 #: netbox/core/data_backends.py:119
msgid "AWS access key ID" msgid "AWS access key ID"
msgstr "ID ключа доступа AWS" msgstr "ID ключа доступа AWS"
#: netbox/core/data_backends.py:122 #: netbox/core/data_backends.py:123
msgid "AWS secret access key" msgid "AWS secret access key"
msgstr "Секретный ключ доступа AWS" msgstr "Секретный ключ доступа AWS"
@ -1872,7 +1872,7 @@ msgstr ""
msgid "last updated" msgid "last updated"
msgstr "последнее обновление" msgstr "последнее обновление"
#: netbox/core/models/data.py:296 netbox/dcim/models/cables.py:442 #: netbox/core/models/data.py:296 netbox/dcim/models/cables.py:444
msgid "path" msgid "path"
msgstr "путь" msgstr "путь"
@ -4652,50 +4652,50 @@ msgstr "Длина"
msgid "length unit" msgid "length unit"
msgstr "длина единицы" msgstr "длина единицы"
#: netbox/dcim/models/cables.py:93 #: netbox/dcim/models/cables.py:95
msgid "cable" msgid "cable"
msgstr "кабель" msgstr "кабель"
#: netbox/dcim/models/cables.py:94 #: netbox/dcim/models/cables.py:96
msgid "cables" msgid "cables"
msgstr "кабели" msgstr "кабели"
#: netbox/dcim/models/cables.py:163 #: netbox/dcim/models/cables.py:165
msgid "Must specify a unit when setting a cable length" msgid "Must specify a unit when setting a cable length"
msgstr "При настройке длины кабеля необходимо указать единицу измерения" msgstr "При настройке длины кабеля необходимо указать единицу измерения"
#: netbox/dcim/models/cables.py:166 #: netbox/dcim/models/cables.py:168
msgid "Must define A and B terminations when creating a new cable." msgid "Must define A and B terminations when creating a new cable."
msgstr "" msgstr ""
"При создании нового кабеля необходимо определить концевые разъемы A и B." "При создании нового кабеля необходимо определить концевые разъемы A и B."
#: netbox/dcim/models/cables.py:173 #: netbox/dcim/models/cables.py:175
msgid "Cannot connect different termination types to same end of cable." msgid "Cannot connect different termination types to same end of cable."
msgstr "" msgstr ""
"Невозможно подключить разные типы разъемов к одному и тому же концу кабеля." "Невозможно подключить разные типы разъемов к одному и тому же концу кабеля."
#: netbox/dcim/models/cables.py:181 #: netbox/dcim/models/cables.py:183
#, python-brace-format #, python-brace-format
msgid "Incompatible termination types: {type_a} and {type_b}" msgid "Incompatible termination types: {type_a} and {type_b}"
msgstr "Несовместимые типы терминации: {type_a} а также {type_b}" msgstr "Несовместимые типы терминации: {type_a} а также {type_b}"
#: netbox/dcim/models/cables.py:191 #: netbox/dcim/models/cables.py:193
msgid "A and B terminations cannot connect to the same object." msgid "A and B terminations cannot connect to the same object."
msgstr "Окончания A и B не могут подключаться к одному и тому же объекту." msgstr "Окончания A и B не могут подключаться к одному и тому же объекту."
#: netbox/dcim/models/cables.py:258 netbox/ipam/models/asns.py:37 #: netbox/dcim/models/cables.py:260 netbox/ipam/models/asns.py:37
msgid "end" msgid "end"
msgstr "конец" msgstr "конец"
#: netbox/dcim/models/cables.py:311 #: netbox/dcim/models/cables.py:313
msgid "cable termination" msgid "cable termination"
msgstr "кабельный терминатор" msgstr "кабельный терминатор"
#: netbox/dcim/models/cables.py:312 #: netbox/dcim/models/cables.py:314
msgid "cable terminations" msgid "cable terminations"
msgstr "кабельные терминаторы" msgstr "кабельные терминаторы"
#: netbox/dcim/models/cables.py:331 #: netbox/dcim/models/cables.py:333
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"Duplicate termination found for {app_label}.{model} {termination_id}: cable " "Duplicate termination found for {app_label}.{model} {termination_id}: cable "
@ -4704,34 +4704,34 @@ msgstr ""
"Обнаружен дубликат увольнения для {app_label}.{model} {termination_id}: " "Обнаружен дубликат увольнения для {app_label}.{model} {termination_id}: "
"кабель {cable_pk}" "кабель {cable_pk}"
#: netbox/dcim/models/cables.py:341 #: netbox/dcim/models/cables.py:343
#, python-brace-format #, python-brace-format
msgid "Cables cannot be terminated to {type_display} interfaces" msgid "Cables cannot be terminated to {type_display} interfaces"
msgstr "Кабели не могут быть подключены к {type_display} интерфейсов" msgstr "Кабели не могут быть подключены к {type_display} интерфейсов"
#: netbox/dcim/models/cables.py:348 #: netbox/dcim/models/cables.py:350
msgid "Circuit terminations attached to a provider network may not be cabled." msgid "Circuit terminations attached to a provider network may not be cabled."
msgstr "" msgstr ""
"Концевые разъемы, подключенные к сети провайдера, могут не подключаться к " "Концевые разъемы, подключенные к сети провайдера, могут не подключаться к "
"кабелям." "кабелям."
#: netbox/dcim/models/cables.py:446 netbox/extras/models/configs.py:50 #: netbox/dcim/models/cables.py:448 netbox/extras/models/configs.py:50
msgid "is active" msgid "is active"
msgstr "активен" msgstr "активен"
#: netbox/dcim/models/cables.py:450 #: netbox/dcim/models/cables.py:452
msgid "is complete" msgid "is complete"
msgstr "завершен" msgstr "завершен"
#: netbox/dcim/models/cables.py:454 #: netbox/dcim/models/cables.py:456
msgid "is split" msgid "is split"
msgstr "разделен" msgstr "разделен"
#: netbox/dcim/models/cables.py:462 #: netbox/dcim/models/cables.py:464
msgid "cable path" msgid "cable path"
msgstr "кабельная трасса" msgstr "кабельная трасса"
#: netbox/dcim/models/cables.py:463 #: netbox/dcim/models/cables.py:465
msgid "cable paths" msgid "cable paths"
msgstr "кабельные трассы" msgstr "кабельные трассы"
@ -5274,11 +5274,11 @@ msgstr "Нанесенное на карту положение на соотв
#: netbox/dcim/models/device_components.py:1007 #: netbox/dcim/models/device_components.py:1007
msgid "front port" msgid "front port"
msgstr "передний порт" msgstr "фронтальный порт"
#: netbox/dcim/models/device_components.py:1008 #: netbox/dcim/models/device_components.py:1008
msgid "front ports" msgid "front ports"
msgstr "передние порты" msgstr "фронтальные порты"
#: netbox/dcim/models/device_components.py:1022 #: netbox/dcim/models/device_components.py:1022
#, python-brace-format #, python-brace-format
@ -5541,7 +5541,7 @@ msgstr ""
#: netbox/dcim/models/devices.py:517 #: netbox/dcim/models/devices.py:517
msgid "platform" msgid "platform"
msgstr "платформ" msgstr "платформа"
#: netbox/dcim/models/devices.py:518 #: netbox/dcim/models/devices.py:518
msgid "platforms" msgid "platforms"
@ -5604,7 +5604,7 @@ msgstr "широта"
#: netbox/dcim/models/devices.py:711 netbox/dcim/models/devices.py:719 #: netbox/dcim/models/devices.py:711 netbox/dcim/models/devices.py:719
#: netbox/dcim/models/sites.py:212 netbox/dcim/models/sites.py:220 #: netbox/dcim/models/sites.py:212 netbox/dcim/models/sites.py:220
msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgid "GPS coordinate in decimal format (xx.yyyyyy)"
msgstr "Координата GPS в десятичном формате (xx.yyyyyy)" msgstr "GPS координата в десятичном формате (xx.yyyyyy)"
#: netbox/dcim/models/devices.py:714 netbox/dcim/models/sites.py:215 #: netbox/dcim/models/devices.py:714 netbox/dcim/models/sites.py:215
msgid "longitude" msgid "longitude"
@ -6059,15 +6059,15 @@ msgstr "Группа сайтов верхнего уровня с этой по
#: netbox/dcim/models/sites.py:115 #: netbox/dcim/models/sites.py:115
msgid "site group" msgid "site group"
msgstr "группа мест" msgstr "группа сайта"
#: netbox/dcim/models/sites.py:116 #: netbox/dcim/models/sites.py:116
msgid "site groups" msgid "site groups"
msgstr "группы мест" msgstr "группы сайтов"
#: netbox/dcim/models/sites.py:141 #: netbox/dcim/models/sites.py:141
msgid "Full name of the site" msgid "Full name of the site"
msgstr "Полное название сайта" msgstr "Полное имя сайта"
#: netbox/dcim/models/sites.py:181 netbox/dcim/models/sites.py:279 #: netbox/dcim/models/sites.py:181 netbox/dcim/models/sites.py:279
msgid "facility" msgid "facility"
@ -6095,11 +6095,11 @@ msgstr "Если отличается от физического адреса"
#: netbox/dcim/models/sites.py:238 #: netbox/dcim/models/sites.py:238
msgid "site" msgid "site"
msgstr "место" msgstr "сайт"
#: netbox/dcim/models/sites.py:239 #: netbox/dcim/models/sites.py:239
msgid "sites" msgid "sites"
msgstr "Сайты" msgstr "сайты"
#: netbox/dcim/models/sites.py:309 #: netbox/dcim/models/sites.py:309
msgid "A location with this name already exists within the specified site." msgid "A location with this name already exists within the specified site."
@ -6137,7 +6137,7 @@ msgstr "Устройство A"
#: netbox/dcim/tables/cables.py:72 netbox/wireless/tables/wirelesslink.py:31 #: netbox/dcim/tables/cables.py:72 netbox/wireless/tables/wirelesslink.py:31
msgid "Device B" msgid "Device B"
msgstr "Устройство B" msgstr "Устройство Б"
#: netbox/dcim/tables/cables.py:78 #: netbox/dcim/tables/cables.py:78
msgid "Location A" msgid "Location A"
@ -6145,7 +6145,7 @@ msgstr "Локация A"
#: netbox/dcim/tables/cables.py:84 #: netbox/dcim/tables/cables.py:84
msgid "Location B" msgid "Location B"
msgstr "Локация B" msgstr "Локация Б"
#: netbox/dcim/tables/cables.py:90 #: netbox/dcim/tables/cables.py:90
msgid "Rack A" msgid "Rack A"
@ -6153,7 +6153,7 @@ msgstr "Стойка A"
#: netbox/dcim/tables/cables.py:96 #: netbox/dcim/tables/cables.py:96
msgid "Rack B" msgid "Rack B"
msgstr "Стойка B" msgstr "Стойка Б"
#: netbox/dcim/tables/cables.py:102 #: netbox/dcim/tables/cables.py:102
msgid "Site A" msgid "Site A"
@ -6161,7 +6161,7 @@ msgstr "Сайт A"
#: netbox/dcim/tables/cables.py:108 #: netbox/dcim/tables/cables.py:108
msgid "Site B" msgid "Site B"
msgstr "Сайт B" msgstr "Сайт Б"
#: netbox/dcim/tables/connections.py:31 netbox/dcim/tables/connections.py:50 #: netbox/dcim/tables/connections.py:31 netbox/dcim/tables/connections.py:50
#: netbox/dcim/tables/connections.py:71 #: netbox/dcim/tables/connections.py:71
@ -6224,11 +6224,11 @@ msgstr "Адрес IPv6"
#: netbox/dcim/tables/devices.py:207 #: netbox/dcim/tables/devices.py:207
msgid "VC Position" msgid "VC Position"
msgstr "Позиция VC" msgstr "Позиция в шасси"
#: netbox/dcim/tables/devices.py:210 #: netbox/dcim/tables/devices.py:210
msgid "VC Priority" msgid "VC Priority"
msgstr "Приоритет VC" msgstr "Приоритет шасси"
#: netbox/dcim/tables/devices.py:217 netbox/templates/dcim/device_edit.html:38 #: netbox/dcim/tables/devices.py:217 netbox/templates/dcim/device_edit.html:38
#: netbox/templates/dcim/devicebay_populate.html:16 #: netbox/templates/dcim/devicebay_populate.html:16
@ -6275,7 +6275,7 @@ msgstr "Интерфейсы"
#: netbox/dcim/tables/devices.py:246 #: netbox/dcim/tables/devices.py:246
msgid "Front ports" msgid "Front ports"
msgstr "Передние порты" msgstr "Фронтальные порты"
#: netbox/dcim/tables/devices.py:252 #: netbox/dcim/tables/devices.py:252
msgid "Device bays" msgid "Device bays"
@ -6472,7 +6472,7 @@ msgstr "Розетки питания"
#: netbox/templates/dcim/module.html:37 #: netbox/templates/dcim/module.html:37
#: netbox/templates/dcim/moduletype/base.html:37 #: netbox/templates/dcim/moduletype/base.html:37
msgid "Front Ports" msgid "Front Ports"
msgstr "Передние порты" msgstr "Фронтальные порты"
#: netbox/dcim/tables/devicetypes.py:131 netbox/dcim/views.py:1018 #: netbox/dcim/tables/devicetypes.py:131 netbox/dcim/views.py:1018
#: netbox/dcim/views.py:1257 netbox/dcim/views.py:1946 #: netbox/dcim/views.py:1257 netbox/dcim/views.py:1946
@ -6957,43 +6957,43 @@ msgstr "Количество отображаемых объектов по ум
msgid "Invalid format. URL parameters must be passed as a dictionary." msgid "Invalid format. URL parameters must be passed as a dictionary."
msgstr "Неверный формат. Параметры URL должны быть переданы в виде словаря." msgstr "Неверный формат. Параметры URL должны быть переданы в виде словаря."
#: netbox/extras/dashboard/widgets.py:284 #: netbox/extras/dashboard/widgets.py:288
msgid "RSS Feed" msgid "RSS Feed"
msgstr "RSS-канал" msgstr "RSS-канал"
#: netbox/extras/dashboard/widgets.py:289 #: netbox/extras/dashboard/widgets.py:293
msgid "Embed an RSS feed from an external website." msgid "Embed an RSS feed from an external website."
msgstr "Вставьте RSS-канал с внешнего веб-сайта." msgstr "Вставьте RSS-канал с внешнего веб-сайта."
#: netbox/extras/dashboard/widgets.py:296 #: netbox/extras/dashboard/widgets.py:300
msgid "Feed URL" msgid "Feed URL"
msgstr "URL-адрес ленты" msgstr "URL-адрес ленты"
#: netbox/extras/dashboard/widgets.py:301 #: netbox/extras/dashboard/widgets.py:305
msgid "The maximum number of objects to display" msgid "The maximum number of objects to display"
msgstr "Максимальное количество отображаемых объектов" msgstr "Максимальное количество отображаемых объектов"
#: netbox/extras/dashboard/widgets.py:306 #: netbox/extras/dashboard/widgets.py:310
msgid "How long to stored the cached content (in seconds)" msgid "How long to stored the cached content (in seconds)"
msgstr "Как долго хранить кэшированный контент (в секундах)" msgstr "Как долго хранить кэшированный контент (в секундах)"
#: netbox/extras/dashboard/widgets.py:358 #: netbox/extras/dashboard/widgets.py:362
#: netbox/templates/account/base.html:10 #: netbox/templates/account/base.html:10
#: netbox/templates/account/bookmarks.html:7 #: netbox/templates/account/bookmarks.html:7
#: netbox/templates/inc/user_menu.html:30 #: netbox/templates/inc/user_menu.html:30
msgid "Bookmarks" msgid "Bookmarks"
msgstr "Закладки" msgstr "Закладки"
#: netbox/extras/dashboard/widgets.py:362 #: netbox/extras/dashboard/widgets.py:366
msgid "Show your personal bookmarks" msgid "Show your personal bookmarks"
msgstr "Покажите свои личные закладки" msgstr "Покажите свои личные закладки"
#: netbox/extras/events.py:134 #: netbox/extras/events.py:137
#, python-brace-format #, python-brace-format
msgid "Unknown action type for an event rule: {action_type}" msgid "Unknown action type for an event rule: {action_type}"
msgstr "Неизвестный тип действия для правила события: {action_type}" msgstr "Неизвестный тип действия для правила события: {action_type}"
#: netbox/extras/events.py:182 #: netbox/extras/events.py:185
#, python-brace-format #, python-brace-format
msgid "Cannot import events pipeline {name} error: {error}" msgid "Cannot import events pipeline {name} error: {error}"
msgstr "Невозможно импортировать конвейер событий {name} ошибка: {error}" msgstr "Невозможно импортировать конвейер событий {name} ошибка: {error}"
@ -9975,7 +9975,7 @@ msgid "Invalid value. Specify a content type as '<app_label>.<model_name>'."
msgstr "" msgstr ""
"Неверное значение. Укажите тип контента как '<app_label>.<model_name>'." "Неверное значение. Укажите тип контента как '<app_label>.<model_name>'."
#: netbox/netbox/authentication/__init__.py:138 #: netbox/netbox/authentication/__init__.py:141
#, python-brace-format #, python-brace-format
msgid "Invalid permission {permission} for model {model}" msgid "Invalid permission {permission} for model {model}"
msgstr "Неверное разрешение {permission} для модели {model}" msgstr "Неверное разрешение {permission} для модели {model}"
@ -10826,43 +10826,43 @@ msgstr "Невозможно добавить магазины в реестр
msgid "Cannot delete stores from registry" msgid "Cannot delete stores from registry"
msgstr "Невозможно удалить магазины из реестра" msgstr "Невозможно удалить магазины из реестра"
#: netbox/netbox/settings.py:741 #: netbox/netbox/settings.py:742
msgid "German" msgid "German"
msgstr "Немецкий" msgstr "Немецкий"
#: netbox/netbox/settings.py:742 #: netbox/netbox/settings.py:743
msgid "English" msgid "English"
msgstr "Английский" msgstr "Английский"
#: netbox/netbox/settings.py:743 #: netbox/netbox/settings.py:744
msgid "Spanish" msgid "Spanish"
msgstr "Испанский" msgstr "Испанский"
#: netbox/netbox/settings.py:744 #: netbox/netbox/settings.py:745
msgid "French" msgid "French"
msgstr "Французский" msgstr "Французский"
#: netbox/netbox/settings.py:745 #: netbox/netbox/settings.py:746
msgid "Japanese" msgid "Japanese"
msgstr "Японский" msgstr "Японский"
#: netbox/netbox/settings.py:746 #: netbox/netbox/settings.py:747
msgid "Portuguese" msgid "Portuguese"
msgstr "Португальский" msgstr "Португальский"
#: netbox/netbox/settings.py:747 #: netbox/netbox/settings.py:748
msgid "Russian" msgid "Russian"
msgstr "Русский" msgstr "Русский"
#: netbox/netbox/settings.py:748 #: netbox/netbox/settings.py:749
msgid "Turkish" msgid "Turkish"
msgstr "Турецкий" msgstr "Турецкий"
#: netbox/netbox/settings.py:749 #: netbox/netbox/settings.py:750
msgid "Ukrainian" msgid "Ukrainian"
msgstr "украинский" msgstr "украинский"
#: netbox/netbox/settings.py:750 #: netbox/netbox/settings.py:751
msgid "Chinese" msgid "Chinese"
msgstr "Китайский" msgstr "Китайский"

View File

@ -5,17 +5,17 @@
# #
# Translators: # Translators:
# Burak Senturk, 2024 # Burak Senturk, 2024
# Jeremy Stretch, 2024
# Hamdi Suat Aknar, 2024 # Hamdi Suat Aknar, 2024
# Jeremy Stretch, 2024
# #
#, fuzzy #, fuzzy
msgid "" 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-07-09 05:02+0000\n" "POT-Creation-Date: 2024-07-11 05:01+0000\n"
"PO-Revision-Date: 2023-10-30 17:48+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n"
"Last-Translator: Hamdi Suat Aknar, 2024\n" "Last-Translator: Jeremy Stretch, 2024\n"
"Language-Team: Turkish (https://app.transifex.com/netbox-community/teams/178115/tr/)\n" "Language-Team: Turkish (https://app.transifex.com/netbox-community/teams/178115/tr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -64,7 +64,7 @@ msgstr "Son Kullanım"
msgid "Allowed IPs" msgid "Allowed IPs"
msgstr "İzin verilen IP'ler" msgstr "İzin verilen IP'ler"
#: netbox/account/views.py:204 #: netbox/account/views.py:214
msgid "Your preferences have been updated." msgid "Your preferences have been updated."
msgstr "Tercihleriniz güncellendi." msgstr "Tercihleriniz güncellendi."
@ -1510,16 +1510,16 @@ msgstr "Şifre"
msgid "Branch" msgid "Branch"
msgstr "Şube" msgstr "Şube"
#: netbox/core/data_backends.py:105 #: netbox/core/data_backends.py:106
#, python-brace-format #, python-brace-format
msgid "Fetching remote data failed ({name}): {error}" msgid "Fetching remote data failed ({name}): {error}"
msgstr "Uzaktan veri getirilemedi ({name}): {error}" msgstr "Uzaktan veri getirilemedi ({name}): {error}"
#: netbox/core/data_backends.py:118 #: netbox/core/data_backends.py:119
msgid "AWS access key ID" msgid "AWS access key ID"
msgstr "AWS erişim anahtarı kimliği" msgstr "AWS erişim anahtarı kimliği"
#: netbox/core/data_backends.py:122 #: netbox/core/data_backends.py:123
msgid "AWS secret access key" msgid "AWS secret access key"
msgstr "AWS gizli erişim anahtarı" msgstr "AWS gizli erişim anahtarı"
@ -1863,7 +1863,7 @@ msgstr ""
msgid "last updated" msgid "last updated"
msgstr "son güncellendi" msgstr "son güncellendi"
#: netbox/core/models/data.py:296 netbox/dcim/models/cables.py:442 #: netbox/core/models/data.py:296 netbox/dcim/models/cables.py:444
msgid "path" msgid "path"
msgstr "yol" msgstr "yol"
@ -4636,48 +4636,48 @@ msgstr "uzunluk"
msgid "length unit" msgid "length unit"
msgstr "uzunluk birimi" msgstr "uzunluk birimi"
#: netbox/dcim/models/cables.py:93 #: netbox/dcim/models/cables.py:95
msgid "cable" msgid "cable"
msgstr "kablo" msgstr "kablo"
#: netbox/dcim/models/cables.py:94 #: netbox/dcim/models/cables.py:96
msgid "cables" msgid "cables"
msgstr "kablolar" msgstr "kablolar"
#: netbox/dcim/models/cables.py:163 #: netbox/dcim/models/cables.py:165
msgid "Must specify a unit when setting a cable length" msgid "Must specify a unit when setting a cable length"
msgstr "Kablo uzunluğu ayarlarken bir birim belirtmeniz gerekir" msgstr "Kablo uzunluğu ayarlarken bir birim belirtmeniz gerekir"
#: netbox/dcim/models/cables.py:166 #: netbox/dcim/models/cables.py:168
msgid "Must define A and B terminations when creating a new cable." msgid "Must define A and B terminations when creating a new cable."
msgstr "Yeni bir kablo oluştururken A ve B sonlandırmalarını tanımlamalıdır." msgstr "Yeni bir kablo oluştururken A ve B sonlandırmalarını tanımlamalıdır."
#: netbox/dcim/models/cables.py:173 #: netbox/dcim/models/cables.py:175
msgid "Cannot connect different termination types to same end of cable." msgid "Cannot connect different termination types to same end of cable."
msgstr "Farklı sonlandırma türleri kablonun aynı ucuna bağlanamaz." msgstr "Farklı sonlandırma türleri kablonun aynı ucuna bağlanamaz."
#: netbox/dcim/models/cables.py:181 #: netbox/dcim/models/cables.py:183
#, python-brace-format #, python-brace-format
msgid "Incompatible termination types: {type_a} and {type_b}" msgid "Incompatible termination types: {type_a} and {type_b}"
msgstr "Uyumsuz sonlandırma türleri: {type_a} ve {type_b}" msgstr "Uyumsuz sonlandırma türleri: {type_a} ve {type_b}"
#: netbox/dcim/models/cables.py:191 #: netbox/dcim/models/cables.py:193
msgid "A and B terminations cannot connect to the same object." msgid "A and B terminations cannot connect to the same object."
msgstr "A ve B sonlandırmaları aynı nesneye bağlanamaz." msgstr "A ve B sonlandırmaları aynı nesneye bağlanamaz."
#: netbox/dcim/models/cables.py:258 netbox/ipam/models/asns.py:37 #: netbox/dcim/models/cables.py:260 netbox/ipam/models/asns.py:37
msgid "end" msgid "end"
msgstr "son" msgstr "son"
#: netbox/dcim/models/cables.py:311 #: netbox/dcim/models/cables.py:313
msgid "cable termination" msgid "cable termination"
msgstr "kablo sonlandırma" msgstr "kablo sonlandırma"
#: netbox/dcim/models/cables.py:312 #: netbox/dcim/models/cables.py:314
msgid "cable terminations" msgid "cable terminations"
msgstr "kablo sonlandırmaları" msgstr "kablo sonlandırmaları"
#: netbox/dcim/models/cables.py:331 #: netbox/dcim/models/cables.py:333
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"Duplicate termination found for {app_label}.{model} {termination_id}: cable " "Duplicate termination found for {app_label}.{model} {termination_id}: cable "
@ -4686,32 +4686,32 @@ msgstr ""
"Yinelenen sonlandırma bulundu {app_label}.{model} {termination_id}: kablo " "Yinelenen sonlandırma bulundu {app_label}.{model} {termination_id}: kablo "
"{cable_pk}" "{cable_pk}"
#: netbox/dcim/models/cables.py:341 #: netbox/dcim/models/cables.py:343
#, python-brace-format #, python-brace-format
msgid "Cables cannot be terminated to {type_display} interfaces" msgid "Cables cannot be terminated to {type_display} interfaces"
msgstr "Kablolar sonlandırılamaz {type_display} arayüzleri" msgstr "Kablolar sonlandırılamaz {type_display} arayüzleri"
#: netbox/dcim/models/cables.py:348 #: netbox/dcim/models/cables.py:350
msgid "Circuit terminations attached to a provider network may not be cabled." msgid "Circuit terminations attached to a provider network may not be cabled."
msgstr "Bir sağlayıcıına bağlı devre sonlandırmaları kablolanmayabilir." msgstr "Bir sağlayıcıına bağlı devre sonlandırmaları kablolanmayabilir."
#: netbox/dcim/models/cables.py:446 netbox/extras/models/configs.py:50 #: netbox/dcim/models/cables.py:448 netbox/extras/models/configs.py:50
msgid "is active" msgid "is active"
msgstr "aktiftir" msgstr "aktiftir"
#: netbox/dcim/models/cables.py:450 #: netbox/dcim/models/cables.py:452
msgid "is complete" msgid "is complete"
msgstr "tamamlandı" msgstr "tamamlandı"
#: netbox/dcim/models/cables.py:454 #: netbox/dcim/models/cables.py:456
msgid "is split" msgid "is split"
msgstr "bölünmüş" msgstr "bölünmüş"
#: netbox/dcim/models/cables.py:462 #: netbox/dcim/models/cables.py:464
msgid "cable path" msgid "cable path"
msgstr "kablo yolu" msgstr "kablo yolu"
#: netbox/dcim/models/cables.py:463 #: netbox/dcim/models/cables.py:465
msgid "cable paths" msgid "cable paths"
msgstr "kablo yolları" msgstr "kablo yolları"
@ -6639,11 +6639,11 @@ msgstr "En eski"
#: netbox/extras/choices.py:126 #: netbox/extras/choices.py:126
msgid "Alphabetical (A-Z)" msgid "Alphabetical (A-Z)"
msgstr "" msgstr "Alfabetik (A-Z)"
#: netbox/extras/choices.py:127 #: netbox/extras/choices.py:127
msgid "Alphabetical (Z-A)" msgid "Alphabetical (Z-A)"
msgstr "" msgstr "Alfabetik (Z-A)"
#: netbox/extras/choices.py:143 netbox/templates/generic/object.html:61 #: netbox/extras/choices.py:143 netbox/templates/generic/object.html:61
msgid "Updated" msgid "Updated"
@ -6831,10 +6831,12 @@ msgstr "Kural seti bir sözlük olmalı, değil {ruleset}."
#: netbox/extras/conditions.py:142 #: netbox/extras/conditions.py:142
msgid "Invalid logic type: must be 'AND' or 'OR'. Please check documentation." msgid "Invalid logic type: must be 'AND' or 'OR'. Please check documentation."
msgstr "" msgstr ""
"Geçersiz mantık türü: 'AND' veya 'OR' olmalıdır. Lütfen belgeleri kontrol "
"edin."
#: netbox/extras/conditions.py:154 #: netbox/extras/conditions.py:154
msgid "Incorrect key(s) informed. Please check documentation." msgid "Incorrect key(s) informed. Please check documentation."
msgstr "" msgstr "Yanlış anahtar (ler) bilgilendirildi. Lütfen belgeleri kontrol edin."
#: netbox/extras/dashboard/forms.py:38 #: netbox/extras/dashboard/forms.py:38
msgid "Widget type" msgid "Widget type"
@ -6894,44 +6896,44 @@ msgstr "Görüntülenecek nesnelerin varsayılan sayısı"
msgid "Invalid format. URL parameters must be passed as a dictionary." msgid "Invalid format. URL parameters must be passed as a dictionary."
msgstr "Geçersiz biçim. URL parametreleri sözlük olarak iletilmelidir." msgstr "Geçersiz biçim. URL parametreleri sözlük olarak iletilmelidir."
#: netbox/extras/dashboard/widgets.py:284 #: netbox/extras/dashboard/widgets.py:288
msgid "RSS Feed" msgid "RSS Feed"
msgstr "RSS Beslemesi" msgstr "RSS Beslemesi"
#: netbox/extras/dashboard/widgets.py:289 #: netbox/extras/dashboard/widgets.py:293
msgid "Embed an RSS feed from an external website." msgid "Embed an RSS feed from an external website."
msgstr "Harici bir web sitesinden bir RSS beslemesi ekleyin." msgstr "Harici bir web sitesinden bir RSS beslemesi ekleyin."
#: netbox/extras/dashboard/widgets.py:296 #: netbox/extras/dashboard/widgets.py:300
msgid "Feed URL" msgid "Feed URL"
msgstr "Akış URL'si" msgstr "Akış URL'si"
#: netbox/extras/dashboard/widgets.py:301 #: netbox/extras/dashboard/widgets.py:305
msgid "The maximum number of objects to display" msgid "The maximum number of objects to display"
msgstr "Görüntülenecek maksimum nesne sayısı" msgstr "Görüntülenecek maksimum nesne sayısı"
#: netbox/extras/dashboard/widgets.py:306 #: netbox/extras/dashboard/widgets.py:310
msgid "How long to stored the cached content (in seconds)" msgid "How long to stored the cached content (in seconds)"
msgstr "" msgstr ""
"Önbelleğe alınan içeriğin ne kadar süre saklanacağı (saniye cinsinden)" "Önbelleğe alınan içeriğin ne kadar süre saklanacağı (saniye cinsinden)"
#: netbox/extras/dashboard/widgets.py:358 #: netbox/extras/dashboard/widgets.py:362
#: netbox/templates/account/base.html:10 #: netbox/templates/account/base.html:10
#: netbox/templates/account/bookmarks.html:7 #: netbox/templates/account/bookmarks.html:7
#: netbox/templates/inc/user_menu.html:30 #: netbox/templates/inc/user_menu.html:30
msgid "Bookmarks" msgid "Bookmarks"
msgstr "Yer İşaretleri" msgstr "Yer İşaretleri"
#: netbox/extras/dashboard/widgets.py:362 #: netbox/extras/dashboard/widgets.py:366
msgid "Show your personal bookmarks" msgid "Show your personal bookmarks"
msgstr "Kişisel yer imlerinizi gösterin" msgstr "Kişisel yer imlerinizi gösterin"
#: netbox/extras/events.py:134 #: netbox/extras/events.py:137
#, python-brace-format #, python-brace-format
msgid "Unknown action type for an event rule: {action_type}" msgid "Unknown action type for an event rule: {action_type}"
msgstr "Bir olay kuralı için bilinmeyen eylem türü: {action_type}" msgstr "Bir olay kuralı için bilinmeyen eylem türü: {action_type}"
#: netbox/extras/events.py:182 #: netbox/extras/events.py:185
#, python-brace-format #, python-brace-format
msgid "Cannot import events pipeline {name} error: {error}" msgid "Cannot import events pipeline {name} error: {error}"
msgstr "Olaylar boru hattı içe aktarılamıyor {name} hata: {error}" msgstr "Olaylar boru hattı içe aktarılamıyor {name} hata: {error}"
@ -9893,7 +9895,7 @@ msgid "Invalid value. Specify a content type as '<app_label>.<model_name>'."
msgstr "" msgstr ""
"Geçersiz değer. İçerik türünü 'olarak belirtin<app_label>.<model_name>'." "Geçersiz değer. İçerik türünü 'olarak belirtin<app_label>.<model_name>'."
#: netbox/netbox/authentication/__init__.py:138 #: netbox/netbox/authentication/__init__.py:141
#, python-brace-format #, python-brace-format
msgid "Invalid permission {permission} for model {model}" msgid "Invalid permission {permission} for model {model}"
msgstr "Geçersiz izin {permission} model için {model}" msgstr "Geçersiz izin {permission} model için {model}"
@ -10198,7 +10200,7 @@ msgstr "Nesne türü (ler)"
#: netbox/netbox/forms/__init__.py:40 #: netbox/netbox/forms/__init__.py:40
msgid "Lookup" msgid "Lookup"
msgstr "" msgstr "Aramak"
#: netbox/netbox/forms/base.py:88 #: netbox/netbox/forms/base.py:88
msgid "" msgid ""
@ -10741,43 +10743,43 @@ msgstr "Başlatıldıktan sonra kayıt defterine mağazalar eklenemiyor"
msgid "Cannot delete stores from registry" msgid "Cannot delete stores from registry"
msgstr "Mağazalar kayıt defterinden silinemiyor" msgstr "Mağazalar kayıt defterinden silinemiyor"
#: netbox/netbox/settings.py:741 #: netbox/netbox/settings.py:742
msgid "German" msgid "German"
msgstr "Alman" msgstr "Alman"
#: netbox/netbox/settings.py:742 #: netbox/netbox/settings.py:743
msgid "English" msgid "English"
msgstr "İngilizce" msgstr "İngilizce"
#: netbox/netbox/settings.py:743 #: netbox/netbox/settings.py:744
msgid "Spanish" msgid "Spanish"
msgstr "İspanyolca" msgstr "İspanyolca"
#: netbox/netbox/settings.py:744 #: netbox/netbox/settings.py:745
msgid "French" msgid "French"
msgstr "Fransızca" msgstr "Fransızca"
#: netbox/netbox/settings.py:745 #: netbox/netbox/settings.py:746
msgid "Japanese" msgid "Japanese"
msgstr "Japonca" msgstr "Japonca"
#: netbox/netbox/settings.py:746 #: netbox/netbox/settings.py:747
msgid "Portuguese" msgid "Portuguese"
msgstr "Portekizce" msgstr "Portekizce"
#: netbox/netbox/settings.py:747 #: netbox/netbox/settings.py:748
msgid "Russian" msgid "Russian"
msgstr "Rusça" msgstr "Rusça"
#: netbox/netbox/settings.py:748 #: netbox/netbox/settings.py:749
msgid "Turkish" msgid "Turkish"
msgstr "Türkçe" msgstr "Türkçe"
#: netbox/netbox/settings.py:749 #: netbox/netbox/settings.py:750
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukraynalı" msgstr "Ukraynalı"
#: netbox/netbox/settings.py:750 #: netbox/netbox/settings.py:751
msgid "Chinese" msgid "Chinese"
msgstr "Çince" msgstr "Çince"
@ -13083,11 +13085,11 @@ msgstr "Veriler yukarı akış dosyasıyla senkronize değil"
#: netbox/templates/inc/table_controls_htmx.html:7 #: netbox/templates/inc/table_controls_htmx.html:7
msgid "Quick search" msgid "Quick search"
msgstr "" msgstr "Hızlı arama"
#: netbox/templates/inc/table_controls_htmx.html:20 #: netbox/templates/inc/table_controls_htmx.html:20
msgid "Saved filter" msgid "Saved filter"
msgstr "" msgstr "Kaydedilen filtre"
#: netbox/templates/inc/user_menu.html:23 #: netbox/templates/inc/user_menu.html:23
msgid "Django Admin" msgid "Django Admin"
@ -14163,7 +14165,7 @@ msgstr "50'den fazla"
#: netbox/utilities/fields.py:30 #: netbox/utilities/fields.py:30
msgid "RGB color in hexadecimal. Example: " msgid "RGB color in hexadecimal. Example: "
msgstr "" msgstr "Onaltılık olarak RGB rengi. Örnek: "
#: netbox/utilities/fields.py:159 #: netbox/utilities/fields.py:159
#, python-format #, python-format
@ -14477,11 +14479,11 @@ msgstr "Aşağı hareket et"
#: netbox/utilities/templates/navigation/menu.html:14 #: netbox/utilities/templates/navigation/menu.html:14
msgid "Search…" msgid "Search…"
msgstr "" msgstr "Arama..."
#: netbox/utilities/templates/navigation/menu.html:14 #: netbox/utilities/templates/navigation/menu.html:14
msgid "Search NetBox" msgid "Search NetBox"
msgstr "" msgstr "Arama NetBox"
#: netbox/utilities/templates/widgets/apiselect.html:7 #: netbox/utilities/templates/widgets/apiselect.html:7
msgid "Open selector" msgid "Open selector"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model, password_validation
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers from rest_framework import serializers
@ -59,6 +59,14 @@ class UserSerializer(ValidatedModelSerializer):
'password': {'write_only': True} 'password': {'write_only': True}
} }
def validate(self, data):
# Enforce password validation rules (if configured)
if not self.nested and data.get('password'):
password_validation.validate_password(data['password'], self.instance)
return super().validate(data)
def create(self, validated_data): def create(self, validated_data):
""" """
Extract the password from validated data and set it separately to ensure proper hash generation. Extract the password from validated data and set it separately to ensure proper hash generation.

View File

@ -1,6 +1,6 @@
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model, password_validation
from django.contrib.postgres.forms import SimpleArrayField from django.contrib.postgres.forms import SimpleArrayField
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -227,6 +227,10 @@ class UserForm(forms.ModelForm):
if self.cleaned_data['password'] and self.cleaned_data['password'] != self.cleaned_data['confirm_password']: if self.cleaned_data['password'] and self.cleaned_data['password'] != self.cleaned_data['confirm_password']:
raise forms.ValidationError(_("Passwords do not match! Please check your input and try again.")) raise forms.ValidationError(_("Passwords do not match! Please check your input and try again."))
# Enforce password validation rules (if configured)
if self.cleaned_data['password']:
password_validation.validate_password(self.cleaned_data['password'], self.instance)
class GroupForm(forms.ModelForm): class GroupForm(forms.ModelForm):
users = DynamicModelMultipleChoiceField( users = DynamicModelMultipleChoiceField(

View File

@ -1,4 +1,5 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.test import override_settings
from django.urls import reverse from django.urls import reverse
from core.models import ObjectType from core.models import ObjectType
@ -93,6 +94,31 @@ class UserTest(APIViewTestCases.APIViewTestCase):
user.refresh_from_db() user.refresh_from_db()
self.assertTrue(user.check_password(data['password'])) self.assertTrue(user.check_password(data['password']))
@override_settings(AUTH_PASSWORD_VALIDATORS=[{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {'min_length': 8}
}])
def test_password_validation_enforced(self):
"""
Test that any configured password validation rules (AUTH_PASSWORD_VALIDATORS) are enforced.
"""
self.add_permissions('users.add_user')
data = {
'username': 'new_user',
'password': 'foo',
}
url = reverse('users-api:user-list')
# Password too short
response = self.client.post(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 400)
# Password long enough
data['password'] = 'foobar123'
response = self.client.post(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 201)
class GroupTest(APIViewTestCases.APIViewTestCase): class GroupTest(APIViewTestCases.APIViewTestCase):
model = Group model = Group

View File

@ -1,6 +1,8 @@
from django.test import override_settings
from core.models import ObjectType from core.models import ObjectType
from users.models import * from users.models import *
from utilities.testing import ViewTestCases, create_test_user from utilities.testing import ViewTestCases, create_test_user, extract_form_failures
class UserTestCase( class UserTestCase(
@ -58,6 +60,34 @@ class UserTestCase(
'last_name': 'newlastname', 'last_name': 'newlastname',
} }
@override_settings(AUTH_PASSWORD_VALIDATORS=[{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {'min_length': 8}
}])
def test_password_validation_enforced(self):
"""
Test that any configured password validation rules (AUTH_PASSWORD_VALIDATORS) are enforced.
"""
self.add_permissions('users.add_user')
data = {
'username': 'new_user',
'password': 'foo',
'confirm_password': 'foo',
}
# Password too short
request = {
'path': self._get_url('add'),
'data': data,
}
response = self.client.post(**request)
self.assertHttpStatus(response, 200)
# Password long enough
data['password'] = 'foobar123'
data['confirm_password'] = 'foobar123'
self.assertHttpStatus(self.client.post(**request), 302)
class GroupTestCase( class GroupTestCase(
ViewTestCases.GetObjectViewTestCase, ViewTestCases.GetObjectViewTestCase,

View File

@ -9,9 +9,9 @@ from django.test import override_settings
from django.urls import reverse from django.urls import reverse
from rest_framework import status from rest_framework import status
from rest_framework.test import APIClient from rest_framework.test import APIClient
from strawberry.lazy_type import LazyType from strawberry.types.base import StrawberryList, StrawberryOptional
from strawberry.type import StrawberryList, StrawberryOptional from strawberry.types.lazy_type import LazyType
from strawberry.union import StrawberryUnion from strawberry.types.union import StrawberryUnion
from core.choices import ObjectChangeActionChoices from core.choices import ObjectChangeActionChoices
from core.models import ObjectChange, ObjectType from core.models import ObjectChange, ObjectType

View File

@ -20,18 +20,18 @@ feedparser==6.0.11
gunicorn==22.0.0 gunicorn==22.0.0
Jinja2==3.1.4 Jinja2==3.1.4
Markdown==3.6 Markdown==3.6
mkdocs-material==9.5.28 mkdocs-material==9.5.30
mkdocstrings[python-legacy]==0.25.1 mkdocstrings[python-legacy]==0.25.2
netaddr==1.3.0 netaddr==1.3.0
nh3==0.2.18 nh3==0.2.18
Pillow==10.4.0 Pillow==10.4.0
psycopg[c,pool]==3.2.1 psycopg[c,pool]==3.2.1
PyYAML==6.0.1 PyYAML==6.0.1
requests==2.32.3 requests==2.32.3
social-auth-app-django==5.4.1 social-auth-app-django==5.4.2
social-auth-core==4.5.4 social-auth-core==4.5.4
strawberry-graphql==0.235.2 strawberry-graphql==0.237.2
strawberry-graphql-django==0.46.1 strawberry-graphql-django==0.47.1
svgwrite==1.4.3 svgwrite==1.4.3
tablib==3.6.1 tablib==3.6.1
tzdata==2024.1 tzdata==2024.1