mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-08 00:28:16 -06:00
Merge branch 'develop' into 16946-graphql-filter-error
This commit is contained in:
commit
28eba4bcce
@ -58,6 +58,8 @@ intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Poli
|
||||
|
||||
* First, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues?q=is%3Aissue) to see if the feature you have in mind has already been proposed. If you happen to find an open feature request that matches your idea, click "add a reaction" in the top right corner of the issue and add a thumbs up ( :thumbsup: ). This ensures that the issue has a better chance of receiving attention. Also feel free to add a comment with any additional justification for the feature.
|
||||
|
||||
* Please don't submit duplicate issues! Sometimes we reject feature requests, for various reasons. Even if you disagree with those reasons, please **do not** submit a duplicate feature request. It is very disrepectful of the maintainers' time, and you may be barred from opening future issues.
|
||||
|
||||
* If you have a rough idea that's not quite ready for formal submission yet, start a [GitHub discussion](https://github.com/netbox-community/netbox/discussions) instead. This is a great way to test the viability and narrow down the scope of a new feature prior to submitting a formal proposal, and can serve to generate interest in your idea from other community members.
|
||||
|
||||
* Once you're ready, submit a feature request [using this template](https://github.com/netbox-community/netbox/issues/new?label=type%3A+feature&template=feature_request.yaml). Be sure to provide sufficient context and detail to convey exactly what you're proposing and why. The stronger your use case, the better chance your proposal has of being accepted.
|
||||
|
@ -2,6 +2,13 @@
|
||||
|
||||
## v4.0.9 (FUTURE)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#13459](https://github.com/netbox-community/netbox/issues/13459) - Correct OpenAPI schema type for `TreeNodeMultipleChoiceFilter`
|
||||
* [#16176](https://github.com/netbox-community/netbox/issues/16176) - Restore ability to select multiple terminating devices when connecting a cable
|
||||
* [#17038](https://github.com/netbox-community/netbox/issues/17038) - Fix AttributeError exception when attempting to export system status data
|
||||
* [#17064](https://github.com/netbox-community/netbox/issues/17064) - Fix misaligned text within rendered Markdown code blocks
|
||||
|
||||
---
|
||||
|
||||
## v4.0.8 (2024-07-26)
|
||||
|
@ -109,7 +109,7 @@ class LoginView(View):
|
||||
# Authenticate user
|
||||
auth_login(request, form.get_user())
|
||||
logger.info(f"User {request.user} successfully authenticated")
|
||||
messages.success(request, f"Logged in as {request.user}.")
|
||||
messages.success(request, _("Logged in as {user}.").format(user=request.user))
|
||||
|
||||
# Ensure the user has a UserConfig defined. (This should normally be handled by
|
||||
# create_userconfig() on user creation.)
|
||||
@ -159,7 +159,7 @@ class LogoutView(View):
|
||||
username = request.user
|
||||
auth_logout(request)
|
||||
logger.info(f"User {username} has logged out")
|
||||
messages.info(request, "You have logged out.")
|
||||
messages.info(request, _("You have logged out."))
|
||||
|
||||
# Delete session key & language cookies (if set) upon logout
|
||||
response = HttpResponseRedirect(resolve_url(settings.LOGOUT_REDIRECT_URL))
|
||||
@ -234,7 +234,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
|
||||
def get(self, request):
|
||||
# LDAP users cannot change their password here
|
||||
if getattr(request.user, 'ldap_username', None):
|
||||
messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.")
|
||||
messages.warning(request, _("LDAP-authenticated user credentials cannot be changed within NetBox."))
|
||||
return redirect('account:profile')
|
||||
|
||||
form = PasswordChangeForm(user=request.user)
|
||||
@ -249,7 +249,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
update_session_auth_hash(request, form.user)
|
||||
messages.success(request, "Your password has been changed successfully.")
|
||||
messages.success(request, _("Your password has been changed successfully."))
|
||||
return redirect('account:profile')
|
||||
|
||||
return render(request, self.template_name, {
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.contrib import messages
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from dcim.views import PathTraceView
|
||||
from netbox.views import generic
|
||||
@ -326,7 +327,9 @@ class CircuitSwapTerminations(generic.ObjectEditView):
|
||||
|
||||
# Circuit must have at least one termination to swap
|
||||
if not circuit.termination_a and not circuit.termination_z:
|
||||
messages.error(request, "No terminations have been defined for circuit {}.".format(circuit))
|
||||
messages.error(request, _(
|
||||
"No terminations have been defined for circuit {circuit}."
|
||||
).format(circuit=circuit))
|
||||
return redirect('circuits:circuit', pk=circuit.pk)
|
||||
|
||||
return render(request, 'circuits/circuit_terminations_swap.html', {
|
||||
@ -374,7 +377,7 @@ class CircuitSwapTerminations(generic.ObjectEditView):
|
||||
circuit.termination_z = None
|
||||
circuit.save()
|
||||
|
||||
messages.success(request, f"Swapped terminations for circuit {circuit}.")
|
||||
messages.success(request, _("Swapped terminations for circuit {circuit}.").format(circuit=circuit))
|
||||
return redirect('circuits:circuit', pk=circuit.pk)
|
||||
|
||||
return render(request, 'circuits/circuit_terminations_swap.html', {
|
||||
|
@ -76,7 +76,10 @@ class DataSourceSyncView(BaseObjectView):
|
||||
datasource = get_object_or_404(self.queryset, pk=pk)
|
||||
job = datasource.enqueue_sync_job(request)
|
||||
|
||||
messages.success(request, f"Queued job #{job.pk} to sync {datasource}")
|
||||
messages.success(
|
||||
request,
|
||||
_("Queued job #{id} to sync {datasource}").format(id=job.pk, datasource=datasource)
|
||||
)
|
||||
return redirect(datasource.get_absolute_url())
|
||||
|
||||
|
||||
@ -235,7 +238,7 @@ class ConfigRevisionRestoreView(ContentTypePermissionRequiredMixin, View):
|
||||
|
||||
candidate_config = get_object_or_404(ConfigRevision, pk=pk)
|
||||
candidate_config.activate()
|
||||
messages.success(request, f"Restored configuration revision #{pk}")
|
||||
messages.success(request, _("Restored configuration revision #{id}").format(id=pk))
|
||||
|
||||
return redirect(candidate_config.get_absolute_url())
|
||||
|
||||
@ -379,9 +382,9 @@ class BackgroundTaskDeleteView(BaseRQView):
|
||||
# Remove job id from queue and delete the actual job
|
||||
queue.connection.lrem(queue.key, 0, job.id)
|
||||
job.delete()
|
||||
messages.success(request, f'Deleted job {job_id}')
|
||||
messages.success(request, _('Job {id} has been deleted.').format(id=job_id))
|
||||
else:
|
||||
messages.error(request, f'Error deleting job: {form.errors[0]}')
|
||||
messages.error(request, _('Error deleting job {id}: {error}').format(id=job_id, error=form.errors[0]))
|
||||
|
||||
return redirect(reverse('core:background_queue_list'))
|
||||
|
||||
@ -394,13 +397,13 @@ class BackgroundTaskRequeueView(BaseRQView):
|
||||
try:
|
||||
job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),)
|
||||
except NoSuchJobError:
|
||||
raise Http404(_("Job {job_id} not found").format(job_id=job_id))
|
||||
raise Http404(_("Job {id} not found.").format(id=job_id))
|
||||
|
||||
queue_index = QUEUES_MAP[job.origin]
|
||||
queue = get_queue_by_index(queue_index)
|
||||
|
||||
requeue_job(job_id, connection=queue.connection, serializer=queue.serializer)
|
||||
messages.success(request, f'You have successfully requeued: {job_id}')
|
||||
messages.success(request, _('Job {id} has been re-enqueued.').format(id=job_id))
|
||||
return redirect(reverse('core:background_task', args=[job_id]))
|
||||
|
||||
|
||||
@ -412,7 +415,7 @@ class BackgroundTaskEnqueueView(BaseRQView):
|
||||
try:
|
||||
job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),)
|
||||
except NoSuchJobError:
|
||||
raise Http404(_("Job {job_id} not found").format(job_id=job_id))
|
||||
raise Http404(_("Job {id} not found.").format(id=job_id))
|
||||
|
||||
queue_index = QUEUES_MAP[job.origin]
|
||||
queue = get_queue_by_index(queue_index)
|
||||
@ -435,7 +438,7 @@ class BackgroundTaskEnqueueView(BaseRQView):
|
||||
registry = ScheduledJobRegistry(queue.name, queue.connection)
|
||||
registry.remove(job)
|
||||
|
||||
messages.success(request, f'You have successfully enqueued: {job_id}')
|
||||
messages.success(request, _('Job {id} has been enqueued.').format(id=job_id))
|
||||
return redirect(reverse('core:background_task', args=[job_id]))
|
||||
|
||||
|
||||
@ -452,11 +455,11 @@ class BackgroundTaskStopView(BaseRQView):
|
||||
queue_index = QUEUES_MAP[job.origin]
|
||||
queue = get_queue_by_index(queue_index)
|
||||
|
||||
stopped, _ = stop_jobs(queue, job_id)
|
||||
if len(stopped) == 1:
|
||||
messages.success(request, f'You have successfully stopped {job_id}')
|
||||
stopped_jobs = stop_jobs(queue, job_id)[0]
|
||||
if len(stopped_jobs) == 1:
|
||||
messages.success(request, _('Job {id} has been stopped.').format(id=job_id))
|
||||
else:
|
||||
messages.error(request, f'Failed to stop {job_id}')
|
||||
messages.error(request, _('Failed to stop job {id}').format(id=job_id))
|
||||
|
||||
return redirect(reverse('core:background_task', args=[job_id]))
|
||||
|
||||
@ -559,13 +562,14 @@ class SystemView(UserPassesTestMixin, View):
|
||||
|
||||
# Raw data export
|
||||
if 'export' in request.GET:
|
||||
params = [param.name for param in PARAMS]
|
||||
data = {
|
||||
**stats,
|
||||
'plugins': {
|
||||
plugin.name: plugin.version for plugin in plugins
|
||||
},
|
||||
'config': {
|
||||
k: config.data[k] for k in sorted(config.data)
|
||||
k: getattr(config, k) for k in sorted(params)
|
||||
},
|
||||
}
|
||||
response = HttpResponse(json.dumps(data, indent=4), content_type='text/json')
|
||||
|
@ -19,7 +19,7 @@ def get_cable_form(a_type, b_type):
|
||||
# Device component
|
||||
if hasattr(term_cls, 'device'):
|
||||
|
||||
attrs[f'termination_{cable_end}_device'] = DynamicModelChoiceField(
|
||||
attrs[f'termination_{cable_end}_device'] = DynamicModelMultipleChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
label=_('Device'),
|
||||
required=False,
|
||||
@ -33,6 +33,7 @@ def get_cable_form(a_type, b_type):
|
||||
label=term_cls._meta.verbose_name.title(),
|
||||
context={
|
||||
'disabled': '_occupied',
|
||||
'parent': 'device',
|
||||
},
|
||||
query_params={
|
||||
'device_id': f'$termination_{cable_end}_device',
|
||||
@ -43,7 +44,7 @@ def get_cable_form(a_type, b_type):
|
||||
# PowerFeed
|
||||
elif term_cls == PowerFeed:
|
||||
|
||||
attrs[f'termination_{cable_end}_powerpanel'] = DynamicModelChoiceField(
|
||||
attrs[f'termination_{cable_end}_powerpanel'] = DynamicModelMultipleChoiceField(
|
||||
queryset=PowerPanel.objects.all(),
|
||||
label=_('Power Panel'),
|
||||
required=False,
|
||||
@ -57,6 +58,7 @@ def get_cable_form(a_type, b_type):
|
||||
label=_('Power Feed'),
|
||||
context={
|
||||
'disabled': '_occupied',
|
||||
'parent': 'powerpanel',
|
||||
},
|
||||
query_params={
|
||||
'power_panel_id': f'$termination_{cable_end}_powerpanel',
|
||||
@ -66,7 +68,7 @@ def get_cable_form(a_type, b_type):
|
||||
# CircuitTermination
|
||||
elif term_cls == CircuitTermination:
|
||||
|
||||
attrs[f'termination_{cable_end}_circuit'] = DynamicModelChoiceField(
|
||||
attrs[f'termination_{cable_end}_circuit'] = DynamicModelMultipleChoiceField(
|
||||
queryset=Circuit.objects.all(),
|
||||
label=_('Circuit'),
|
||||
selector=True,
|
||||
@ -79,6 +81,7 @@ def get_cable_form(a_type, b_type):
|
||||
label=_('Side'),
|
||||
context={
|
||||
'disabled': '_occupied',
|
||||
'parent': 'circuit',
|
||||
},
|
||||
query_params={
|
||||
'circuit_id': f'$termination_{cable_end}_circuit',
|
||||
|
@ -2059,7 +2059,7 @@ class DeviceRenderConfigView(generic.ObjectView):
|
||||
try:
|
||||
rendered_config = config_template.render(context=context_data)
|
||||
except TemplateError as e:
|
||||
messages.error(request, f"An error occurred while rendering the template: {e}")
|
||||
messages.error(request, _("An error occurred while rendering the template: {error}").format(error=e))
|
||||
rendered_config = traceback.format_exc()
|
||||
|
||||
return {
|
||||
@ -2823,7 +2823,13 @@ class DeviceBayPopulateView(generic.ObjectEditView):
|
||||
device_bay.snapshot()
|
||||
device_bay.installed_device = form.cleaned_data['installed_device']
|
||||
device_bay.save()
|
||||
messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay))
|
||||
messages.success(
|
||||
request,
|
||||
_("Installed device {device} in bay {device_bay}.").format(
|
||||
device=device_bay.installed_device,
|
||||
device_bay=device_bay
|
||||
)
|
||||
)
|
||||
return_url = self.get_return_url(request)
|
||||
|
||||
return redirect(return_url)
|
||||
@ -2858,7 +2864,13 @@ class DeviceBayDepopulateView(generic.ObjectEditView):
|
||||
removed_device = device_bay.installed_device
|
||||
device_bay.installed_device = None
|
||||
device_bay.save()
|
||||
messages.success(request, f"{removed_device} has been removed from {device_bay}.")
|
||||
messages.success(
|
||||
request,
|
||||
_("Removed device {device} from bay {device_bay}.").format(
|
||||
device=removed_device,
|
||||
device_bay=device_bay
|
||||
)
|
||||
)
|
||||
return_url = self.get_return_url(request, device_bay.device)
|
||||
|
||||
return redirect(return_url)
|
||||
@ -3426,7 +3438,7 @@ class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMix
|
||||
|
||||
membership_form.save()
|
||||
messages.success(request, mark_safe(
|
||||
f'Added member <a href="{device.get_absolute_url()}">{escape(device)}</a>'
|
||||
_('Added member <a href="{url}">{escape(device)}</a>').format(url=device.get_absolute_url())
|
||||
))
|
||||
|
||||
if '_addanother' in request.POST:
|
||||
@ -3471,7 +3483,10 @@ class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURL
|
||||
# Protect master device from being removed
|
||||
virtual_chassis = VirtualChassis.objects.filter(master=device).first()
|
||||
if virtual_chassis is not None:
|
||||
messages.error(request, f'Unable to remove master device {device} from the virtual chassis.')
|
||||
messages.error(
|
||||
request,
|
||||
_('Unable to remove master device {device} from the virtual chassis.').format(device=device)
|
||||
)
|
||||
return redirect(device.get_absolute_url())
|
||||
|
||||
if form.is_valid():
|
||||
@ -3483,7 +3498,10 @@ class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURL
|
||||
device.vc_priority = None
|
||||
device.save()
|
||||
|
||||
msg = 'Removed {} from virtual chassis {}'.format(device, device.virtual_chassis)
|
||||
msg = _('Removed {device} from virtual chassis {chassis}').format(
|
||||
device=device,
|
||||
chassis=device.virtual_chassis
|
||||
)
|
||||
messages.success(request, msg)
|
||||
|
||||
return redirect(self.get_return_url(request, device))
|
||||
|
@ -6,6 +6,7 @@ from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.db.models.fields.related import RelatedField
|
||||
from django.db.models.fields.reverse_related import ManyToOneRel
|
||||
from django.urls import reverse
|
||||
from django.urls.exceptions import NoReverseMatch
|
||||
from django.utils.safestring import mark_safe
|
||||
@ -102,7 +103,7 @@ class BaseTable(tables.Table):
|
||||
field = model._meta.get_field(field_name)
|
||||
except FieldDoesNotExist:
|
||||
break
|
||||
if isinstance(field, RelatedField):
|
||||
if isinstance(field, (RelatedField, ManyToOneRel)):
|
||||
# Follow ForeignKeys to the related model
|
||||
prefetch_path.append(field_name)
|
||||
model = field.remote_field.model
|
||||
|
@ -106,7 +106,13 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
||||
try:
|
||||
return template.render_to_response(self.queryset)
|
||||
except Exception as e:
|
||||
messages.error(request, f"There was an error rendering the selected export template ({template.name}): {e}")
|
||||
messages.error(
|
||||
request,
|
||||
_("There was an error rendering the selected export template ({template}): {error}").format(
|
||||
template=template.name,
|
||||
error=e
|
||||
)
|
||||
)
|
||||
# Strip the `export` param and redirect user to the filtered objects list
|
||||
query_params = request.GET.copy()
|
||||
query_params.pop('export')
|
||||
@ -668,7 +674,10 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
# Retrieve objects being edited
|
||||
table = self.table(self.queryset.filter(pk__in=pk_list), orderable=False)
|
||||
if not table.rows:
|
||||
messages.warning(request, "No {} were selected.".format(model._meta.verbose_name_plural))
|
||||
messages.warning(
|
||||
request,
|
||||
_("No {object_type} were selected.").format(object_type=model._meta.verbose_name_plural)
|
||||
)
|
||||
return redirect(self.get_return_url(request))
|
||||
|
||||
return render(request, self.template_name, {
|
||||
@ -745,8 +754,13 @@ class BulkRenameView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
if self.queryset.filter(pk__in=renamed_pks).count() != len(selected_objects):
|
||||
raise PermissionsViolation
|
||||
|
||||
model_name = self.queryset.model._meta.verbose_name_plural
|
||||
messages.success(request, f"Renamed {len(selected_objects)} {model_name}")
|
||||
messages.success(
|
||||
request,
|
||||
_("Renamed {count} {object_type}").format(
|
||||
count=len(selected_objects),
|
||||
object_type=self.queryset.model._meta.verbose_name_plural
|
||||
)
|
||||
)
|
||||
return redirect(self.get_return_url(request))
|
||||
|
||||
except (AbortRequest, PermissionsViolation) as e:
|
||||
@ -838,7 +852,10 @@ class BulkDeleteView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
messages.error(request, mark_safe(e.message))
|
||||
return redirect(self.get_return_url(request))
|
||||
|
||||
msg = f"Deleted {deleted_count} {model._meta.verbose_name_plural}"
|
||||
msg = _("Deleted {count} {object_type}").format(
|
||||
count=deleted_count,
|
||||
object_type=model._meta.verbose_name_plural
|
||||
)
|
||||
logger.info(msg)
|
||||
messages.success(request, msg)
|
||||
return redirect(self.get_return_url(request))
|
||||
@ -855,7 +872,10 @@ class BulkDeleteView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
# Retrieve objects being deleted
|
||||
table = self.table(self.queryset.filter(pk__in=pk_list), orderable=False)
|
||||
if not table.rows:
|
||||
messages.warning(request, "No {} were selected for deletion.".format(model._meta.verbose_name_plural))
|
||||
messages.warning(
|
||||
request,
|
||||
_("No {object_type} were selected.").format(object_type=model._meta.verbose_name_plural)
|
||||
)
|
||||
return redirect(self.get_return_url(request))
|
||||
|
||||
return render(request, self.template_name, {
|
||||
@ -900,7 +920,10 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
|
||||
selected_objects = self.parent_model.objects.filter(pk__in=pk_list)
|
||||
if not selected_objects:
|
||||
messages.warning(request, "No {} were selected.".format(self.parent_model._meta.verbose_name_plural))
|
||||
messages.warning(
|
||||
request,
|
||||
_("No {object_type} were selected.").format(object_type=self.parent_model._meta.verbose_name_plural)
|
||||
)
|
||||
return redirect(self.get_return_url(request))
|
||||
table = self.table(selected_objects, orderable=False)
|
||||
|
||||
|
@ -202,11 +202,14 @@ class ObjectSyncDataView(View):
|
||||
obj = get_object_or_404(qs, **kwargs)
|
||||
|
||||
if not obj.data_file:
|
||||
messages.error(request, f"Unable to synchronize data: No data file set.")
|
||||
messages.error(request, _("Unable to synchronize data: No data file set."))
|
||||
return redirect(obj.get_absolute_url())
|
||||
|
||||
obj.sync(save=True)
|
||||
messages.success(request, f"Synchronized data for {model._meta.verbose_name} {obj}.")
|
||||
messages.success(request, _("Synchronized data for {object_type} {object}.").format(
|
||||
object_type=model._meta.verbose_name,
|
||||
object=obj
|
||||
))
|
||||
|
||||
return redirect(obj.get_absolute_url())
|
||||
|
||||
@ -228,7 +231,9 @@ class BulkSyncDataView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
for obj in selected_objects:
|
||||
obj.sync(save=True)
|
||||
|
||||
model_name = self.queryset.model._meta.verbose_name_plural
|
||||
messages.success(request, f"Synced {len(selected_objects)} {model_name}")
|
||||
messages.success(request, _("Synced {count} {object_type}").format(
|
||||
count=len(selected_objects),
|
||||
object_type=self.queryset.model._meta.verbose_name_plural
|
||||
))
|
||||
|
||||
return redirect(self.get_return_url(request))
|
||||
|
BIN
netbox/project-static/dist/netbox.css
vendored
BIN
netbox/project-static/dist/netbox.css
vendored
Binary file not shown.
@ -44,3 +44,7 @@ table a {
|
||||
[data-bs-theme=dark] ::selection {
|
||||
background-color: rgba(var(--tblr-primary-rgb),.48)
|
||||
}
|
||||
|
||||
pre code {
|
||||
padding: unset;
|
||||
}
|
||||
|
@ -867,13 +867,20 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
braces@^3.0.2, braces@~3.0.2:
|
||||
braces@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
braces@~3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
|
||||
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
|
||||
dependencies:
|
||||
fill-range "^7.1.1"
|
||||
|
||||
call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
|
||||
@ -1520,10 +1527,10 @@ file-entry-cache@^6.0.1:
|
||||
dependencies:
|
||||
flat-cache "^3.0.4"
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
|
||||
fill-range@^7.0.1, fill-range@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
||||
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
@ -1816,9 +1823,9 @@ ignore@^5.2.0:
|
||||
integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
|
||||
|
||||
immutable@^4.0.0:
|
||||
version "4.3.6"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.6.tgz#6a05f7858213238e587fb83586ffa3b4b27f0447"
|
||||
integrity sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==
|
||||
version "4.3.7"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.7.tgz#c70145fc90d89fb02021e65c84eb0226e4e5a381"
|
||||
integrity sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==
|
||||
|
||||
import-fresh@^3.2.1:
|
||||
version "3.3.0"
|
||||
|
@ -24,7 +24,12 @@
|
||||
</div>
|
||||
{% endblock page-header %}
|
||||
|
||||
{% block title %}{{ status|capfirst }} {% trans "Workers in " %}{{ queue.name }}{% endblock %}
|
||||
{% block title %}
|
||||
{{ status|capfirst }}
|
||||
{% blocktrans trimmed with queue_name=queue.name %}
|
||||
Workers in {{ queue_name }}
|
||||
{% endblocktrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block controls %}{% endblock %}
|
||||
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-07-27 05:02+0000\n"
|
||||
"POT-Creation-Date: 2024-08-11 05:02+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -58,10 +58,27 @@ msgstr ""
|
||||
msgid "Allowed IPs"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/account/views.py:112
|
||||
#, python-brace-format
|
||||
msgid "Logged in as {user}."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/account/views.py:162
|
||||
msgid "You have logged out."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/account/views.py:214
|
||||
msgid "Your preferences have been updated."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/account/views.py:237
|
||||
msgid "LDAP-authenticated user credentials cannot be changed within NetBox."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/account/views.py:252
|
||||
msgid "Your password has been changed successfully."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/circuits/choices.py:21 netbox/dcim/choices.py:20
|
||||
#: netbox/dcim/choices.py:102 netbox/dcim/choices.py:174
|
||||
#: netbox/dcim/choices.py:220 netbox/dcim/choices.py:1459
|
||||
@ -309,7 +326,7 @@ msgstr ""
|
||||
#: netbox/circuits/forms/filtersets.py:212
|
||||
#: netbox/circuits/forms/model_forms.py:109
|
||||
#: netbox/circuits/forms/model_forms.py:131
|
||||
#: netbox/circuits/tables/circuits.py:98 netbox/dcim/forms/connections.py:71
|
||||
#: netbox/circuits/tables/circuits.py:98 netbox/dcim/forms/connections.py:73
|
||||
#: netbox/templates/circuits/circuit.html:15
|
||||
#: netbox/templates/circuits/circuittermination.html:19
|
||||
#: netbox/templates/dcim/inc/cable_termination.html:55
|
||||
@ -532,7 +549,7 @@ msgstr ""
|
||||
#: netbox/dcim/tables/devices.py:802 netbox/dcim/tables/power.py:77
|
||||
#: netbox/extras/forms/bulk_import.py:39 netbox/extras/tables/tables.py:290
|
||||
#: netbox/extras/tables/tables.py:362 netbox/extras/tables/tables.py:480
|
||||
#: netbox/netbox/tables/tables.py:239 netbox/templates/circuits/circuit.html:30
|
||||
#: netbox/netbox/tables/tables.py:240 netbox/templates/circuits/circuit.html:30
|
||||
#: netbox/templates/core/datasource.html:38 netbox/templates/dcim/cable.html:15
|
||||
#: netbox/templates/dcim/consoleport.html:36
|
||||
#: netbox/templates/dcim/consoleserverport.html:36
|
||||
@ -964,7 +981,7 @@ msgstr ""
|
||||
#: netbox/ipam/forms/filtersets.py:266 netbox/ipam/forms/filtersets.py:307
|
||||
#: netbox/ipam/forms/filtersets.py:382 netbox/ipam/forms/filtersets.py:475
|
||||
#: netbox/ipam/forms/filtersets.py:534 netbox/ipam/forms/filtersets.py:552
|
||||
#: netbox/netbox/tables/tables.py:255
|
||||
#: netbox/netbox/tables/tables.py:256
|
||||
#: netbox/virtualization/forms/filtersets.py:45
|
||||
#: netbox/virtualization/forms/filtersets.py:103
|
||||
#: netbox/virtualization/forms/filtersets.py:194
|
||||
@ -1377,6 +1394,16 @@ msgstr ""
|
||||
msgid "ASN Count"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/circuits/views.py:331
|
||||
#, python-brace-format
|
||||
msgid "No terminations have been defined for circuit {circuit}."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/circuits/views.py:380
|
||||
#, python-brace-format
|
||||
msgid "Swapped terminations for circuit {circuit}."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/api/views.py:36
|
||||
msgid "This user does not have permission to synchronize this data source."
|
||||
msgstr ""
|
||||
@ -1959,7 +1986,7 @@ msgstr ""
|
||||
|
||||
#: netbox/core/tables/jobs.py:10 netbox/core/tables/tasks.py:76
|
||||
#: netbox/dcim/tables/devicetypes.py:165 netbox/extras/tables/tables.py:185
|
||||
#: netbox/extras/tables/tables.py:357 netbox/netbox/tables/tables.py:188
|
||||
#: netbox/extras/tables/tables.py:357 netbox/netbox/tables/tables.py:189
|
||||
#: netbox/templates/dcim/virtualchassis_edit.html:52
|
||||
#: netbox/utilities/forms/forms.py:73 netbox/wireless/tables/wirelesslink.py:16
|
||||
msgid "ID"
|
||||
@ -1969,7 +1996,7 @@ msgstr ""
|
||||
#: netbox/extras/tables/tables.py:248 netbox/extras/tables/tables.py:294
|
||||
#: netbox/extras/tables/tables.py:367 netbox/extras/tables/tables.py:485
|
||||
#: netbox/extras/tables/tables.py:516 netbox/extras/tables/tables.py:556
|
||||
#: netbox/extras/tables/tables.py:593 netbox/netbox/tables/tables.py:243
|
||||
#: netbox/extras/tables/tables.py:593 netbox/netbox/tables/tables.py:244
|
||||
#: netbox/templates/extras/eventrule.html:84
|
||||
#: netbox/templates/extras/journalentry.html:18
|
||||
#: netbox/templates/extras/objectchange.html:58
|
||||
@ -2007,7 +2034,7 @@ msgstr ""
|
||||
msgid "Oldest Task"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/tables/tasks.py:42 netbox/templates/core/rq_worker_list.html:34
|
||||
#: netbox/core/tables/tasks.py:42 netbox/templates/core/rq_worker_list.html:39
|
||||
msgid "Workers"
|
||||
msgstr ""
|
||||
|
||||
@ -2063,12 +2090,56 @@ msgstr ""
|
||||
msgid "No workers found"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:331 netbox/core/views.py:374 netbox/core/views.py:397
|
||||
#: netbox/core/views.py:415 netbox/core/views.py:450
|
||||
#: netbox/core/views.py:81
|
||||
#, python-brace-format
|
||||
msgid "Queued job #{id} to sync {datasource}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:241
|
||||
#, python-brace-format
|
||||
msgid "Restored configuration revision #{id}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:334 netbox/core/views.py:377 netbox/core/views.py:453
|
||||
#, python-brace-format
|
||||
msgid "Job {job_id} not found"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:385
|
||||
#, python-brace-format
|
||||
msgid "Job {id} has been deleted."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:387
|
||||
#, python-brace-format
|
||||
msgid "Error deleting job {id}: {error}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:400 netbox/core/views.py:418
|
||||
#, python-brace-format
|
||||
msgid "Job {id} not found."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:406
|
||||
#, python-brace-format
|
||||
msgid "Job {id} has been re-enqueued."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:441
|
||||
#, python-brace-format
|
||||
msgid "Job {id} has been enqueued."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:460
|
||||
#, python-brace-format
|
||||
msgid "Job {id} has been stopped."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/views.py:462
|
||||
#, python-brace-format
|
||||
msgid "Failed to stop job {id}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/api/serializers_/devices.py:50
|
||||
#: netbox/dcim/api/serializers_/devicetypes.py:26
|
||||
msgid "Position (U)"
|
||||
@ -4091,7 +4162,7 @@ msgstr ""
|
||||
msgid "A {model} named {name} already exists"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/forms/connections.py:48 netbox/dcim/forms/model_forms.py:686
|
||||
#: netbox/dcim/forms/connections.py:49 netbox/dcim/forms/model_forms.py:686
|
||||
#: netbox/dcim/tables/power.py:66
|
||||
#: netbox/templates/dcim/inc/cable_termination.html:37
|
||||
#: netbox/templates/dcim/powerfeed.html:24
|
||||
@ -4100,13 +4171,13 @@ msgstr ""
|
||||
msgid "Power Panel"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/forms/connections.py:57 netbox/dcim/forms/model_forms.py:713
|
||||
#: netbox/dcim/forms/connections.py:58 netbox/dcim/forms/model_forms.py:713
|
||||
#: netbox/templates/dcim/powerfeed.html:21
|
||||
#: netbox/templates/dcim/powerport.html:80
|
||||
msgid "Power Feed"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/forms/connections.py:79
|
||||
#: netbox/dcim/forms/connections.py:81
|
||||
msgid "Side"
|
||||
msgstr ""
|
||||
|
||||
@ -6081,7 +6152,7 @@ msgstr ""
|
||||
#: netbox/templates/virtualization/virtualmachine/base.html:27
|
||||
#: netbox/templates/virtualization/virtualmachine_list.html:14
|
||||
#: netbox/virtualization/tables/virtualmachines.py:100
|
||||
#: netbox/virtualization/views.py:363 netbox/wireless/tables/wirelesslan.py:55
|
||||
#: netbox/virtualization/views.py:365 netbox/wireless/tables/wirelesslan.py:55
|
||||
msgid "Interfaces"
|
||||
msgstr ""
|
||||
|
||||
@ -6381,24 +6452,53 @@ msgstr ""
|
||||
#: netbox/dcim/views.py:2019 netbox/extras/forms/model_forms.py:453
|
||||
#: netbox/templates/extras/configcontext.html:10
|
||||
#: netbox/virtualization/forms/model_forms.py:225
|
||||
#: netbox/virtualization/views.py:404
|
||||
#: netbox/virtualization/views.py:406
|
||||
msgid "Config Context"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:2029 netbox/virtualization/views.py:414
|
||||
#: netbox/dcim/views.py:2029 netbox/virtualization/views.py:416
|
||||
msgid "Render Config"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:2062 netbox/virtualization/views.py:449
|
||||
#, python-brace-format
|
||||
msgid "An error occurred while rendering the template: {error}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:2080 netbox/extras/tables/tables.py:447
|
||||
#: netbox/netbox/navigation/menu.py:234 netbox/netbox/navigation/menu.py:236
|
||||
#: netbox/virtualization/views.py:179
|
||||
msgid "Virtual Machines"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:2963 netbox/ipam/tables/ip.py:234
|
||||
#: netbox/dcim/views.py:2828
|
||||
#, python-brace-format
|
||||
msgid "Installed device {device} in bay {device_bay}."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:2869
|
||||
#, python-brace-format
|
||||
msgid "Removed device {device} from bay {device_bay}."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:2975 netbox/ipam/tables/ip.py:234
|
||||
msgid "Children"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:3441
|
||||
msgid "Added member <a href=\"{url}\">{escape(device)}</a>"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:3488
|
||||
#, python-brace-format
|
||||
msgid "Unable to remove master device {device} from the virtual chassis."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/dcim/views.py:3501
|
||||
#, python-brace-format
|
||||
msgid "Removed {device} from virtual chassis {chassis}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/api/customfields.py:88
|
||||
#, python-brace-format
|
||||
msgid "Unknown related object(s): {name}"
|
||||
@ -10167,7 +10267,7 @@ msgstr ""
|
||||
#: netbox/templates/virtualization/virtualmachine/base.html:32
|
||||
#: netbox/templates/virtualization/virtualmachine_list.html:21
|
||||
#: netbox/virtualization/tables/virtualmachines.py:103
|
||||
#: netbox/virtualization/views.py:385
|
||||
#: netbox/virtualization/views.py:387
|
||||
msgid "Virtual Disks"
|
||||
msgstr ""
|
||||
|
||||
@ -10538,17 +10638,17 @@ msgstr ""
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/tables/tables.py:57
|
||||
#: netbox/netbox/tables/tables.py:58
|
||||
#, python-brace-format
|
||||
msgid "No {model_name} found"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/tables/tables.py:248
|
||||
#: netbox/netbox/tables/tables.py:249
|
||||
#: netbox/templates/generic/bulk_import.html:117
|
||||
msgid "Field"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/tables/tables.py:251
|
||||
#: netbox/netbox/tables/tables.py:252
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
@ -10556,11 +10656,35 @@ msgstr ""
|
||||
msgid "Dummy Plugin"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/bulk_views.py:405
|
||||
#: netbox/netbox/views/generic/bulk_views.py:111
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"There was an error rendering the selected export template ({template}): "
|
||||
"{error}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/bulk_views.py:411
|
||||
#, python-brace-format
|
||||
msgid "Row {i}: Object with ID {id} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/bulk_views.py:679
|
||||
#: netbox/netbox/views/generic/bulk_views.py:877
|
||||
#: netbox/netbox/views/generic/bulk_views.py:925
|
||||
#, python-brace-format
|
||||
msgid "No {object_type} were selected."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/bulk_views.py:759
|
||||
#, python-brace-format
|
||||
msgid "Renamed {count} {object_type}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/bulk_views.py:855
|
||||
#, python-brace-format
|
||||
msgid "Deleted {count} {object_type}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/feature_views.py:38
|
||||
msgid "Changelog"
|
||||
msgstr ""
|
||||
@ -10569,6 +10693,20 @@ msgstr ""
|
||||
msgid "Journal"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/feature_views.py:205
|
||||
msgid "Unable to synchronize data: No data file set."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/feature_views.py:209
|
||||
#, python-brace-format
|
||||
msgid "Synchronized data for {object_type} {object}."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/feature_views.py:234
|
||||
#, python-brace-format
|
||||
msgid "Synced {count} {object_type}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/netbox/views/generic/object_views.py:108
|
||||
#, python-brace-format
|
||||
msgid "{class_name} must implement get_children()"
|
||||
@ -11128,8 +11266,8 @@ msgstr ""
|
||||
|
||||
#: netbox/templates/core/rq_queue_list.html:24
|
||||
#: netbox/templates/core/rq_queue_list.html:25
|
||||
#: netbox/templates/core/rq_worker_list.html:44
|
||||
#: netbox/templates/core/rq_worker_list.html:45
|
||||
#: netbox/templates/core/rq_worker_list.html:49
|
||||
#: netbox/templates/core/rq_worker_list.html:50
|
||||
#: netbox/templates/extras/script_result.html:49
|
||||
#: netbox/templates/extras/script_result.html:51
|
||||
#: netbox/templates/inc/table_controls_htmx.html:30
|
||||
@ -11234,8 +11372,9 @@ msgstr ""
|
||||
msgid "Background Workers"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/core/rq_worker_list.html:27
|
||||
msgid "Workers in "
|
||||
#: netbox/templates/core/rq_worker_list.html:29
|
||||
#, python-format
|
||||
msgid "Workers in %(queue_name)s"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/core/system.html:11
|
||||
@ -14334,6 +14473,16 @@ msgstr ""
|
||||
msgid "virtual disks"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/virtualization/views.py:274
|
||||
#, python-brace-format
|
||||
msgid "Added {count} devices to cluster {cluster}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/virtualization/views.py:309
|
||||
#, python-brace-format
|
||||
msgid "Removed {count} devices from cluster {cluster}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/vpn/choices.py:31
|
||||
msgid "IPsec - Transport"
|
||||
msgstr ""
|
||||
|
@ -3,8 +3,8 @@ from django import forms
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django_filters.constants import EMPTY_VALUES
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
|
||||
__all__ = (
|
||||
'ContentTypeFilter',
|
||||
@ -116,6 +116,7 @@ class MultiValueWWNFilter(django_filters.MultipleChoiceFilter):
|
||||
field_class = multivalue_field_factory(forms.CharField)
|
||||
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
class TreeNodeMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilter):
|
||||
"""
|
||||
Filters for a set of Models, including all descendant models within a Tree. Example: [<Region: R1>,<Region: R2>]
|
||||
|
@ -271,8 +271,9 @@ class ClusterAddDevicesView(generic.ObjectEditView):
|
||||
device.cluster = cluster
|
||||
device.save()
|
||||
|
||||
messages.success(request, "Added {} devices to cluster {}".format(
|
||||
len(device_pks), cluster
|
||||
messages.success(request, _("Added {count} devices to cluster {cluster}").format(
|
||||
count=len(device_pks),
|
||||
cluster=cluster
|
||||
))
|
||||
return redirect(cluster.get_absolute_url())
|
||||
|
||||
@ -305,8 +306,9 @@ class ClusterRemoveDevicesView(generic.ObjectEditView):
|
||||
device.cluster = None
|
||||
device.save()
|
||||
|
||||
messages.success(request, "Removed {} devices from cluster {}".format(
|
||||
len(device_pks), cluster
|
||||
messages.success(request, _("Removed {count} devices from cluster {cluster}").format(
|
||||
count=len(device_pks),
|
||||
cluster=cluster
|
||||
))
|
||||
return redirect(cluster.get_absolute_url())
|
||||
|
||||
@ -444,7 +446,7 @@ class VirtualMachineRenderConfigView(generic.ObjectView):
|
||||
try:
|
||||
rendered_config = config_template.render(context=context_data)
|
||||
except TemplateError as e:
|
||||
messages.error(request, f"An error occurred while rendering the template: {e}")
|
||||
messages.error(request, _("An error occurred while rendering the template: {error}").format(error=e))
|
||||
rendered_config = traceback.format_exc()
|
||||
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user