nautobot 3.0.0a2__py3-none-any.whl → 3.0.0a3__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.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (420) hide show
  1. nautobot/apps/choices.py +0 -2
  2. nautobot/apps/filters.py +7 -9
  3. nautobot/apps/models.py +2 -2
  4. nautobot/apps/ui.py +9 -1
  5. nautobot/circuits/filters.py +3 -2
  6. nautobot/circuits/navigation.py +3 -2
  7. nautobot/circuits/templates/circuits/circuit.html +1 -1
  8. nautobot/circuits/templates/circuits/circuit_create.html +3 -3
  9. nautobot/circuits/templates/circuits/circuittermination.html +1 -1
  10. nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
  11. nautobot/circuits/templates/circuits/circuittype.html +1 -1
  12. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
  13. nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
  14. nautobot/circuits/templates/circuits/providernetwork.html +1 -1
  15. nautobot/circuits/tests/integration/test_circuit.py +10 -13
  16. nautobot/cloud/filters.py +1 -1
  17. nautobot/cloud/navigation.py +3 -2
  18. nautobot/core/api/schema.py +1 -1
  19. nautobot/core/api/serializers.py +6 -1
  20. nautobot/core/api/urls.py +1 -0
  21. nautobot/core/api/views.py +8 -0
  22. nautobot/core/apps/__init__.py +11 -10
  23. nautobot/core/celery/__init__.py +3 -5
  24. nautobot/core/checks.py +46 -0
  25. nautobot/core/cli/bootstrap_v3_to_v5.py +70 -1
  26. nautobot/core/cli/migrate_deprecated_templates.py +200 -0
  27. nautobot/core/constants.py +3 -0
  28. nautobot/core/context_processors.py +9 -1
  29. nautobot/core/forms/forms.py +1 -1
  30. nautobot/core/jobs/__init__.py +6 -3
  31. nautobot/core/jobs/groups.py +31 -1
  32. nautobot/core/management/commands/generate_test_data.py +28 -9
  33. nautobot/core/models/generics.py +9 -1
  34. nautobot/core/models/tree_queries.py +10 -5
  35. nautobot/core/settings.py +18 -12
  36. nautobot/core/settings.yaml +13 -7
  37. nautobot/core/signals.py +12 -1
  38. nautobot/core/tables.py +13 -6
  39. nautobot/core/templates/40x.html +1 -1
  40. nautobot/core/templates/500.html +2 -2
  41. nautobot/core/templates/admin/config/config.html +12 -12
  42. nautobot/core/templates/admin/index.html +3 -3
  43. nautobot/core/templates/buttons/export.html +1 -1
  44. nautobot/core/templates/components/button/dropdown.html +5 -3
  45. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
  46. nautobot/core/templates/components/panel/panel.html +3 -3
  47. nautobot/core/templates/components/tab/content_wrapper.html +2 -3
  48. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
  49. nautobot/core/templates/echarts/echarts.html +1 -1
  50. nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
  51. nautobot/core/templates/generic/object_bulk_create.html +4 -3
  52. nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
  53. nautobot/core/templates/generic/object_bulk_remove.html +2 -2
  54. nautobot/core/templates/generic/object_bulk_update.html +5 -4
  55. nautobot/core/templates/generic/object_create.html +5 -4
  56. nautobot/core/templates/generic/object_import.html +2 -1
  57. nautobot/core/templates/generic/object_list.html +12 -4
  58. nautobot/core/templates/generic/object_notes.html +5 -3
  59. nautobot/core/templates/generic/object_retrieve.html +2 -3
  60. nautobot/core/templates/graphene/graphiql.html +7 -7
  61. nautobot/core/templates/home.html +1 -1
  62. nautobot/core/templates/import_success.html +2 -1
  63. nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
  64. nautobot/core/templates/inc/created_updated.html +7 -3
  65. nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
  66. nautobot/core/templates/inc/form_static_field.html +6 -0
  67. nautobot/core/templates/inc/header.html +1 -1
  68. nautobot/core/templates/inc/image_attachments.html +2 -1
  69. nautobot/core/templates/inc/nav_menu.html +2 -1
  70. nautobot/core/templates/inc/search_panel.html +4 -4
  71. nautobot/core/templates/login.html +4 -2
  72. nautobot/core/templates/nautobot_config.py.j2 +6 -5
  73. nautobot/core/templates/redoc_ui.html +7 -0
  74. nautobot/core/templates/search.html +1 -1
  75. nautobot/core/templates/swagger_ui.html +17 -3
  76. nautobot/core/templates/system_jobs/import_objects.html +1 -2
  77. nautobot/core/templates/utilities/confirmation_form.html +2 -2
  78. nautobot/core/templates/utilities/obj_table.html +10 -2
  79. nautobot/core/templates/utilities/render_field.html +7 -7
  80. nautobot/core/templates/utilities/render_jinja2.html +2 -2
  81. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +4 -4
  82. nautobot/core/templates/utilities/theme_preview.html +16 -3
  83. nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
  84. nautobot/core/templatetags/helpers.py +52 -6
  85. nautobot/core/testing/api.py +68 -9
  86. nautobot/core/testing/filters.py +0 -23
  87. nautobot/core/testing/integration.py +23 -10
  88. nautobot/core/testing/mixins.py +2 -0
  89. nautobot/core/testing/views.py +4 -0
  90. nautobot/core/tests/integration/test_app_home.py +34 -30
  91. nautobot/core/tests/integration/test_app_navbar.py +3 -0
  92. nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
  93. nautobot/core/tests/runner.py +9 -1
  94. nautobot/core/tests/test_api.py +5 -3
  95. nautobot/core/tests/test_breadcrumbs.py +6 -7
  96. nautobot/core/tests/test_checks.py +28 -0
  97. nautobot/core/tests/test_cli.py +40 -0
  98. nautobot/core/tests/test_config.py +2 -1
  99. nautobot/core/tests/test_forms.py +55 -13
  100. nautobot/core/tests/test_jobs.py +75 -1
  101. nautobot/core/tests/test_nautobot_server.py +2 -0
  102. nautobot/core/tests/test_navigations.py +76 -1
  103. nautobot/core/tests/test_patch_social_django.py +42 -0
  104. nautobot/core/tests/test_tables.py +3 -1
  105. nautobot/core/tests/test_templatetags_helpers.py +53 -13
  106. nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
  107. nautobot/core/tests/test_tree_queries.py +14 -1
  108. nautobot/core/tests/test_ui.py +1 -1
  109. nautobot/core/tests/test_utils.py +31 -4
  110. nautobot/core/tests/test_views.py +159 -31
  111. nautobot/core/ui/breadcrumbs.py +2 -12
  112. nautobot/core/ui/choices.py +142 -10
  113. nautobot/core/ui/constants.py +76 -12
  114. nautobot/core/ui/object_detail.py +92 -12
  115. nautobot/core/urls.py +12 -1
  116. nautobot/core/utils/cache.py +2 -1
  117. nautobot/core/utils/filtering.py +17 -17
  118. nautobot/core/utils/lookup.py +3 -8
  119. nautobot/core/utils/module_loading.py +21 -0
  120. nautobot/core/utils/patch_social_django.py +128 -0
  121. nautobot/core/views/__init__.py +38 -1
  122. nautobot/core/views/generic.py +3 -3
  123. nautobot/core/views/mixins.py +15 -3
  124. nautobot/core/views/renderers.py +2 -0
  125. nautobot/core/views/viewsets.py +2 -1
  126. nautobot/data_validation/apps.py +1 -5
  127. nautobot/data_validation/custom_validators.py +4 -4
  128. nautobot/data_validation/filters.py +1 -1
  129. nautobot/data_validation/forms.py +40 -0
  130. nautobot/data_validation/migrations/0001_initial.py +0 -7
  131. nautobot/data_validation/migrations/0002_data_migration_from_app.py +0 -12
  132. nautobot/data_validation/models.py +16 -7
  133. nautobot/data_validation/navigation.py +8 -1
  134. nautobot/data_validation/tables.py +12 -5
  135. nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
  136. nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
  137. nautobot/data_validation/tests/__init__.py +2 -2
  138. nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
  139. nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
  140. nautobot/data_validation/tests/test_filters.py +8 -6
  141. nautobot/data_validation/tests/test_models.py +15 -0
  142. nautobot/data_validation/tests/test_views.py +190 -32
  143. nautobot/data_validation/urls.py +2 -5
  144. nautobot/data_validation/views.py +73 -40
  145. nautobot/dcim/api/serializers.py +0 -13
  146. nautobot/dcim/apps.py +4 -0
  147. nautobot/dcim/choices.py +16 -0
  148. nautobot/dcim/custom_validators.py +84 -0
  149. nautobot/dcim/filter_mixins.py +353 -4
  150. nautobot/dcim/{filters/__init__.py → filters.py} +2 -35
  151. nautobot/dcim/forms.py +1 -1
  152. nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
  153. nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
  154. nautobot/dcim/models/device_components.py +81 -68
  155. nautobot/dcim/models/devices.py +13 -16
  156. nautobot/dcim/navigation.py +7 -6
  157. nautobot/dcim/tables/devices.py +3 -0
  158. nautobot/dcim/tables/template_code.py +14 -14
  159. nautobot/dcim/templates/dcim/cable.html +2 -61
  160. nautobot/dcim/templates/dcim/cable_connect.html +28 -112
  161. nautobot/dcim/templates/dcim/cable_edit.html +2 -5
  162. nautobot/dcim/templates/dcim/cable_retrieve.html +61 -0
  163. nautobot/dcim/templates/dcim/cable_trace.html +1 -3
  164. nautobot/dcim/templates/dcim/cable_update.html +5 -0
  165. nautobot/dcim/templates/dcim/consoleport.html +6 -5
  166. nautobot/dcim/templates/dcim/consoleserverport.html +6 -5
  167. nautobot/dcim/templates/dcim/device/config.html +2 -2
  168. nautobot/dcim/templates/dcim/device/consoleports.html +1 -1
  169. nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -1
  170. nautobot/dcim/templates/dcim/device/devicebays.html +1 -1
  171. nautobot/dcim/templates/dcim/device/frontports.html +1 -1
  172. nautobot/dcim/templates/dcim/device/interfaces.html +1 -1
  173. nautobot/dcim/templates/dcim/device/inventory.html +1 -1
  174. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
  175. nautobot/dcim/templates/dcim/device/modulebays.html +1 -1
  176. nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -1
  177. nautobot/dcim/templates/dcim/device/powerports.html +1 -1
  178. nautobot/dcim/templates/dcim/device/rearports.html +1 -1
  179. nautobot/dcim/templates/dcim/device/status.html +8 -8
  180. nautobot/dcim/templates/dcim/device/wireless.html +1 -1
  181. nautobot/dcim/templates/dcim/device.html +1 -1
  182. nautobot/dcim/templates/dcim/device_component_add.html +2 -2
  183. nautobot/dcim/templates/dcim/device_create.html +5 -3
  184. nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
  185. nautobot/dcim/templates/dcim/device_list.html +73 -10
  186. nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
  187. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  188. nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
  189. nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
  190. nautobot/dcim/templates/dcim/frontport.html +9 -8
  191. nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
  192. nautobot/dcim/templates/dcim/interface.html +26 -6
  193. nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
  194. nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
  195. nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
  196. nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
  197. nautobot/dcim/templates/dcim/location_retrieve.html +1 -242
  198. nautobot/dcim/templates/dcim/module/base.html +49 -9
  199. nautobot/dcim/templates/dcim/module_list.html +57 -8
  200. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
  201. nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
  202. nautobot/dcim/templates/dcim/platform_create.html +1 -1
  203. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  204. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  205. nautobot/dcim/templates/dcim/powerport.html +5 -4
  206. nautobot/dcim/templates/dcim/rack_elevation_list.html +16 -4
  207. nautobot/dcim/templates/dcim/rack_retrieve.html +33 -15
  208. nautobot/dcim/templates/dcim/rearport.html +7 -6
  209. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  210. nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
  211. nautobot/dcim/templates/dcim/virtualchassis_update.html +14 -6
  212. nautobot/dcim/tests/integration/test_controller.py +1 -0
  213. nautobot/dcim/tests/test_api.py +8 -0
  214. nautobot/dcim/tests/test_custom_validators.py +229 -0
  215. nautobot/dcim/tests/test_filters.py +12 -6
  216. nautobot/dcim/tests/test_models.py +63 -4
  217. nautobot/dcim/tests/test_views.py +63 -22
  218. nautobot/dcim/urls.py +64 -21
  219. nautobot/dcim/utils.py +3 -3
  220. nautobot/dcim/views.py +547 -273
  221. nautobot/extras/api/views.py +9 -1
  222. nautobot/extras/choices.py +2 -13
  223. nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
  224. nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
  225. nautobot/extras/{filters/__init__.py → filters.py} +14 -46
  226. nautobot/extras/forms/forms.py +5 -13
  227. nautobot/extras/forms/mixins.py +0 -41
  228. nautobot/extras/management/__init__.py +9 -0
  229. nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
  230. nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
  231. nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
  232. nautobot/extras/models/__init__.py +1 -2
  233. nautobot/extras/models/approvals.py +22 -13
  234. nautobot/extras/models/contacts.py +2 -0
  235. nautobot/extras/models/groups.py +44 -5
  236. nautobot/extras/models/jobs.py +59 -1
  237. nautobot/extras/models/mixins.py +28 -0
  238. nautobot/extras/models/models.py +13 -0
  239. nautobot/extras/models/secrets.py +1 -0
  240. nautobot/extras/models/statuses.py +0 -15
  241. nautobot/extras/navigation.py +13 -9
  242. nautobot/extras/plugins/__init__.py +33 -55
  243. nautobot/extras/plugins/tables.py +3 -3
  244. nautobot/extras/plugins/urls.py +2 -21
  245. nautobot/extras/plugins/utils.py +1 -33
  246. nautobot/extras/plugins/views.py +0 -4
  247. nautobot/extras/signals.py +20 -19
  248. nautobot/extras/tables.py +52 -68
  249. nautobot/extras/templates/extras/approval_dashboard.html +7 -5
  250. nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
  251. nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
  252. nautobot/extras/templates/extras/computedfield.html +1 -1
  253. nautobot/extras/templates/extras/configcontext.html +1 -1
  254. nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
  255. nautobot/extras/templates/extras/customfield.html +1 -1
  256. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
  257. nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
  258. nautobot/extras/templates/extras/gitrepository_result.html +0 -2
  259. nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -96
  260. nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
  261. nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
  262. nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
  263. nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
  264. nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
  265. nautobot/extras/templates/extras/inc/json_format.html +10 -3
  266. nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
  267. nautobot/extras/templates/extras/job.html +29 -12
  268. nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
  269. nautobot/extras/templates/extras/job_edit.html +52 -46
  270. nautobot/extras/templates/extras/job_list.html +29 -25
  271. nautobot/extras/templates/extras/marketplace.html +5 -9
  272. nautobot/extras/templates/extras/object_configcontext.html +1 -1
  273. nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
  274. nautobot/extras/templates/extras/objectchange_retrieve.html +19 -37
  275. nautobot/extras/templates/extras/plugin_detail.html +26 -21
  276. nautobot/extras/templates/extras/plugins_list.html +16 -26
  277. nautobot/extras/templates/extras/role_retrieve.html +64 -0
  278. nautobot/extras/templates/extras/scheduledjob.html +4 -2
  279. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  280. nautobot/extras/templates/extras/tag.html +1 -1
  281. nautobot/extras/templatetags/custom_links.py +12 -12
  282. nautobot/extras/templatetags/job_buttons.py +14 -12
  283. nautobot/extras/test_jobs/invalid_import.py +9 -0
  284. nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
  285. nautobot/extras/test_jobs/missing_import.py +11 -0
  286. nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
  287. nautobot/extras/tests/integration/test_customfields.py +8 -7
  288. nautobot/extras/tests/integration/test_dynamicgroups.py +5 -1
  289. nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
  290. nautobot/extras/tests/integration/test_plugins.py +18 -6
  291. nautobot/extras/tests/test_api.py +27 -18
  292. nautobot/extras/tests/test_approvals.py +38 -38
  293. nautobot/extras/tests/test_changelog.py +35 -3
  294. nautobot/extras/tests/test_customfields.py +22 -13
  295. nautobot/extras/tests/test_customfields_filters.py +479 -0
  296. nautobot/extras/tests/test_dynamicgroups.py +39 -1
  297. nautobot/extras/tests/test_filters.py +21 -19
  298. nautobot/extras/tests/test_forms.py +18 -21
  299. nautobot/extras/tests/test_jobs.py +25 -4
  300. nautobot/extras/tests/test_migrations.py +1 -0
  301. nautobot/extras/tests/test_models.py +13 -31
  302. nautobot/extras/tests/test_plugins.py +36 -10
  303. nautobot/extras/tests/test_views.py +31 -30
  304. nautobot/extras/views.py +81 -19
  305. nautobot/ipam/factory.py +7 -0
  306. nautobot/ipam/filter_mixins.py +38 -0
  307. nautobot/ipam/filters.py +27 -38
  308. nautobot/ipam/formfields.py +1 -1
  309. nautobot/ipam/forms.py +6 -3
  310. nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
  311. nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
  312. nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
  313. nautobot/ipam/models.py +29 -2
  314. nautobot/ipam/navigation.py +3 -2
  315. nautobot/ipam/signals.py +71 -0
  316. nautobot/ipam/tables.py +13 -6
  317. nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
  318. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
  319. nautobot/ipam/templates/ipam/ipaddress.html +14 -0
  320. nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
  321. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
  322. nautobot/ipam/templates/ipam/namespace_update.html +15 -0
  323. nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
  324. nautobot/ipam/templates/ipam/prefix_list.html +14 -13
  325. nautobot/ipam/templates/ipam/service.html +1 -1
  326. nautobot/ipam/templates/ipam/vlan.html +1 -1
  327. nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
  328. nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
  329. nautobot/ipam/tests/migration/test_migrations.py +89 -0
  330. nautobot/ipam/tests/test_api.py +13 -6
  331. nautobot/ipam/tests/test_filters.py +10 -0
  332. nautobot/ipam/tests/test_forms.py +1 -1
  333. nautobot/ipam/tests/test_models.py +43 -1
  334. nautobot/ipam/tests/test_tables.py +1 -2
  335. nautobot/ipam/tests/test_utils.py +1 -1
  336. nautobot/ipam/tests/test_views.py +13 -14
  337. nautobot/ipam/ui.py +0 -17
  338. nautobot/ipam/utils/migrations.py +16 -2
  339. nautobot/ipam/utils/testing.py +9 -3
  340. nautobot/ipam/views.py +46 -6
  341. nautobot/project-static/dist/css/nautobot.css +1 -1
  342. nautobot/project-static/dist/css/nautobot.css.map +1 -1
  343. nautobot/project-static/dist/js/nautobot.js +1 -1
  344. nautobot/project-static/dist/js/nautobot.js.map +1 -1
  345. nautobot/project-static/js/cabletrace.js +1 -1
  346. nautobot/project-static/js/interface_filtering.js +20 -16
  347. nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
  348. nautobot/project-static/nautobot-icons/cloud.svg +1 -1
  349. nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
  350. nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
  351. nautobot/project-static/nautobot-icons/elements.svg +1 -1
  352. nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
  353. nautobot/project-static/nautobot-icons/hammer.svg +1 -1
  354. nautobot/project-static/nautobot-icons/organization.svg +3 -0
  355. nautobot/project-static/nautobot-icons/secrets.svg +1 -1
  356. nautobot/project-static/nautobot-icons/security.svg +3 -0
  357. nautobot/project-static/nautobot-icons/server.svg +1 -1
  358. nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
  359. nautobot/project-static/nautobot-icons/star.svg +1 -1
  360. nautobot/tenancy/api/serializers.py +1 -0
  361. nautobot/tenancy/api/views.py +2 -1
  362. nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
  363. nautobot/tenancy/navigation.py +3 -1
  364. nautobot/tenancy/tests/test_filters.py +0 -2
  365. nautobot/tenancy/views.py +2 -1
  366. nautobot/ui/src/js/collapse.js +3 -3
  367. nautobot/ui/src/js/nautobot.js +16 -0
  368. nautobot/ui/src/scss/colors.scss +1 -1
  369. nautobot/ui/src/scss/nautobot.scss +61 -28
  370. nautobot/users/templates/users/profile.html +45 -12
  371. nautobot/users/templates/users/sessionkey_delete.html +1 -1
  372. nautobot/users/tests/test_api.py +4 -0
  373. nautobot/users/views.py +4 -2
  374. nautobot/virtualization/models.py +1 -68
  375. nautobot/virtualization/navigation.py +3 -2
  376. nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
  377. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  378. nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
  379. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
  380. nautobot/virtualization/tests/test_api.py +3 -0
  381. nautobot/virtualization/tests/test_models.py +44 -4
  382. nautobot/vpn/__init__.py +0 -0
  383. nautobot/vpn/api/serializers.py +113 -0
  384. nautobot/vpn/api/urls.py +19 -0
  385. nautobot/vpn/api/views.py +70 -0
  386. nautobot/vpn/apps.py +8 -0
  387. nautobot/vpn/choices.py +171 -0
  388. nautobot/vpn/factory.py +209 -0
  389. nautobot/vpn/filters.py +233 -0
  390. nautobot/vpn/forms.py +486 -0
  391. nautobot/vpn/homepage.py +19 -0
  392. nautobot/vpn/migrations/0001_initial.py +541 -0
  393. nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
  394. nautobot/vpn/migrations/__init__.py +0 -0
  395. nautobot/vpn/models.py +527 -0
  396. nautobot/vpn/navigation.py +98 -0
  397. nautobot/vpn/tables.py +380 -0
  398. nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
  399. nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
  400. nautobot/vpn/tests/__init__.py +0 -0
  401. nautobot/vpn/tests/test_api.py +341 -0
  402. nautobot/vpn/tests/test_filters.py +139 -0
  403. nautobot/vpn/tests/test_forms.py +294 -0
  404. nautobot/vpn/tests/test_models.py +97 -0
  405. nautobot/vpn/tests/test_views.py +281 -0
  406. nautobot/vpn/urls.py +16 -0
  407. nautobot/vpn/views.py +437 -0
  408. nautobot/wireless/navigation.py +3 -2
  409. nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
  410. nautobot/wireless/tests/test_api.py +1 -1
  411. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +14 -14
  412. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +417 -366
  413. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
  414. nautobot/data_validation/template_content.py +0 -42
  415. nautobot/dcim/filters/mixins.py +0 -354
  416. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
  417. /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
  418. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
  419. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
  420. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
