From 42bcd5d955e7fe8af591426bea5ad76384fb9464 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:22:59 +0000 Subject: [PATCH] Changes before error encountered Co-authored-by: vanschelven <223833+vanschelven@users.noreply.github.com> --- bugsink/app_settings.py | 17 +++++ bugsink/context_processors.py | 3 +- bugsink/settings/default.py | 7 ++ bugsink/test_subpath.py | 67 +++++++++++++++++++ issues/templates/issues/base.html | 25 +++---- issues/templates/issues/event_list.html | 5 +- issues/templates/issues/issue_list.html | 3 +- "ith_prefix }}\"|g" | 1 + projects/templates/projects/project_list.html | 3 +- templates/bugsink/login.html | 3 +- templates/signup.html | 3 +- theme/templates/bare_base.html | 6 +- theme/templates/base.html | 16 ++--- theme/templatetags/urls.py | 27 ++++++++ users/templates/users/confirm_email.html | 3 +- users/templates/users/confirm_email_sent.html | 3 +- users/templates/users/logged_out.html | 3 +- .../users/request_reset_password.html | 3 +- .../templates/users/resend_confirmation.html | 3 +- users/templates/users/reset_password.html | 3 +- .../users/reset_password_email_sent.html | 3 +- 21 files changed, 170 insertions(+), 37 deletions(-) create mode 100644 bugsink/test_subpath.py create mode 100644 "ith_prefix }}\"|g" create mode 100644 theme/templatetags/urls.py diff --git a/bugsink/app_settings.py b/bugsink/app_settings.py index 5bd1ce4..c855b17 100644 --- a/bugsink/app_settings.py +++ b/bugsink/app_settings.py @@ -2,6 +2,7 @@ # alternative would be: just put "everything" in the big settings.py (or some mix-using-imports version of that). # but for now I like the idea of keeping the bugsink-as-an-app stuff separate from the regular Django/db/global stuff. import os +import urllib.parse from contextlib import contextmanager from django.conf import settings @@ -110,6 +111,22 @@ def _sanitize(settings): settings["TEAM_CREATION"] = CB_NOBODY +def get_path_prefix(): + """Extract the path prefix from BASE_URL for subpath hosting support.""" + base_url = get_settings().BASE_URL + parsed_url = urllib.parse.urlparse(base_url) + path = parsed_url.path + + # Ensure path starts with / and doesn't end with / (unless it's just "/") + if path and path != "/": + if not path.startswith("/"): + path = "/" + path + if path.endswith("/"): + path = path[:-1] + return path + return "" + + def get_settings(): global _settings if _settings is None: diff --git a/bugsink/context_processors.py b/bugsink/context_processors.py index 172560e..e1ea690 100644 --- a/bugsink/context_processors.py +++ b/bugsink/context_processors.py @@ -10,7 +10,7 @@ from django.contrib.auth.models import AnonymousUser from django.db.utils import OperationalError from django.db.models import Sum -from bugsink.app_settings import get_settings, CB_ANYBODY +from bugsink.app_settings import get_settings, CB_ANYBODY, get_path_prefix from bugsink.transaction import durable_atomic from bugsink.timed_sqlite_backend.base import different_runtime_limit @@ -135,6 +135,7 @@ def useful_settings_processor(request): 'site_title': get_settings().SITE_TITLE, 'registration_enabled': get_settings().USER_REGISTRATION == CB_ANYBODY, 'app_settings': get_settings(), + 'path_prefix': get_path_prefix(), 'system_warnings': get_system_warnings, } diff --git a/bugsink/settings/default.py b/bugsink/settings/default.py index f4881fe..6356a56 100644 --- a/bugsink/settings/default.py +++ b/bugsink/settings/default.py @@ -343,3 +343,10 @@ if I_AM_RUNNING == "SNAPPEA": for logger in LOGGING['loggers'].values(): if "handlers" in logger and "console" in logger["handlers"]: logger["handlers"] = ["snappea"] + +# Set FORCE_SCRIPT_NAME for subpath hosting support +# Import here to avoid circular dependency +from bugsink.app_settings import get_path_prefix +_path_prefix = get_path_prefix() +if _path_prefix: + FORCE_SCRIPT_NAME = _path_prefix diff --git a/bugsink/test_subpath.py b/bugsink/test_subpath.py new file mode 100644 index 0000000..2936c72 --- /dev/null +++ b/bugsink/test_subpath.py @@ -0,0 +1,67 @@ +from django.test import TestCase, override_settings +from django.template import Template, Context + +from bugsink.app_settings import get_path_prefix, override_settings as override_bugsink_settings +from bugsink.context_processors import useful_settings_processor + + +class SubpathHostingTestCase(TestCase): + """Test subpath hosting functionality.""" + + def test_get_path_prefix_default(self): + """Test that get_path_prefix returns empty string for default BASE_URL.""" + with override_bugsink_settings(BASE_URL="http://localhost:8000"): + self.assertEqual(get_path_prefix(), "") + + def test_get_path_prefix_subpath(self): + """Test that get_path_prefix extracts path from BASE_URL.""" + with override_bugsink_settings(BASE_URL="https://example.com/bugsink"): + self.assertEqual(get_path_prefix(), "/bugsink") + + def test_get_path_prefix_subpath_with_trailing_slash(self): + """Test that get_path_prefix handles trailing slash correctly.""" + with override_bugsink_settings(BASE_URL="https://example.com/bugsink/"): + self.assertEqual(get_path_prefix(), "/bugsink") + + def test_get_path_prefix_deeper_path(self): + """Test that get_path_prefix handles deeper paths.""" + with override_bugsink_settings(BASE_URL="https://example.com/tools/bugsink"): + self.assertEqual(get_path_prefix(), "/tools/bugsink") + + def test_context_processor_includes_path_prefix(self): + """Test that context processor includes path prefix.""" + from django.http import HttpRequest + + request = HttpRequest() + request.user = None # Mock an anonymous user + + with override_bugsink_settings(BASE_URL="https://example.com/bugsink"): + context = useful_settings_processor(request) + self.assertEqual(context['path_prefix'], "/bugsink") + + def test_template_filter_with_prefix(self): + """Test the with_prefix template filter.""" + template = Template('{% load urls %}{{ url | with_prefix }}') + + with override_bugsink_settings(BASE_URL="https://example.com/bugsink"): + context = Context({'url': '/admin/'}) + result = template.render(context) + self.assertEqual(result, "/bugsink/admin/") + + def test_template_filter_without_prefix(self): + """Test the with_prefix template filter with no prefix.""" + template = Template('{% load urls %}{{ url | with_prefix }}') + + with override_bugsink_settings(BASE_URL="http://localhost:8000"): + context = Context({'url': '/admin/'}) + result = template.render(context) + self.assertEqual(result, "/admin/") + + def test_template_filter_relative_url(self): + """Test the with_prefix template filter with relative URLs.""" + template = Template('{% load urls %}{{ url | with_prefix }}') + + with override_bugsink_settings(BASE_URL="https://example.com/bugsink"): + context = Context({'url': 'admin/'}) + result = template.render(context) + self.assertEqual(result, "admin/") # Should not add prefix to relative URLs \ No newline at end of file diff --git a/issues/templates/issues/base.html b/issues/templates/issues/base.html index e4d1704..d05329b 100644 --- a/issues/templates/issues/base.html +++ b/issues/templates/issues/base.html @@ -5,6 +5,7 @@ {% load stricter_templates %} {% load add_to_qs %} {% load i18n %} +{% load urls %} {% block title %}{{ issue.title }} · {{ block.super }}{% endblock %} @@ -109,13 +110,13 @@ {# overflow-x-auto is needed at the level of the flex item such that it works at the level where we need it (the code listings)#}