nautobot 2.4.21__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 (919) hide show
  1. nautobot/apps/choices.py +2 -2
  2. nautobot/apps/filters.py +9 -9
  3. nautobot/apps/forms.py +2 -0
  4. nautobot/apps/models.py +7 -2
  5. nautobot/apps/ui.py +20 -1
  6. nautobot/apps/utils.py +2 -3
  7. nautobot/apps/views.py +7 -1
  8. nautobot/circuits/filters.py +8 -23
  9. nautobot/circuits/navigation.py +3 -1
  10. nautobot/circuits/templates/circuits/circuit_create.html +9 -9
  11. nautobot/circuits/templates/circuits/circuit_terminations_swap.html +2 -2
  12. nautobot/circuits/templates/circuits/circuittermination_create.html +24 -33
  13. nautobot/circuits/templates/circuits/inc/circuit_termination.html +10 -10
  14. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +13 -13
  15. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +6 -6
  16. nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +3 -3
  17. nautobot/circuits/templates/circuits/inc/speed_widget.html +13 -13
  18. nautobot/circuits/templates/circuits/provider_create.html +9 -9
  19. nautobot/circuits/tests/integration/test_circuit.py +19 -19
  20. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +3 -0
  21. nautobot/circuits/tests/integration/test_relationships.py +4 -12
  22. nautobot/circuits/views.py +0 -2
  23. nautobot/cloud/filters.py +1 -13
  24. nautobot/cloud/navigation.py +3 -1
  25. nautobot/cloud/templates/cloud/cloudnetwork_update.html +9 -9
  26. nautobot/cloud/templates/cloud/cloudservice_update.html +6 -6
  27. nautobot/core/api/fields.py +30 -2
  28. nautobot/core/api/schema.py +1 -1
  29. nautobot/core/api/serializers.py +9 -2
  30. nautobot/core/api/urls.py +2 -0
  31. nautobot/core/api/views.py +58 -37
  32. nautobot/core/apps/__init__.py +6 -12
  33. nautobot/core/branching.py +83 -0
  34. nautobot/core/celery/__init__.py +11 -6
  35. nautobot/core/celery/backends.py +2 -0
  36. nautobot/core/celery/encoders.py +7 -0
  37. nautobot/core/celery/task.py +44 -0
  38. nautobot/core/checks.py +60 -0
  39. nautobot/core/cli/bootstrap_v3_to_v5.py +776 -0
  40. nautobot/core/constants.py +9 -0
  41. nautobot/core/context_processors.py +84 -0
  42. nautobot/core/filters.py +131 -2
  43. nautobot/core/forms/__init__.py +4 -2
  44. nautobot/core/forms/fields.py +10 -8
  45. nautobot/core/forms/forms.py +21 -9
  46. nautobot/core/forms/search.py +0 -15
  47. nautobot/core/forms/widgets.py +3 -2
  48. nautobot/core/graphql/__init__.py +8 -26
  49. nautobot/core/graphql/generators.py +16 -6
  50. nautobot/core/graphql/schema.py +1 -1
  51. nautobot/core/graphql/schema_init.py +1 -2
  52. nautobot/core/graphql/utils.py +7 -9
  53. nautobot/core/jobs/__init__.py +158 -0
  54. nautobot/core/management/commands/generate_test_data.py +28 -9
  55. nautobot/core/models/__init__.py +17 -2
  56. nautobot/core/models/fields.py +3 -2
  57. nautobot/core/models/generics.py +9 -1
  58. nautobot/core/models/name_color_content_types.py +1 -1
  59. nautobot/core/models/ordering.py +7 -5
  60. nautobot/core/models/querysets.py +77 -2
  61. nautobot/core/models/tree_queries.py +6 -4
  62. nautobot/core/settings.py +30 -16
  63. nautobot/core/settings.yaml +13 -7
  64. nautobot/core/tables.py +114 -44
  65. nautobot/core/templates/403.html +1 -1
  66. nautobot/core/templates/403_csrf_failure.html +1 -1
  67. nautobot/core/templates/404.html +1 -1
  68. nautobot/core/templates/40x.html +8 -8
  69. nautobot/core/templates/500.html +10 -10
  70. nautobot/core/templates/about.html +13 -12
  71. nautobot/core/templates/admin/actions.html +1 -1
  72. nautobot/core/templates/admin/app_index.html +3 -3
  73. nautobot/core/templates/admin/base.html +45 -52
  74. nautobot/core/templates/admin/base_site.html +0 -9
  75. nautobot/core/templates/admin/change_form.html +5 -5
  76. nautobot/core/templates/admin/change_list.html +8 -12
  77. nautobot/core/templates/admin/change_list_results.html +3 -3
  78. nautobot/core/templates/admin/config/config.html +24 -24
  79. nautobot/core/templates/admin/delete_confirmation.html +5 -5
  80. nautobot/core/templates/admin/edit_inline/stacked.html +5 -5
  81. nautobot/core/templates/admin/edit_inline/tabular.html +3 -3
  82. nautobot/core/templates/admin/includes/fieldset.html +15 -15
  83. nautobot/core/templates/admin/index.html +8 -8
  84. nautobot/core/templates/admin/submit_line.html +5 -5
  85. nautobot/core/templates/base_django.html +36 -32
  86. nautobot/core/templates/buttons/add.html +1 -1
  87. nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +2 -2
  88. nautobot/core/templates/buttons/export.html +17 -18
  89. nautobot/core/templates/buttons/job_import.html +2 -2
  90. nautobot/core/templates/components/breadcrumbs.html +19 -17
  91. nautobot/core/templates/components/button/dropdown.html +7 -5
  92. nautobot/core/templates/components/echarts.html +2 -0
  93. nautobot/core/templates/components/layout/one_over_two.html +3 -3
  94. nautobot/core/templates/components/layout/two_over_one.html +3 -3
  95. nautobot/core/templates/components/panel/body_content_data_table.html +2 -2
  96. nautobot/core/templates/components/panel/body_content_tags.html +1 -1
  97. nautobot/core/templates/components/panel/body_wrapper_generic.html +4 -2
  98. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
  99. nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +5 -3
  100. nautobot/core/templates/components/panel/body_wrapper_table.html +4 -2
  101. nautobot/core/templates/components/panel/footer_contacts_table.html +4 -4
  102. nautobot/core/templates/components/panel/footer_content_table.html +3 -3
  103. nautobot/core/templates/components/panel/grouping_toggle.html +12 -11
  104. nautobot/core/templates/components/panel/header_extra_content_table.html +2 -11
  105. nautobot/core/templates/components/panel/panel.html +6 -3
  106. nautobot/core/templates/components/panel/stats_panel_body.html +9 -7
  107. nautobot/core/templates/components/tab/content_wrapper.html +29 -1
  108. nautobot/core/templates/components/tab/label_wrapper.html +10 -2
  109. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +11 -4
  110. nautobot/core/templates/echarts/echarts.html +20 -0
  111. nautobot/core/templates/exceptions/import_error.html +2 -2
  112. nautobot/core/templates/exceptions/permission_error.html +1 -1
  113. nautobot/core/templates/exceptions/programming_error.html +2 -2
  114. nautobot/core/templates/generic/object_bulk_add_component.html +29 -20
  115. nautobot/core/templates/generic/object_bulk_create.html +87 -75
  116. nautobot/core/templates/generic/object_bulk_destroy.html +35 -37
  117. nautobot/core/templates/generic/object_bulk_remove.html +30 -26
  118. nautobot/core/templates/generic/object_bulk_rename.html +53 -40
  119. nautobot/core/templates/generic/object_bulk_update.html +36 -29
  120. nautobot/core/templates/generic/object_create.html +40 -27
  121. nautobot/core/templates/generic/object_import.html +36 -24
  122. nautobot/core/templates/generic/object_list.html +279 -215
  123. nautobot/core/templates/generic/object_notes.html +21 -11
  124. nautobot/core/templates/generic/object_retrieve.html +161 -213
  125. nautobot/core/templates/graphene/graphiql.html +113 -60
  126. nautobot/core/templates/home.html +164 -87
  127. nautobot/core/templates/import_success.html +3 -2
  128. nautobot/core/templates/inc/ajax_loader.html +1 -1
  129. nautobot/core/templates/inc/computed_fields/panel_data.html +25 -13
  130. nautobot/core/templates/inc/created_updated.html +12 -7
  131. nautobot/core/templates/inc/custom_fields/panel_data.html +28 -16
  132. nautobot/core/templates/inc/custom_fields_panel.html +3 -3
  133. nautobot/core/templates/inc/dynamic_groups_panel.html +3 -3
  134. nautobot/core/templates/inc/extras_features_edit_form_fields.html +15 -15
  135. nautobot/core/templates/inc/footer.html +90 -40
  136. nautobot/core/templates/inc/form_static_field.html +6 -0
  137. nautobot/core/templates/inc/header.html +75 -0
  138. nautobot/core/templates/inc/header_banners.html +17 -0
  139. nautobot/core/templates/inc/header_messages.html +6 -0
  140. nautobot/core/templates/inc/image_attachments.html +9 -9
  141. nautobot/core/templates/inc/javascript.html +7 -24
  142. nautobot/core/templates/inc/media.html +4 -29
  143. nautobot/core/templates/inc/modal.html +2 -2
  144. nautobot/core/templates/inc/nav_favorites.html +27 -0
  145. nautobot/core/templates/inc/nav_menu.html +150 -108
  146. nautobot/core/templates/inc/object_details_advanced_panel.html +84 -71
  147. nautobot/core/templates/inc/page_title.html +23 -0
  148. nautobot/core/templates/inc/paginator.html +39 -28
  149. nautobot/core/templates/inc/relationships/panel_override.html +3 -3
  150. nautobot/core/templates/inc/relationships_panel.html +3 -3
  151. nautobot/core/templates/inc/relationships_table_rows.html +1 -1
  152. nautobot/core/templates/inc/search_panel.html +22 -16
  153. nautobot/core/templates/inc/table.html +61 -36
  154. nautobot/core/templates/inc/tenancy_form_panel.html +3 -3
  155. nautobot/core/templates/login.html +17 -59
  156. nautobot/core/templates/modals/modal_theme.html +12 -23
  157. nautobot/core/templates/nautobot_config.py.j2 +6 -5
  158. nautobot/core/templates/panel_table.html +8 -12
  159. nautobot/core/templates/redoc_ui.html +80 -0
  160. nautobot/core/templates/rest_framework/api.html +43 -21
  161. nautobot/core/templates/search.html +12 -13
  162. nautobot/core/templates/swagger_ui.html +19 -4
  163. nautobot/core/templates/system_jobs/import_objects.html +70 -58
  164. nautobot/core/templates/template.css +0 -6
  165. nautobot/core/templates/utilities/comment_form.html +34 -0
  166. nautobot/core/templates/utilities/confirmation_form.html +17 -9
  167. nautobot/core/templates/utilities/obj_table.html +19 -11
  168. nautobot/core/templates/utilities/render_field.html +27 -21
  169. nautobot/core/templates/utilities/render_jinja2.html +22 -25
  170. nautobot/core/templates/utilities/templatetags/advanced_filter_indicator.html +8 -0
  171. nautobot/core/templates/utilities/templatetags/badge.html +1 -1
  172. nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +2 -3
  173. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +482 -0
  174. nautobot/core/templates/utilities/templatetags/modal_form_as_dialog.html +14 -18
  175. nautobot/core/templates/utilities/templatetags/saved_view_modal.html +11 -11
  176. nautobot/core/templates/utilities/templatetags/table_config_form.html +51 -24
  177. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  178. nautobot/core/templates/utilities/templatetags/utilization_graph.html +3 -3
  179. nautobot/core/templates/utilities/theme_preview.html +829 -566
  180. nautobot/core/templates/utilities/worker_status.html +42 -41
  181. nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
  182. nautobot/core/templates/widgets/sluginput.html +2 -2
  183. nautobot/core/templatetags/buttons.py +38 -40
  184. nautobot/core/templatetags/helpers.py +105 -28
  185. nautobot/core/templatetags/ui_framework.py +17 -0
  186. nautobot/core/testing/api.py +76 -12
  187. nautobot/core/testing/filters.py +11 -27
  188. nautobot/core/testing/integration.py +128 -10
  189. nautobot/core/testing/mixins.py +7 -4
  190. nautobot/core/testing/utils.py +28 -5
  191. nautobot/core/testing/views.py +125 -27
  192. nautobot/core/tests/integration/test_app_home.py +39 -35
  193. nautobot/core/tests/integration/test_app_navbar.py +60 -67
  194. nautobot/core/tests/integration/test_filters.py +123 -55
  195. nautobot/core/tests/integration/test_general_functionality.py +1 -1
  196. nautobot/core/tests/integration/test_home.py +10 -18
  197. nautobot/core/tests/integration/test_import_objects_ui.py +2 -9
  198. nautobot/core/tests/integration/test_navbar.py +41 -16
  199. nautobot/core/tests/integration/test_swagger.py +1 -7
  200. nautobot/core/tests/integration/test_theme.py +3 -0
  201. nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
  202. nautobot/core/tests/runner.py +6 -1
  203. nautobot/core/tests/test_api.py +5 -3
  204. nautobot/core/tests/test_branching.py +154 -0
  205. nautobot/core/tests/test_breadcrumbs.py +7 -8
  206. nautobot/core/tests/test_checks.py +28 -0
  207. nautobot/core/tests/test_commands.py +0 -41
  208. nautobot/core/tests/test_config.py +2 -1
  209. nautobot/core/tests/test_csv.py +4 -7
  210. nautobot/core/tests/test_filters.py +326 -318
  211. nautobot/core/tests/test_forms.py +19 -30
  212. nautobot/core/tests/test_graphql.py +67 -57
  213. nautobot/core/tests/test_models.py +1 -1
  214. nautobot/core/tests/test_nautobot_server.py +2 -0
  215. nautobot/core/tests/test_navigations.py +78 -10
  216. nautobot/core/tests/test_tables.py +3 -1
  217. nautobot/core/tests/test_templatetags_helpers.py +61 -21
  218. nautobot/core/tests/test_templatetags_ui_framework.py +36 -18
  219. nautobot/core/tests/test_ui.py +207 -2
  220. nautobot/core/tests/test_utils.py +147 -2
  221. nautobot/core/tests/test_views.py +201 -64
  222. nautobot/core/tests/test_views_utils.py +1 -1
  223. nautobot/core/ui/breadcrumbs.py +2 -12
  224. nautobot/core/ui/choices.py +190 -0
  225. nautobot/core/ui/constants.py +86 -0
  226. nautobot/core/ui/echarts.py +474 -0
  227. nautobot/core/ui/nav.py +5 -1
  228. nautobot/core/ui/object_detail.py +180 -16
  229. nautobot/core/urls.py +13 -1
  230. nautobot/core/utils/cache.py +71 -0
  231. nautobot/core/utils/data.py +8 -5
  232. nautobot/core/utils/filtering.py +8 -2
  233. nautobot/core/utils/git.py +3 -3
  234. nautobot/core/utils/lookup.py +87 -13
  235. nautobot/core/utils/migrations.py +22 -0
  236. nautobot/core/utils/module_loading.py +26 -0
  237. nautobot/core/utils/permissions.py +9 -5
  238. nautobot/core/views/__init__.py +114 -63
  239. nautobot/core/views/generic.py +34 -27
  240. nautobot/core/views/mixins.py +49 -27
  241. nautobot/core/views/renderers.py +3 -5
  242. nautobot/core/views/utils.py +10 -5
  243. nautobot/core/views/viewsets.py +2 -1
  244. nautobot/data_validation/__init__.py +0 -0
  245. nautobot/data_validation/api/__init__.py +1 -0
  246. nautobot/data_validation/api/serializers.py +80 -0
  247. nautobot/data_validation/api/urls.py +20 -0
  248. nautobot/data_validation/api/views.py +44 -0
  249. nautobot/data_validation/apps.py +18 -0
  250. nautobot/data_validation/custom_validators.py +330 -0
  251. nautobot/data_validation/filters.py +133 -0
  252. nautobot/data_validation/form_mixin.py +25 -0
  253. nautobot/data_validation/forms.py +342 -0
  254. nautobot/data_validation/migrations/0001_initial.py +224 -0
  255. nautobot/data_validation/migrations/0002_data_migration_from_app.py +324 -0
  256. nautobot/data_validation/migrations/__init__.py +0 -0
  257. nautobot/data_validation/models.py +361 -0
  258. nautobot/data_validation/navigation.py +74 -0
  259. nautobot/data_validation/signals.py +30 -0
  260. nautobot/data_validation/tables.py +259 -0
  261. nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +1 -0
  262. nautobot/data_validation/templates/data_validation/datacompliance_tab.html +11 -0
  263. nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
  264. nautobot/data_validation/tests/__init__.py +20 -0
  265. nautobot/data_validation/tests/migrations/__init__.py +0 -0
  266. nautobot/data_validation/tests/migrations/test_migrations.py +489 -0
  267. nautobot/data_validation/tests/test_api.py +238 -0
  268. nautobot/data_validation/tests/test_custom_validators.py +423 -0
  269. nautobot/data_validation/tests/test_data_compliance_rules.py +85 -0
  270. nautobot/data_validation/tests/test_filters.py +240 -0
  271. nautobot/data_validation/tests/test_form_mixin.py +115 -0
  272. nautobot/data_validation/tests/test_models.py +393 -0
  273. nautobot/data_validation/tests/test_views.py +435 -0
  274. nautobot/data_validation/urls.py +21 -0
  275. nautobot/data_validation/views.py +227 -0
  276. nautobot/dcim/api/serializers.py +10 -13
  277. nautobot/dcim/api/urls.py +2 -0
  278. nautobot/dcim/api/views.py +7 -0
  279. nautobot/dcim/apps.py +4 -0
  280. nautobot/dcim/choices.py +16 -0
  281. nautobot/dcim/custom_validators.py +84 -0
  282. nautobot/dcim/filter_mixins.py +353 -4
  283. nautobot/dcim/{filters/__init__.py → filters.py} +70 -157
  284. nautobot/dcim/forms.py +12 -6
  285. nautobot/dcim/graphql/types.py +1 -0
  286. nautobot/dcim/migrations/0075_add_deviceclusterassignment.py +52 -0
  287. nautobot/dcim/migrations/0076_device_cluster_to_clusters_data_migration.py +40 -0
  288. nautobot/dcim/migrations/0077_remove_device_cluster.py +14 -0
  289. nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
  290. nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
  291. nautobot/dcim/models/__init__.py +2 -0
  292. nautobot/dcim/models/device_components.py +3 -1
  293. nautobot/dcim/models/devices.py +115 -51
  294. nautobot/dcim/navigation.py +7 -3
  295. nautobot/dcim/querysets.py +6 -0
  296. nautobot/dcim/signals.py +19 -0
  297. nautobot/dcim/tables/devices.py +9 -5
  298. nautobot/dcim/tables/template_code.py +191 -102
  299. nautobot/dcim/templates/dcim/cable.html +1 -1
  300. nautobot/dcim/templates/dcim/cable_connect.html +62 -146
  301. nautobot/dcim/templates/dcim/cable_retrieve.html +10 -10
  302. nautobot/dcim/templates/dcim/cable_trace.html +15 -17
  303. nautobot/dcim/templates/dcim/console_port_connection_list.html +2 -2
  304. nautobot/dcim/templates/dcim/consoleport.html +18 -17
  305. nautobot/dcim/templates/dcim/consoleserverport.html +18 -17
  306. nautobot/dcim/templates/dcim/controller_create.html +12 -8
  307. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +1 -1
  308. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +6 -6
  309. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -1
  310. nautobot/dcim/templates/dcim/device/config.html +17 -19
  311. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +4 -4
  312. nautobot/dcim/templates/dcim/device/status.html +20 -20
  313. nautobot/dcim/templates/dcim/device_component_add.html +24 -15
  314. nautobot/dcim/templates/dcim/device_create.html +120 -120
  315. nautobot/dcim/templates/dcim/device_list.html +75 -12
  316. nautobot/dcim/templates/dcim/devicebay.html +7 -7
  317. nautobot/dcim/templates/dcim/devicebay_populate.html +29 -23
  318. nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +6 -6
  319. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  320. nautobot/dcim/templates/dcim/devicetype_component_add.html +25 -19
  321. nautobot/dcim/templates/dcim/devicetype_list.html +4 -4
  322. nautobot/dcim/templates/dcim/devicetype_update.html +9 -9
  323. nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +3 -3
  324. nautobot/dcim/templates/dcim/frontport.html +21 -20
  325. nautobot/dcim/templates/dcim/inc/cable_form.html +7 -7
  326. nautobot/dcim/templates/dcim/inc/cable_termination.html +1 -1
  327. nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +18 -9
  328. nautobot/dcim/templates/dcim/inc/detail_softwareversion_softwareimagefile_rows.html +1 -1
  329. nautobot/dcim/templates/dcim/inc/device_interface_filter.html +1 -1
  330. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +10 -10
  331. nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
  332. nautobot/dcim/templates/dcim/inc/homepage_connections.html +2 -2
  333. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +10 -10
  334. nautobot/dcim/templates/dcim/inc/rack_elevation.html +2 -2
  335. nautobot/dcim/templates/dcim/interface.html +42 -22
  336. nautobot/dcim/templates/dcim/interface_connection_list.html +2 -2
  337. nautobot/dcim/templates/dcim/interface_edit.html +26 -11
  338. nautobot/dcim/templates/dcim/interfaceredundancygroupassociation_create.html +3 -3
  339. nautobot/dcim/templates/dcim/inventoryitem.html +3 -3
  340. nautobot/dcim/templates/dcim/inventoryitem_add.html +21 -10
  341. nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
  342. nautobot/dcim/templates/dcim/inventoryitem_edit.html +6 -4
  343. nautobot/dcim/templates/dcim/location.html +1 -1
  344. nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +24 -18
  345. nautobot/dcim/templates/dcim/location_retrieve.html +1 -1
  346. nautobot/dcim/templates/dcim/location_update.html +9 -9
  347. nautobot/dcim/templates/dcim/locationtype.html +0 -1
  348. nautobot/dcim/templates/dcim/module/base.html +67 -27
  349. nautobot/dcim/templates/dcim/module_consoleports.html +13 -15
  350. nautobot/dcim/templates/dcim/module_consoleserverports.html +13 -15
  351. nautobot/dcim/templates/dcim/module_frontports.html +13 -15
  352. nautobot/dcim/templates/dcim/module_interfaces.html +14 -16
  353. nautobot/dcim/templates/dcim/module_list.html +59 -10
  354. nautobot/dcim/templates/dcim/module_modulebays.html +12 -14
  355. nautobot/dcim/templates/dcim/module_poweroutlets.html +13 -15
  356. nautobot/dcim/templates/dcim/module_powerports.html +13 -15
  357. nautobot/dcim/templates/dcim/module_rearports.html +13 -15
  358. nautobot/dcim/templates/dcim/module_retrieve.html +3 -3
  359. nautobot/dcim/templates/dcim/module_update.html +15 -9
  360. nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -93
  361. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +7 -7
  362. nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
  363. nautobot/dcim/templates/dcim/moduletype_retrieve.html +74 -35
  364. nautobot/dcim/templates/dcim/platform_create.html +9 -9
  365. nautobot/dcim/templates/dcim/power_port_connection_list.html +3 -3
  366. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  367. nautobot/dcim/templates/dcim/powerfeed_edit.html +15 -15
  368. nautobot/dcim/templates/dcim/poweroutlet.html +13 -13
  369. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  370. nautobot/dcim/templates/dcim/powerport.html +17 -16
  371. nautobot/dcim/templates/dcim/rack.html +1 -1
  372. nautobot/dcim/templates/dcim/rack_elevation.html +2 -2
  373. nautobot/dcim/templates/dcim/rack_elevation_list.html +21 -9
  374. nautobot/dcim/templates/dcim/rack_retrieve.html +75 -57
  375. nautobot/dcim/templates/dcim/rack_update.html +14 -14
  376. nautobot/dcim/templates/dcim/rackreservation.html +1 -1
  377. nautobot/dcim/templates/dcim/rackreservation_edit.html +6 -6
  378. nautobot/dcim/templates/dcim/rearport.html +19 -18
  379. nautobot/dcim/templates/dcim/trace/cable.html +1 -1
  380. nautobot/dcim/templates/dcim/trace/circuit.html +1 -1
  381. nautobot/dcim/templates/dcim/trace/device.html +1 -1
  382. nautobot/dcim/templates/dcim/trace/powerpanel.html +1 -1
  383. nautobot/dcim/templates/dcim/trace/termination.html +1 -1
  384. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  385. nautobot/dcim/templates/dcim/virtualchassis_add_member.html +25 -16
  386. nautobot/dcim/templates/dcim/virtualchassis_create.html +6 -6
  387. nautobot/dcim/templates/dcim/virtualchassis_edit.html +1 -1
  388. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -1
  389. nautobot/dcim/templates/dcim/virtualchassis_update.html +36 -22
  390. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +9 -9
  391. nautobot/dcim/tests/integration/test_controller.py +6 -6
  392. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +7 -7
  393. nautobot/dcim/tests/integration/test_create_device.py +9 -9
  394. nautobot/dcim/tests/integration/test_device_bulk_operations.py +7 -2
  395. nautobot/dcim/tests/integration/test_fileinputpicker.py +5 -7
  396. nautobot/dcim/tests/integration/test_location_bulk_operations.py +2 -0
  397. nautobot/dcim/tests/integration/test_module_bay_position.py +4 -1
  398. nautobot/dcim/tests/test_api.py +86 -6
  399. nautobot/dcim/tests/test_custom_validators.py +229 -0
  400. nautobot/dcim/tests/test_filters.py +159 -110
  401. nautobot/dcim/tests/test_graphql.py +32 -36
  402. nautobot/dcim/tests/test_jobs.py +1 -1
  403. nautobot/dcim/tests/test_models.py +229 -1
  404. nautobot/dcim/tests/test_views.py +31 -20
  405. nautobot/dcim/utils.py +3 -3
  406. nautobot/dcim/views.py +77 -41
  407. nautobot/extras/api/serializers.py +83 -19
  408. nautobot/extras/api/urls.py +7 -0
  409. nautobot/extras/api/views.py +243 -140
  410. nautobot/extras/choices.py +34 -13
  411. nautobot/extras/constants.py +1 -1
  412. nautobot/extras/context_managers.py +26 -26
  413. nautobot/extras/datasources/git.py +22 -0
  414. nautobot/extras/datasources/registry.py +3 -0
  415. nautobot/extras/exceptions.py +5 -0
  416. nautobot/extras/factory.py +11 -1
  417. nautobot/extras/{filters/mixins.py → filter_mixins.py} +4 -3
  418. nautobot/extras/{filters/__init__.py → filters.py} +203 -58
  419. nautobot/extras/forms/base.py +2 -1
  420. nautobot/extras/forms/forms.py +225 -20
  421. nautobot/extras/forms/mixins.py +0 -41
  422. nautobot/extras/homepage.py +21 -2
  423. nautobot/extras/jobs.py +2 -8
  424. nautobot/extras/jobs_ui.py +2 -2
  425. nautobot/extras/management/__init__.py +9 -0
  426. nautobot/extras/managers.py +31 -22
  427. nautobot/extras/migrations/0126_approval_workflow_pre_check.py +58 -0
  428. nautobot/extras/migrations/0127_approval_workflow_models.py +266 -0
  429. nautobot/extras/migrations/0128_remove_job_approval_required_and_more.py +29 -0
  430. nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
  431. nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
  432. nautobot/extras/models/__init__.py +14 -3
  433. nautobot/extras/models/approvals.py +556 -0
  434. nautobot/extras/models/change_logging.py +1 -0
  435. nautobot/extras/models/contacts.py +2 -0
  436. nautobot/extras/models/customfields.py +57 -22
  437. nautobot/extras/models/datasources.py +21 -0
  438. nautobot/extras/models/groups.py +2 -0
  439. nautobot/extras/models/jobs.py +122 -39
  440. nautobot/extras/models/metadata.py +2 -3
  441. nautobot/extras/models/mixins.py +129 -1
  442. nautobot/extras/models/models.py +22 -14
  443. nautobot/extras/models/relationships.py +47 -10
  444. nautobot/extras/models/secrets.py +1 -0
  445. nautobot/extras/models/statuses.py +0 -15
  446. nautobot/extras/models/tags.py +1 -1
  447. nautobot/extras/navigation.py +42 -15
  448. nautobot/extras/plugins/__init__.py +33 -55
  449. nautobot/extras/plugins/marketplace_manifest.yml +1 -23
  450. nautobot/extras/plugins/tables.py +8 -6
  451. nautobot/extras/plugins/urls.py +2 -21
  452. nautobot/extras/plugins/utils.py +1 -33
  453. nautobot/extras/plugins/validators.py +10 -10
  454. nautobot/extras/plugins/views.py +1 -5
  455. nautobot/extras/querysets.py +17 -21
  456. nautobot/extras/signals.py +23 -8
  457. nautobot/extras/tables.py +420 -99
  458. nautobot/extras/templates/extras/approval_dashboard.html +15 -0
  459. nautobot/extras/templates/extras/approval_workflow/approve.html +11 -0
  460. nautobot/extras/templates/extras/approval_workflow/comment.html +9 -0
  461. nautobot/extras/templates/extras/approval_workflow/deny.html +10 -0
  462. nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +77 -0
  463. nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +29 -0
  464. nautobot/extras/templates/extras/configcontext_update.html +12 -12
  465. nautobot/extras/templates/extras/configcontextschema.html +1 -1
  466. nautobot/extras/templates/extras/configcontextschema_retrieve.html +9 -9
  467. nautobot/extras/templates/extras/configcontextschema_update.html +6 -6
  468. nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
  469. nautobot/extras/templates/extras/customfield_update.html +12 -12
  470. nautobot/extras/templates/extras/dynamicgroup.html +1 -1
  471. nautobot/extras/templates/extras/dynamicgroup_edit.html +1 -1
  472. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +17 -17
  473. nautobot/extras/templates/extras/dynamicgroup_update.html +24 -24
  474. nautobot/extras/templates/extras/externalintegration_update.html +6 -6
  475. nautobot/extras/templates/extras/gitrepository.html +1 -1
  476. nautobot/extras/templates/extras/gitrepository_object_edit.html +1 -1
  477. nautobot/extras/templates/extras/gitrepository_result.html +1 -1
  478. nautobot/extras/templates/extras/gitrepository_retrieve.html +12 -12
  479. nautobot/extras/templates/extras/gitrepository_update.html +25 -7
  480. nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -1
  481. nautobot/extras/templates/extras/inc/approval_buttons_column.html +38 -0
  482. nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +14 -13
  483. nautobot/extras/templates/extras/inc/configcontext_format.html +11 -4
  484. nautobot/extras/templates/extras/inc/graphqlquery_execute.html +7 -7
  485. nautobot/extras/templates/extras/inc/job_label.html +5 -5
  486. nautobot/extras/templates/extras/inc/job_table.html +23 -10
  487. nautobot/extras/templates/extras/inc/job_tiles.html +33 -21
  488. nautobot/extras/templates/extras/inc/jobresult.html +6 -6
  489. nautobot/extras/templates/extras/inc/json_format.html +11 -4
  490. nautobot/extras/templates/extras/inc/object_contact_header.html +6 -6
  491. nautobot/extras/templates/extras/inc/overridable_field.html +16 -15
  492. nautobot/extras/templates/extras/inc/panel_approvalworkflowstage.html +34 -0
  493. nautobot/extras/templates/extras/inc/panel_changelog.html +9 -9
  494. nautobot/extras/templates/extras/inc/panel_jobhistory.html +8 -6
  495. nautobot/extras/templates/extras/inc/tags_panel.html +3 -3
  496. nautobot/extras/templates/extras/job.html +154 -155
  497. nautobot/extras/templates/extras/job_approval_confirmation.html +4 -27
  498. nautobot/extras/templates/extras/job_bulk_edit.html +18 -1
  499. nautobot/extras/templates/extras/job_detail.html +1 -1
  500. nautobot/extras/templates/extras/job_edit.html +69 -64
  501. nautobot/extras/templates/extras/job_list.html +37 -60
  502. nautobot/extras/templates/extras/jobresult.html +1 -1
  503. nautobot/extras/templates/extras/jobresult_retrieve.html +17 -17
  504. nautobot/extras/templates/extras/marketplace.html +62 -71
  505. nautobot/extras/templates/extras/metadatatype_create.html +9 -9
  506. nautobot/extras/templates/extras/note.html +1 -1
  507. nautobot/extras/templates/extras/object_approvalworkflow.html +36 -0
  508. nautobot/extras/templates/extras/object_assign_contact_or_team.html +16 -7
  509. nautobot/extras/templates/extras/object_configcontext.html +20 -20
  510. nautobot/extras/templates/extras/object_new_contact.html +6 -6
  511. nautobot/extras/templates/extras/object_new_team.html +6 -6
  512. nautobot/extras/templates/extras/objectchange.html +1 -1
  513. nautobot/extras/templates/extras/objectchange_retrieve.html +37 -56
  514. nautobot/extras/templates/extras/plugin_detail.html +40 -41
  515. nautobot/extras/templates/extras/plugins_list.html +23 -38
  516. nautobot/extras/templates/extras/plugins_tiles.html +28 -28
  517. nautobot/extras/templates/extras/role_retrieve.html +112 -48
  518. nautobot/extras/templates/extras/scheduledjob.html +25 -28
  519. nautobot/extras/templates/extras/secret_create.html +11 -11
  520. nautobot/extras/templates/extras/secretsgroup_update.html +6 -6
  521. nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +3 -3
  522. nautobot/extras/templates/extras/status.html +1 -1
  523. nautobot/extras/templates/extras/tag.html +1 -1
  524. nautobot/extras/templates/extras/tag_update.html +3 -3
  525. nautobot/extras/templates/extras/templatetags/log_level.html +1 -1
  526. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -2
  527. nautobot/extras/templates/extras/webhook.html +12 -12
  528. nautobot/extras/templatetags/approvals.py +19 -0
  529. nautobot/extras/templatetags/custom_links.py +12 -12
  530. nautobot/extras/templatetags/job_buttons.py +14 -12
  531. nautobot/extras/test_jobs/invalid_import.py +9 -0
  532. nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
  533. nautobot/extras/test_jobs/missing_import.py +11 -0
  534. nautobot/extras/tests/integration/test_computedfields.py +5 -8
  535. nautobot/extras/tests/integration/test_configcontextschema.py +43 -48
  536. nautobot/extras/tests/integration/test_customfields.py +33 -33
  537. nautobot/extras/tests/integration/test_dynamicgroups.py +5 -10
  538. nautobot/extras/tests/integration/test_jobs.py +2 -4
  539. nautobot/extras/tests/integration/test_notes.py +3 -9
  540. nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
  541. nautobot/extras/tests/integration/test_plugins.py +35 -27
  542. nautobot/extras/tests/integration/test_relationships.py +7 -11
  543. nautobot/extras/tests/integration/test_tagfilter.py +3 -11
  544. nautobot/extras/tests/test_api.py +786 -242
  545. nautobot/extras/tests/test_approvals.py +715 -0
  546. nautobot/extras/tests/test_changelog.py +18 -14
  547. nautobot/extras/tests/test_customfields.py +14 -13
  548. nautobot/extras/tests/test_datasources.py +1 -1
  549. nautobot/extras/tests/test_dynamicgroups.py +9 -4
  550. nautobot/extras/tests/test_filters.py +443 -13
  551. nautobot/extras/tests/test_forms.py +18 -57
  552. nautobot/extras/tests/test_jobs.py +25 -4
  553. nautobot/extras/tests/test_migrations.py +81 -1
  554. nautobot/extras/tests/test_models.py +378 -47
  555. nautobot/extras/tests/test_plugins.py +47 -13
  556. nautobot/extras/tests/test_relationships.py +7 -2
  557. nautobot/extras/tests/test_utils.py +2 -0
  558. nautobot/extras/tests/test_views.py +780 -493
  559. nautobot/extras/urls.py +36 -12
  560. nautobot/extras/utils.py +58 -12
  561. nautobot/extras/views.py +668 -209
  562. nautobot/ipam/factory.py +7 -0
  563. nautobot/ipam/filter_mixins.py +38 -0
  564. nautobot/ipam/filters.py +35 -71
  565. nautobot/ipam/formfields.py +1 -1
  566. nautobot/ipam/forms.py +6 -3
  567. nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
  568. nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
  569. nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
  570. nautobot/ipam/models.py +29 -2
  571. nautobot/ipam/navigation.py +3 -1
  572. nautobot/ipam/querysets.py +1 -2
  573. nautobot/ipam/tables.py +26 -17
  574. nautobot/ipam/templates/ipam/inc/ipadress_edit_header.html +6 -6
  575. nautobot/ipam/templates/ipam/inc/service.html +8 -8
  576. nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
  577. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +3 -2
  578. nautobot/ipam/templates/ipam/ipaddress.html +27 -13
  579. nautobot/ipam/templates/ipam/ipaddress_assign.html +31 -24
  580. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +3 -3
  581. nautobot/ipam/templates/ipam/ipaddress_edit.html +9 -9
  582. nautobot/ipam/templates/ipam/ipaddress_interfaces.html +7 -9
  583. nautobot/ipam/templates/ipam/ipaddress_merge.html +195 -186
  584. nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +7 -9
  585. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +7 -5
  586. nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
  587. nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
  588. nautobot/ipam/templates/ipam/namespace_update.html +15 -0
  589. nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
  590. nautobot/ipam/templates/ipam/prefix_create.html +9 -9
  591. nautobot/ipam/templates/ipam/prefix_list.html +15 -14
  592. nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -1
  593. nautobot/ipam/templates/ipam/vlan.html +1 -1
  594. nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
  595. nautobot/ipam/templates/ipam/vlan_update.html +6 -6
  596. nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
  597. nautobot/ipam/templates/ipam/vrf_edit.html +15 -15
  598. nautobot/ipam/tests/integration/test_prefixes.py +5 -13
  599. nautobot/ipam/tests/migration/test_migrations.py +89 -0
  600. nautobot/ipam/tests/test_api.py +20 -7
  601. nautobot/ipam/tests/test_filters.py +10 -0
  602. nautobot/ipam/tests/test_forms.py +1 -1
  603. nautobot/ipam/tests/test_models.py +1 -1
  604. nautobot/ipam/tests/test_tables.py +1 -2
  605. nautobot/ipam/tests/test_utils.py +1 -1
  606. nautobot/ipam/tests/test_views.py +24 -21
  607. nautobot/ipam/ui.py +0 -17
  608. nautobot/ipam/utils/migrations.py +16 -2
  609. nautobot/ipam/utils/testing.py +9 -3
  610. nautobot/ipam/views.py +49 -7
  611. nautobot/project-static/dist/css/graphql-libraries.css +655 -0
  612. nautobot/project-static/dist/css/graphql-libraries.css.map +1 -0
  613. nautobot/project-static/dist/css/materialdesignicons.css +3 -0
  614. nautobot/project-static/dist/css/materialdesignicons.css.map +1 -0
  615. nautobot/project-static/dist/css/nautobot.css +13 -0
  616. nautobot/project-static/dist/css/nautobot.css.map +1 -0
  617. nautobot/project-static/dist/js/graphql-libraries.js +3 -0
  618. nautobot/project-static/dist/js/graphql-libraries.js.LICENSE.txt +62 -0
  619. nautobot/project-static/dist/js/graphql-libraries.js.map +1 -0
  620. nautobot/project-static/dist/js/libraries.js +3 -0
  621. nautobot/project-static/dist/js/libraries.js.LICENSE.txt +65 -0
  622. nautobot/project-static/dist/js/libraries.js.map +1 -0
  623. nautobot/project-static/dist/js/materialdesignicons.js +0 -0
  624. nautobot/project-static/dist/js/nautobot-graphiql.js +2 -0
  625. nautobot/project-static/dist/js/nautobot-graphiql.js.map +1 -0
  626. nautobot/project-static/dist/js/nautobot.js +2 -0
  627. nautobot/project-static/dist/js/nautobot.js.map +1 -0
  628. nautobot/project-static/fonts/Montserrat-v30-Bold.woff2 +0 -0
  629. nautobot/project-static/fonts/Montserrat-v30-Light.woff2 +0 -0
  630. nautobot/project-static/fonts/Montserrat-v30-Regular.woff2 +0 -0
  631. nautobot/project-static/fonts/Roboto-v48-Bold.woff2 +0 -0
  632. nautobot/project-static/fonts/Roboto-v48-Light.woff2 +0 -0
  633. nautobot/project-static/fonts/Roboto-v48-Regular.woff2 +0 -0
  634. nautobot/project-static/img/jinja_logo.svg +21 -92
  635. nautobot/project-static/js/cabletrace.js +1 -1
  636. nautobot/project-static/js/editor.js +4 -4
  637. nautobot/project-static/js/forms.js +67 -717
  638. nautobot/project-static/js/job_result.js +2 -2
  639. nautobot/project-static/nautobot-icons/360-degrees.svg +3 -0
  640. nautobot/project-static/nautobot-icons/arrow-decision.svg +3 -0
  641. nautobot/project-static/nautobot-icons/arrows-expand-rec.svg +3 -0
  642. nautobot/project-static/nautobot-icons/arrows-move-2-rec.svg +3 -0
  643. nautobot/project-static/nautobot-icons/arrows-move-rec.svg +3 -0
  644. nautobot/project-static/nautobot-icons/atom.svg +3 -0
  645. nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
  646. nautobot/project-static/nautobot-icons/branch.svg +3 -0
  647. nautobot/project-static/nautobot-icons/briefcase-2.svg +3 -0
  648. nautobot/project-static/nautobot-icons/cable-data-2.svg +3 -0
  649. nautobot/project-static/nautobot-icons/cable-data.svg +3 -0
  650. nautobot/project-static/nautobot-icons/cast.svg +3 -0
  651. nautobot/project-static/nautobot-icons/check-circle.svg +3 -0
  652. nautobot/project-static/nautobot-icons/checkbox-circle.svg +3 -0
  653. nautobot/project-static/nautobot-icons/checkbox-rec.svg +3 -0
  654. nautobot/project-static/nautobot-icons/cloud-check.svg +3 -0
  655. nautobot/project-static/nautobot-icons/cloud-lightning.svg +3 -0
  656. nautobot/project-static/nautobot-icons/cloud-upload.svg +3 -0
  657. nautobot/project-static/nautobot-icons/cloud.svg +3 -0
  658. nautobot/project-static/nautobot-icons/compass.svg +3 -0
  659. nautobot/project-static/nautobot-icons/control-panel.svg +3 -0
  660. nautobot/project-static/nautobot-icons/credit-card.svg +3 -0
  661. nautobot/project-static/nautobot-icons/device-lifecycle.svg +3 -0
  662. nautobot/project-static/nautobot-icons/direction.svg +3 -0
  663. nautobot/project-static/nautobot-icons/elements.svg +3 -0
  664. nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
  665. nautobot/project-static/nautobot-icons/globe-2.svg +3 -0
  666. nautobot/project-static/nautobot-icons/globe.svg +3 -0
  667. nautobot/project-static/nautobot-icons/hammer.svg +3 -0
  668. nautobot/project-static/nautobot-icons/history.svg +3 -0
  669. nautobot/project-static/nautobot-icons/ip.svg +3 -0
  670. nautobot/project-static/nautobot-icons/laptop.svg +3 -0
  671. nautobot/project-static/nautobot-icons/lightning.svg +3 -0
  672. nautobot/project-static/nautobot-icons/list-unordered.svg +3 -0
  673. nautobot/project-static/nautobot-icons/map-view.svg +3 -0
  674. nautobot/project-static/nautobot-icons/organization.svg +3 -0
  675. nautobot/project-static/nautobot-icons/pin-2.svg +3 -0
  676. nautobot/project-static/nautobot-icons/pin-3.svg +3 -0
  677. nautobot/project-static/nautobot-icons/plug.svg +3 -0
  678. nautobot/project-static/nautobot-icons/refresh-cw.svg +3 -0
  679. nautobot/project-static/nautobot-icons/rocket-2.svg +3 -0
  680. nautobot/project-static/nautobot-icons/rotate-cw.svg +3 -0
  681. nautobot/project-static/nautobot-icons/route.svg +3 -0
  682. nautobot/project-static/nautobot-icons/secrets.svg +3 -0
  683. nautobot/project-static/nautobot-icons/security.svg +3 -0
  684. nautobot/project-static/nautobot-icons/server-2.svg +3 -0
  685. nautobot/project-static/nautobot-icons/server.svg +3 -0
  686. nautobot/project-static/nautobot-icons/share.svg +3 -0
  687. nautobot/project-static/nautobot-icons/shield-check.svg +3 -0
  688. nautobot/project-static/nautobot-icons/sitemap-outline.svg +3 -0
  689. nautobot/project-static/nautobot-icons/sliders-vert-2.svg +3 -0
  690. nautobot/project-static/nautobot-icons/sliders-vert.svg +3 -0
  691. nautobot/project-static/nautobot-icons/star-filled.svg +3 -0
  692. nautobot/project-static/nautobot-icons/star.svg +3 -0
  693. nautobot/project-static/nautobot-icons/transform.svg +3 -0
  694. nautobot/project-static/nautobot-icons/wifi.svg +3 -0
  695. nautobot/tenancy/api/serializers.py +1 -0
  696. nautobot/tenancy/api/views.py +2 -1
  697. nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
  698. nautobot/tenancy/navigation.py +3 -1
  699. nautobot/tenancy/templates/tenancy/tenant_create.html +6 -6
  700. nautobot/tenancy/tests/test_filters.py +0 -2
  701. nautobot/tenancy/views.py +2 -1
  702. nautobot/ui/.gitignore +137 -0
  703. nautobot/ui/.node-version +1 -0
  704. nautobot/ui/.prettierignore +3 -0
  705. nautobot/ui/eslint.config.js +33 -0
  706. nautobot/ui/package-lock.json +6594 -0
  707. nautobot/ui/package.json +67 -0
  708. nautobot/ui/prettier.config.js +9 -0
  709. nautobot/ui/src/js/collapse.js +69 -0
  710. nautobot/ui/src/js/cookie.js +31 -0
  711. nautobot/ui/src/js/draggable.js +101 -0
  712. nautobot/ui/src/js/drawer.js +106 -0
  713. nautobot/ui/src/js/form.js +23 -0
  714. nautobot/ui/src/js/history.js +51 -0
  715. nautobot/ui/src/js/nautobot-graphiql.js +19 -0
  716. nautobot/ui/src/js/nautobot.js +128 -0
  717. nautobot/ui/src/js/search.js +274 -0
  718. nautobot/ui/src/js/select2.js +318 -0
  719. nautobot/ui/src/js/sidenav.js +87 -0
  720. nautobot/ui/src/js/tabs.js +139 -0
  721. nautobot/ui/src/js/theme.js +104 -0
  722. nautobot/ui/src/js/utils.js +54 -0
  723. nautobot/ui/src/scss/colors.scss +58 -0
  724. nautobot/ui/src/scss/nautobot.scss +2471 -0
  725. nautobot/ui/webpack.config.js +148 -0
  726. nautobot/users/apps.py +3 -0
  727. nautobot/users/filters.py +7 -11
  728. nautobot/users/forms.py +10 -0
  729. nautobot/users/models.py +8 -0
  730. nautobot/users/templates/users/advanced_settings_edit.html +31 -21
  731. nautobot/users/templates/users/api_tokens.html +61 -51
  732. nautobot/users/templates/users/base.html +23 -31
  733. nautobot/users/templates/users/change_password.html +29 -19
  734. nautobot/users/templates/users/preferences.html +55 -45
  735. nautobot/users/templates/users/profile.html +45 -14
  736. nautobot/users/tests/test_api.py +4 -0
  737. nautobot/users/urls.py +2 -0
  738. nautobot/users/views.py +70 -2
  739. nautobot/virtualization/api/views.py +1 -1
  740. nautobot/virtualization/filters.py +18 -32
  741. nautobot/virtualization/forms.py +22 -59
  742. nautobot/virtualization/models.py +1 -19
  743. nautobot/virtualization/navigation.py +3 -1
  744. nautobot/virtualization/tables.py +10 -6
  745. nautobot/virtualization/templates/virtualization/cluster.html +13 -13
  746. nautobot/virtualization/templates/virtualization/cluster_edit.html +6 -6
  747. nautobot/virtualization/templates/virtualization/inc/virtualmachine_vminterface_filter.html +1 -1
  748. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  749. nautobot/virtualization/templates/virtualization/virtualmachine_component_add.html +24 -16
  750. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +1 -1
  751. nautobot/virtualization/templates/virtualization/virtualmachine_list.html +4 -4
  752. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +27 -25
  753. nautobot/virtualization/templates/virtualization/vminterface.html +5 -5
  754. nautobot/virtualization/templates/virtualization/vminterface_edit.html +27 -11
  755. nautobot/virtualization/tests/test_api.py +3 -0
  756. nautobot/virtualization/tests/test_models.py +20 -5
  757. nautobot/virtualization/tests/test_views.py +3 -5
  758. nautobot/virtualization/urls.py +0 -11
  759. nautobot/virtualization/views.py +5 -122
  760. nautobot/vpn/__init__.py +0 -0
  761. nautobot/vpn/api/serializers.py +113 -0
  762. nautobot/vpn/api/urls.py +19 -0
  763. nautobot/vpn/api/views.py +70 -0
  764. nautobot/vpn/apps.py +8 -0
  765. nautobot/vpn/choices.py +171 -0
  766. nautobot/vpn/factory.py +209 -0
  767. nautobot/vpn/filters.py +233 -0
  768. nautobot/vpn/forms.py +486 -0
  769. nautobot/vpn/homepage.py +19 -0
  770. nautobot/vpn/migrations/0001_initial.py +541 -0
  771. nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
  772. nautobot/vpn/migrations/__init__.py +0 -0
  773. nautobot/vpn/models.py +527 -0
  774. nautobot/vpn/navigation.py +98 -0
  775. nautobot/vpn/tables.py +380 -0
  776. nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
  777. nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
  778. nautobot/vpn/tests/__init__.py +0 -0
  779. nautobot/vpn/tests/test_api.py +341 -0
  780. nautobot/vpn/tests/test_filters.py +139 -0
  781. nautobot/vpn/tests/test_forms.py +294 -0
  782. nautobot/vpn/tests/test_models.py +97 -0
  783. nautobot/vpn/tests/test_views.py +281 -0
  784. nautobot/vpn/urls.py +16 -0
  785. nautobot/vpn/views.py +437 -0
  786. nautobot/wireless/filters.py +0 -8
  787. nautobot/wireless/navigation.py +3 -1
  788. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +6 -6
  789. nautobot/wireless/tests/integration/test_radio_profile.py +3 -7
  790. nautobot/wireless/tests/test_api.py +1 -1
  791. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +5 -4
  792. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +802 -707
  793. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
  794. nautobot/core/management/commands/check_job_approval_status.py +0 -47
  795. nautobot/core/templates/search_form.html +0 -9
  796. nautobot/core/templates/utilities/templatetags/filter_form_modal.html +0 -87
  797. nautobot/dcim/filters/mixins.py +0 -354
  798. nautobot/extras/templates/extras/job_approval_request.html +0 -134
  799. nautobot/extras/templates/extras/scheduled_jobs_approval_queue_list.html +0 -28
  800. nautobot/ipam/mixins.py +0 -32
  801. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
  802. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +0 -587
  803. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +0 -1
  804. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +0 -6
  805. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +0 -1
  806. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +0 -6865
  807. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +0 -1
  808. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +0 -6
  809. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +0 -1
  810. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.eot +0 -0
  811. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.svg +0 -288
  812. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  813. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff +0 -0
  814. nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
  815. nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.js +0 -2580
  816. nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.min.js +0 -6
  817. nautobot/project-static/bootstrap-3.4.1-dist/js/npm.js +0 -13
  818. nautobot/project-static/clipboard.js-2.0.9/clipboard.min.js +0 -7
  819. nautobot/project-static/css/base.css +0 -1040
  820. nautobot/project-static/css/dark.css +0 -282
  821. nautobot/project-static/flatpickr-4.6.9/flatpickr.min.js +0 -2
  822. nautobot/project-static/flatpickr-4.6.9/themes/light.min.css +0 -1
  823. nautobot/project-static/graphiql-1.5.16/graphiql.min.css +0 -12
  824. nautobot/project-static/graphiql-1.5.16/graphiql.min.js +0 -11
  825. nautobot/project-static/highlight.js-11.9.0/github-dark.min.css +0 -10
  826. nautobot/project-static/highlight.js-11.9.0/github.min.css +0 -10
  827. nautobot/project-static/highlight.js-11.9.0/highlight.min.js +0 -378
  828. nautobot/project-static/jquery/jquery-3.7.1.min.js +0 -2
  829. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_444444_256x240.png +0 -0
  830. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_555555_256x240.png +0 -0
  831. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777620_256x240.png +0 -0
  832. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777777_256x240.png +0 -0
  833. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_cc0000_256x240.png +0 -0
  834. nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_ffffff_256x240.png +0 -0
  835. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +0 -7
  836. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +0 -6
  837. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +0 -5
  838. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.theme.min.css +0 -5
  839. nautobot/project-static/js/homepage_layout.js +0 -182
  840. nautobot/project-static/js/nav_menu.js +0 -250
  841. nautobot/project-static/js/theme.js +0 -133
  842. nautobot/project-static/materialdesignicons-7.4.47/LICENSE +0 -20
  843. nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +0 -3
  844. nautobot/project-static/react-16.14.0/react.production.min.js +0 -32
  845. nautobot/project-static/react-dom-16.14.0/react-dom.production.min.js +0 -239
  846. nautobot/project-static/select2-4.0.13/i18n/af.js +0 -3
  847. nautobot/project-static/select2-4.0.13/i18n/ar.js +0 -3
  848. nautobot/project-static/select2-4.0.13/i18n/az.js +0 -3
  849. nautobot/project-static/select2-4.0.13/i18n/bg.js +0 -3
  850. nautobot/project-static/select2-4.0.13/i18n/bn.js +0 -3
  851. nautobot/project-static/select2-4.0.13/i18n/bs.js +0 -3
  852. nautobot/project-static/select2-4.0.13/i18n/ca.js +0 -3
  853. nautobot/project-static/select2-4.0.13/i18n/cs.js +0 -3
  854. nautobot/project-static/select2-4.0.13/i18n/da.js +0 -3
  855. nautobot/project-static/select2-4.0.13/i18n/de.js +0 -3
  856. nautobot/project-static/select2-4.0.13/i18n/dsb.js +0 -3
  857. nautobot/project-static/select2-4.0.13/i18n/el.js +0 -3
  858. nautobot/project-static/select2-4.0.13/i18n/en.js +0 -3
  859. nautobot/project-static/select2-4.0.13/i18n/es.js +0 -3
  860. nautobot/project-static/select2-4.0.13/i18n/et.js +0 -3
  861. nautobot/project-static/select2-4.0.13/i18n/eu.js +0 -3
  862. nautobot/project-static/select2-4.0.13/i18n/fa.js +0 -3
  863. nautobot/project-static/select2-4.0.13/i18n/fi.js +0 -3
  864. nautobot/project-static/select2-4.0.13/i18n/fr.js +0 -3
  865. nautobot/project-static/select2-4.0.13/i18n/gl.js +0 -3
  866. nautobot/project-static/select2-4.0.13/i18n/he.js +0 -3
  867. nautobot/project-static/select2-4.0.13/i18n/hi.js +0 -3
  868. nautobot/project-static/select2-4.0.13/i18n/hr.js +0 -3
  869. nautobot/project-static/select2-4.0.13/i18n/hsb.js +0 -3
  870. nautobot/project-static/select2-4.0.13/i18n/hu.js +0 -3
  871. nautobot/project-static/select2-4.0.13/i18n/hy.js +0 -3
  872. nautobot/project-static/select2-4.0.13/i18n/id.js +0 -3
  873. nautobot/project-static/select2-4.0.13/i18n/is.js +0 -3
  874. nautobot/project-static/select2-4.0.13/i18n/it.js +0 -3
  875. nautobot/project-static/select2-4.0.13/i18n/ja.js +0 -3
  876. nautobot/project-static/select2-4.0.13/i18n/ka.js +0 -3
  877. nautobot/project-static/select2-4.0.13/i18n/km.js +0 -3
  878. nautobot/project-static/select2-4.0.13/i18n/ko.js +0 -3
  879. nautobot/project-static/select2-4.0.13/i18n/lt.js +0 -3
  880. nautobot/project-static/select2-4.0.13/i18n/lv.js +0 -3
  881. nautobot/project-static/select2-4.0.13/i18n/mk.js +0 -3
  882. nautobot/project-static/select2-4.0.13/i18n/ms.js +0 -3
  883. nautobot/project-static/select2-4.0.13/i18n/nb.js +0 -3
  884. nautobot/project-static/select2-4.0.13/i18n/ne.js +0 -3
  885. nautobot/project-static/select2-4.0.13/i18n/nl.js +0 -3
  886. nautobot/project-static/select2-4.0.13/i18n/pl.js +0 -3
  887. nautobot/project-static/select2-4.0.13/i18n/ps.js +0 -3
  888. nautobot/project-static/select2-4.0.13/i18n/pt-BR.js +0 -3
  889. nautobot/project-static/select2-4.0.13/i18n/pt.js +0 -3
  890. nautobot/project-static/select2-4.0.13/i18n/ro.js +0 -3
  891. nautobot/project-static/select2-4.0.13/i18n/ru.js +0 -3
  892. nautobot/project-static/select2-4.0.13/i18n/sk.js +0 -3
  893. nautobot/project-static/select2-4.0.13/i18n/sl.js +0 -3
  894. nautobot/project-static/select2-4.0.13/i18n/sq.js +0 -3
  895. nautobot/project-static/select2-4.0.13/i18n/sr-Cyrl.js +0 -3
  896. nautobot/project-static/select2-4.0.13/i18n/sr.js +0 -3
  897. nautobot/project-static/select2-4.0.13/i18n/sv.js +0 -3
  898. nautobot/project-static/select2-4.0.13/i18n/th.js +0 -3
  899. nautobot/project-static/select2-4.0.13/i18n/tk.js +0 -3
  900. nautobot/project-static/select2-4.0.13/i18n/tr.js +0 -3
  901. nautobot/project-static/select2-4.0.13/i18n/uk.js +0 -3
  902. nautobot/project-static/select2-4.0.13/i18n/vi.js +0 -3
  903. nautobot/project-static/select2-4.0.13/i18n/zh-CN.js +0 -3
  904. nautobot/project-static/select2-4.0.13/i18n/zh-TW.js +0 -3
  905. nautobot/project-static/select2-4.0.13/select2.min.css +0 -1
  906. nautobot/project-static/select2-4.0.13/select2.min.js +0 -2
  907. nautobot/project-static/select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css +0 -7
  908. nautobot/project-static/subscriptions-transport-ws-0.9.18/client.min.js +0 -8
  909. nautobot/project-static/whatwg-fetch-3.6.2/fetch.umd.min.js +0 -8
  910. nautobot/virtualization/templates/virtualization/cluster_add_devices.html +0 -37
  911. /nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +0 -0
  912. /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.ttf → dist/1fcc36272ea3e53d0031.ttf} +0 -0
  913. /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.eot → dist/2146c3c82b553977abc7.eot} +0 -0
  914. /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff → dist/e55a20c80650829ec5fd.woff} +0 -0
  915. /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 → dist/ec024da790d2972da002.woff2} +0 -0
  916. /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
  917. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
  918. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
  919. {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
@@ -17,105 +17,73 @@ function slugify(s, num_chars) {
17
17
  return s.substring(0, num_chars); // Trim to first num_chars chars
18
18
  }
19
19
 
20
- // Parse URLs which may contain variable references to other field values
21
- function parseURL(url) {
22
- var filter_regex = /\{\{([a-z_]+)\}\}/g;
23
- var match;
24
- var rendered_url = url;
25
- var filter_field;
26
- while (match = filter_regex.exec(url)) {
27
- filter_field = $('#id_' + match[1]);
28
- var custom_attr = $('option:selected', filter_field).attr('api-value');
29
- if (custom_attr) {
30
- rendered_url = rendered_url.replace(match[0], custom_attr);
31
- } else if (filter_field.val()) {
32
- rendered_url = rendered_url.replace(match[0], filter_field.val());
33
- } else if (filter_field.attr('data-null-option')) {
34
- rendered_url = rendered_url.replace(match[0], 'null');
35
- }
36
- }
37
- return rendered_url
38
- }
39
-
40
- // Assign color picker selection classes
41
- function colorPickerClassCopy(data, container) {
42
- if (data.element) {
43
- // Swap the style
44
- $(container).attr('style', $(data.element).attr("style"));
45
- }
46
- return data.text;
47
- }
48
-
49
-
50
20
  /* ===========================
51
21
  * JS-ify Inputs
52
22
  */
53
23
 
54
- // Static choice selection
55
- function initializeStaticChoiceSelection(context, dropdownParent=null){
56
- this_context = $(context);
57
- this_context.find('.nautobot-select2-static').select2({
58
- allowClear: true,
59
- placeholder: "---------",
60
- theme: "bootstrap",
61
- width: "off",
62
- dropdownParent: dropdownParent
63
- });
64
- }
65
-
66
24
  // Static choice selection
67
25
  function initializeCheckboxes(context){
68
- this_context = $(context);
69
-
70
26
  // Track last selected checkbox for range selection
71
27
  let lastSelectedIndex = null;
72
28
 
73
29
  // "Toggle" checkbox for object lists (PK column)
74
- this_context.find('input:checkbox.toggle').click(function() {
75
- $(this).closest('table').find('input:checkbox[name=pk]:visible').prop('checked', $(this).prop('checked'));
76
-
77
- // Show the "select all" box if present
78
- if ($(this).is(':checked')) {
79
- $('#select_all_box').removeClass('hidden');
80
- } else {
81
- $('#select_all').prop('checked', false);
82
- $('#select_all_box').addClass('hidden');
83
- }
30
+ document.querySelectorAll('input[type="checkbox"].toggle').forEach(toggleCheckbox => {
31
+ toggleCheckbox.addEventListener('click', function () {
32
+ const isChecked = this.checked;
33
+ // Check/uncheck all pk column checkboxes in the table
34
+ this.closest('table')
35
+ .querySelectorAll('input[type="checkbox"][name="pk"]:not([visually-hidden])')
36
+ .forEach(checkbox => checkbox.checked = isChecked);
37
+
38
+ // Show the "select all" box if present
39
+ const selectAllBox = document.getElementById('select_all_box');
40
+ if (selectAllBox) {
41
+ if (isChecked) {
42
+ // unhide the select all objects form that contains the bulk action buttons
43
+ selectAllBox.classList.remove('visually-hidden');
44
+ } else {
45
+ const selectAll = document.getElementById('select_all');
46
+ if (selectAll) selectAll.checked = false;
47
+ }
48
+ }
49
+ });
84
50
 
85
51
  // Reset last selected index when using toggle all
86
52
  lastSelectedIndex = null;
87
53
  });
88
54
 
89
- // Enhanced checkbox click handler with shift-click range selection
90
- this_context.find('input:checkbox[name=pk]').click(function (event) {
91
- const $table = $(this).closest('table');
92
- const $allCheckboxes = $table.find('input:checkbox[name=pk]:visible');
93
- const currentIndex = $allCheckboxes.index(this);
94
-
95
- // Handle shift-click for range selection/deselection
96
- if (event.shiftKey && lastSelectedIndex !== null) {
97
- // Create range from previous click to current click
98
- const startIndex = Math.min(lastSelectedIndex, currentIndex);
99
- const endIndex = Math.max(lastSelectedIndex, currentIndex);
100
-
101
- // Use the clicked item's new state for entire range
102
- const shouldSelect = this.checked;
103
-
104
- // Apply to entire range
105
- for (let i = startIndex; i <= endIndex; i++) {
106
- $allCheckboxes.eq(i).prop('checked', shouldSelect);
55
+ // Uncheck the "toggle" and "select all" checkboxes if an item is unchecked
56
+ document.querySelectorAll('input[type="checkbox"][name="pk"]').forEach((itemCheckbox) => {
57
+ itemCheckbox.addEventListener('click', function () {
58
+ const table = this.closest('table');
59
+ const allCheckboxes = Array.from(table.querySelectorAll('input[type="checkbox"][name="pk"]:not([visually-hidden])'));
60
+ const currentIndex = allCheckboxes.indexOf(this);
61
+
62
+ // Handle shift-click for range selection/deselection
63
+ if (event.shiftKey && lastSelectedIndex !== null) {
64
+ // Create range from previous click to current click
65
+ const startIndex = Math.min(lastSelectedIndex, currentIndex);
66
+ const endIndex = Math.max(lastSelectedIndex, currentIndex);
67
+
68
+ // Use the clicked item's new state for entire range
69
+ const shouldSelect = this.checked;
70
+
71
+ // Apply to entire range
72
+ for (let i = startIndex; i <= endIndex; i++) {
73
+ allCheckboxes[i].checked = shouldSelect;
74
+ }
107
75
  }
108
- }
109
76
 
110
- // Always update anchor to current click (normal click or shift+click)
111
- lastSelectedIndex = currentIndex;
77
+ // Always update anchor to current click (normal click or shift+click)
78
+ lastSelectedIndex = currentIndex;
112
79
 
113
- // Uncheck the "toggle" and "select all" checkboxes if any item is unchecked
114
- const hasUnchecked = $allCheckboxes.filter(':not(:checked)').length > 0;
115
- if (hasUnchecked) {
116
- $('input:checkbox.toggle, #select_all').prop('checked', false);
117
- $('#select_all_box').addClass('hidden');
118
- }
80
+ // Uncheck the "toggle" and "select all" checkboxes if any item is unchecked
81
+ const hasUnchecked = allCheckboxes.some(checkbox => !checkbox.checked);
82
+ if (hasUnchecked) {
83
+ document.querySelectorAll('input[type="checkbox"].toggle, #select_all')
84
+ .forEach(checkbox => checkbox.checked = false);
85
+ }
86
+ });
119
87
  });
120
88
  }
121
89
 
@@ -227,248 +195,18 @@ function initializeBulkEditNullification(context){
227
195
  });
228
196
  }
229
197
 
230
- // Color Picker
231
- function initializeColorPicker(context, dropdownParent=null){
232
- this_context = $(context);
233
- this_context.find('.nautobot-select2-color-picker').select2({
234
- allowClear: true,
235
- placeholder: "---------",
236
- theme: "bootstrap",
237
- templateResult: colorPickerClassCopy,
238
- templateSelection: colorPickerClassCopy,
239
- width: "off",
240
- dropdownParent: dropdownParent
241
- });
242
- }
243
-
244
- /**
245
- * Retrieves the value of a property from a nested object using a string path.
246
- *
247
- * This method supports accessing deeply nested properties within an object.
248
- * It is created to support extraction of nested values in the display-field
249
- * and value-field for DynamicChoiceField.
250
- *
251
- * @param {Object} response - The object from which to retrieve the value.
252
- * @param {string} fieldPath - The string representing the path to the desired property.
253
- * @returns {*} The value of the specified property, or null if the path is invalid or the object is not found.
254
- *
255
- * @example
256
- * let response = {
257
- * "id": 1234,
258
- * "vlan": {
259
- * "name": "myvlan"
260
- * },
261
- * "interfaces": [
262
- * { "name": "eth0", "status": "up" },
263
- * { "name": "eth1", "status": "down" }
264
- * ]
265
- * }
266
- * // returns "myvlan"
267
- * resolvePath(response, "vlan.name")
268
- *
269
- * // returns "eth0"
270
- * resolvePath(response, "interfaces[0].name")
271
- *
272
- * // returns "eth0"
273
- * resolvePath(response, "interfaces.0.name")
274
- */
275
- function resolvePath(response, fieldPath) {
276
- if (!fieldPath)
277
- return null;
278
-
279
- if (typeof response !== 'object' || response === null || !response) {
280
- console.error('Invalid response object');
281
- return null;
282
- }
283
-
284
- return fieldPath
285
- .replace(/\[|\]\.?/g, '.')
286
- .split('.')
287
- .filter(value => value)
288
- .reduce((memo, value) => memo && memo[value], response);
289
- }
290
-
291
- // Dynamic Choice Selection
292
- function initializeDynamicChoiceSelection(context, dropdownParent=null){
293
- this_context = $(context);
294
- this_context.find('.nautobot-select2-api').each(function(){
295
- thisobj = $(this);
296
- placeholder = "---------";
297
- thisobj.select2({
298
- allowClear: true,
299
- placeholder: placeholder,
300
- theme: "bootstrap",
301
- width: "off",
302
- dropdownParent: dropdownParent,
303
- ajax: {
304
- delay: 500,
305
-
306
- url: function(params) {
307
- var element = this[0];
308
- var url = parseURL(element.getAttribute("data-url"));
309
-
310
- if (url.includes("{{")) {
311
- // URL is not fully rendered yet, abort the request
312
- return false;
313
- }
314
- return url;
315
- },
316
-
317
- data: function(params) {
318
- var element = this[0];
319
- // Paging. Note that `params.page` indexes at 1
320
- var offset = (params.page - 1) * 50 || 0;
321
- // Base query params
322
- var parameters = {
323
- q: params.term,
324
- limit: 50,
325
- offset: offset,
326
- };
327
-
328
- // Set api_version
329
- api_version = $(element).attr("data-api-version");
330
- if(api_version){
331
- parameters["api_version"] = api_version;
332
- }
333
-
334
-
335
- // Allow for controlling the depth setting from within APISelect
336
- parameters.depth = parseInt($(element).attr('data-depth'))
337
-
338
- // Attach any extra query parameters
339
- $.each(element.attributes, function(index, attr){
340
- if (attr.name.includes("data-query-param-")){
341
- var param_name = attr.name.split("data-query-param-")[1];
342
-
343
- $.each($.parseJSON(attr.value), function(index, value) {
344
- // Referencing the value of another form field
345
- if (value.startsWith('$')) {
346
- let element_id = $(element).attr("id");
347
- let ref_field;
348
-
349
- if(element_id.includes("id_form-")){
350
- let id_prefix = element_id.match(/id_form-[0-9]+-/i, "")[0];
351
- ref_field = $("#" + id_prefix + value.slice(1));
352
- }
353
- // If the element is in a table row with a class containing "dynamic-formset"
354
- // We need to find the reference field in the same row
355
- else if ($(element).closest("tr") && $(element).closest("tr").attr("class") && $(element).closest("tr").attr("class").includes("dynamic-formset")){
356
- ref_field = $(element).closest("tr").find("select[id*=" + value.slice(1) + "]");
357
- }
358
- else {
359
- ref_field = $('#id_' + value.slice(1));
360
- }
361
-
362
- if (ref_field.val() && ref_field.is(":visible")) {
363
- value = ref_field.val();
364
- } else if (ref_field.attr("required") && ref_field.attr("data-null-option")) {
365
- value = "null";
366
- } else {
367
- return true; // Skip if ref_field has no value
368
- }
369
- }
370
- if (param_name in parameters) {
371
- if (Array.isArray(parameters[param_name])) {
372
- parameters[param_name].push(value);
373
- } else {
374
- parameters[param_name] = [parameters[param_name], value];
375
- }
376
- } else {
377
- parameters[param_name] = value;
378
- }
379
- });
380
- }
381
- });
382
-
383
- // Attach contenttype to parameters
384
- contenttype = $(element).attr("data-contenttype");
385
- if(contenttype){
386
- parameters["content_type"] = contenttype;
387
- }
388
-
389
- // This will handle params with multiple values (i.e. for list filter forms)
390
- return $.param(parameters, true);
391
- },
392
-
393
- processResults: function (data) {
394
- var element = this.$element[0];
395
- $(element).children('option').attr('disabled', false);
396
- var results = data.results;
397
-
398
- results = results.reduce((results,record,idx) => {
399
- record.text = resolvePath(record, element.getAttribute('display-field')) || record.name;
400
- record.id = resolvePath(record, element.getAttribute('value-field')) || record.id;
401
- if(element.getAttribute('disabled-indicator') && record[element.getAttribute('disabled-indicator')]) {
402
- // The disabled-indicator equated to true, so we disable this option
403
- record.disabled = true;
404
- }
405
-
406
- if( record.group !== undefined && record.group !== null && record.site !== undefined && record.site !== null ) {
407
- results[record.site.name + ":" + record.group.name] = results[record.site.name + ":" + record.group.name] || { text: record.site.name + " / " + record.group.name, children: [] };
408
- results[record.site.name + ":" + record.group.name].children.push(record);
409
- }
410
- else if( record.group !== undefined && record.group !== null ) {
411
- results[record.group.name] = results[record.group.name] || { text: record.group.name, children: [] };
412
- results[record.group.name].children.push(record);
413
- }
414
- else if( record.site !== undefined && record.site !== null ) {
415
- results[record.site.name] = results[record.site.name] || { text: record.site.name, children: [] };
416
- results[record.site.name].children.push(record);
417
- }
418
- else if ( (record.group !== undefined || record.group == null) && (record.site !== undefined || record.site === null) ) {
419
- results['global'] = results['global'] || { text: 'Global', children: [] };
420
- results['global'].children.push(record);
421
- }
422
- else {
423
- results[idx] = record;
424
- }
425
- // DynamicGroupSerializer has a `children` field which fits an inappropriate if condition
426
- // in select2.min.js, which will result in the incorrect rendering of DynamicGroup DynamicChoiceField.
427
- // So we nullify the field here since we do not need this field.
428
- if (record?.url ? record.url.includes("dynamic-groups") : false){
429
- record.children = undefined;
430
- }
431
-
432
- return results;
433
- },Object.create(null));
434
-
435
- results = Object.values(results);
436
-
437
- // Handle the null option, but only add it once
438
- if (element.getAttribute('data-null-option') && data.previous === null) {
439
- results.unshift({
440
- id: 'null',
441
- text: element.getAttribute('data-null-option')
442
- });
443
- }
444
-
445
- // Check if there are more results to page
446
- var page = data.next !== null;
447
- return {
448
- results: results,
449
- pagination: {
450
- more: page
451
- }
452
- };
453
- }
454
- }
455
- });
456
- });
457
- }
458
-
459
198
  // Flatpickr selectors
460
199
  function initializeDateTimePicker(context){
461
- this_context = $(context);
462
- this_context.find('.date-picker').flatpickr({
200
+ flatpickr('.date-picker', {
463
201
  allowInput: true
464
202
  });
465
- this_context.find('.datetime-picker').flatpickr({
203
+ flatpickr('.datetime-picker', {
466
204
  allowInput: true,
467
205
  enableSeconds: true,
468
206
  enableTime: true,
469
207
  time_24hr: true
470
208
  });
471
- this_context.find('.time-picker').flatpickr({
209
+ flatpickr('.time-picker', {
472
210
  allowInput: true,
473
211
  enableSeconds: true,
474
212
  enableTime: true,
@@ -477,84 +215,6 @@ function initializeDateTimePicker(context){
477
215
  });
478
216
  }
479
217
 
480
- function initializeTags(context, dropdownParent=null){
481
- this_context = $(context);
482
- this_tag_field = this_context.find('#id_tags.tagfield')
483
- var tags = this_tag_field;
484
- if (tags.length > 0 && tags.val().length > 0){
485
- tags = this_tag_field.val().split(/,\s*/);
486
- } else {
487
- tags = [];
488
- }
489
- tag_objs = $.map(tags, function (tag) {
490
- return {
491
- id: tag,
492
- text: tag,
493
- selected: true
494
- }
495
- });
496
- // Replace the django issued text input with a select element
497
- this_tag_field.replaceWith('<select name="tags" id="id_tags" class="form-control tagfield"></select>');
498
- this_tag_field.select2({
499
- tags: true,
500
- data: tag_objs,
501
- multiple: true,
502
- allowClear: true,
503
- placeholder: "Tags",
504
- theme: "bootstrap",
505
- width: "off",
506
- dropdownParent: dropdownParent,
507
- ajax: {
508
- delay: 250,
509
- url: nautobot_api_path + "extras/tags/",
510
-
511
- data: function(params) {
512
- // Paging. Note that `params.page` indexes at 1
513
- var offset = (params.page - 1) * 50 || 0;
514
- var parameters = {
515
- q: params.term,
516
- limit: 50,
517
- offset: offset,
518
- };
519
- return parameters;
520
- },
521
-
522
- processResults: function (data) {
523
- var results = $.map(data.results, function (obj) {
524
- // If tag contains space add double quotes
525
- if (/\s/.test(obj.name))
526
- obj.name = '"' + obj.name + '"'
527
-
528
- return {
529
- id: obj.name,
530
- text: obj.name
531
- }
532
- });
533
-
534
- // Check if there are more results to page
535
- var page = data.next !== null;
536
- return {
537
- results: results,
538
- pagination: {
539
- more: page
540
- }
541
- };
542
- }
543
- }
544
- });
545
- this_tag_field.closest('form').submit(function(event){
546
- // django-taggit can only accept a single comma seperated string value
547
- // TODO(bryan): the element find here should just be event.target
548
- var value = $('#id_tags.tagfield').val();
549
- if (value.length > 0){
550
- var final_tags = value.join(', ');
551
- $('#id_tags.tagfield').val(null).trigger('change');
552
- var option = new Option(final_tags, final_tags, true, true);
553
- $('#id_tags.tagfield').append(option).trigger('change');
554
- }
555
- });
556
- }
557
-
558
218
  function initializeVLANModeSelection(context){
559
219
  this_context = $(context);
560
220
  if( this_context.find('select#id_mode').length > 0 ) { // Not certain for the length check here as if none is find it should not apply the onChange
@@ -588,291 +248,6 @@ function initializeVLANModeSelection(context){
588
248
  }
589
249
  }
590
250
 
591
- function initializeMultiValueChar(context, dropdownParent=null){
592
- this_context = $(context);
593
- this_context.find('.nautobot-select2-multi-value-char').select2({
594
- allowClear: true,
595
- tags: true,
596
- theme: "bootstrap",
597
- placeholder: "---------",
598
- multiple: true,
599
- dropdownParent: dropdownParent,
600
- width: "off",
601
- "language": {
602
- "noResults": function(){
603
- return "Type something to add it as an option";
604
- }
605
- },
606
- });
607
- }
608
-
609
- function initializeDynamicFilterForm(context){
610
- this_context = $(context);
611
-
612
- function initializeDynamicFilterSelect(element) {
613
- // On change of a select field in default filter form
614
- // Replicate that change into dynamic filter form and vice-versa
615
- $(element).on("change", function (e){
616
- let field_name = $(this).attr("name");
617
- let field_values = $(this).select2('data');
618
- let form_id = $(this).parents("form").attr("id");
619
-
620
- let default_filters_field_dom = $(`#default-filter form select[name=${field_name}]`);
621
- let advanced_filters_field_dom = $(`#advanced-filter #filterform-table tbody tr td select[name=${field_name}]`);
622
-
623
- // Only apply logic if fields with same name attr are on both advanced and default filter form
624
- if(default_filters_field_dom.length && advanced_filters_field_dom.length){
625
- let default_filters_field_ids = default_filters_field_dom.select2('data').map(data => data["id"]);
626
- let advanced_filters_field_ids = advanced_filters_field_dom.select2('data').map(data => data["id"]);
627
-
628
- // Only change field value if both fields do not have equal values
629
- if (JSON.stringify(advanced_filters_field_ids) !== JSON.stringify(default_filters_field_ids)){
630
- if(form_id === "dynamic-filter-form"){
631
- changeSelect2FieldValue(default_filters_field_dom, field_values);
632
- }
633
- else {
634
- changeSelect2FieldValue(advanced_filters_field_dom, field_values);
635
- }
636
- }
637
- }
638
- });
639
- }
640
-
641
- function initializeDynamicFilterInput(element) {
642
- // On change of input field in default filter form
643
- // Replicate that change into dynamic filter form and vice-versa
644
- $(element).on("change", function (e){
645
- let field_name = $(this).attr("name");
646
- let field_value = $(this).val();
647
- let form_id = $(this).parents("form").attr("id");
648
- let default_filters_field_dom = $(`#default-filter form input[name=${field_name}]`);
649
- let advanced_filters_field_dom = $(`#advanced-filter #filterform-table tbody tr td input[name=${field_name}]`);
650
-
651
- // Only apply logic if fields with same name attr are on both advanced and default filter form
652
- if(default_filters_field_dom.length && advanced_filters_field_dom.length){
653
- // Only change field value if both fields do not have equal values
654
- if (default_filters_field_dom.val() !== advanced_filters_field_dom.val()){
655
- if(form_id === "dynamic-filter-form"){
656
- default_filters_field_dom.val(field_value);
657
- }
658
- else {
659
- advanced_filters_field_dom.val(field_value);
660
- }
661
- }
662
- }
663
- })
664
- }
665
-
666
- // Dynamic filter form
667
- this_context.find(".lookup_type-select").bind("change", function(){
668
- let parent_element = $(this).parents("tr")
669
- let lookup_type = parent_element.find(".lookup_type-select")
670
- let lookup_type_val = lookup_type.val()
671
- let contenttype = lookup_type.attr("data-contenttype")
672
- let lookup_value_element = parent_element.find(".lookup_value-input")
673
-
674
- if(lookup_type_val){
675
- $.ajax({
676
- url: `/api/ui/core/filterset-fields/lookup-value-dom-element/?field_name=${lookup_type_val}&content_type=${contenttype}`,
677
- async: true,
678
- headers: {'Accept': '*/*'},
679
- type: 'GET',
680
- }).done(function (response) {
681
- newEl = $(response)
682
- newEl.addClass("lookup_value-input")
683
- replaceEl(lookup_value_element, newEl)
684
- if (newEl.prop("tagName") == "SELECT") {
685
- initializeDynamicFilterSelect(newEl);
686
- } else {
687
- initializeDynamicFilterInput(newEl);
688
- }
689
- }).fail(function (xhr, status, error) {
690
- // Default to Input:text field if error occurs
691
- createInput(lookup_value_element)
692
- });
693
- }
694
-
695
- })
696
-
697
- // On change of lookup_field or lookup_type field in filter form reset field value
698
- this_context.find(".lookup_field-select, .lookup_type-select").on("change", function(){
699
- let parent_element = $(this).parents("tr")
700
- let lookup_field_element = parent_element.find(".lookup_field-select")
701
- let lookup_type_element = parent_element.find(".lookup_type-select")
702
- let lookup_value_element = parent_element.find(".lookup_value-input")
703
-
704
- if ($(this)[0] == lookup_field_element[0]) {
705
- lookup_type_element.val(null).trigger('change');
706
- }
707
- lookup_value_element.val(null).trigger('change')
708
-
709
- })
710
-
711
- // By default on lookup_value field names are form-\d-lookup_value, thats why
712
- // on page load we change all `lookup_value` name to its relevant `lookup_type` value
713
- this_context.find(".dynamic-filterform").each(function(){
714
- lookup_type_value = $(this).find(".lookup_type-select").val();
715
- lookup_value = $(this).find(".lookup_value-input");
716
- lookup_value.attr("name", lookup_type_value);
717
- })
718
-
719
- // Remove applied filters
720
- this_context.find(".remove-filter-param").on("click", function(){
721
- let query_params = new URLSearchParams(location.search);
722
- if (query_params.has("saved_view")) {
723
- // Need to reverse-engineer the "real" query params from the rendered page
724
- for (let element of document.getElementsByClassName("filter-selection-choice-remove")) {
725
- let key = element.getAttribute("data-field-parent");
726
- let value = element.getAttribute("data-field-value");
727
- if (!query_params.has(key, value)) {
728
- query_params.append(key, value);
729
- }
730
- }
731
- }
732
- let type = $(this).attr("data-field-type");
733
- let field_value = $(this).attr("data-field-value");
734
-
735
- if (type === "parent") {
736
- // Remove all instances of this query param
737
- query_params.delete(field_value);
738
-
739
- } else {
740
- // Remove this specific instance of this query param
741
- let parent = $(this).attr("data-field-parent");
742
- query_params.delete(parent, field_value);
743
- }
744
- if (query_params.has("saved_view")) {
745
- var all_filters_removed = true
746
-
747
- const non_filter_params = ["saved_view", "sort", "per_page", "table_changes_pending", "all_filters_removed", "clear_view"]
748
-
749
- query_params.forEach((value, key) => {
750
- if (!non_filter_params.includes(key)){
751
- all_filters_removed = false
752
- }
753
- })
754
-
755
- if (all_filters_removed && !query_params.has("all_filters_removed")){
756
- query_params.append("all_filters_removed", true);
757
- }
758
- }
759
- location.assign("?" + query_params);
760
- })
761
-
762
- // On submit of filter form
763
- this_context.find("#dynamic-filter-form, #default-filter form").on("submit", function(e){
764
- e.preventDefault()
765
- let dynamic_form = $("#dynamic-filter-form");
766
- dynamic_form.find(`input[name*="form-"], select[name*="form-"]`).removeAttr("name")
767
-
768
- // Append q form field to dynamic filter form via hidden input
769
- let q_field = $('#id_q')
770
- let q_field_phantom = $('<input type="hidden" name="q" />')
771
- q_field_phantom.val(q_field.val())
772
- dynamic_form.append(q_field_phantom);
773
-
774
- // Get the serialized data from the forms and:
775
- // 1) filter out query_params which values are empty e.g ?sam=&dan=2 becomes dan=2
776
- // 2) combine the two forms into a single set of data without duplicate entries
777
- let search_query = new URLSearchParams();
778
- let dynamic_query = new URLSearchParams(new FormData(document.getElementById("dynamic-filter-form")));
779
- const urlParams = new URLSearchParams(window.location.search);
780
- const non_filter_params = ["saved_view", "sort", "per_page", "table_changes_pending", "clear_view"]
781
- urlParams.forEach((value, key) => {
782
- if (non_filter_params.includes(key)){
783
- search_query.append(key, value)
784
- }
785
- })
786
- dynamic_query.forEach((value, key) => { if (value != "") { search_query.append(key, value); }});
787
- // Some list views may lack a default-filter form
788
- let default_query = new URLSearchParams(new FormData(document.getElementById("default-filter")?.firstElementChild));
789
- default_query.forEach((value, key) => {
790
- if (value != "" && !search_query.has(key, value)) { search_query.append(key, value); }
791
- });
792
- $("#FilterForm_modal").modal("hide");
793
-
794
- if (search_query.has("saved_view")) {
795
- var all_filters_removed = true
796
-
797
- const non_filter_params = ["saved_view", "sort", "per_page", "table_changes_pending", "all_filters_removed", "clear_view"]
798
-
799
- search_query.forEach((value, key) => {
800
- if (!non_filter_params.includes(key)){
801
- all_filters_removed = false
802
- }
803
- })
804
-
805
- if (all_filters_removed && !search_query.has("all_filters_removed")){
806
- search_query.append("all_filters_removed", true);
807
- }
808
- }
809
- location.assign("?" + search_query);
810
- })
811
-
812
- // On submit of filter search form
813
- this_context.find("#search-form").on("submit", function(e){
814
- // Since the Dynamic Filter Form will already grab my q field, just have it do a majority of the work.
815
- e.preventDefault()
816
- $("#dynamic-filter-form").submit()
817
- })
818
-
819
- // On clear of filter form
820
- this_context.find("#dynamic-filter-form, #default-filter form").on("reset", function(e){
821
- e.preventDefault()
822
- // make two copies of url params
823
- const urlParams = new URLSearchParams(window.location.search);
824
- const newUrlParams = new URLSearchParams(window.location.search);
825
- // every query string that is non-filter-related
826
- const non_filter_params = ["saved_view", "sort", "per_page", "table_changes_pending", "all_filters_removed", "clear_view"]
827
- for (const [key, value] of urlParams.entries()) {
828
- // remove filter params
829
- if (non_filter_params.includes(key) === false) {
830
- newUrlParams.delete(key, value)
831
- }
832
- }
833
- if (!newUrlParams.has("all_filters_removed")){
834
- newUrlParams.append("all_filters_removed", true)
835
- }
836
- location.assign("?" + newUrlParams.toString())
837
- })
838
-
839
-
840
- // Clear new row values upon creation
841
- this_context.find(".dynamic-filterform-add .add-row").click(function(){
842
- let new_fields_parent_element = $(".dynamic-filterform").last()
843
- let lookup_field_classes = [".lookup_field-select", ".lookup_type-select", ".lookup_value-input"];
844
- lookup_field_classes.forEach(field_class => {
845
- let element = new_fields_parent_element.find(field_class);
846
- element.val(null).trigger('change')
847
- })
848
- // reinitialize jsify_form
849
- initializeDynamicFilterForm($(document));
850
- })
851
-
852
- function changeSelect2FieldValue(dom_element, values){
853
- dom_element.val(null)
854
- values.forEach(function (value){
855
- // Does an element already exist?
856
- if (!dom_element.find("option[value='" + value.id + "']").length) {
857
- let new_option = new Option(value.text, value.id, true, true);
858
- dom_element.append(new_option);
859
- }
860
- })
861
- dom_element.val(values.map(data => data["id"]));
862
- dom_element.trigger('change');
863
- }
864
-
865
- this_context.find("#default-filter form select, #advanced-filter select").each(function() {
866
- initializeDynamicFilterSelect(this);
867
- });
868
-
869
- // On change of input field in default filter form
870
- // Replicate that change into dynamic filter form and vice-versa
871
- this_context.find("#default-filter form input, #advanced-filter input").each(function() {
872
- initializeDynamicFilterInput(this);
873
- });
874
- }
875
-
876
251
  function initializeSortableList(context){
877
252
  this_context = $(context);
878
253
  // Rearrange options within a <select> list
@@ -946,14 +321,20 @@ function initializeImagePreview(context){
946
321
  }
947
322
 
948
323
  function initializeSelectAllForm(context){
949
- this_context = $(context);
950
- this_context.find('#select_all').click(function() {
951
- if ($(this).is(':checked')) {
952
- $('#select_all_box').find('button').prop('disabled', '');
953
- } else {
954
- $('#select_all_box').find('button').prop('disabled', 'disabled');
955
- }
956
- });
324
+ // selectAll is the checkbox that selects all objects
325
+ const selectAll = document.querySelector('#select_all');
326
+ // selectAllBox is the div that contains the form bulk action buttons
327
+ const selectAllBox = document.querySelector('#select_all_box');
328
+
329
+ if (selectAll && selectAllBox) {
330
+ selectAll.addEventListener('click', function () {
331
+ // If the selectAll checkbox is checked, enable all form bulk action buttons
332
+ const isChecked = this.checked;
333
+ selectAllBox.querySelectorAll('button').forEach(button => {
334
+ button.disabled = !isChecked;
335
+ });
336
+ });
337
+ }
957
338
  }
958
339
 
959
340
  function initializeResultPerPageSelection(context){
@@ -963,39 +344,20 @@ function initializeResultPerPageSelection(context){
963
344
  });
964
345
  }
965
346
 
966
- function replaceEl(replaced_el, replacing_el) {
967
- parent = replaced_el.parent()
968
- parent.html(replacing_el)
969
- initializeInputs(parent)
970
- }
971
-
972
347
  function initializeInputs(context) {
973
348
  const this_context = $(context);
974
- initializeStaticChoiceSelection(this_context)
975
349
  initializeCheckboxes(this_context)
976
350
  initializeSlugField(this_context)
977
351
  initializeAutoPopulateField(this_context)
978
352
  initializeFormActionClick(this_context)
979
353
  initializeBulkEditNullification(this_context)
980
- initializeColorPicker(this_context)
981
- initializeDynamicChoiceSelection(this_context)
982
354
  initializeDateTimePicker(this_context)
983
- initializeTags(this_context)
984
355
  initializeVLANModeSelection(this_context)
985
356
  initializeSortableList(this_context)
986
357
  initializeImagePreview(this_context)
987
- initializeDynamicFilterForm(this_context)
988
358
  initializeSelectAllForm(this_context)
989
- initializeMultiValueChar(this_context)
990
359
 
991
- $(this_context).find(".modal").each(function() {
992
- const this_modal = $(this)
993
- initializeStaticChoiceSelection(this_modal, this_modal)
994
- initializeColorPicker(this_modal, this_modal)
995
- initializeDynamicChoiceSelection(this_modal, this_modal)
996
- initializeTags(this_modal, this_modal)
997
- initializeMultiValueChar(this_modal, this_modal)
998
- })
360
+ window.nb.select2.initializeSelect2Fields(this_context)
999
361
  }
1000
362
 
1001
363
  function jsify_form(context) {
@@ -1008,18 +370,6 @@ function jsify_form(context) {
1008
370
  * Input Creators
1009
371
  */
1010
372
 
1011
-
1012
- function createInput(element){
1013
- input_field = `
1014
- <input
1015
- type="text"
1016
- name="${element.attr('name')}"
1017
- class="lookup_value-input form-control"
1018
- id="${element.attr('id')}"
1019
- />`
1020
- replaceEl(element, input_field)
1021
- }
1022
-
1023
373
  function submitOnEnter(event) {
1024
374
  if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
1025
375
  if (!event.repeat) {