@@ -758,16 +758,22 @@ properties:
758
758
  "`nautobot-server send_installation_metrics`": "../tools/nautobot-server.md#send_installation_metrics"
759
759
  type: "string"
760
760
  version_added: "1.6.0"
761
- DEVICE_NAME_AS_NATURAL_KEY:
762
- default: false
761
+ DEVICE_UNIQUENESS:
762
+ default: "location_tenant_name"
763
763
  description: >-
764
764
  `Device` names are not guaranteed globally-unique by Nautobot but in practice they often are.
765
- Set this to `True` to use the device `name` alone as the natural key for `Device` objects.
766
- Set this to `False` to use the sequence `(name, tenant, location)` as the natural key instead.
767
- environment_variable: "NAUTOBOT_DEVICE_NAME_AS_NATURAL_KEY"
765
+ Select how Devices are uniquely identified:
766
+ - 'location_tenant_name': combination of Location + Tenant + Name
767
+ - 'name': Device name must be globally unique
768
+ - 'none': No enforced uniqueness (rely on other validation rules or custom validators)
769
+ The `DEVICE_UNIQUENESS` setting also determines which fields Nautobot treats as the natural key for a Device.
770
+ - If set to "location_tenant_name", the natural key is the combination of Name, Tenant, and Location.
771
+ - If set to "name", the natural key is the device name alone.
772
+ - If set to "none", Nautobot falls back to using the primary key for uniqueness.
773
+ environment_variable: "NAUTOBOT_DEVICE_UNIQUENESS"
768
774
  is_constance_config: true
