nautobot 3.0.0rc1__py3-none-any.whl → 3.0.1__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.
- nautobot/apps/forms.py +8 -0
- nautobot/apps/templatetags.py +231 -0
- nautobot/apps/testing.py +11 -1
- nautobot/apps/ui.py +21 -1
- nautobot/apps/utils.py +26 -1
- nautobot/core/celery/__init__.py +46 -1
- nautobot/core/cli/bootstrap_v3_to_v5.py +185 -44
- nautobot/core/cli/bootstrap_v3_to_v5_changes.yaml +314 -0
- nautobot/core/graphql/generators.py +2 -2
- nautobot/core/jobs/bulk_actions.py +12 -6
- nautobot/core/jobs/cleanup.py +13 -1
- nautobot/core/settings.py +13 -0
- nautobot/core/settings.yaml +22 -0
- nautobot/core/settings_funcs.py +11 -1
- nautobot/core/tables.py +19 -1
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +9 -3
- nautobot/core/templates/generic/object_create.html +1 -1
- nautobot/core/templates/inc/header.html +9 -10
- nautobot/core/templates/login.html +16 -1
- nautobot/core/templates/nautobot_config.py.j2 +14 -1
- nautobot/core/templates/redoc_ui.html +3 -0
- nautobot/core/templatetags/helpers.py +3 -3
- nautobot/core/testing/views.py +3 -1
- nautobot/core/tests/test_graphql.py +13 -0
- nautobot/core/tests/test_jobs.py +118 -0
- nautobot/core/tests/test_views.py +24 -0
- nautobot/core/ui/bulk_buttons.py +2 -3
- nautobot/core/utils/lookup.py +2 -3
- nautobot/core/utils/permissions.py +1 -1
- nautobot/core/views/generic.py +1 -0
- nautobot/core/views/mixins.py +37 -10
- nautobot/core/views/renderers.py +1 -0
- nautobot/core/views/utils.py +3 -3
- nautobot/data_validation/views.py +1 -9
- nautobot/dcim/forms.py +9 -9
- nautobot/dcim/models/devices.py +3 -3
- nautobot/dcim/tables/power.py +3 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +1 -1
- nautobot/dcim/views.py +30 -44
- nautobot/extras/api/views.py +14 -3
- nautobot/extras/choices.py +3 -0
- nautobot/extras/jobs.py +48 -2
- nautobot/extras/migrations/0132_approval_workflow_seed_data.py +127 -0
- nautobot/extras/models/approvals.py +11 -1
- nautobot/extras/models/models.py +19 -0
- nautobot/extras/models/relationships.py +3 -1
- nautobot/extras/tables.py +35 -18
- nautobot/extras/templates/extras/approval_workflow/approve.html +9 -2
- nautobot/extras/templates/extras/approval_workflow/deny.html +9 -3
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +1 -1
- nautobot/extras/templates/extras/customfield_update.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_update.html +2 -2
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +10 -2
- nautobot/extras/templates/extras/inc/job_tiles.html +2 -2
- nautobot/extras/templates/extras/inc/jobresult.html +1 -1
- nautobot/extras/templates/extras/metadatatype_create.html +1 -1
- nautobot/extras/templates/extras/object_approvalworkflow.html +2 -3
- nautobot/extras/templates/extras/secretsgroup_update.html +1 -1
- nautobot/extras/tests/test_api.py +57 -3
- nautobot/extras/tests/test_customfields_filters.py +84 -4
- nautobot/extras/tests/test_views.py +323 -6
- nautobot/extras/views.py +114 -39
- nautobot/ipam/constants.py +2 -2
- nautobot/ipam/tables.py +7 -6
- nautobot/load_balancers/constants.py +6 -0
- nautobot/load_balancers/migrations/0001_initial.py +14 -3
- nautobot/load_balancers/models.py +5 -4
- nautobot/load_balancers/tables.py +5 -0
- nautobot/project-static/dist/css/nautobot.css +1 -1
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/dist/js/graphql-libraries.js +1 -1
- nautobot/project-static/dist/js/graphql-libraries.js.map +1 -1
- nautobot/project-static/dist/js/libraries.js +1 -1
- nautobot/project-static/dist/js/libraries.js.LICENSE.txt +38 -2
- nautobot/project-static/dist/js/libraries.js.map +1 -1
- nautobot/project-static/dist/js/nautobot-graphiql.js +1 -1
- nautobot/project-static/dist/js/nautobot-graphiql.js.map +1 -1
- nautobot/project-static/dist/js/nautobot.js +1 -1
- nautobot/project-static/dist/js/nautobot.js.map +1 -1
- nautobot/project-static/img/dark-theme.png +0 -0
- nautobot/project-static/img/light-theme.png +0 -0
- nautobot/project-static/img/system-theme.png +0 -0
- nautobot/project-static/js/forms.js +1 -85
- nautobot/tenancy/tables.py +3 -2
- nautobot/tenancy/views.py +3 -2
- nautobot/ui/package-lock.json +553 -569
- nautobot/ui/package.json +10 -10
- nautobot/ui/src/js/checkbox.js +132 -0
- nautobot/ui/src/js/nautobot.js +6 -0
- nautobot/ui/src/js/select2.js +69 -73
- nautobot/ui/src/js/theme.js +129 -39
- nautobot/ui/src/scss/nautobot.scss +11 -1
- nautobot/vpn/templates/vpn/vpnprofile_create.html +2 -2
- nautobot/wireless/filters.py +15 -1
- nautobot/wireless/tables.py +18 -14
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +1 -1
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/METADATA +2 -2
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/RECORD +103 -98
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/WHEEL +0 -0
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/entry_points.txt +0 -0
|
@@ -3,6 +3,8 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
5
|
|
|
6
|
+
import yaml
|
|
7
|
+
|
|
6
8
|
from .migrate_deprecated_templates import replace_deprecated_templates
|
|
7
9
|
|
|
8
10
|
logger = logging.getLogger(__name__)
|
|
@@ -547,6 +549,97 @@ def _fix_breadcrumbs_block(html_string: str, stats: dict) -> str:
|
|
|
547
549
|
return block_pattern.sub(process_match, html_string)
|
|
548
550
|
|
|
549
551
|
|
|
552
|
+
# --- Grid Breakpoints Resize Function ---
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def _resize_grid_breakpoints(html_string: str, class_combinations: list[str], stats: dict, file_path: str) -> str:
|
|
556
|
+
"""
|
|
557
|
+
Resizes grid breakpoints in `col-*` and `offset-*` classes one step up. Uses given `class_combinations` for known
|
|
558
|
+
class pattern replacements and otherwise does generic xs → sm and md → lg breakpoint resize. In case class list
|
|
559
|
+
contains grid breakpoints other than xs and md, flags it for manual review.
|
|
560
|
+
"""
|
|
561
|
+
# Define the breakpoint mapping
|
|
562
|
+
breakpoint_map = {"xs": "sm", "sm": "md", "md": "lg", "lg": "xl", "xl": "xxl"}
|
|
563
|
+
breakpoint_map_keys = list(breakpoint_map.keys())
|
|
564
|
+
|
|
565
|
+
if "manual_grid_template_lines" not in stats:
|
|
566
|
+
stats["manual_grid_template_lines"] = []
|
|
567
|
+
|
|
568
|
+
def create_grid_class_regex(breakpoints=breakpoint_map_keys): # pylint: disable=dangerous-default-value
|
|
569
|
+
# Create regex matching Bootstrap grid classes, i.e. `col-*` and `offset-*`, within given breakpoints.
|
|
570
|
+
return re.compile(rf"\b(col|offset)-({'|'.join(breakpoints)})([a-zA-Z0-9-]*)")
|
|
571
|
+
|
|
572
|
+
# Resize all given grid `breakpoints` in `string` according to defined `breakpoint_map`
|
|
573
|
+
def resize_breakpoints(string, breakpoints=breakpoint_map_keys, count_stats=False): # pylint: disable=dangerous-default-value
|
|
574
|
+
def regex_repl(match):
|
|
575
|
+
new_breakpoint = breakpoint_map[match.group(2)]
|
|
576
|
+
if count_stats:
|
|
577
|
+
stats["grid_breakpoints"] += 1
|
|
578
|
+
return f"{match.group(1)}-{new_breakpoint}{match.group(3)}"
|
|
579
|
+
|
|
580
|
+
# Replace with regex, e.g., col-xs-12 → col-sm-12
|
|
581
|
+
regex = create_grid_class_regex(breakpoints)
|
|
582
|
+
return regex.sub(regex_repl, string)
|
|
583
|
+
|
|
584
|
+
# Resize given `class_combinations` and create an additional joint array from the two. This is required to determine
|
|
585
|
+
# whether a known class combination is present in certain element class list and handle one of the following cases:
|
|
586
|
+
# 1. No, but identified grid breakpoints other than xs and md: flag for manual review.
|
|
587
|
+
# 2. No, and only xs and md grid breakpoints found: generic xs → sm and md → lg replacement.
|
|
588
|
+
# 3. Yes, but has not been resized yet: resize with proper combination.
|
|
589
|
+
# 4. Yes, and has already been resized: do nothing.
|
|
590
|
+
resized_class_combinations = [resize_breakpoints(class_combination) for class_combination in class_combinations]
|
|
591
|
+
known_class_combinations = [*class_combinations, *resized_class_combinations]
|
|
592
|
+
|
|
593
|
+
def grid_breakpoints_replacer(match):
|
|
594
|
+
classes = match.group(1)
|
|
595
|
+
# Remove Django template tag blocks, variables and comments and split individual classes into separate strings.
|
|
596
|
+
raw_classes = re.compile(r"{{?((?!{|}).)*}}?").sub(" ", classes).split()
|
|
597
|
+
# Filter out all non-grid classes, keep only `col-*` and `offset-*`.
|
|
598
|
+
grid_class_regex = create_grid_class_regex()
|
|
599
|
+
grid_classes = [cls for cls in raw_classes if grid_class_regex.search(cls)]
|
|
600
|
+
|
|
601
|
+
# Check whether given class list consists of any of the known class combinations.
|
|
602
|
+
known_class_combination = None
|
|
603
|
+
for class_combination in known_class_combinations:
|
|
604
|
+
# Look for an exact match, when all classes from given combination are included in element classes and vice versa.
|
|
605
|
+
if all(cls in classes for cls in class_combination.split()) and all(
|
|
606
|
+
grid_class in class_combination for grid_class in grid_classes
|
|
607
|
+
):
|
|
608
|
+
known_class_combination = class_combination
|
|
609
|
+
break
|
|
610
|
+
|
|
611
|
+
if known_class_combination is None:
|
|
612
|
+
# Class combination has not been found.
|
|
613
|
+
if any("xs" not in grid_class and "md" not in grid_class for grid_class in grid_classes):
|
|
614
|
+
# Class list contains grid breakpoints other than xs and md, require manual review.
|
|
615
|
+
linenum = match.string.count("\n", 0, match.start()) + 1
|
|
616
|
+
stats["manual_grid_template_lines"].append(
|
|
617
|
+
f"{file_path}:{linenum} - Please review manually '{match.group(0)}'"
|
|
618
|
+
)
|
|
619
|
+
else:
|
|
620
|
+
# Class list contains only xs and md grid breakpoints, do generic xs → sm and md → lg replacement
|
|
621
|
+
return f'class="{resize_breakpoints(classes, breakpoints=["xs", "md"], count_stats=True)}"'
|
|
622
|
+
|
|
623
|
+
elif known_class_combination not in resized_class_combinations:
|
|
624
|
+
# Class combination has been found, but has not been resized yet: resize with proper combination.
|
|
625
|
+
resized_classes = resized_class_combinations[class_combinations.index(known_class_combination)].split()
|
|
626
|
+
|
|
627
|
+
def class_replacer(m):
|
|
628
|
+
current_class = m.group(0)
|
|
629
|
+
stats["grid_breakpoints"] += 1
|
|
630
|
+
return resized_classes[known_class_combination.split().index(current_class)]
|
|
631
|
+
|
|
632
|
+
# Replace all classes from given combination by mapping them individually to their resized equivalents.
|
|
633
|
+
return f'class="{re.compile("|".join(known_class_combination.split())).sub(class_replacer, classes)}"'
|
|
634
|
+
|
|
635
|
+
# Return unchanged string if conditions above are not satisfied, i.e. do nothing.
|
|
636
|
+
return match.group(0)
|
|
637
|
+
|
|
638
|
+
# Find all `class="..."` matches and execute grid breakpoint replacement on them.
|
|
639
|
+
pattern = re.compile(r'class="([^"]*)"')
|
|
640
|
+
return pattern.sub(grid_breakpoints_replacer, html_string)
|
|
641
|
+
|
|
642
|
+
|
|
550
643
|
# --- Main Conversion Function ---
|
|
551
644
|
|
|
552
645
|
|
|
@@ -564,7 +657,9 @@ def convert_bootstrap_classes(html_input: str, file_path: str) -> tuple[str, dic
|
|
|
564
657
|
"nav_items": 0,
|
|
565
658
|
"dropdown_items": 0,
|
|
566
659
|
"panel_classes": 0,
|
|
660
|
+
"grid_breakpoints": 0,
|
|
567
661
|
"manual_nav_template_lines": [],
|
|
662
|
+
"manual_grid_template_lines": [],
|
|
568
663
|
}
|
|
569
664
|
|
|
570
665
|
# --- Stage 1: Apply rules that work directly on the HTML string (simple string/regex replacements) ---
|
|
@@ -628,6 +723,26 @@ def convert_bootstrap_classes(html_input: str, file_path: str) -> tuple[str, dic
|
|
|
628
723
|
"data-backdrop": "data-bs-backdrop", # Bootstrap 5 uses data-bs-* attributes
|
|
629
724
|
}
|
|
630
725
|
|
|
726
|
+
standard_grid_breakpoint_combinations = [
|
|
727
|
+
"col-md-6 col-sm-6 col-xs-12", # Rack elevation container: nautobot/dcim/templates/dcim/rack_elevation.html:2
|
|
728
|
+
"col-sm-3 col-md-2 col-md-offset-1", # Legacy user page nav pills container: nautobot/users/templates/users/base.html:10
|
|
729
|
+
"col-sm-3 col-md-2 offset-md-1",
|
|
730
|
+
"col-sm-4 col-sm-offset-4", # Selected centered panel containers, e.g. on 404 and 500 error pages and legacy login page: nautobot/core/templates/login.html:54
|
|
731
|
+
"col-sm-4 offset-sm-4",
|
|
732
|
+
"col-sm-4 col-md-3", # Legacy header search form container: nautobot/core/templates/generic/object_list.html:32
|
|
733
|
+
"col-sm-8 col-md-9 col-sm-12 col-md-12", # Legacy breadcrumbs container variation on change list page: nautobot/core/templates/admin/change_list.html:33
|
|
734
|
+
"col-sm-8 col-md-9 col-md-12", # Legacy breadcrumbs container variation on generic object list view page: nautobot/core/templates/generic/object_list.html:13
|
|
735
|
+
"col-sm-8 col-md-9", # Legacy breadcrumbs container: nautobot/core/templates/generic/object_retrieve.html:16
|
|
736
|
+
"col-sm-9 col-md-8", # Legacy user page content container: nautobot/users/templates/users/base.html:31
|
|
737
|
+
"col-md-5 col-sm-12", # Cable trace form left-hand side container: nautobot/dcim/templates/dcim/cable_trace.html:10
|
|
738
|
+
"col-md-7 col-sm-12", # Cable trace form right-hand side container: nautobot/dcim/templates/dcim/cable_trace.html:86
|
|
739
|
+
"col-lg-6 col-md-6", # Jinja template/rendered template panel containers: nautobot/core/templates/utilities/render_jinja2.html:29
|
|
740
|
+
"col-md-4 col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1", # Standard centered form container variation on generic object bulk update page: nautobot/core/templates/generic/object_bulk_update.html:39
|
|
741
|
+
"col-md-4 col-lg-8 col-lg-offset-2 col-md-10 offset-md-1",
|
|
742
|
+
"col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1", # Standard centered form container, e.g. on generic object create page: nautobot/core/templates/generic/object_create.html:12
|
|
743
|
+
"col-lg-8 offset-lg-2 col-md-10 offset-md-1",
|
|
744
|
+
]
|
|
745
|
+
|
|
631
746
|
current_html = _replace_attributes(current_html, attribute_replacements, stats)
|
|
632
747
|
current_html = _replace_classes(current_html, class_replacements, stats, file_path=file_path)
|
|
633
748
|
current_html = _fix_extra_breadcrumbs_block(current_html, stats)
|
|
@@ -642,6 +757,7 @@ def convert_bootstrap_classes(html_input: str, file_path: str) -> tuple[str, dic
|
|
|
642
757
|
current_html = _convert_hover_copy_buttons(current_html, stats)
|
|
643
758
|
current_html = _fix_nav_tabs_items(current_html, stats, file_path=file_path)
|
|
644
759
|
current_html = _fix_dropdown_items(current_html, stats, file_path=file_path)
|
|
760
|
+
current_html = _resize_grid_breakpoints(current_html, standard_grid_breakpoint_combinations, stats, file_path)
|
|
645
761
|
|
|
646
762
|
return current_html, stats
|
|
647
763
|
|
|
@@ -649,11 +765,10 @@ def convert_bootstrap_classes(html_input: str, file_path: str) -> tuple[str, dic
|
|
|
649
765
|
# --- File Processing ---
|
|
650
766
|
|
|
651
767
|
|
|
652
|
-
def fix_html_files_in_directory(directory: str,
|
|
768
|
+
def fix_html_files_in_directory(directory: str, dry_run=False, skip_templates=False) -> None:
|
|
653
769
|
"""
|
|
654
770
|
Recursively finds all .html files in the given directory, applies convert_bootstrap_classes,
|
|
655
|
-
and overwrites each file with the fixed content.
|
|
656
|
-
breakpoints (This should only be done once.).
|
|
771
|
+
and overwrites each file with the fixed content.
|
|
657
772
|
"""
|
|
658
773
|
|
|
659
774
|
totals = {
|
|
@@ -665,11 +780,9 @@ def fix_html_files_in_directory(directory: str, resize=False, dry_run=False, ski
|
|
|
665
780
|
"nav_items",
|
|
666
781
|
"dropdown_items",
|
|
667
782
|
"panel_classes",
|
|
668
|
-
"
|
|
783
|
+
"grid_breakpoints",
|
|
669
784
|
]
|
|
670
785
|
}
|
|
671
|
-
# Breakpoints that are not xs do not count as failures in djlint, so we keep a separate counter
|
|
672
|
-
resizing_other = 0
|
|
673
786
|
|
|
674
787
|
if not os.path.exists(directory):
|
|
675
788
|
raise FileNotFoundError(directory)
|
|
@@ -680,9 +793,6 @@ def fix_html_files_in_directory(directory: str, resize=False, dry_run=False, ski
|
|
|
680
793
|
else:
|
|
681
794
|
only_filename = None
|
|
682
795
|
|
|
683
|
-
# Define the breakpoint mapping
|
|
684
|
-
breakpoint_map = {"xs": "sm", "sm": "md", "md": "lg", "lg": "xl", "xl": "xxl"}
|
|
685
|
-
|
|
686
796
|
for root, _, files in os.walk(directory):
|
|
687
797
|
for filename in files:
|
|
688
798
|
if only_filename and only_filename != filename:
|
|
@@ -695,29 +805,6 @@ def fix_html_files_in_directory(directory: str, resize=False, dry_run=False, ski
|
|
|
695
805
|
|
|
696
806
|
content = original_content
|
|
697
807
|
|
|
698
|
-
if resize:
|
|
699
|
-
# If resize is True, we only change the breakpoints
|
|
700
|
-
# This is a one-time operation to adjust the breakpoints.
|
|
701
|
-
logger.info("Resizing Breakpoints: %s", file_path)
|
|
702
|
-
|
|
703
|
-
resizing_other = 0
|
|
704
|
-
|
|
705
|
-
# Iterate from the highest breakpoint to the lowest
|
|
706
|
-
for bkpt in ["xl", "lg", "md", "sm", "xs"]:
|
|
707
|
-
# Replace with regex, e.g., col-xs-12 → col-sm-12
|
|
708
|
-
regex = re.compile(rf"(\bcol-{bkpt})([a-zA-Z0-9-]*)")
|
|
709
|
-
|
|
710
|
-
def regex_repl(m, captured_bkpt=bkpt):
|
|
711
|
-
nonlocal resizing_other
|
|
712
|
-
if captured_bkpt == "xs":
|
|
713
|
-
totals["resizing_xs"] += 1
|
|
714
|
-
else:
|
|
715
|
-
resizing_other += 1
|
|
716
|
-
new_bkpt = breakpoint_map[captured_bkpt]
|
|
717
|
-
return f"col-{new_bkpt}{m.group(2)}"
|
|
718
|
-
|
|
719
|
-
content = regex.sub(regex_repl, content)
|
|
720
|
-
|
|
721
808
|
fixed_content, stats = convert_bootstrap_classes(content, file_path=file_path)
|
|
722
809
|
|
|
723
810
|
if dry_run:
|
|
@@ -741,12 +828,18 @@ def fix_html_files_in_directory(directory: str, resize=False, dry_run=False, ski
|
|
|
741
828
|
print(f"{stats['dropdown_items']} dropdown-items, ", end="")
|
|
742
829
|
if stats["panel_classes"]:
|
|
743
830
|
print(f"{stats['panel_classes']} panel replacements, ", end="")
|
|
831
|
+
if stats["grid_breakpoints"]:
|
|
832
|
+
print(f"{stats['grid_breakpoints']} grid breakpoint replacements, ", end="")
|
|
744
833
|
print()
|
|
745
834
|
|
|
746
835
|
if stats.get("manual_nav_template_lines"):
|
|
747
836
|
print(" !!! Manual review needed for nav-item fixes at:")
|
|
748
837
|
for line in stats["manual_nav_template_lines"]:
|
|
749
838
|
print(f" - {line}")
|
|
839
|
+
if stats.get("manual_grid_template_lines"):
|
|
840
|
+
print(" !!! Manual review needed for non-standard grid breakpoints at:")
|
|
841
|
+
for line in stats["manual_grid_template_lines"]:
|
|
842
|
+
print(f" - {line}")
|
|
750
843
|
for k, v in stats.items():
|
|
751
844
|
if k in totals:
|
|
752
845
|
totals[k] += v
|
|
@@ -763,21 +856,63 @@ def fix_html_files_in_directory(directory: str, resize=False, dry_run=False, ski
|
|
|
763
856
|
print(f"- <li> in <ul.nav-tabs>: {totals['nav_items']}")
|
|
764
857
|
print(f"- <a> in <ul.dropdown-menu>: {totals['dropdown_items']}")
|
|
765
858
|
print(f"- Panel class replacements: {totals['panel_classes']}")
|
|
766
|
-
print(f"-
|
|
767
|
-
print("-------------------------------------")
|
|
768
|
-
print(f"- Resizing other breakpoints: {resizing_other}")
|
|
859
|
+
print(f"- Grid breakpoint resizes: {totals['grid_breakpoints']}")
|
|
769
860
|
print("-------------------------------------")
|
|
770
861
|
print(f"- Deprecated templates replaced: {templates_replaced}")
|
|
771
862
|
|
|
772
863
|
|
|
864
|
+
def check_python_files_for_legacy_html(directory: str):
|
|
865
|
+
exclude_dirs = [
|
|
866
|
+
"__pycache__",
|
|
867
|
+
"node_modules",
|
|
868
|
+
]
|
|
869
|
+
exclude_files = [
|
|
870
|
+
"bootstrap_v3_to_v5.py",
|
|
871
|
+
]
|
|
872
|
+
|
|
873
|
+
with open(os.path.join(os.path.dirname(__file__), "bootstrap_v3_to_v5_changes.yaml")) as yaml_file:
|
|
874
|
+
try:
|
|
875
|
+
bootstrap_v3_to_v5_changes = yaml.safe_load(yaml_file)
|
|
876
|
+
except yaml.YAMLError:
|
|
877
|
+
print("`bootstrap_v3_to_v5_changes.yaml` file is corrupted.")
|
|
878
|
+
return 1
|
|
879
|
+
|
|
880
|
+
def has_multiline_pattern(change):
|
|
881
|
+
return "(?s)" in change["Search Regex"][1:-2]
|
|
882
|
+
|
|
883
|
+
multiline_pattern_changes = [change for change in bootstrap_v3_to_v5_changes if has_multiline_pattern(change)]
|
|
884
|
+
standard_pattern_changes = [change for change in bootstrap_v3_to_v5_changes if not has_multiline_pattern(change)]
|
|
885
|
+
|
|
886
|
+
matches = 0
|
|
887
|
+
for dirpath, dirnames, filenames in os.walk(directory):
|
|
888
|
+
for filename in filenames:
|
|
889
|
+
if filename in exclude_files or not filename.endswith(".py"):
|
|
890
|
+
continue
|
|
891
|
+
with open(os.path.join(dirpath, filename), "rt") as fh:
|
|
892
|
+
contents = fh.readlines()
|
|
893
|
+
full_contents = "".join(contents)
|
|
894
|
+
|
|
895
|
+
for linenum, line in enumerate(contents, start=1):
|
|
896
|
+
for change in standard_pattern_changes:
|
|
897
|
+
if re.search(change["Search Regex"][1:-2], line):
|
|
898
|
+
print(f"{os.path.join(dirpath, filename)}({linenum}):\t{line}\t:\t{change['Bootstrap v5']}")
|
|
899
|
+
matches += 1
|
|
900
|
+
for change in multiline_pattern_changes:
|
|
901
|
+
multiline_matches = re.finditer(change["Search Regex"][1:-2], full_contents)
|
|
902
|
+
for multiline_match in multiline_matches:
|
|
903
|
+
linenum = multiline_match.string.count("\n", 0, multiline_match.start()) + 1
|
|
904
|
+
substring = multiline_match.string[multiline_match.start() : multiline_match.end()]
|
|
905
|
+
print(f"{os.path.join(dirpath, filename)}({linenum}):\t{substring}\n\t:\t{change['Bootstrap v5']}")
|
|
906
|
+
matches += 1
|
|
907
|
+
for exclude_dir in exclude_dirs:
|
|
908
|
+
if exclude_dir in dirnames:
|
|
909
|
+
dirnames.remove(exclude_dir)
|
|
910
|
+
|
|
911
|
+
return matches
|
|
912
|
+
|
|
913
|
+
|
|
773
914
|
def main():
|
|
774
915
|
parser = argparse.ArgumentParser(description="Bootstrap 3 to 5 HTML fixer.")
|
|
775
|
-
parser.add_argument(
|
|
776
|
-
"-r",
|
|
777
|
-
"--resize",
|
|
778
|
-
action="store_true",
|
|
779
|
-
help="Change column breakpoints to be one level higher, such as 'col-xs-*' to 'col-sm-*'",
|
|
780
|
-
)
|
|
781
916
|
parser.add_argument(
|
|
782
917
|
"-d",
|
|
783
918
|
"--dry-run",
|
|
@@ -788,11 +923,17 @@ def main():
|
|
|
788
923
|
parser.add_argument(
|
|
789
924
|
"-st", "--skip-template-replacement", action="store_true", help="Skip replacing deprecated templates."
|
|
790
925
|
)
|
|
926
|
+
parser.add_argument("-p", "--check-python-files", action="store_true", help="Check Python files for legacy HTML.")
|
|
927
|
+
parser.add_argument("--no-fix-html-templates", action="store_true", help="Do not fix HTML template files.")
|
|
791
928
|
args = parser.parse_args()
|
|
792
929
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
930
|
+
exit_code = 0
|
|
931
|
+
if args.check_python_files:
|
|
932
|
+
exit_code = check_python_files_for_legacy_html(args.path)
|
|
933
|
+
if not args.no_fix_html_templates:
|
|
934
|
+
fix_html_files_in_directory(args.path, dry_run=args.dry_run, skip_templates=args.skip_template_replacement)
|
|
935
|
+
|
|
936
|
+
return exit_code
|
|
796
937
|
|
|
797
938
|
|
|
798
939
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
- "Bootstrap v3": btn-default
|
|
2
|
+
"Bootstrap v5": btn-secondary
|
|
3
|
+
"Search Regex": |
|
|
4
|
+
`("|')([^"']*)(?<=\s|"|')btn-default(?=\s|"|')([^"']*)("|')`
|
|
5
|
+
|
|
6
|
+
- "Bootstrap v3": btn-lg
|
|
7
|
+
"Bootstrap v5": btn
|
|
8
|
+
"Search Regex": |
|
|
9
|
+
`("|')([^"']*)(?<=\s|"|')btn-lg(?=\s|"|')([^"']*)("|')`
|
|
10
|
+
|
|
11
|
+
- "Bootstrap v3": btn-xs
|
|
12
|
+
"Bootstrap v5": btn-sm
|
|
13
|
+
"Search Regex": |
|
|
14
|
+
`("|')([^"']*)(?<=\s|"|')btn-xs(?=\s|"|')([^"']*)("|')`
|
|
15
|
+
|
|
16
|
+
- "Bootstrap v3": caret
|
|
17
|
+
"Bootstrap v5": mdi mdi-chevron-down
|
|
18
|
+
"Search Regex": |
|
|
19
|
+
`("|')([^"']*)(?<=\s|"|')caret(?=\s|"|')([^"']*)("|')`
|
|
20
|
+
|
|
21
|
+
- "Bootstrap v3": center-block
|
|
22
|
+
"Bootstrap v5": d-block mx-auto
|
|
23
|
+
"Search Regex": |
|
|
24
|
+
`("|')([^"']*)(?<=\s|"|')center-block(?=\s|"|')([^"']*)("|')`
|
|
25
|
+
|
|
26
|
+
- "Bootstrap v3": checkbox
|
|
27
|
+
"Bootstrap v5": form-check
|
|
28
|
+
"Search Regex": |
|
|
29
|
+
`(?<!type=)("|')([^"']*)(?<=\s|"|')checkbox(?=\s|"|')([^"']*)("|')`
|
|
30
|
+
|
|
31
|
+
- "Bootstrap v3": checkbox-inline
|
|
32
|
+
"Bootstrap v5": form-check-input
|
|
33
|
+
"Search Regex": |
|
|
34
|
+
`("|')([^"']*)(?<=\s|"|')checkbox-inline(?=\s|"|')([^"']*)("|')`
|
|
35
|
+
|
|
36
|
+
- "Bootstrap v3": close
|
|
37
|
+
"Bootstrap v5": btn-close
|
|
38
|
+
"Search Regex": |
|
|
39
|
+
`(?<=class=)("|')([^"']*)(?<=\s|"|')close(?=\s|"|')([^"']*)("|')`
|
|
40
|
+
|
|
41
|
+
- "Bootstrap v3": col-*-offset-*
|
|
42
|
+
"Bootstrap v5": offset-*-*
|
|
43
|
+
"Search Regex": |
|
|
44
|
+
`("|')([^"']*)(?<=\s|"|')col-(xs|sm|md|lg|x?xl)-offset-\d{1,2}(?=\s|"|')([^"']*)("|')`
|
|
45
|
+
|
|
46
|
+
- "Bootstrap v3": control-label
|
|
47
|
+
"Bootstrap v5": col-form-label
|
|
48
|
+
"Search Regex": |
|
|
49
|
+
`("|')([^"']*)(?<=\s|"|')control-label(?=\s|"|')([^"']*)("|')`
|
|
50
|
+
|
|
51
|
+
- "Bootstrap v3": dropdown-menu-left
|
|
52
|
+
"Bootstrap v5": dropdown-menu-start
|
|
53
|
+
"Search Regex": |
|
|
54
|
+
`("|')([^"']*)(?<=\s|"|')dropdown-menu-left(?=\s|"|')([^"']*)("|')`
|
|
55
|
+
|
|
56
|
+
- "Bootstrap v3": dropdown-menu-right
|
|
57
|
+
"Bootstrap v5": dropdown-menu-end
|
|
58
|
+
"Search Regex": |
|
|
59
|
+
`("|')([^"']*)(?<=\s|"|')dropdown-menu-right(?=\s|"|')([^"']*)("|')`
|
|
60
|
+
|
|
61
|
+
- "Bootstrap v3": form-control-static
|
|
62
|
+
"Bootstrap v5": form-control-plaintext
|
|
63
|
+
"Search Regex": |
|
|
64
|
+
`("|')([^"']*)(?<=\s|"|')form-control-static(?=\s|"|')([^"']*)("|')`
|
|
65
|
+
|
|
66
|
+
- "Bootstrap v3": form-group
|
|
67
|
+
"Bootstrap v5": mb-10 d-flex justify-content-center
|
|
68
|
+
"Search Regex": |
|
|
69
|
+
`("|')([^"']*)(?<=\s|"|')form-group(?=\s|"|')([^"']*)("|')`
|
|
70
|
+
|
|
71
|
+
- "Bootstrap v3": help-block
|
|
72
|
+
"Bootstrap v5": form-text
|
|
73
|
+
"Search Regex": |
|
|
74
|
+
`("|')([^"']*)(?<=\s|"|')help-block(?=\s|"|')([^"']*)("|')`
|
|
75
|
+
|
|
76
|
+
- "Bootstrap v3": hidden
|
|
77
|
+
"Bootstrap v5": d-none
|
|
78
|
+
"Search Regex": |
|
|
79
|
+
`(?<!type=)("|')([^"']*)(?<=\s|"|')hidden(?=\s|"|')([^"']*)("|')`
|
|
80
|
+
|
|
81
|
+
- "Bootstrap v3": label
|
|
82
|
+
"Bootstrap v5": badge
|
|
83
|
+
"Search Regex": |
|
|
84
|
+
`(?<=class=)("|')([^"']*)(?<=\s|"|')label(?=\s|"|')([^"']*)("|')`
|
|
85
|
+
|
|
86
|
+
- "Bootstrap v3": label-default
|
|
87
|
+
"Bootstrap v5": bg-default
|
|
88
|
+
"Search Regex": |
|
|
89
|
+
`("|')([^"']*)(?<=\s|"|')label-default(?=\s|"|')([^"']*)("|')`
|
|
90
|
+
|
|
91
|
+
- "Bootstrap v3": label-danger
|
|
92
|
+
"Bootstrap v5": bg-danger
|
|
93
|
+
"Search Regex": |
|
|
94
|
+
`("|')([^"']*)(?<=\s|"|')label-danger(?=\s|"|')([^"']*)("|')`
|
|
95
|
+
|
|
96
|
+
- "Bootstrap v3": label-info
|
|
97
|
+
"Bootstrap v5": bg-info
|
|
98
|
+
"Search Regex": |
|
|
99
|
+
`("|')([^"']*)(?<=\s|"|')label-info(?=\s|"|')([^"']*)("|')`
|
|
100
|
+
|
|
101
|
+
- "Bootstrap v3": label-primary
|
|
102
|
+
"Bootstrap v5": bg-primary
|
|
103
|
+
"Search Regex": |
|
|
104
|
+
`("|')([^"']*)(?<=\s|"|')label-primary(?=\s|"|')([^"']*)("|')`
|
|
105
|
+
|
|
106
|
+
- "Bootstrap v3": label-success
|
|
107
|
+
"Bootstrap v5": bg-success
|
|
108
|
+
"Search Regex": |
|
|
109
|
+
`("|')([^"']*)(?<=\s|"|')label-success(?=\s|"|')([^"']*)("|')`
|
|
110
|
+
|
|
111
|
+
- "Bootstrap v3": label-transparent
|
|
112
|
+
"Bootstrap v5": bg-transparent
|
|
113
|
+
"Search Regex": |
|
|
114
|
+
`("|')([^"']*)(?<=\s|"|')label-transparent(?=\s|"|')([^"']*)("|')`
|
|
115
|
+
|
|
116
|
+
- "Bootstrap v3": label-warning
|
|
117
|
+
"Bootstrap v5": bg-warning
|
|
118
|
+
"Search Regex": |
|
|
119
|
+
`("|')([^"']*)(?<=\s|"|')label-warning(?=\s|"|')([^"']*)("|')`
|
|
120
|
+
|
|
121
|
+
- "Bootstrap v3": noprint
|
|
122
|
+
"Bootstrap v5": d-print-none
|
|
123
|
+
"Search Regex": |
|
|
124
|
+
`("|')([^"']*)(?<=\s|"|')noprint(?=\s|"|')([^"']*)("|')`
|
|
125
|
+
|
|
126
|
+
- "Bootstrap v3": panel
|
|
127
|
+
"Bootstrap v5": card
|
|
128
|
+
"Search Regex": |
|
|
129
|
+
`("|')([^"']*)(?<=\s|"|')panel(?=\s|"|')([^"']*)("|')`
|
|
130
|
+
|
|
131
|
+
- "Bootstrap v3": panel-body
|
|
132
|
+
"Bootstrap v5": card-body
|
|
133
|
+
"Search Regex": |
|
|
134
|
+
`("|')([^"']*)(?<=\s|"|')panel-body(?=\s|"|')([^"']*)("|')`
|
|
135
|
+
|
|
136
|
+
- "Bootstrap v3": panel-footer
|
|
137
|
+
"Bootstrap v5": card-footer
|
|
138
|
+
"Search Regex": |
|
|
139
|
+
`("|')([^"']*)(?<=\s|"|')panel-footer(?=\s|"|')([^"']*)("|')`
|
|
140
|
+
|
|
141
|
+
- "Bootstrap v3": panel-heading
|
|
142
|
+
"Bootstrap v5": card-header
|
|
143
|
+
"Search Regex": |
|
|
144
|
+
`("|')([^"']*)(?<=\s|"|')panel-heading(?=\s|"|')([^"']*)("|')`
|
|
145
|
+
|
|
146
|
+
- "Bootstrap v3": pull-left
|
|
147
|
+
"Bootstrap v5": float-start
|
|
148
|
+
"Search Regex": |
|
|
149
|
+
`("|')([^"']*)(?<=\s|"|')pull-left(?=\s|"|')([^"']*)("|')`
|
|
150
|
+
|
|
151
|
+
- "Bootstrap v3": pull-right
|
|
152
|
+
"Bootstrap v5": float-end
|
|
153
|
+
"Search Regex": |
|
|
154
|
+
`("|')([^"']*)(?<=\s|"|')pull-right(?=\s|"|')([^"']*)("|')`
|
|
155
|
+
|
|
156
|
+
- "Bootstrap v3": sr-only
|
|
157
|
+
"Bootstrap v5": visually-hidden
|
|
158
|
+
"Search Regex": |
|
|
159
|
+
`("|')([^"']*)(?<=\s|"|')sr-only(?=\s|"|')([^"']*)("|')`
|
|
160
|
+
|
|
161
|
+
- "Bootstrap v3": sr-only-focusable
|
|
162
|
+
"Bootstrap v5": visually-hidden-focusable
|
|
163
|
+
"Search Regex": |
|
|
164
|
+
`("|')([^"']*)(?<=\s|"|')sr-only-focusable(?=\s|"|')([^"']*)("|')`
|
|
165
|
+
|
|
166
|
+
- "Bootstrap v3": text-left
|
|
167
|
+
"Bootstrap v5": text-start
|
|
168
|
+
"Search Regex": |
|
|
169
|
+
`("|')([^"']*)(?<=\s|"|')text-left(?=\s|"|')([^"']*)("|')`
|
|
170
|
+
|
|
171
|
+
- "Bootstrap v3": text-muted
|
|
172
|
+
"Bootstrap v5": text-secondary
|
|
173
|
+
"Search Regex": |
|
|
174
|
+
`("|')([^"']*)(?<=\s|"|')text-muted(?=\s|"|')([^"']*)("|')`
|
|
175
|
+
|
|
176
|
+
- "Bootstrap v3": text-right
|
|
177
|
+
"Bootstrap v5": text-end
|
|
178
|
+
"Search Regex": |
|
|
179
|
+
`("|')([^"']*)(?<=\s|"|')text-right(?=\s|"|')([^"']*)("|')`
|
|
180
|
+
|
|
181
|
+
- "Bootstrap v3": accordion-toggle
|
|
182
|
+
"Bootstrap v5": nb-collapse-toggle
|
|
183
|
+
"Search Regex": |
|
|
184
|
+
`("|')([^"']*)(?<=\s|"|')accordion-toggle(?=\s|"|')([^"']*)("|')`
|
|
185
|
+
|
|
186
|
+
- "Bootstrap v3": accordion-toggle-all
|
|
187
|
+
"Bootstrap v5": data-nb-toggle
|
|
188
|
+
"Search Regex": |
|
|
189
|
+
`("|')([^"']*)(?<=\s|"|')accordion-toggle-all(?=\s|"|')([^"']*)("|')`
|
|
190
|
+
|
|
191
|
+
- "Bootstrap v3": banner-bottom
|
|
192
|
+
"Bootstrap v5": nb-banner-bottom
|
|
193
|
+
"Search Regex": |
|
|
194
|
+
`("|')([^"']*)(?<=\s|"|')banner-bottom(?=\s|"|')([^"']*)("|')`
|
|
195
|
+
|
|
196
|
+
- "Bootstrap v3": btn-inline
|
|
197
|
+
"Bootstrap v5": nb-btn-inline-hover
|
|
198
|
+
"Search Regex": |
|
|
199
|
+
`("|')([^"']*)(?<=\s|"|')btn-inline(?=\s|"|')([^"']*)("|')`
|
|
200
|
+
|
|
201
|
+
- "Bootstrap v3": color-block
|
|
202
|
+
"Bootstrap v5": nb-color-block
|
|
203
|
+
"Search Regex": |
|
|
204
|
+
`("|')([^"']*)(?<=\s|"|')color-block(?=\s|"|')([^"']*)("|')`
|
|
205
|
+
|
|
206
|
+
- "Bootstrap v3": description
|
|
207
|
+
"Bootstrap v5": nb-description
|
|
208
|
+
"Search Regex": |
|
|
209
|
+
`(?<=class=)("|')([^"']*)(?<=\s|"|')description(?=\s|"|')([^"']*)("|')`
|
|
210
|
+
|
|
211
|
+
- "Bootstrap v3": editor-container
|
|
212
|
+
"Bootstrap v5": nb-editor-container
|
|
213
|
+
"Search Regex": |
|
|
214
|
+
`("|')([^"']*)(?<=\s|"|')editor-container(?=\s|"|')([^"']*)("|')`
|
|
215
|
+
|
|
216
|
+
- "Bootstrap v3": loading
|
|
217
|
+
"Bootstrap v5": nb-loading
|
|
218
|
+
"Search Regex": |
|
|
219
|
+
`("|')([^"']*)(?<=\s|"|')loading(?=\s|"|')([^"']*)("|')`
|
|
220
|
+
|
|
221
|
+
- "Bootstrap v3": report-stats
|
|
222
|
+
"Bootstrap v5": nb-report-stats
|
|
223
|
+
"Search Regex": |
|
|
224
|
+
`("|')([^"']*)(?<=\s|"|')report-stats(?=\s|"|')([^"']*)("|')`
|
|
225
|
+
|
|
226
|
+
- "Bootstrap v3": required
|
|
227
|
+
"Bootstrap v5": nb-required
|
|
228
|
+
"Search Regex": |
|
|
229
|
+
`("|')([^"']*)(?<=\s|"|')required(?=\s|"|')([^"']*)("|')`
|
|
230
|
+
|
|
231
|
+
- "Bootstrap v3": right-side-panel
|
|
232
|
+
"Bootstrap v5": nb-right-side-panel
|
|
233
|
+
"Search Regex": |
|
|
234
|
+
`("|')([^"']*)(?<=\s|"|')right-side-panel(?=\s|"|')([^"']*)("|')`
|
|
235
|
+
|
|
236
|
+
- "Bootstrap v3": software-image-hierarchy
|
|
237
|
+
"Bootstrap v5": nb-software-image-hierarchy
|
|
238
|
+
"Search Regex": |
|
|
239
|
+
`("|')([^"']*)(?<=\s|"|')software-image-hierarchy(?=\s|"|')([^"']*)("|')`
|
|
240
|
+
|
|
241
|
+
- "Bootstrap v3": style-line
|
|
242
|
+
"Bootstrap v5": nb-style-line
|
|
243
|
+
"Search Regex": |
|
|
244
|
+
`("|')([^"']*)(?<=\s|"|')style-line(?=\s|"|')([^"']*)("|')`
|
|
245
|
+
|
|
246
|
+
- "Bootstrap v3": table-headings
|
|
247
|
+
"Bootstrap v5": nb-table-headings
|
|
248
|
+
"Search Regex": |
|
|
249
|
+
`("|')([^"']*)(?<=\s|"|')table-headings(?=\s|"|')([^"']*)("|')`
|
|
250
|
+
|
|
251
|
+
- "Bootstrap v3": tile
|
|
252
|
+
"Bootstrap v5": nb-tile
|
|
253
|
+
"Search Regex": |
|
|
254
|
+
`("|')([^"']*)(?<=\s|"|')tile(?=\s|"|')([^"']*)("|')`
|
|
255
|
+
|
|
256
|
+
- "Bootstrap v3": tile-description
|
|
257
|
+
"Bootstrap v5": nb-tile-description
|
|
258
|
+
"Search Regex": |
|
|
259
|
+
`("|')([^"']*)(?<=\s|"|')tile-description(?=\s|"|')([^"']*)("|')`
|
|
260
|
+
|
|
261
|
+
- "Bootstrap v3": tile-footer
|
|
262
|
+
"Bootstrap v5": nb-tile-footer
|
|
263
|
+
"Search Regex": |
|
|
264
|
+
`("|')([^"']*)(?<=\s|"|')tile-footer(?=\s|"|')([^"']*)("|')`
|
|
265
|
+
|
|
266
|
+
- "Bootstrap v3": tile-header
|
|
267
|
+
"Bootstrap v5": nb-tile-header
|
|
268
|
+
"Search Regex": |
|
|
269
|
+
`("|')([^"']*)(?<=\s|"|')tile-header(?=\s|"|')([^"']*)("|')`
|
|
270
|
+
|
|
271
|
+
- "Bootstrap v3": tiles
|
|
272
|
+
"Bootstrap v5": nb-tiles
|
|
273
|
+
"Search Regex": |
|
|
274
|
+
`("|')([^"']*)(?<=\s|"|')tiles(?=\s|"|')([^"']*)("|')`
|
|
275
|
+
|
|
276
|
+
- "Bootstrap v3": tree-hierarchy
|
|
277
|
+
"Bootstrap v5": nb-tree-hierarchy
|
|
278
|
+
"Search Regex": |
|
|
279
|
+
`("|')([^"']*)(?<=\s|"|')tree-hierarchy(?=\s|"|')([^"']*)("|')`
|
|
280
|
+
|
|
281
|
+
- "Bootstrap v3": filter-container
|
|
282
|
+
"Bootstrap v5": removed
|
|
283
|
+
"Search Regex": |
|
|
284
|
+
`("|')([^"']*)(?<=\s|"|')filter-container(?=\s|"|')([^"']*)("|')`
|
|
285
|
+
|
|
286
|
+
- "Bootstrap v3": data-backdrop
|
|
287
|
+
"Bootstrap v5": data-bs-backdrop
|
|
288
|
+
"Search Regex": |
|
|
289
|
+
`data-backdrop`
|
|
290
|
+
|
|
291
|
+
- "Bootstrap v3": data-dismiss
|
|
292
|
+
"Bootstrap v5": data-bs-dismiss
|
|
293
|
+
"Search Regex": |
|
|
294
|
+
`data-dismiss`
|
|
295
|
+
|
|
296
|
+
- "Bootstrap v3": data-target
|
|
297
|
+
"Bootstrap v5": data-bs-target
|
|
298
|
+
"Search Regex": |
|
|
299
|
+
`data-target`
|
|
300
|
+
|
|
301
|
+
- "Bootstrap v3": data-title
|
|
302
|
+
"Bootstrap v5": data-bs-title
|
|
303
|
+
"Search Regex": |
|
|
304
|
+
`data-title`
|
|
305
|
+
|
|
306
|
+
- "Bootstrap v3": data-toggle
|
|
307
|
+
"Bootstrap v5": data-bs-toggle
|
|
308
|
+
"Search Regex": |
|
|
309
|
+
`data-toggle`
|
|
310
|
+
|
|
311
|
+
- "Bootstrap v3": Breadcrumbs, Dropdowns, Tabs
|
|
312
|
+
"Bootstrap v5": breadcrumb-item, dropdown-item, nav-item, nav-link
|
|
313
|
+
"Search Regex": |
|
|
314
|
+
`(?s)<li((?!<li|</li>|breadcrumb-item).)*(?=<a|<button)((?!<li|</li>|breadcrumb-item|dropdown-item|nav-item|nav-link).)*</li>`
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
import graphene
|
|
6
|
-
from graphene_django.fields import DjangoListField
|
|
7
6
|
import graphene_django_optimizer as gql_optimizer
|
|
8
7
|
from graphql import GraphQLError
|
|
9
8
|
|
|
@@ -375,7 +374,8 @@ def generate_attrs_for_schema_type(schema_type):
|
|
|
375
374
|
# Define Attributes for single item and list with their search parameters
|
|
376
375
|
search_params = generate_list_search_parameters(schema_type)
|
|
377
376
|
attrs[single_item_name] = graphene.Field(schema_type, id=graphene.ID())
|
|
378
|
-
|
|
377
|
+
# TODO: refactor to use DjangoListField instead of graphene.List (https://github.com/nautobot/nautobot/issues/4690)
|
|
378
|
+
attrs[list_name] = graphene.List(schema_type, **search_params)
|
|
379
379
|
|
|
380
380
|
# Define Resolvers for both single item and list
|
|
381
381
|
single_item_resolver_name = f"{RESOLVER_PREFIX}{single_item_name}"
|