nautobot 3.0.4__py3-none-any.whl → 3.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nautobot/core/tables.py +28 -17
- nautobot/core/templates/graphene/graphiql.html +3 -5
- nautobot/core/templates/inc/javascript.html +5 -10
- nautobot/core/templates/inc/media.html +5 -4
- nautobot/core/templates/inc/media_failure.html +73 -0
- nautobot/core/templates/media_failure.html +1 -0
- nautobot/core/tests/test_templatetags_helpers.py +9 -9
- nautobot/core/ui/object_detail.py +1 -0
- nautobot/dcim/tables/devices.py +4 -4
- nautobot/dcim/tables/template_code.py +8 -4
- nautobot/dcim/tests/test_tables.py +5 -6
- nautobot/dcim/views.py +6 -7
- nautobot/extras/tables.py +3 -3
- nautobot/extras/templates/extras/inc/jobresult_js.html +1 -2
- nautobot/extras/templates/extras/scheduledjob.html +3 -1
- nautobot/extras/tests/test_customfields.py +75 -9
- nautobot/ipam/filters.py +58 -3
- nautobot/ipam/tables.py +8 -4
- nautobot/ipam/tests/test_filters.py +55 -0
- nautobot/project-static/dist/css/nautobot.css +1 -1
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/docs/404.html +64 -8
- nautobot/project-static/docs/apps/index.html +64 -8
- nautobot/project-static/docs/apps/nautobot-apps.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/templatetags.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +64 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +64 -8
- nautobot/project-static/docs/development/apps/api/configuration-view.html +64 -8
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +64 -8
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +67 -11
- nautobot/project-static/docs/development/apps/api/models/global-search.html +64 -8
- nautobot/project-static/docs/development/apps/api/models/graphql.html +64 -8
- nautobot/project-static/docs/development/apps/api/models/index.html +64 -8
- nautobot/project-static/docs/development/apps/api/models/queryset.html +13440 -0
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/prepopulating-data.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +64 -8
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +64 -8
- nautobot/project-static/docs/development/apps/api/prometheus.html +64 -8
- nautobot/project-static/docs/development/apps/api/setup.html +64 -8
- nautobot/project-static/docs/development/apps/api/testing.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +64 -8
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/base-template.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/index.html +67 -11
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/notes.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +64 -8
- nautobot/project-static/docs/development/apps/api/views/urls.html +64 -8
- nautobot/project-static/docs/development/apps/index.html +64 -8
- nautobot/project-static/docs/development/apps/migration/code-updates.html +64 -8
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +64 -8
- nautobot/project-static/docs/development/apps/migration/from-v1.html +64 -8
- nautobot/project-static/docs/development/apps/migration/from-v2/migrating-v2-to-v3.html +64 -8
- nautobot/project-static/docs/development/apps/migration/from-v2/new-nautobot-custom-ui-apis.html +64 -8
- nautobot/project-static/docs/development/apps/migration/from-v2/overview.html +68 -8
- nautobot/project-static/docs/development/apps/migration/from-v2/upgrading-from-bootstrap-v3-to-v5.html +64 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +64 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +64 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +64 -8
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +64 -8
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +64 -8
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +64 -8
- nautobot/project-static/docs/development/core/application-registry.html +64 -8
- nautobot/project-static/docs/development/core/best-practices.html +64 -8
- nautobot/project-static/docs/development/core/caching.html +64 -8
- nautobot/project-static/docs/development/core/controllers.html +64 -8
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +64 -8
- nautobot/project-static/docs/development/core/docs-media-standards.html +64 -8
- nautobot/project-static/docs/development/core/generic-views.html +64 -8
- nautobot/project-static/docs/development/core/getting-started.html +64 -8
- nautobot/project-static/docs/development/core/homepage.html +64 -8
- nautobot/project-static/docs/development/core/index.html +64 -8
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +64 -8
- nautobot/project-static/docs/development/core/model-checklist.html +64 -8
- nautobot/project-static/docs/development/core/model-features.html +64 -8
- nautobot/project-static/docs/development/core/natural-keys.html +64 -8
- nautobot/project-static/docs/development/core/navigation-menu.html +64 -8
- nautobot/project-static/docs/development/core/release-checklist.html +64 -8
- nautobot/project-static/docs/development/core/role-internals.html +64 -8
- nautobot/project-static/docs/development/core/settings.html +64 -8
- nautobot/project-static/docs/development/core/style-guide.html +64 -8
- nautobot/project-static/docs/development/core/templates.html +64 -8
- nautobot/project-static/docs/development/core/testing.html +64 -8
- nautobot/project-static/docs/development/core/ui-best-practices.html +64 -8
- nautobot/project-static/docs/development/core/ui-component-framework.html +64 -8
- nautobot/project-static/docs/development/core/user-preferences.html +64 -8
- nautobot/project-static/docs/development/index.html +64 -8
- nautobot/project-static/docs/development/jobs/getting-started.html +64 -8
- nautobot/project-static/docs/development/jobs/index.html +64 -8
- nautobot/project-static/docs/development/jobs/installation.html +64 -8
- nautobot/project-static/docs/development/jobs/job-extensions.html +64 -8
- nautobot/project-static/docs/development/jobs/job-logging.html +64 -8
- nautobot/project-static/docs/development/jobs/job-patterns.html +64 -8
- nautobot/project-static/docs/development/jobs/job-structure.html +64 -8
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +64 -8
- nautobot/project-static/docs/development/jobs/testing.html +64 -8
- nautobot/project-static/docs/index.html +64 -8
- nautobot/project-static/docs/overview/application_stack.html +64 -8
- nautobot/project-static/docs/overview/design_philosophy.html +64 -8
- nautobot/project-static/docs/release-notes/index.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.0.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.1.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.2.html +65 -9
- nautobot/project-static/docs/release-notes/version-1.3.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.4.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.5.html +64 -8
- nautobot/project-static/docs/release-notes/version-1.6.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.0.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.1.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.2.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.3.html +64 -8
- nautobot/project-static/docs/release-notes/version-2.4.html +584 -8
- nautobot/project-static/docs/release-notes/version-3.0.html +232 -8
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +337 -329
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +64 -8
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +64 -8
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +72 -9
- nautobot/project-static/docs/user-guide/administration/installation/index.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +64 -8
- nautobot/project-static/docs/user-guide/administration/installation/services.html +64 -8
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +65 -9
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +64 -8
- nautobot/project-static/docs/user-guide/administration/security/index.html +64 -8
- nautobot/project-static/docs/user-guide/administration/security/notices.html +64 -8
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +64 -8
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +67 -11
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +64 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v2/index.html +68 -8
- nautobot/project-static/docs/user-guide/administration/upgrading/postgresql.html +13391 -0
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +125 -15
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/certificateprofile.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/healthcheckmonitor.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/index.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/loadbalancerpool.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/loadbalancerpoolmember.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/load-balancers/virtualserver.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/index.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpn.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpnphase1policy.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpnphase2policy.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpnprofile.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpntunnel.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/vpn/vpntunnelendpoint.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +64 -8
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/data-compliance.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/load-balancers.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +64 -8
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +64 -8
- nautobot/project-static/docs/user-guide/index.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/approval-workflow.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/data-validation.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/echarts.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +116 -34
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/user-interface/configurablecolumns.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/user-interface/savedview.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/user-interface/search.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/users/groups.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +64 -8
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +64 -8
- nautobot/ui/src/scss/nautobot.scss +2 -1
- {nautobot-3.0.4.dist-info → nautobot-3.0.5.dist-info}/METADATA +1 -1
- {nautobot-3.0.4.dist-info → nautobot-3.0.5.dist-info}/RECORD +363 -360
- {nautobot-3.0.4.dist-info → nautobot-3.0.5.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.4.dist-info → nautobot-3.0.5.dist-info}/NOTICE +0 -0
- {nautobot-3.0.4.dist-info → nautobot-3.0.5.dist-info}/WHEEL +0 -0
- {nautobot-3.0.4.dist-info → nautobot-3.0.5.dist-info}/entry_points.txt +0 -0
nautobot/core/tables.py
CHANGED
|
@@ -9,7 +9,7 @@ from django.db.models import Prefetch, QuerySet
|
|
|
9
9
|
from django.db.models.fields.related import ForeignKey, RelatedField
|
|
10
10
|
from django.db.models.fields.reverse_related import ManyToOneRel
|
|
11
11
|
from django.urls import reverse
|
|
12
|
-
from django.utils.html import
|
|
12
|
+
from django.utils.html import format_html, format_html_join
|
|
13
13
|
from django.utils.http import urlencode
|
|
14
14
|
from django.utils.safestring import mark_safe
|
|
15
15
|
from django.utils.text import Truncator
|
|
@@ -36,6 +36,7 @@ class BaseTable(django_tables2.Table):
|
|
|
36
36
|
attrs = {
|
|
37
37
|
"class": "table table-hover nb-table-headings",
|
|
38
38
|
}
|
|
39
|
+
default = helpers.HTML_NONE
|
|
39
40
|
|
|
40
41
|
def __init__(
|
|
41
42
|
self,
|
|
@@ -579,7 +580,13 @@ class ColoredLabelColumn(django_tables2.TemplateColumn):
|
|
|
579
580
|
|
|
580
581
|
template_code = """
|
|
581
582
|
{% load helpers %}
|
|
582
|
-
{% if value %}
|
|
583
|
+
{% if value %}
|
|
584
|
+
<span class="badge" style="color: {{ value.color|fgcolor }}; background-color: #{{ value.color }}">
|
|
585
|
+
{{ value }}
|
|
586
|
+
</span>
|
|
587
|
+
{% else %}
|
|
588
|
+
<span class="text-secondary">—</span>
|
|
589
|
+
{% endif %}
|
|
583
590
|
"""
|
|
584
591
|
|
|
585
592
|
def __init__(self, *args, **kwargs):
|
|
@@ -757,29 +764,33 @@ class CustomFieldColumn(django_tables2.Column):
|
|
|
757
764
|
Display custom fields in the appropriate format.
|
|
758
765
|
"""
|
|
759
766
|
|
|
760
|
-
# Add [] to empty_values so when there is no choice populated for multiselect_cf i.e. [], "—" is returned automatically.
|
|
761
|
-
empty_values = (None, "", [])
|
|
762
|
-
|
|
763
767
|
def __init__(self, customfield, *args, **kwargs):
|
|
764
768
|
self.customfield = customfield
|
|
765
769
|
kwargs["accessor"] = Accessor(f"_custom_field_data__{customfield.key}")
|
|
766
770
|
kwargs["verbose_name"] = customfield.label
|
|
771
|
+
if self.customfield.type == choices.CustomFieldTypeChoices.TYPE_MULTISELECT:
|
|
772
|
+
# Add [] to empty_values so when there is no choice populated i.e. [], "—" is returned automatically.
|
|
773
|
+
kwargs.setdefault("empty_values", (None, "", []))
|
|
767
774
|
|
|
768
775
|
super().__init__(*args, **kwargs)
|
|
769
776
|
|
|
770
777
|
def render(self, *, record, bound_column, value): # pylint: disable=arguments-differ # tables2 varies its kwargs
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
778
|
+
# TODO: this logic could be unified with _ObjectCustomFieldsPanel.render_value
|
|
779
|
+
if self.customfield.type == choices.CustomFieldTypeChoices.TYPE_BOOLEAN and value is not None:
|
|
780
|
+
value = helpers.render_boolean(value)
|
|
781
|
+
elif self.customfield.type == choices.CustomFieldTypeChoices.TYPE_JSON and value is not None:
|
|
782
|
+
value = helpers.render_json(value, pretty_print=True)
|
|
783
|
+
elif self.customfield.type == choices.CustomFieldTypeChoices.TYPE_MARKDOWN and value is not None:
|
|
784
|
+
value = helpers.render_markdown(value)
|
|
785
|
+
elif self.customfield.type == choices.CustomFieldTypeChoices.TYPE_MULTISELECT and value:
|
|
786
|
+
value = format_html_join(" ", '<span class="badge bg-secondary">{}</span>', ((v,) for v in value))
|
|
787
|
+
elif self.customfield.type == choices.CustomFieldTypeChoices.TYPE_SELECT and value is not None:
|
|
788
|
+
value = format_html('<span class="badge bg-secondary">{}</span>', value)
|
|
789
|
+
elif self.customfield.type == choices.CustomFieldTypeChoices.TYPE_URL and value:
|
|
790
|
+
value = format_html('<a href="{}">{}</a>', value, value)
|
|
791
|
+
# else (TEXT, INTEGER, DATE) or None value -- no need to do special rendering
|
|
781
792
|
|
|
782
|
-
return
|
|
793
|
+
return value
|
|
783
794
|
|
|
784
795
|
|
|
785
796
|
class RelationshipColumn(django_tables2.Column):
|
|
@@ -811,7 +822,7 @@ class RelationshipColumn(django_tables2.Column):
|
|
|
811
822
|
# Handle Symmetric Relationships
|
|
812
823
|
# List `value` could be empty here [] after the filtering from above
|
|
813
824
|
if len(value) < 1:
|
|
814
|
-
return
|
|
825
|
+
return helpers.HTML_NONE
|
|
815
826
|
|
|
816
827
|
v = value[0]
|
|
817
828
|
peer = v.get_peer(record)
|
|
@@ -81,11 +81,9 @@ add "&raw" to the end of the URL within a browser.
|
|
|
81
81
|
<!-- As Nautobot may be run without internet access, we source these files locally rather than from an online CDN -->
|
|
82
82
|
<link rel="stylesheet"
|
|
83
83
|
href="{% static 'dist/css/graphql-libraries.css' %}"
|
|
84
|
-
onerror="
|
|
85
|
-
<script src="{% static 'dist/js/graphql-libraries.js' %}"
|
|
86
|
-
|
|
87
|
-
<script src="{% static 'dist/js/nautobot-graphiql.js' %}"
|
|
88
|
-
onerror="window.location='{% url 'media_failure' %}?filename=dist/js/nautobot-graphiql.js'"></script>
|
|
84
|
+
onerror="nb.media.handleFailure(this)">
|
|
85
|
+
<script src="{% static 'dist/js/graphql-libraries.js' %}" onerror="nb.media.handleFailure(this)"></script>
|
|
86
|
+
<script src="{% static 'dist/js/nautobot-graphiql.js' %}" onerror="nb.media.handleFailure(this)"></script>
|
|
89
87
|
</head>
|
|
90
88
|
<body>
|
|
91
89
|
<!-- Nautobot page contents -->
|
|
@@ -2,16 +2,11 @@
|
|
|
2
2
|
{% load plugins %}
|
|
3
3
|
{% load static %}
|
|
4
4
|
|
|
5
|
-
<script src="{% static 'dist/js/nautobot.js' %}"
|
|
6
|
-
|
|
7
|
-
<script src="{%
|
|
8
|
-
|
|
9
|
-
<script src="{% versioned_static 'js/
|
|
10
|
-
onerror="window.location='{% url 'media_failure' %}?filename=js/forms.js'"></script>
|
|
11
|
-
<script src="{% versioned_static 'js/table_sorting_indicator.js' %}"
|
|
12
|
-
onerror="window.location='{% url 'media_failure' %}?filename=js/table_sorting_indicator.js'"></script>
|
|
13
|
-
<script src="{% versioned_static 'js/dropdown.js' %}"
|
|
14
|
-
onerror="window.location='{% url 'media_failure' %}?filename=js/dropdown.js'"></script>
|
|
5
|
+
<script src="{% static 'dist/js/nautobot.js' %}" onerror="nb.media.handleFailure(this)"></script>
|
|
6
|
+
<script src="{% static 'dist/js/libraries.js' %}" onerror="nb.media.handleFailure(this)"></script>
|
|
7
|
+
<script src="{% versioned_static 'js/forms.js' %}" onerror="nb.media.handleFailure(this)"></script>
|
|
8
|
+
<script src="{% versioned_static 'js/table_sorting_indicator.js' %}" onerror="nb.media.handleFailure(this)"></script>
|
|
9
|
+
<script src="{% versioned_static 'js/dropdown.js' %}" onerror="nb.media.handleFailure(this)"></script>
|
|
15
10
|
<script type="text/javascript">
|
|
16
11
|
var nautobot_static_url = "{% static '' %}";
|
|
17
12
|
var nautobot_api_path = "{% url 'api-root' %}";
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
{% load static %}
|
|
2
2
|
{% load helpers %}
|
|
3
3
|
{% load plugins %}
|
|
4
|
+
{% include 'inc/media_failure.html' %}
|
|
4
5
|
<link rel="stylesheet" id="template-theme"
|
|
5
6
|
href="{% url 'template_css' %}">
|
|
6
7
|
<link rel="stylesheet"
|
|
7
8
|
href="{% static 'dist/css/nautobot.css' %}"
|
|
8
|
-
onerror="
|
|
9
|
+
onerror="nb.media.handleFailure(this)">
|
|
9
10
|
<link rel="stylesheet"
|
|
10
11
|
href="{% static 'dist/css/materialdesignicons.css' %}"
|
|
11
|
-
onerror="
|
|
12
|
+
onerror="nb.media.handleFailure(this)">
|
|
12
13
|
{% with cookie_theme=request.COOKIES|get_item:'theme' %}
|
|
13
14
|
{% if not cookie_theme or cookie_theme == 'light' %}
|
|
14
15
|
<link rel="stylesheet"
|
|
15
16
|
href="{% static 'dist/css/github.min.css' %}"
|
|
16
|
-
onerror="
|
|
17
|
+
onerror="nb.media.handleFailure(this)"
|
|
17
18
|
{% if not cookie_theme %}media="not (prefers-color-scheme: dark)"{% endif %}>
|
|
18
19
|
{% endif %}
|
|
19
20
|
{% if not cookie_theme or cookie_theme == 'dark' %}
|
|
20
21
|
<link rel="stylesheet"
|
|
21
22
|
href="{% static 'dist/css/github-dark.min.css' %}"
|
|
22
|
-
onerror="
|
|
23
|
+
onerror="nb.media.handleFailure(this)"
|
|
23
24
|
{% if not cookie_theme %}media="(prefers-color-scheme: dark)"{% endif %}>
|
|
24
25
|
{% endif %}
|
|
25
26
|
{% endwith %}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{% load static %}
|
|
2
|
+
|
|
3
|
+
<script>
|
|
4
|
+
(function initMediaFailure() {
|
|
5
|
+
// Create React-style `ref` reference to the media failure alert element.
|
|
6
|
+
const failureAlertRef = { current: null };
|
|
7
|
+
|
|
8
|
+
// Store all media failures inside `Map` containing `Set` values to ensure uniqueness of all reported failures.
|
|
9
|
+
const failures = new Map();
|
|
10
|
+
|
|
11
|
+
function handleFailure(element) {
|
|
12
|
+
const url = element.href || element.src || '';
|
|
13
|
+
|
|
14
|
+
if (url) {
|
|
15
|
+
// Update `failures` with a proper entry corresponding to the current media failure and update media failure alert.
|
|
16
|
+
const elements = failures.get(url) || new Set();
|
|
17
|
+
elements.add(element);
|
|
18
|
+
failures.set(url, elements);
|
|
19
|
+
updateFailureAlert();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function updateFailureAlert() {
|
|
24
|
+
const count = Array.from(failures.values()).reduce((count, elements) => count + elements.size, 0);
|
|
25
|
+
|
|
26
|
+
const innerHTML = `
|
|
27
|
+
<div style="margin: auto; max-width: 50rem;">
|
|
28
|
+
<h2 style="margin-top: 0; text-align: center;">Static Media Failure</h2>
|
|
29
|
+
<p><strong>${count}</strong> static media file${count > 1 ? 's' : ''} failed to load.</p>
|
|
30
|
+
<p style="margin-bottom: 0;">
|
|
31
|
+
Check whether
|
|
32
|
+
<code><strong>nautobot-server collectstatic</strong></code>
|
|
33
|
+
was run during the most recent upgrade and that the HTTP service (e.g. NGINX) is configured to serve static files.
|
|
34
|
+
Refer to
|
|
35
|
+
<a href="https://docs.nautobot.com/projects/core/en/v{{ settings.VERSION }}/installation/http-server/#static-media-failure" target="_blank">
|
|
36
|
+
the installation documentation
|
|
37
|
+
</a>
|
|
38
|
+
for further guidance.
|
|
39
|
+
</p>
|
|
40
|
+
</div>
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
// If media failure alert already exists, just update its content and return early.
|
|
44
|
+
if (failureAlertRef.current) {
|
|
45
|
+
failureAlertRef.current.innerHTML = innerHTML;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// If media failure alert does not exist yet, create one.
|
|
50
|
+
const alert = document.createElement('div');
|
|
51
|
+
alert.classList.add('alert', 'alert-danger', 'media-failure-banner');
|
|
52
|
+
alert.setAttribute('role', 'alert');
|
|
53
|
+
alert.innerHTML = innerHTML;
|
|
54
|
+
|
|
55
|
+
// Copy and hardcode Bootstrap `"alert alert-danger"` styles here to make the alert core styles independent of other CSS.
|
|
56
|
+
alert.style.setProperty('background-color', 'var(--bs-alert-bg, var(--bs-danger-bg-subtle, #fce8e8))');
|
|
57
|
+
alert.style.setProperty('border', 'var(--bs-alert-border, var(--bs-border-width, 0.0625rem) solid var(--bs-alert-border-color, var(--bs-danger-border-subtle, #f9d2d2)))');
|
|
58
|
+
alert.style.setProperty('border-radius', '0');
|
|
59
|
+
alert.style.setProperty('color', 'var(--bs-alert-color, var(--bs-danger-text-emphasis, #e01f1f))');
|
|
60
|
+
alert.style.setProperty('grid-area', 'banner-global-area');
|
|
61
|
+
alert.style.setProperty('padding', 'var(--bs-alert-padding-y, 1rem) 1.25rem');
|
|
62
|
+
alert.style.setProperty('position', 'relative');
|
|
63
|
+
|
|
64
|
+
// Update React-style `ref` to simplify later alert updates if required and place the alert as the first child of document body.
|
|
65
|
+
failureAlertRef.current = alert;
|
|
66
|
+
document.body.prepend(alert);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Export `media` "API" to `window` global object to enable accessing it from other scripts.
|
|
70
|
+
window.nb = window.nb || {};
|
|
71
|
+
window.nb.media = { failures, handleFailure }
|
|
72
|
+
})();
|
|
73
|
+
</script>
|
|
@@ -48,23 +48,23 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
48
48
|
self.assertEqual(
|
|
49
49
|
helpers.hyperlinked_email("admin@example.com"), '<a href="mailto:admin@example.com">admin@example.com</a>'
|
|
50
50
|
)
|
|
51
|
-
self.assertEqual(helpers.hyperlinked_email(None),
|
|
51
|
+
self.assertEqual(helpers.hyperlinked_email(None), helpers.HTML_NONE)
|
|
52
52
|
|
|
53
53
|
def test_hyperlinked_phone_number(self):
|
|
54
54
|
self.assertEqual(helpers.hyperlinked_phone_number("555-1234"), '<a href="tel:555-1234">555-1234</a>')
|
|
55
|
-
self.assertEqual(helpers.hyperlinked_phone_number(None),
|
|
55
|
+
self.assertEqual(helpers.hyperlinked_phone_number(None), helpers.HTML_NONE)
|
|
56
56
|
|
|
57
57
|
def test_placeholder(self):
|
|
58
|
-
self.assertEqual(helpers.placeholder(None),
|
|
59
|
-
self.assertEqual(helpers.placeholder([]),
|
|
58
|
+
self.assertEqual(helpers.placeholder(None), helpers.HTML_NONE)
|
|
59
|
+
self.assertEqual(helpers.placeholder([]), helpers.HTML_NONE)
|
|
60
60
|
self.assertEqual(helpers.placeholder("something"), "something")
|
|
61
61
|
|
|
62
62
|
def test_pre_tag(self):
|
|
63
|
-
self.assertEqual(helpers.pre_tag(None),
|
|
63
|
+
self.assertEqual(helpers.pre_tag(None), helpers.HTML_NONE)
|
|
64
64
|
self.assertEqual(helpers.pre_tag([]), "<pre>[]</pre>")
|
|
65
65
|
self.assertEqual(helpers.pre_tag("something"), "<pre>something</pre>")
|
|
66
|
-
self.assertEqual(helpers.pre_tag("", format_empty_value=False),
|
|
67
|
-
self.assertEqual(helpers.pre_tag([], format_empty_value=False),
|
|
66
|
+
self.assertEqual(helpers.pre_tag("", format_empty_value=False), helpers.HTML_NONE)
|
|
67
|
+
self.assertEqual(helpers.pre_tag([], format_empty_value=False), helpers.HTML_NONE)
|
|
68
68
|
self.assertEqual(helpers.pre_tag("something", format_empty_value=False), "<pre>something</pre>")
|
|
69
69
|
|
|
70
70
|
def test_add_html_id(self):
|
|
@@ -263,7 +263,7 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
263
263
|
helpers.render_boolean(value),
|
|
264
264
|
'<span class="text-danger"><i class="mdi mdi-close-thick" title="No"></i></span>',
|
|
265
265
|
)
|
|
266
|
-
self.assertEqual(helpers.render_boolean(None),
|
|
266
|
+
self.assertEqual(helpers.render_boolean(None), helpers.HTML_NONE)
|
|
267
267
|
|
|
268
268
|
def test_hyperlinked_object_with_color(self):
|
|
269
269
|
vlan_with_role = VLAN.objects.filter(role__isnull=False).first()
|
|
@@ -276,7 +276,7 @@ class NautobotTemplatetagsHelperTest(TestCase):
|
|
|
276
276
|
f'<span class="badge" style="color: {fbcolor}; background-color: #{color}">{display}</span>',
|
|
277
277
|
)
|
|
278
278
|
# Assert when obj is None
|
|
279
|
-
self.assertEqual(helpers.hyperlinked_object_with_color(obj=None),
|
|
279
|
+
self.assertEqual(helpers.hyperlinked_object_with_color(obj=None), helpers.HTML_NONE)
|
|
280
280
|
|
|
281
281
|
@tag("example_app")
|
|
282
282
|
@override_settings(BANNER_TOP="¡Hola, mundo!")
|
|
@@ -1917,6 +1917,7 @@ class _ObjectCustomFieldsPanel(GroupedKeyValueTablePanel):
|
|
|
1917
1917
|
|
|
1918
1918
|
def render_value(self, key, value, context: Context):
|
|
1919
1919
|
"""Render a given custom field value appropriately depending on what type of custom field it is."""
|
|
1920
|
+
# TODO: this logic could be unified with CustomFieldColumn.render()?
|
|
1920
1921
|
cf = key
|
|
1921
1922
|
if cf.type == CustomFieldTypeChoices.TYPE_BOOLEAN:
|
|
1922
1923
|
return render_boolean(value)
|
nautobot/dcim/tables/devices.py
CHANGED
|
@@ -11,7 +11,7 @@ from nautobot.core.tables import (
|
|
|
11
11
|
TagColumn,
|
|
12
12
|
ToggleColumn,
|
|
13
13
|
)
|
|
14
|
-
from nautobot.core.templatetags.helpers import humanize_speed
|
|
14
|
+
from nautobot.core.templatetags.helpers import HTML_NONE, humanize_speed
|
|
15
15
|
from nautobot.dcim.models import (
|
|
16
16
|
ConsolePort,
|
|
17
17
|
ConsoleServerPort,
|
|
@@ -263,7 +263,7 @@ class DeviceTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
263
263
|
def render_capabilities(self, value):
|
|
264
264
|
"""Render capabilities."""
|
|
265
265
|
if not value:
|
|
266
|
-
return
|
|
266
|
+
return HTML_NONE
|
|
267
267
|
return format_html_join(" ", '<span class="badge bg-secondary">{}</span>', ((v,) for v in value))
|
|
268
268
|
|
|
269
269
|
|
|
@@ -1505,7 +1505,7 @@ class ControllerTable(StatusTableMixin, RoleTableMixin, BaseTable):
|
|
|
1505
1505
|
def render_capabilities(self, value):
|
|
1506
1506
|
"""Render capabilities."""
|
|
1507
1507
|
if not value:
|
|
1508
|
-
return
|
|
1508
|
+
return HTML_NONE
|
|
1509
1509
|
return format_html_join(" ", '<span class="badge bg-secondary">{}</span>', ((v,) for v in value))
|
|
1510
1510
|
|
|
1511
1511
|
|
|
@@ -1569,7 +1569,7 @@ class ControllerManagedDeviceGroupTable(BaseTable):
|
|
|
1569
1569
|
def render_capabilities(self, value):
|
|
1570
1570
|
"""Render capabilities."""
|
|
1571
1571
|
if not value:
|
|
1572
|
-
return
|
|
1572
|
+
return HTML_NONE
|
|
1573
1573
|
return format_html_join(" ", '<span class="badge bg-secondary">{}</span>', ((v,) for v in value))
|
|
1574
1574
|
|
|
1575
1575
|
|
|
@@ -4,7 +4,7 @@ CABLETERMINATION = """
|
|
|
4
4
|
<i class="mdi mdi-chevron-right"></i>
|
|
5
5
|
<a href="{{ value.get_absolute_url }}">{{ value }}</a>
|
|
6
6
|
{% else %}
|
|
7
|
-
|
|
7
|
+
<span class="text-secondary">—</span>
|
|
8
8
|
{% endif %}
|
|
9
9
|
"""
|
|
10
10
|
|
|
@@ -26,12 +26,16 @@ PATHENDPOINT = """
|
|
|
26
26
|
{% endfor %}
|
|
27
27
|
{% endwith %}
|
|
28
28
|
{% else %}
|
|
29
|
-
|
|
29
|
+
<span class="text-secondary">—</span>
|
|
30
30
|
{% endif %}
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
CABLE_LENGTH = """
|
|
34
|
-
{% if record.length %}
|
|
34
|
+
{% if record.length %}
|
|
35
|
+
{{ record.length }} {{ record.get_length_unit_display }}
|
|
36
|
+
{% else %}
|
|
37
|
+
<span class="text-secondary">—</span>
|
|
38
|
+
{% endif %}
|
|
35
39
|
"""
|
|
36
40
|
|
|
37
41
|
CABLE_TERMINATION_PARENT = """
|
|
@@ -83,7 +87,7 @@ INTERFACE_TAGGED_VLANS = """
|
|
|
83
87
|
{% elif record.mode == 'tagged-all' %}
|
|
84
88
|
All
|
|
85
89
|
{% else %}
|
|
86
|
-
|
|
90
|
+
<span class="text-secondary">—</span>
|
|
87
91
|
{% endif %}
|
|
88
92
|
"""
|
|
89
93
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from django.test import TestCase
|
|
2
2
|
|
|
3
|
+
from nautobot.core.templatetags import helpers
|
|
3
4
|
from nautobot.dcim.choices import InterfaceDuplexChoices, InterfaceSpeedChoices, InterfaceTypeChoices
|
|
4
5
|
from nautobot.dcim.models import Device, DeviceType, Interface, InterfaceTemplate, Location, LocationType, Manufacturer
|
|
5
6
|
from nautobot.dcim.tables.devices import DeviceModuleInterfaceTable, InterfaceTable
|
|
@@ -52,7 +53,6 @@ class InterfaceTableRenderMixin:
|
|
|
52
53
|
|
|
53
54
|
def test_render_speed_duplex_with_none(self):
|
|
54
55
|
"""Test that the table handles None speed value and renders an emdash."""
|
|
55
|
-
emdash = "\u2014"
|
|
56
56
|
interface = Interface.objects.create(
|
|
57
57
|
device=self.device,
|
|
58
58
|
name="eth1",
|
|
@@ -67,8 +67,8 @@ class InterfaceTableRenderMixin:
|
|
|
67
67
|
rendered_speed = bound_row.get_cell("speed")
|
|
68
68
|
rendered_duplex = bound_row.get_cell("duplex")
|
|
69
69
|
|
|
70
|
-
self.assertEqual(rendered_speed,
|
|
71
|
-
self.assertEqual(rendered_duplex,
|
|
70
|
+
self.assertEqual(rendered_speed, helpers.HTML_NONE)
|
|
71
|
+
self.assertEqual(rendered_duplex, helpers.HTML_NONE)
|
|
72
72
|
|
|
73
73
|
def test_render_speed_various(self):
|
|
74
74
|
"""Test that the table correctly humanizes various speed values."""
|
|
@@ -146,7 +146,6 @@ class InterfaceTemplateTableTestCase(TestCase):
|
|
|
146
146
|
self.assertEqual(rendered_duplex, "Full")
|
|
147
147
|
|
|
148
148
|
def test_render_speed_duplex_with_none(self):
|
|
149
|
-
emdash = "\u2014"
|
|
150
149
|
interface_template = InterfaceTemplate.objects.create(
|
|
151
150
|
device_type=self.device_type,
|
|
152
151
|
name="tmpl-eth1",
|
|
@@ -156,5 +155,5 @@ class InterfaceTemplateTableTestCase(TestCase):
|
|
|
156
155
|
bound_row = table.rows[0]
|
|
157
156
|
rendered_speed = bound_row.get_cell("speed") # pylint: disable=no-member
|
|
158
157
|
rendered_duplex = bound_row.get_cell("duplex") # pylint: disable=no-member
|
|
159
|
-
self.assertEqual(rendered_speed,
|
|
160
|
-
self.assertEqual(rendered_duplex,
|
|
158
|
+
self.assertEqual(rendered_speed, helpers.HTML_NONE)
|
|
159
|
+
self.assertEqual(rendered_duplex, helpers.HTML_NONE)
|
nautobot/dcim/views.py
CHANGED
|
@@ -35,7 +35,6 @@ from nautobot.core.exceptions import AbortTransaction
|
|
|
35
35
|
from nautobot.core.forms import BulkRenameForm, ConfirmationForm, ImportForm, restrict_form_fields
|
|
36
36
|
from nautobot.core.models.querysets import count_related
|
|
37
37
|
from nautobot.core.templatetags import helpers
|
|
38
|
-
from nautobot.core.templatetags.helpers import bettertitle, has_perms
|
|
39
38
|
from nautobot.core.ui import object_detail
|
|
40
39
|
from nautobot.core.ui.breadcrumbs import (
|
|
41
40
|
AncestorsInstanceBreadcrumbItem,
|
|
@@ -559,14 +558,14 @@ class MigrateLocationDataToContactView(generic.ObjectEditView):
|
|
|
559
558
|
migrate_action = request.POST.get("action")
|
|
560
559
|
try:
|
|
561
560
|
with transaction.atomic():
|
|
562
|
-
if not has_perms(request.user, ["extras.add_contactassociation"]):
|
|
561
|
+
if not helpers.has_perms(request.user, ["extras.add_contactassociation"]):
|
|
563
562
|
raise PermissionDenied(
|
|
564
563
|
"ObjectPermission extras.add_contactassociation is needed to perform this action"
|
|
565
564
|
)
|
|
566
565
|
contact = None
|
|
567
566
|
team = None
|
|
568
567
|
if migrate_action == LocationDataToContactActionChoices.CREATE_AND_ASSIGN_NEW_CONTACT:
|
|
569
|
-
if not has_perms(request.user, ["extras.add_contact"]):
|
|
568
|
+
if not helpers.has_perms(request.user, ["extras.add_contact"]):
|
|
570
569
|
raise PermissionDenied("ObjectPermission extras.add_contact is needed to perform this action")
|
|
571
570
|
contact = Contact(
|
|
572
571
|
name=request.POST.get("name"),
|
|
@@ -577,7 +576,7 @@ class MigrateLocationDataToContactView(generic.ObjectEditView):
|
|
|
577
576
|
# Trigger permission check
|
|
578
577
|
Contact.objects.restrict(request.user, "view").get(pk=contact.pk)
|
|
579
578
|
elif migrate_action == LocationDataToContactActionChoices.CREATE_AND_ASSIGN_NEW_TEAM:
|
|
580
|
-
if not has_perms(request.user, ["extras.add_team"]):
|
|
579
|
+
if not helpers.has_perms(request.user, ["extras.add_team"]):
|
|
581
580
|
raise PermissionDenied("ObjectPermission extras.add_team is needed to perform this action")
|
|
582
581
|
team = Team(
|
|
583
582
|
name=request.POST.get("name"),
|
|
@@ -958,7 +957,7 @@ class DeviceTypeFieldsPanel(object_detail.ObjectFieldsPanel):
|
|
|
958
957
|
image.url,
|
|
959
958
|
image.name,
|
|
960
959
|
)
|
|
961
|
-
return
|
|
960
|
+
return helpers.HTML_NONE
|
|
962
961
|
|
|
963
962
|
return super().render_value(key, value, context)
|
|
964
963
|
|
|
@@ -2309,7 +2308,7 @@ class DeviceComponentPageMixin:
|
|
|
2309
2308
|
view_name=device_breadcrumb_url,
|
|
2310
2309
|
should_render=lambda c: c["object"].device is not None,
|
|
2311
2310
|
reverse_kwargs=lambda c: {"pk": c["object"].device.pk},
|
|
2312
|
-
label=lambda c: bettertitle(c["object"]._meta.verbose_name_plural),
|
|
2311
|
+
label=lambda c: helpers.bettertitle(c["object"]._meta.verbose_name_plural),
|
|
2313
2312
|
),
|
|
2314
2313
|
)
|
|
2315
2314
|
|
|
@@ -2319,7 +2318,7 @@ class DeviceComponentPageMixin:
|
|
|
2319
2318
|
view_name=module_breadcrumb_url,
|
|
2320
2319
|
should_render=lambda c: c["object"].device is None,
|
|
2321
2320
|
reverse_kwargs=lambda c: {"pk": c["object"].module.pk},
|
|
2322
|
-
label=lambda c: bettertitle(c["object"]._meta.verbose_name_plural),
|
|
2321
|
+
label=lambda c: helpers.bettertitle(c["object"]._meta.verbose_name_plural),
|
|
2323
2322
|
),
|
|
2324
2323
|
)
|
|
2325
2324
|
|
nautobot/extras/tables.py
CHANGED
|
@@ -374,11 +374,11 @@ class ApprovalWorkflowStageTable(BaseTable):
|
|
|
374
374
|
actions_needed = tables.TemplateColumn(
|
|
375
375
|
template_code="""
|
|
376
376
|
{% if record.remaining_approvals == 1 %}
|
|
377
|
-
|
|
377
|
+
{{ record.remaining_approvals }} more approval needed
|
|
378
378
|
{% elif record.remaining_approvals == 0 %}
|
|
379
|
-
|
|
379
|
+
<span class="text-secondary">—</span>
|
|
380
380
|
{% else %}
|
|
381
|
-
|
|
381
|
+
{{ record.remaining_approvals }} more approvals needed
|
|
382
382
|
{% endif %}
|
|
383
383
|
""",
|
|
384
384
|
orderable=False,
|
|
@@ -10,5 +10,4 @@
|
|
|
10
10
|
var job_result_id = "{{ result.pk }}";
|
|
11
11
|
|
|
12
12
|
</script>
|
|
13
|
-
<script src="{% versioned_static 'js/job_result.js' %}"
|
|
14
|
-
onerror="window.location='{% url 'media_failure' %}?filename=js/job_result.js'"></script>
|
|
13
|
+
<script src="{% versioned_static 'js/job_result.js' %}" onerror="nb.media.handleFailure(this)"></script>
|
|
@@ -143,7 +143,9 @@
|
|
|
143
143
|
<td>{% if value is None %}–{% else %}<code>{{ value }}</code>{% endif %}</td>
|
|
144
144
|
</tr>
|
|
145
145
|
{% empty %}
|
|
146
|
-
<tr
|
|
146
|
+
<tr>
|
|
147
|
+
<td><span class="text-secondary">—</span></td>
|
|
148
|
+
</tr>
|
|
147
149
|
{% endfor %}
|
|
148
150
|
</table>
|
|
149
151
|
</div>
|
|
@@ -2338,6 +2338,22 @@ class CustomFieldTableTest(TestCase):
|
|
|
2338
2338
|
cf_multi_select.default = ["Foo", "Bar"]
|
|
2339
2339
|
cf_multi_select.validated_save()
|
|
2340
2340
|
|
|
2341
|
+
# JSON custom field
|
|
2342
|
+
cf_json = CustomField(
|
|
2343
|
+
type=CustomFieldTypeChoices.TYPE_JSON,
|
|
2344
|
+
label="JSON Field",
|
|
2345
|
+
)
|
|
2346
|
+
cf_json.validated_save()
|
|
2347
|
+
cf_json.content_types.set([content_type])
|
|
2348
|
+
|
|
2349
|
+
# Markdown custom field
|
|
2350
|
+
cf_markdown = CustomField(
|
|
2351
|
+
type=CustomFieldTypeChoices.TYPE_MARKDOWN,
|
|
2352
|
+
label="Markdown Field",
|
|
2353
|
+
)
|
|
2354
|
+
cf_markdown.validated_save()
|
|
2355
|
+
cf_markdown.content_types.set([content_type])
|
|
2356
|
+
|
|
2341
2357
|
statuses = Status.objects.get_for_model(Location)
|
|
2342
2358
|
|
|
2343
2359
|
# Create a location
|
|
@@ -2346,7 +2362,7 @@ class CustomFieldTableTest(TestCase):
|
|
|
2346
2362
|
name="Location Custom", status=statuses.first(), location_type=location_type
|
|
2347
2363
|
)
|
|
2348
2364
|
|
|
2349
|
-
# Assign custom field values for location
|
|
2365
|
+
# Assign custom field values for location
|
|
2350
2366
|
self.location._custom_field_data = {
|
|
2351
2367
|
cf_text.key: "bar",
|
|
2352
2368
|
cf_integer.key: 456,
|
|
@@ -2355,16 +2371,39 @@ class CustomFieldTableTest(TestCase):
|
|
|
2355
2371
|
cf_url.key: "http://example.com/2",
|
|
2356
2372
|
cf_select.key: "Bar",
|
|
2357
2373
|
cf_multi_select.key: ["Bar", "Baz"],
|
|
2374
|
+
cf_json.key: {"hello": "world"},
|
|
2375
|
+
cf_markdown.key: "## Heading",
|
|
2358
2376
|
}
|
|
2359
2377
|
self.location.validated_save()
|
|
2360
2378
|
|
|
2379
|
+
# Create a second location
|
|
2380
|
+
self.location_2 = Location.objects.create(
|
|
2381
|
+
name="Location Custom 2", status=statuses.first(), location_type=location_type
|
|
2382
|
+
)
|
|
2383
|
+
|
|
2384
|
+
# Assign custom field values for location 2
|
|
2385
|
+
self.location_2._custom_field_data = {
|
|
2386
|
+
cf_text.key: "<script></script>",
|
|
2387
|
+
cf_integer.key: 0,
|
|
2388
|
+
cf_boolean.key: False,
|
|
2389
|
+
cf_date.key: None,
|
|
2390
|
+
cf_url.key: "",
|
|
2391
|
+
cf_select.key: None,
|
|
2392
|
+
cf_multi_select.key: [],
|
|
2393
|
+
cf_json.key: {},
|
|
2394
|
+
cf_markdown.key: "",
|
|
2395
|
+
}
|
|
2396
|
+
self.location_2.validated_save()
|
|
2397
|
+
|
|
2398
|
+
self.maxDiff = None
|
|
2399
|
+
|
|
2361
2400
|
def test_custom_field_table_render(self):
|
|
2362
|
-
queryset = Location.objects.filter(
|
|
2401
|
+
queryset = Location.objects.filter(name__in=[self.location.name, self.location_2.name])
|
|
2363
2402
|
location_table = LocationTable(queryset)
|
|
2364
2403
|
|
|
2365
2404
|
custom_column_expected = {
|
|
2366
2405
|
"text_field": "bar",
|
|
2367
|
-
"number_field":
|
|
2406
|
+
"number_field": 456,
|
|
2368
2407
|
"boolean_field": '<span class="text-success"><i class="mdi mdi-check-bold" title="Yes"></i></span>',
|
|
2369
2408
|
"date_field": "2020-01-02",
|
|
2370
2409
|
"url_field": '<a href="http://example.com/2">http://example.com/2</a>',
|
|
@@ -2372,18 +2411,45 @@ class CustomFieldTableTest(TestCase):
|
|
|
2372
2411
|
"multi_choice_field": (
|
|
2373
2412
|
'<span class="badge bg-secondary">Bar</span> <span class="badge bg-secondary">Baz</span>'
|
|
2374
2413
|
),
|
|
2414
|
+
"json_field": '<pre><code class="language-json">{\n"hello": "world"\n}</code></pre>',
|
|
2415
|
+
"markdown_field": "<h2>Heading</h2>",
|
|
2375
2416
|
}
|
|
2376
2417
|
|
|
2377
2418
|
bound_row = location_table.rows[0]
|
|
2378
2419
|
|
|
2379
2420
|
for col_name, col_expected_value in custom_column_expected.items():
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2421
|
+
with self.subTest(col_name=col_name, col_expected_value=col_expected_value):
|
|
2422
|
+
internal_col_name = "cf_" + col_name
|
|
2423
|
+
custom_column = location_table.base_columns.get(internal_col_name)
|
|
2424
|
+
self.assertIsNotNone(custom_column, internal_col_name)
|
|
2425
|
+
self.assertIsInstance(custom_column, CustomFieldColumn)
|
|
2426
|
+
|
|
2427
|
+
rendered_value = bound_row.get_cell(internal_col_name) # pylint: disable=no-member
|
|
2428
|
+
self.assertHTMLEqual(str(rendered_value), str(col_expected_value))
|
|
2429
|
+
|
|
2430
|
+
custom_column_expected_2 = {
|
|
2431
|
+
"text_field": "<script></script>",
|
|
2432
|
+
"number_field": 0,
|
|
2433
|
+
"boolean_field": '<span class="text-danger"><i class="mdi mdi-close-thick" title="No"></i></span>',
|
|
2434
|
+
"date_field": '<span class="text-secondary">—</span>',
|
|
2435
|
+
"url_field": '<span class="text-secondary">—</span>',
|
|
2436
|
+
"choice_field": '<span class="text-secondary">—</span>',
|
|
2437
|
+
"multi_choice_field": '<span class="text-secondary">—</span>',
|
|
2438
|
+
"json_field": '<pre><code class="language-json">{}</code></pre>',
|
|
2439
|
+
"markdown_field": '<span class="text-secondary">—</span>',
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
bound_row = location_table.rows[1]
|
|
2443
|
+
|
|
2444
|
+
for col_name, col_expected_value in custom_column_expected_2.items():
|
|
2445
|
+
with self.subTest(col_name=col_name, col_expected_value=col_expected_value):
|
|
2446
|
+
internal_col_name = "cf_" + col_name
|
|
2447
|
+
custom_column = location_table.base_columns.get(internal_col_name)
|
|
2448
|
+
self.assertIsNotNone(custom_column, internal_col_name)
|
|
2449
|
+
self.assertIsInstance(custom_column, CustomFieldColumn)
|
|
2384
2450
|
|
|
2385
|
-
|
|
2386
|
-
|
|
2451
|
+
rendered_value = bound_row.get_cell(internal_col_name) # pylint: disable=no-member
|
|
2452
|
+
self.assertHTMLEqual(str(rendered_value), str(col_expected_value))
|
|
2387
2453
|
|
|
2388
2454
|
|
|
2389
2455
|
class CustomFieldFilterFormTest(TestCase):
|