769
- type: "boolean"
770
- version_added: "2.0.0"
775
+ type: "string"
776
+ version_added: "3.0.0"
771
777
  EVENT_BROKERS:
772
778
  default: {}
773
779
  description: >-
nautobot/core/signals.py CHANGED
@@ -67,10 +67,21 @@ def invalidate_max_depth_cache(sender, **kwargs):
67
67
 
68
68
  Note that this signal is connected in `TreeModel.__init_subclass__()` so as to only apply to those models.
69
69
  """
70
- from nautobot.core.models.tree_queries import TreeManager
70
+ from nautobot.core.models.tree_queries import TreeManager, TreeNode
71
71
 
72
72
  if not isinstance(sender.objects, TreeManager):
73
73
  return
74
74
 
75
+ # If the instance is a TreeNode, and it has siblings, skip invalidating the cache.
76
+ instance = kwargs.get("instance", None)
77
+ if isinstance(instance, TreeNode):
78
+ try:
79
+ parent = getattr(instance, "parent", None)
80
+ except instance.DoesNotExist:
81
+ parent = None
82
+ if parent and getattr(parent, "children", None) and parent.children.count() > 1:
83
+ # TreeNode has siblings, depth can't change
84
+ return
85
+
75
86
  with contextlib.suppress(redis.exceptions.ConnectionError):
76
87
  cache.delete(sender.objects.max_depth_cache_key)
nautobot/core/tables.py CHANGED
@@ -370,12 +370,17 @@ class ToggleColumn(django_tables2.CheckBoxColumn):
370
370
  default = kwargs.pop("default", "")
371
371
  visible = kwargs.pop("visible", False)
372
372
  if "attrs" not in kwargs:
373
- kwargs["attrs"] = {"td": {"class": "nb-w-0"}}
373
+ kwargs["attrs"] = {
374
+ "input": {"class": "form-check-input nb-form-check-input-sm mt-2"},
375
+ "td": {"class": "nb-w-0"},
376
+ }
374
377
  super().__init__(*args, default=default, visible=visible, **kwargs)
375
378
 
376
379
  @property
377
380
  def header(self):
378
- return mark_safe('<input type="checkbox" class="toggle" title="Toggle all" />')
381
+ return mark_safe(
382
+ '<input type="checkbox" class="toggle form-check-input nb-form-check-input-sm mt-2" title="Toggle all" />'
383
+ )
379
384
 
380
385
 
381
386
  class BooleanColumn(django_tables2.Column):
@@ -405,7 +410,8 @@ class ButtonsColumn(django_tables2.TemplateColumn):
405
410
  "th": {"class": "nb-actionable nb-w-0"},
406
411
  }
407
412
  # Note that braces are escaped to allow for string formatting prior to template rendering
408
- template_code = """
413
+ template_code = """\
414
+ {{% if record.present_in_database %}}
409
415
  <div class="dropdown">
410
416
  <button class="btn dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
411
417
  <span class="mdi mdi-dots-vertical" aria-hidden="true"></span>
@@ -439,6 +445,7 @@ class ButtonsColumn(django_tables2.TemplateColumn):
439
445
  {{% endif %}}
440
446
  </ul>
441
447
  </div>
448
+ {{% endif %}}
442
449
  """
