diff --git a/AIPscan/__init__.py b/AIPscan/__init__.py index 10af21ed..74d2207b 100644 --- a/AIPscan/__init__.py +++ b/AIPscan/__init__.py @@ -4,6 +4,7 @@ from flask_sqlalchemy import SQLAlchemy from AIPscan.celery import configure_celery +from AIPscan.navbar import NavBar from config import CONFIGS db = SQLAlchemy() @@ -34,6 +35,24 @@ def create_app(config_name="default"): db.create_all() + # Define navigation bar sections and route to section mapping (needed + # given that the "AIPs" and "Reports" sections are in the same Blueprint) + navbar = NavBar() + navbar.add_section( + "Archivematica Storage Services", "aggregator.storage_services" + ) + navbar.add_section("AIPs", "reporter.view_aips") + navbar.add_section("Reports", "reporter.reports") + + navbar.map_route("reporter.view_aip", "AIPs") + navbar.map_route("reporter.view_file", "AIPs") + + # Inject navigation bar into templates + @app.context_processor + def inject_navbar(): + return dict(navbar=navbar) + + # Set up 404 handling @app.errorhandler(404) def page_not_found(e): return render_template("error/404.html"), 404 diff --git a/AIPscan/navbar.py b/AIPscan/navbar.py new file mode 100644 index 00000000..de9ec32b --- /dev/null +++ b/AIPscan/navbar.py @@ -0,0 +1,65 @@ +from flask import url_for + + +class NavBar: + sections: list + route_map: dict + + def __init__(self): + self.sections = [] + self.route_map = {} + + def add_section(self, label, route): + self.sections.append(NavBarSection(label, route, self)) + + def map_route(self, route, label): + self.route_map[route] = label + + +class NavBarSection: + label: str + route: str + nav_bar: NavBar + + def __init__(self, label, route, nav_bar): + self.label = label # Navigation link text + self.route = route # Route ("transfer.index" for example) + self.nav_bar = nav_bar # Parent NavBar (Needed to access route map) + + def get_url(self): + return url_for(self.route) + + def is_active(self, request): + # Don't check if request isn't for a static asset + rule = request.url_rule + + if rule is None: + return False + + # Check for arbitrary mapping of request route to nav bar section + if rule.endpoint in self.nav_bar.route_map: + return self.nav_bar.route_map[rule.endpoint] == self.label + + request_blueprint = self.blueprint_of(rule.endpoint) + + if rule.endpoint == self.route: + # A section's default route has been requested + return True + elif not self.blueprint_has_mapped_route(request_blueprint): + # A route within a section representing an entire Blueprint may have been requested + section_route_blueprint = self.blueprint_of(self.route) + + # Found Blueprint-level match between section and request + return request_blueprint == section_route_blueprint + + def blueprint_has_mapped_route(self, blueprint): + for mapped_route in self.nav_bar.route_map: + route_map_blueprint = self.blueprint_of(mapped_route) + + if route_map_blueprint == blueprint: + return True + + return False + + def blueprint_of(self, route): + return route.split(".")[0] diff --git a/AIPscan/templates/base.html b/AIPscan/templates/base.html index 75b42045..56392f02 100644 --- a/AIPscan/templates/base.html +++ b/AIPscan/templates/base.html @@ -25,15 +25,13 @@ AIPscan