443
450
 
444
451
  def __init__(
@@ -490,7 +497,7 @@ class ApprovalButtonsColumn(django_tables2.TemplateColumn):
490
497
  """
491
498
 
492
499
  buttons = ("detail", "changelog", "approve", "deny")
493
- attrs = {"td": {"class": "d-print-none text-right text-nowrap"}}
500
+ attrs = {"td": {"class": "d-print-none text-end text-nowrap"}}
494
501
  template_name = "extras/inc/approval_buttons_column.html"
495
502
 
496
503
  def __init__(
@@ -652,11 +659,11 @@ class LinkedCountColumn(django_tables2.Column):
652
659
  {k: (getattr(record, v) or settings.FILTERS_NULL_CHOICE_VALUE) for k, v in self.url_params.items()}
653
660
  )
654
661
  if value > 1:
655
- return format_html('<a href="{}" class="badge">{}</a>', url, value)
662
+ return format_html('<a href="{}" class="badge bg-primary">{}</a>', url, value)
656
663
  if related_record is not None:
657
664
  return helpers.hyperlinked_object(related_record, self.display_field)
658
665
  if value == 1:
659
- return format_html('<a href="{}" class="badge">{}</a>', url, value)
666
+ return format_html('<a href="{}" class="badge bg-primary">{}</a>', url, value)
660
667
  return helpers.placeholder(value)
661
668
 
662
669
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  {% block content %}
5
5
  <div class="row justify-content-center" style="margin-top: 9.375rem;">
6
- <div class="col-md-4 offset-md-4">
6
+ <div class="col-sm-12 col-md-9 col-lg-7 nb-lg-max-width">
7
7
  <div class="card">
8
8
  <div class="card-header">
9
9
  <strong class="hstack gap-4">{% block icon %}{% endblock %}{% block title %}{% endblock %}</strong>
@@ -5,7 +5,7 @@
5
5
 
6
6
  {% block content %}
7
7
  <div class="row justify-content-center" style="margin-top: 9.375rem;">
8
- <div class="col-md-4 offset-md-4">
8
+ <div class="col-sm-12 col-md-9 col-lg-7 nb-lg-max-width">
9
9
  <div class="card">
10
10
  <div class="card-header">
11
11
  <strong class="hstack gap-4"><span class="mdi mdi-alert"></span>Server Error</strong>
@@ -16,7 +16,7 @@
16
16
  There was a problem with your request. Please contact an administrator.
17
17
  </p>
18
18
  {% endblock %}
19
- <hr />
19
+ <hr class="border-top" />
20
20
  <p>
21
21
  The complete exception is provided below:
22
22
  </p>
@@ -13,8 +13,8 @@
13
13
  {% block content %}
14
14
  <form action="" method="post" enctype="multipart/form-data" id="changelist-form" class="form form-horizontal">
15
15
  {% csrf_token %}
16
- <div class="row">
17
- <div class="col-lg-6 offset-lg-3">
16
+ <div class="row justify-content-center">
17
+ <div class="col-lg-8">
18
18
  {% if form.non_field_errors %}
19
19
  <div class="card border-danger">
20
20
  <div class="card-header bg-danger-subtle border-danger text-body"><strong>Errors</strong></div>
@@ -59,14 +59,14 @@
59
59
  </div>
60
60
  {{ item.form_field.as_hidden }}
61
61
  {% else %}
62
- <div class="mb-10 d-flex justify-content-center{% if item.form_field.errors %} has-error{% endif %}">
63
- <label class="col-lg-3 col-form-label" for="{{ item.form_field.id_for_label }}">
62
+ <div class="mb-10 d-md-flex align-content-start justify-content-between{% if item.form_field.errors %} has-error{% endif %}">
63
+ <label class="col-12 col-md-3 col-form-label" for="{{ item.form_field.id_for_label }}">
64
64
  {{ name | split:"_" | join:" " }}
65
- <span class="form-text">
65
+ <span class="form-text ps-2">
66
66
  (default: <code>{{ item.default }}</code>)
67
67
  </span>
68
68
  </label>
69
- <div class="col-lg-9">
69
+ <div class="col-12 col-md-9">
70
70
  {{ item.form_field.errors }}
71
71
  {% if item.is_file %}{% trans "Current file" %}: <a href="{% get_media_prefix as MEDIA_URL %}{{ MEDIA_URL }}{{ item.value }}" target="_blank">{{ item.value }}</a>{% endif %}
72
72
  {{ item.form_field }}
@@ -89,14 +89,14 @@
89
89
  </div>
90
90
  {{ item.form_field.as_hidden }}
91
91
  {% else %}
92
- <div class="mb-10 d-flex justify-content-center{% if item.form_field.errors %} has-error{% endif %}">
93
- <label class="col-lg-3 col-form-label" for="{{ item.form_field.id_for_label }}">
92
+ <div class="mb-10 d-md-flex align-content-start justify-content-between{% if item.form_field.errors %} has-error{% endif %}">
93
+ <label class="col-12 col-md-3 d-flex d-md-block col-form-label" for="{{ item.form_field.id_for_label }}">
94
94
  {{ item.name | split:"_" | join:" " }}
95
- <span class="form-text">
95
+ <span class="form-text ps-2">
96
96
  (default: <code>{{ item.default }}</code>)
97
97
  </span>
98
98
  </label>
99
- <div class="col-lg-9">
99
+ <div class="col-12 col-md-9">
100
100
  {{ item.form_field.errors }}
101
101
  {% if item.is_file %}{% trans "Current file" %}: <a href="{% get_media_prefix as MEDIA_URL %}{{ MEDIA_URL }}{{ item.value }}" target="_blank">{{ item.value }}</a>{% endif %}
102
102
  {{ item.form_field }}
@@ -112,8 +112,8 @@
112
112
  </div>
113
113
  </div>
114
114
  </div>
115
- <div class="row">
116
- <div class="col-lg-6 offset-lg-3 text-end">
115
+ <div class="row justify-content-center">
116
+ <div class="col-lg-8 text-end">
117
117
  <input type="submit" name="_save" class="btn btn-primary" value="{% trans 'Save' %}"/>
118
118
  </div>
119
119
  </div>
@@ -14,19 +14,19 @@
14
14
  <ul class="dropdown-menu" role="menu">
15
15
  {% for app in app_list %}
16
16
  <li>
17
- <a href="#"><strong>{% render_app_name app %}</strong></a>
17
+ <a class="dropdown-item" href="#"><strong>{% render_app_name app %}</strong></a>
18
18
  </li>
19
19
  {% for model in app.models %}
20
20
  <li>
21
21
  {% if model.admin_url %}
22
- <a href="{{ model.admin_url }}">{{ model.name }}</a>
22
+ <a class="dropdown-item" href="{{ model.admin_url }}">{{ model.name }}</a>
23
23
  {% else %}
24
24
  {{ model.name }}
25
25
  {% endif %}
26
26
  </li>
27
27
  {% endfor %}
28
28
  {% if not forloop.last %}
29
- <li role="presentation" class="divider"></li>
29
+ <li><hr class="dropdown-divider"></li>
30
30
  {% endif %}
31
31
  {% endfor %}
32
32
  </ul>
@@ -1,7 +1,7 @@
1
1
  {% if export_url %}
2
2
  {% if list_element %}
3
3
  {% if export_templates %}
4
- <li class="text-center">Export Templates</li>
4
+ <li class="dropdown-header fs-5">Export Templates</li>
5
5
  {% endif %}
6
6
  {% if include_yaml_option %}
7
7
  <li>
@@ -1,4 +1,4 @@
1
- <div class="btn-group">
1
+ <div class="dropdown d-inline-flex">
2
2
  <button type="button" class="btn btn-{{ color }} dropdown-toggle" data-bs-toggle="dropdown"
3
3
  aria-haspopup="true" aria-expanded="false"
4
4
  {% for key, value in attributes.items %}{{ key }}="{{ value }}" {% endfor %}
@@ -9,12 +9,14 @@
9
9
  </button>
10
10
  <ul class="dropdown-menu">
11
11
  {% for child in children %}
12
- <li><a href="{{ child.link }}" class="text-{{ child.color }}"
12
+ <li>
13
+ <a href="{{ child.link }}" class="dropdown-item text-{{ child.color }}"
13
14
  {% for key, value in child.attributes.items %}{{ key }}="{{ value }}" {% endfor %}
14
15
  >
15
16
  {% if child.icon %}<span class="mdi {{ child.icon }}" aria-hidden="true"></span>{% endif %}
16
17
  {{ child.label }}
17
- </a></li>
18
+ </a>
19
+ </li>
18
20
  {% endfor %}
19
21
  </ul>
20
22
  </div>
@@ -1,3 +1,3 @@
1
- <table class="table table-hover card-body"{% if body_id %} id="{{ body_id }}"{% endif %}>
1
+ <table class="collapse show table table-hover card-body"{% if body_id %} id="{{ body_id }}"{% endif %}>
2
2
  {{ body_content }}
3
3
  </table>
@@ -1,6 +1,6 @@
1
- <div class="card">
1
+ <div class="card{% if css_class %} border-{{ css_class }}{% endif %}">
2
2
  {% if label.strip or header_extra_content.strip %}
3
- <div class="card-header">
3
+ <div class="card-header{% if css_class %} bg-{{ css_class }}-subtle border-{{ css_class }}{% endif %}">
4
4
  {% if label %}
5
5
  <strong>{{ label }}</strong>
6
6
  {% endif %}
@@ -12,7 +12,7 @@
12
12
  {% endif %}
13
13
  {{ body }}
14
14
  {% if footer_content.strip %}
15
- <div class="card-footer">
15
+ <div class="card-footer{% if css_class %} bg-{{ css_class }}-subtle border-{{ css_class }}{% endif %}">
16
16
  {{ footer_content }}
17
17
  </div>
18
18
  {% endif %}
@@ -9,15 +9,14 @@
9
9
  <div class="align-items-center d-flex flex-wrap mb-16">
10
10
  {% include 'inc/created_updated.html' %}
11
11
  <div class="flex-grow-0 flex-shrink-0 d-print-none">
12
+ {% custom_links object %}
13
+ {% job_buttons object %}
12
14
  {% block buttons %}
13
15
  {% plugin_buttons object %}
14
16
  {% render_detail_view_extra_buttons %}
15
17
  {% block extra_buttons %}{% endblock extra_buttons %}
16
18
  {% consolidate_detail_view_action_buttons %}
17
19
  {% endblock buttons %}
18
- {% custom_links object %}
19
- {% job_buttons object %}
20
- {% block panel_buttons %}{% endblock panel_buttons %}
21
20
  </div>
22
21
  </div>
23
22
  {% else %}
@@ -1,7 +1,7 @@
1
1
  {% comment %}disabled_message is not part of the default render context but a tab can define it if needed.{% endcomment %}
2
2
  {% if disabled_message %}
3
3
  <li class="nav-item" role="presentation" title="{{ disabled_message }}">
4
- <a class="nav-link disabled" href="#">{{ label }}</a>
4
+ <a aria-disabled="true" class="nav-link disabled">{{ label }}</a>
5
5
  </li>
6
6
  {% else %}
7
7
  <li class="nav-item" role="presentation">
@@ -1,4 +1,4 @@
1
- <div id="{{ chart_container_id }}" style="width: {{ chart_width }}; height: {{ chart_height }};"></div>
1
+ <div class="collapse show" id="{{ chart_container_id }}" style="width: {{ chart_width }}; height: {{ chart_height }};"></div>
2
2
  <!-- Safe JSON export from Django -->
3
3
  {% with "echarts-config-"|add:chart_container_id as chart_config_id %}
4
4
  {{ chart_config|json_script:chart_config_id }}
@@ -1,6 +1,8 @@
1
1
  {% extends 'base.html' %}
2
2
  {% load form_helpers %}
3
3
 
4
+ {% block title %}Add {{ model_name|title }}{% endblock %}
5
+
4
6
  {% block content %}
5
7
  <form action="." method="post" class="h-100 vstack">
6
8
  {% csrf_token %}
@@ -11,7 +13,6 @@
11
13
  {{ field }}
12
14
  {% endfor %}
13
15
  <div class="row align-content-start flex-fill">
14
- <h1 class="mb-16">{% block title %}Add {{ model_name|title }}{% endblock %}</h1>
15
16
  <p class="mb-16">{{ table.rows|length }} {{ parent_model_name }} selected</p>
16
17
  <div class="col-lg-7">
17
18
  <div class="card">
@@ -25,11 +25,12 @@ Leaving it here for now in case apps are making use of it.
25
25
  </style>
26
26
  {% endblock %}
27
27
 
28
+ {% block title %}{{ obj_type|bettertitle }} Bulk Import{% endblock %}
29
+
28
30
  {% block content %}
29
31
  {% block tabs %}{% endblock %}
30
- <div class="row">
31
- <div class="col-md-8 offset-md-2">
32
- <h1 class="mb-16">{% block title %}{{ obj_type|bettertitle }} Bulk Import{% endblock %}</h1>
32
+ <div class="row justify-content-center">
33
+ <div class="col-lg-8 col-md-10">
33
34
  {% if form.non_field_errors %}
34
35
  <div class="card border-danger">
35
36
  <div class="card-header bg-danger-subtle border-danger text-body">
@@ -13,8 +13,8 @@
13
13
  {% for field in form.hidden_fields %}
14
14
  {{ field }}
15
15
  {% endfor %}
16
- <div class="row align-content-start flex-fill">
17
- <div class="col-md-8 offset-md-2">
16
+ <div class="row align-content-start justify-content-center flex-fill">
17
+ <div class="col-lg-8 col-md-10 d-flex justify-content-center">
18
18
  <div class="card border-danger" id="confirm-bulk-deletion">
19
19
  <div class="card-header bg-danger-subtle border-danger text-body">
20
20
  <strong>Confirm Bulk Deletion</strong>
@@ -26,7 +26,7 @@
26
26
  </div>
27
27
  </div>
28
28
  {% if table %}
29
- <div class="col-md-8 offset-md-2">
29
+ <div class="col-lg-8 col-md-10">
30
30
  <div class="card">
31
31
  <div class="table-responsive">
32
32
  {% render_table table 'inc/table.html' %}
@@ -9,8 +9,8 @@
9
9
  {% for field in form.hidden_fields %}
10
10
  {{ field }}
11
11
  {% endfor %}
12
- <div class="row align-content-start flex-fill">
13
- <div class="col-md-8 offset-md-2">
12
+ <div class="row align-content-start justify-content-center flex-fill">
13
+ <div class="col-lg-8 col-md-10 d-flex justify-content-center">
14
14
  <div class="card border-danger">
15
15
  <div class="card-header bg-danger-subtle border-danger text-body">
16
16
  <strong>Confirm Bulk Removal</strong>
@@ -3,6 +3,8 @@
3
3
  {% load form_helpers %}
4
4
  {% load render_table from django_tables2 %}
5
5
 
6
+ {% block title %}Editing {{ objs_count }} {{ obj_type_plural|bettertitle }}{% endblock %}
7
+
6
8
  {% block content %}
7
9
  <form action="" method="post" class="h-100 vstack">
8
10
  {% csrf_token %}
@@ -12,8 +14,7 @@
12
14
  {% for field in form.hidden_fields %}
13
15
  {{ field }}
14
16
  {% endfor %}
15
- <div class="row align-content-start flex-fill">
16
- <h1 class="mb-16">{% block title %}Editing {{ objs_count }} {{ obj_type_plural|bettertitle }}{% endblock %}</h1>
17
+ <div class="row justify-content-center align-content-start flex-fill">
17
18
  {% if form.errors %}
18
19
  <div class="card border-danger">
19
20
  <div class="card-header bg-danger-subtle border-danger text-body">
@@ -30,7 +31,7 @@
30
31
  </div>
31
32
  {% endif %}
32
33
  {% if table %}
33
- <div class="col-md-8">
34
+ <div class="col-lg-8">
34
35
  <div class="card">
35
36
  <div class="table-responsive">
36
37
  {% render_table table 'inc/table.html' %}
@@ -38,7 +39,7 @@
38
39
  </div>
39
40
  </div>
40
41
  {% endif %}
41
- <div class="{% if table %}col-md-4{% else %}col-lg-8 offset-lg-2 col-md-10 offset-md-1{% endif %}">
42
+ <div class="{% if table %}col-lg-4{% else %}col-lg-8 col-md-12{% endif %}">
42
43
  <div class="card">
43
44
  <div class="card-header"><strong>{% block form_title %}Attributes{% endblock %}</strong></div>
44
45
  <div class="card-body">
@@ -2,14 +2,16 @@
2
2
  {% load form_helpers %}
3
3
  {% load helpers %}
4
4
 
5
+ {% block title %}{% if editing %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}{% endblock %}
6
+
5
7
  {% block content %}
6
8
  <form action="" method="post" enctype="multipart/form-data" class="h-100 vstack">
7
9
  {% csrf_token %}
8
10
  {% for field in form.hidden_fields %}
9
11
  {{ field }}
10
12
  {% endfor %}
11
- <div class="row align-content-start flex-fill">
12
- <div class="col-lg-8 offset-lg-2 col-md-10 offset-md-1 mb-10">
13
+ <div class="row justify-content-center align-content-start flex-fill">
14
+ <div class="col-lg-8 col-md-10 mb-10">
13
15
  <h3>
14
16
  {% if obj %}
15
17
  {% with obj|get_docs_url as docs_url %}
@@ -20,10 +22,9 @@
20
22
  {% endif %}
21
23
  {% endwith %}
22
24
  {% endif %}
23
- {% block title %}{% if editing %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}{% endblock %}
24
25
  </h3>
25
26
  </div>
26
- <div class="col-lg-8 offset-lg-2 col-md-10 offset-md-1">
27
+ <div class="col-lg-8 col-md-10">
27
28
  {% block tabs %}{% endblock %}
28
29
  {% block form_errors %}
29
30
  {% if form.non_field_errors or model_form.non_field_errors %}
@@ -2,13 +2,14 @@
2
2
  {% load helpers %}
3
3
  {% load form_helpers %}
4
4
 
5
+ {% block title %}{{ obj_type|bettertitle }} Import{% endblock %}
6
+
5
7
  {% block content %}
6
8
  {% block tabs %}{% endblock %}
7
9
  <form action="" method="post" class="object-import-form h-100 vstack">
8
10
  {% csrf_token %}
9
11
  <div class="row align-content-start flex-fill">
10
12
  <div class="col-md-8 offset-md-2">
11
- <h1>{% block title %}{{ obj_type|bettertitle }} Import{% endblock %}</h1>
12
13
  {% if form.non_field_errors %}
13
14
  <div class="card border-danger">
14
15
  <div class="card-header bg-danger-subtle border-danger text-body">
@@ -156,17 +156,25 @@
156
156
  data-bs-target="#dynamic_group_assignment_modal"
157
157
  data-objects="all"
158
158
  class="btn btn-primary btn-sm"
159
- disabled="disabled">
159
+ disabled>
160
160
  <span class="mdi mdi-group me-4" aria-hidden="true"></span>Update Group Assignment for All
161
161
  </button>
162
162
  {% endif %}
163
163
  {% if bulk_edit_url and permissions.change %}
164
- <button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm" disabled="disabled">
164
+ <button type="submit"
165
+ name="_edit"
166
+ formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}"
167
+ class="btn btn-warning btn-sm"
168
+ disabled>
165
169
  <span class="mdi mdi-pencil me-4" aria-hidden="true"></span>Edit All
166
170
  </button>
167
171
  {% endif %}
168
172
  {% if bulk_delete_url and permissions.delete %}
169
- <button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm" disabled="disabled">
173
+ <button type="submit"
174
+ name="_delete"
175
+ formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}"
176
+ class="btn btn-danger btn-sm"
177
+ disabled>
170
178
  <span class="mdi mdi-trash-can-outline me-4" aria-hidden="true"></span>Delete All
171
179
  </button>
172
180
  {% endif %}
@@ -297,7 +305,7 @@
297
305
  </a>
298
306
  {% if current_saved_view %}
299
307
  {% if current_saved_view.owner == user %}
300
- <a class="btn btn-danger" href="{% url 'extras:savedview_delete' pk=current_saved_view.pk %}">
308
+ <a class="btn btn-danger" href="{% url 'extras:savedview_delete' pk=current_saved_view.pk %}?return_url={{ request.path }}">
301
309
  <span class="mdi mdi-trash-can-outline me-4" aria-hidden="true"></span>Delete Current
302
310
  </a>
303
311
  {% else %}
@@ -5,8 +5,8 @@
5
5
  {% block content %}
6
6
  {% if perms.extras.add_note %}
7
7
  <form action="{% url 'extras:note_add' %}?return_url={{ request.path }}?tab=notes" method="post" enctype="multipart/form-data">
8
- <div class="row">
9
- <div class="col-md-6 offset-md-3">
8
+ <div class="row justify-content-center">
9
+ <div class="col-md-10 col-lg-8">
10
10
  <h3 class="mb-16">Add a new note</h3>
11
11
  <div class="card">
12
12
  <div class="card-header">
@@ -18,7 +18,9 @@
18
18
  </div>
19
19
  </div>
20
20
  </div>
21
- <div class="col-md-6 offset-md-3 gap-8 hstack justify-content-end mb-16">
21
+ </div>
22
+ <div class="row justify-content-center">
23
+ <div class="col-md-10 col-lg-8 gap-8 hstack justify-content-end mb-16">
22
24
  <button id="createNote" type="submit" class="btn btn-primary">
23
25
  <span aria-hidden="true" class="mdi mdi-check me-4"></span><!--
24
26
  -->Create
@@ -132,15 +132,14 @@
132
132
  <div class="align-items-center d-flex flex-wrap mb-16">
133
133
  {% include 'inc/created_updated.html' %}
134
134
  <div class="flex-grow-0 flex-shrink-0 d-print-none">
135
+ {% custom_links object %}
136
+ {% job_buttons object %}
135
137
  {% block buttons %}
136
138
  {% plugin_buttons object %}
137
139
  {% render_detail_view_extra_buttons %}
138
140
  {% block extra_buttons %}{% endblock extra_buttons %}
139
141
  {% consolidate_detail_view_action_buttons %}
140
142
  {% endblock buttons %}
141
- {% custom_links object %}
142
- {% job_buttons object %}
143
- {% block panel_buttons %}{% endblock panel_buttons %}
144
143
  </div>
145
144
  </div>
146
145
  <div class="row">
@@ -49,13 +49,13 @@ add "&raw" to the end of the URL within a browser.
49
49
  [data-bs-theme="dark"] .graphiql-dialog-overlay,
50
50
  [data-bs-theme="dark"] .graphiql-tooltip,
51
51
  [data-bs-theme="dark"] [data-radix-popper-content-wrapper] {
52
- --color-primary: 211, 96%, 36% !important; /* $blue-0-dark: #045ab4; */
53
- --color-secondary: 218, 14%, 36% !important; /* $gray-3-dark: #4f5868; */
54
- --color-tertiary: 0, 0%, 80% !important; /* $black-0-dark: #cbcbcb; */
55
- --color-success: 126, 100%, 18% !important; /* $green-0-dark: #005c09; */
56
- --color-warning: 31, 94%, 45% !important; /* $orange-0-dark: #e07807; */
57
- --color-error: 0, 92%, 31% !important; /* $red-0-dark: #960606; */
58
- --color-base: 222, 20%, 10% !important; /* $gray-0-dark: #14171e; */
52
+ --color-primary: 209, 100%, 60% !important; /* $blue-0-dark: #339dff; */
53
+ --color-secondary: 0, 0%, 71% !important; /* $gray-3-dark: #b5b5b5; */
54
+ --color-tertiary: 0, 0%, 100% !important; /* $black-0-dark: #ffffff; */
55
+ --color-success: 27, 63%, 49% !important; /* $green-0-dark: #2ecc40; */
56
+ --color-warning: 30, 100%, 60% !important; /* $orange-0-dark: #ff9933; */
57
+ --color-error: 0, 100%, 65% !important; /* $red-0-dark: #ff4c4c; */
58
+ --color-base: 0, 0%, 12% !important; /* $gray-0-dark: #1e1e1e; */
59
59
  }
60
60
 
61
61
  #main-content {
@@ -2,7 +2,7 @@
2
2
  {% load helpers %}
3
3
  {% load registry %}
4
4
 
5
- {# TODO: Figure out better solution: this is a quick hack to remove "chevrons" from homepage because in this commit I'm adding the cheverons in base_django.html #}
5
+ {# TODO: Figure out better solution: this is a quick hack to remove "chevrons" from homepage because in this commit I'm adding the chevrons in base_django.html #}
6
6
  {% block page_title %}{% endblock %}
7
7
 
8
8
  {% block header %}
@@ -1,7 +1,8 @@
1
1
  {% extends 'base.html' %}
2
2
 
3
+ {% block title %}Import Completed{% endblock %}
4
+
3
5
  {% block content %}
4
- <h1>{% block title %}Import Completed{% endblock %}</h1>
5
6
  {% include 'responsive_table.html' %}
6
7
  <a href="{{ request.path }}" class="btn btn-primary">
7
8
  <span class="mdi mdi-database-import-outline" aria-hidden="true"></span>
@@ -10,7 +10,7 @@
10
10
  data-nb-toggle="collapse-all"
11
11
  type="button"
12
12
  >
13
- Collapse All
13
+ Collapse All Groups
14
14
  </button>
15
15
  </div>
16
16
  <table id="computed_fields_accordion_{{ advanced_ui }}" class="table table-hover card-body attr-table">