We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Site administration option is shown for all users
### Describe the bug
I just noticed that the site administration button in the menu is shown for everyone (I think): https://github.com/svthalia/concrexit/blob/master/website/thaliawebsite/templates/base.html#L184

Clicking it results in a looped redirect, so it doesn't work either.

![Screenshot 2023-06-02 at 10 37 10](https://github.com/svthalia/concrexit/assets/1799914/e358fc0b-911d-4c2f-bfa8-5843d11f5884)



### How to reproduce
Steps to reproduce the behaviour:
1. Go to the website
2. Check the menu

### Expected behaviour
Do not show the menu item

### Screenshots
If applicable, add screenshots to help explain your problem.

### Additional context
Add any other context about the problem here.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `website/thaliawebsite/views.py`
Content:
```
1 """General views for the website."""
2 
3 from django.contrib.admin.views.decorators import staff_member_required
4 from django.contrib.auth.views import LoginView, PasswordResetView
5 from django.http import HttpResponse, HttpResponseForbidden
6 from django.utils.decorators import method_decorator
7 from django.views.generic import ListView, TemplateView
8 from django.views.generic.base import View
9 
10 from django_ratelimit.decorators import ratelimit
11 
12 
13 class IndexView(TemplateView):
14     template_name = "index.html"
15 
16 
17 @method_decorator(staff_member_required, "dispatch")
18 class TestCrashView(View):
19     """Test view to intentionally crash to test the error handling."""
20 
21     def dispatch(self, request, *args, **kwargs) -> HttpResponse:
22         if not request.user.is_superuser:
23             return HttpResponseForbidden("This is not for you")
24         raise Exception("Test exception")
25 
26 
27 class PagedView(ListView):
28     """A ListView with automatic pagination."""
29 
30     def get_context_data(self, **kwargs) -> dict:
31         context = super().get_context_data(**kwargs)
32         page = context["page_obj"].number
33         paginator = context["paginator"]
34 
35         # Show the two pages before and after the current page
36         page_range_start = max(1, page - 2)
37         page_range_stop = min(page + 3, paginator.num_pages + 1)
38 
39         # Add extra pages if we show less than 5 pages
40         page_range_start = min(page_range_start, page_range_stop - 5)
41         page_range_start = max(1, page_range_start)
42 
43         # Add extra pages if we still show less than 5 pages
44         page_range_stop = max(page_range_stop, page_range_start + 5)
45         page_range_stop = min(page_range_stop, paginator.num_pages + 1)
46 
47         page_range = range(page_range_start, page_range_stop)
48 
49         context.update(
50             {
51                 "page_range": page_range,
52             }
53         )
54 
55         return context
56 
57 
58 class RateLimitedPasswordResetView(PasswordResetView):
59     @method_decorator(ratelimit(key="ip", rate="5/h"))
60     def post(self, request, *args, **kwargs):
61         return super().post(request, *args, **kwargs)
62 
63 
64 class RateLimitedLoginView(LoginView):
65     @method_decorator(ratelimit(key="ip", rate="30/h"))
66     @method_decorator(ratelimit(key="post:username", rate="30/h"))
67     def post(self, request, *args, **kwargs):
68         return super().post(request, *args, **kwargs)
69 
70 
71 def rate_limited_view(request, *args, **kwargs):
72     return HttpResponse("You are rate limited", status=429)
73
```

Path: `website/thaliawebsite/urls.py`
Content:
```
1 """Thalia's root URL Configuration.
2 
3 The ``urlpatterns`` list routes URLs to views. For more information please see:
4 https://docs.djangoproject.com/en/dev/topics/http/urls/
5 
6 Examples:
7 * Function views
8 
9   1. Add an import: ``from my_app import views``
10   2. Add a URL to ``urlpatterns``: ``path('', views.home, name='home')``
11 
12 * Class-based views
13 
14   1. Add an import: ``from other_app.views import Home``
15   2. Add a URL to urlpatterns: ``path('', Home.as_view(), name='home')``
16 
17 * Including another URLconf
18 
19   1. Import the ``include()`` function::
20 
21         from django.conf.urls import url, include
22 
23   2. Add a URL to urlpatterns: ``path('blog/', include('blog.urls'))``
24 """
25 
26 import os.path
27 
28 from django.conf import settings
29 from django.conf.urls import include
30 from django.conf.urls.static import static
31 from django.contrib import admin
32 from django.contrib.sitemaps.views import sitemap
33 from django.urls import path, re_path
34 from django.views.generic import RedirectView
35 from django.views.i18n import JavaScriptCatalog
36 
37 import debug_toolbar
38 from oauth2_provider.urls import base_urlpatterns
39 from oauth2_provider.views import (
40     AuthorizedTokenDeleteView,
41     AuthorizedTokensListView,
42     ConnectDiscoveryInfoView,
43     JwksInfoView,
44     UserInfoView,
45 )
46 
47 from activemembers.sitemaps import sitemap as activemembers_sitemap
48 from documents.sitemaps import sitemap as documents_sitemap
49 from education.sitemaps import sitemap as education_sitemap
50 from events.sitemaps import sitemap as events_sitemap
51 from members.sitemaps import sitemap as members_sitemap
52 from partners.sitemaps import sitemap as partners_sitemap
53 from singlepages.sitemaps import sitemap as singlepages_sitemap
54 from thabloid.sitemaps import sitemap as thabloid_sitemap
55 from thaliawebsite.forms import AuthenticationForm
56 from thaliawebsite.views import (
57     IndexView,
58     RateLimitedLoginView,
59     RateLimitedPasswordResetView,
60     TestCrashView,
61 )
62 from utils.media.views import private_media
63 
64 from .sitemaps import StaticViewSitemap
65 
66 __all__ = ["urlpatterns"]
67 
68 THALIA_SITEMAP = {
69     "main-static": StaticViewSitemap,
70 }
71 THALIA_SITEMAP.update(activemembers_sitemap)
72 THALIA_SITEMAP.update(members_sitemap)
73 THALIA_SITEMAP.update(documents_sitemap)
74 THALIA_SITEMAP.update(thabloid_sitemap)
75 THALIA_SITEMAP.update(partners_sitemap)
76 THALIA_SITEMAP.update(education_sitemap)
77 THALIA_SITEMAP.update(events_sitemap)
78 THALIA_SITEMAP.update(singlepages_sitemap)
79 
80 urlpatterns = [
81     path(
82         "admin/login/",
83         RedirectView.as_view(url="/user/login", query_string=True),
84         name="login-redirect",
85     ),
86     path("admin/", admin.site.urls),
87     path("", IndexView.as_view(), name="index"),
88     # Default helpers
89     path(
90         "",
91         include(
92             (
93                 [
94                     path(
95                         "user/oauth/",
96                         include(
97                             base_urlpatterns
98                             + [
99                                 path(
100                                     "authorised-apps/",
101                                     AuthorizedTokensListView.as_view(),
102                                     name="authorized-token-list",
103                                 ),
104                                 path(
105                                     "authorised-apps/<int:pk>/delete/",
106                                     AuthorizedTokenDeleteView.as_view(),
107                                     name="authorized-token-delete",
108                                 ),
109                                 path(
110                                     "keys/",
111                                     JwksInfoView.as_view(),
112                                     name="jwks-info",
113                                 ),
114                                 path(
115                                     "info/",
116                                     UserInfoView.as_view(),
117                                     name="user-info",
118                                 ),
119                             ]
120                         ),
121                     ),
122                     path(
123                         ".well-known/openid-configuration/",
124                         ConnectDiscoveryInfoView.as_view(),
125                         name="oidc-connect-discovery-info",
126                     ),
127                 ],
128                 "oauth2_provider",
129             ),
130             namespace="oauth2_provider",
131         ),
132     ),
133     path(
134         "user/",
135         include(
136             [
137                 path(
138                     "login/",
139                     RateLimitedLoginView.as_view(
140                         authentication_form=AuthenticationForm,
141                         redirect_authenticated_user=True,
142                     ),
143                     name="login",
144                 ),
145                 path(
146                     "password_reset/",
147                     RateLimitedPasswordResetView.as_view(),
148                     name="password_reset",
149                 ),
150                 path("", include("django.contrib.auth.urls")),
151             ]
152         ),
153     ),
154     path(
155         "i18n/",
156         include(
157             [
158                 path("", include("django.conf.urls.i18n")),
159                 path("js/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
160             ]
161         ),
162     ),
163     # Apps
164     path("", include("singlepages.urls")),
165     path("", include("merchandise.urls")),
166     path("", include("thabloid.urls")),
167     path("", include("registrations.urls")),
168     path("", include("newsletters.urls")),
169     path("", include("announcements.urls")),
170     path("", include("pushnotifications.urls")),
171     path("", include("facedetection.urls")),
172     path("", include("photos.urls")),
173     path("", include("members.urls")),
174     path("", include("payments.urls")),
175     path("", include("education.urls")),
176     path("", include("activemembers.urls")),
177     path("", include("documents.urls")),
178     path("", include("events.urls")),
179     path("", include("pizzas.urls")),
180     path("", include("partners.urls")),
181     path("", include("sales.urls")),
182     path("api/", include("thaliawebsite.api.urls")),
183     # Sitemap
184     path(
185         "sitemap.xml",
186         sitemap,
187         {"sitemaps": THALIA_SITEMAP},
188         name="django.contrib.sitemaps.views.sitemap",
189     ),
190     # Dependencies
191     path("tinymce/", include("tinymce.urls")),
192     path("__debug__/", include(debug_toolbar.urls)),
193     # Provide something to test error handling. Limited to admins.
194     path("crash/", TestCrashView.as_view()),
195     # Custom media paths
196     re_path(
197         r"^media/private/(?P<request_path>.*)$", private_media, name="private-media"
198     ),
199     path("", include("shortlinks.urls")),
200     re_path(r"^fp/", include("django_drf_filepond.urls")),
201 ] + static(
202     settings.PUBLIC_MEDIA_URL,
203     document_root=os.path.join(settings.MEDIA_ROOT, settings.PUBLIC_MEDIA_LOCATION),
204 )
205
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/website/thaliawebsite/urls.py b/website/thaliawebsite/urls.py
--- a/website/thaliawebsite/urls.py
+++ b/website/thaliawebsite/urls.py
@@ -31,7 +31,6 @@
 from django.contrib import admin
 from django.contrib.sitemaps.views import sitemap
 from django.urls import path, re_path
-from django.views.generic import RedirectView
 from django.views.i18n import JavaScriptCatalog
 
 import debug_toolbar
@@ -58,6 +57,7 @@
     RateLimitedLoginView,
     RateLimitedPasswordResetView,
     TestCrashView,
+    admin_unauthorized_view,
 )
 from utils.media.views import private_media
 
@@ -80,7 +80,7 @@
 urlpatterns = [
     path(
         "admin/login/",
-        RedirectView.as_view(url="/user/login", query_string=True),
+        admin_unauthorized_view,
         name="login-redirect",
     ),
     path("admin/", admin.site.urls),
diff --git a/website/thaliawebsite/views.py b/website/thaliawebsite/views.py
--- a/website/thaliawebsite/views.py
+++ b/website/thaliawebsite/views.py
@@ -2,7 +2,9 @@
 
 from django.contrib.admin.views.decorators import staff_member_required
 from django.contrib.auth.views import LoginView, PasswordResetView
+from django.core.exceptions import PermissionDenied
 from django.http import HttpResponse, HttpResponseForbidden
+from django.shortcuts import redirect
 from django.utils.decorators import method_decorator
 from django.views.generic import ListView, TemplateView
 from django.views.generic.base import View
@@ -70,3 +72,17 @@
 
 def rate_limited_view(request, *args, **kwargs):
     return HttpResponse("You are rate limited", status=429)
+
+
+def admin_unauthorized_view(request):
+    if not request.member:
+        url = "/user/login"
+        args = request.META.get("QUERY_STRING", "")
+        if args:
+            url = f"{url}?{args}"
+        return redirect(url)
+    elif not request.member.is_staff:
+        # user is logged in but not authorized
+        raise PermissionDenied("You are not allowed to access the administration page.")
+    else:
+        return redirect(request.GET.get("next", "/"))

{"golden_diff": "diff --git a/website/thaliawebsite/urls.py b/website/thaliawebsite/urls.py\n--- a/website/thaliawebsite/urls.py\n+++ b/website/thaliawebsite/urls.py\n@@ -31,7 +31,6 @@\n from django.contrib import admin\n from django.contrib.sitemaps.views import sitemap\n from django.urls import path, re_path\n-from django.views.generic import RedirectView\n from django.views.i18n import JavaScriptCatalog\n \n import debug_toolbar\n@@ -58,6 +57,7 @@\n     RateLimitedLoginView,\n     RateLimitedPasswordResetView,\n     TestCrashView,\n+    admin_unauthorized_view,\n )\n from utils.media.views import private_media\n \n@@ -80,7 +80,7 @@\n urlpatterns = [\n     path(\n         \"admin/login/\",\n-        RedirectView.as_view(url=\"/user/login\", query_string=True),\n+        admin_unauthorized_view,\n         name=\"login-redirect\",\n     ),\n     path(\"admin/\", admin.site.urls),\ndiff --git a/website/thaliawebsite/views.py b/website/thaliawebsite/views.py\n--- a/website/thaliawebsite/views.py\n+++ b/website/thaliawebsite/views.py\n@@ -2,7 +2,9 @@\n \n from django.contrib.admin.views.decorators import staff_member_required\n from django.contrib.auth.views import LoginView, PasswordResetView\n+from django.core.exceptions import PermissionDenied\n from django.http import HttpResponse, HttpResponseForbidden\n+from django.shortcuts import redirect\n from django.utils.decorators import method_decorator\n from django.views.generic import ListView, TemplateView\n from django.views.generic.base import View\n@@ -70,3 +72,17 @@\n \n def rate_limited_view(request, *args, **kwargs):\n     return HttpResponse(\"You are rate limited\", status=429)\n+\n+\n+def admin_unauthorized_view(request):\n+    if not request.member:\n+        url = \"/user/login\"\n+        args = request.META.get(\"QUERY_STRING\", \"\")\n+        if args:\n+            url = f\"{url}?{args}\"\n+        return redirect(url)\n+    elif not request.member.is_staff:\n+        # user is logged in but not authorized\n+        raise PermissionDenied(\"You are not allowed to access the administration page.\")\n+    else:\n+        return redirect(request.GET.get(\"next\", \"/\"))\n", "issue": "Site administration option is shown for all users\n### Describe the bug\r\nI just noticed that the site administration button in the menu is shown for everyone (I think): https://github.com/svthalia/concrexit/blob/master/website/thaliawebsite/templates/base.html#L184\r\n\r\nClicking it results in a looped redirect, so it doesn't work either.\r\n\r\n![Screenshot 2023-06-02 at 10 37 10](https://github.com/svthalia/concrexit/assets/1799914/e358fc0b-911d-4c2f-bfa8-5843d11f5884)\r\n\r\n\r\n\r\n### How to reproduce\r\nSteps to reproduce the behaviour:\r\n1. Go to the website\r\n2. Check the menu\r\n\r\n### Expected behaviour\r\nDo not show the menu item\r\n\r\n### Screenshots\r\nIf applicable, add screenshots to help explain your problem.\r\n\r\n### Additional context\r\nAdd any other context about the problem here.\r\n\n", "before_files": [{"content": "\"\"\"General views for the website.\"\"\"\n\nfrom django.contrib.admin.views.decorators import staff_member_required\nfrom django.contrib.auth.views import LoginView, PasswordResetView\nfrom django.http import HttpResponse, HttpResponseForbidden\nfrom django.utils.decorators import method_decorator\nfrom django.views.generic import ListView, TemplateView\nfrom django.views.generic.base import View\n\nfrom django_ratelimit.decorators import ratelimit\n\n\nclass IndexView(TemplateView):\n    template_name = \"index.html\"\n\n\n@method_decorator(staff_member_required, \"dispatch\")\nclass TestCrashView(View):\n    \"\"\"Test view to intentionally crash to test the error handling.\"\"\"\n\n    def dispatch(self, request, *args, **kwargs) -> HttpResponse:\n        if not request.user.is_superuser:\n            return HttpResponseForbidden(\"This is not for you\")\n        raise Exception(\"Test exception\")\n\n\nclass PagedView(ListView):\n    \"\"\"A ListView with automatic pagination.\"\"\"\n\n    def get_context_data(self, **kwargs) -> dict:\n        context = super().get_context_data(**kwargs)\n        page = context[\"page_obj\"].number\n        paginator = context[\"paginator\"]\n\n        # Show the two pages before and after the current page\n        page_range_start = max(1, page - 2)\n        page_range_stop = min(page + 3, paginator.num_pages + 1)\n\n        # Add extra pages if we show less than 5 pages\n        page_range_start = min(page_range_start, page_range_stop - 5)\n        page_range_start = max(1, page_range_start)\n\n        # Add extra pages if we still show less than 5 pages\n        page_range_stop = max(page_range_stop, page_range_start + 5)\n        page_range_stop = min(page_range_stop, paginator.num_pages + 1)\n\n        page_range = range(page_range_start, page_range_stop)\n\n        context.update(\n            {\n                \"page_range\": page_range,\n            }\n        )\n\n        return context\n\n\nclass RateLimitedPasswordResetView(PasswordResetView):\n    @method_decorator(ratelimit(key=\"ip\", rate=\"5/h\"))\n    def post(self, request, *args, **kwargs):\n        return super().post(request, *args, **kwargs)\n\n\nclass RateLimitedLoginView(LoginView):\n    @method_decorator(ratelimit(key=\"ip\", rate=\"30/h\"))\n    @method_decorator(ratelimit(key=\"post:username\", rate=\"30/h\"))\n    def post(self, request, *args, **kwargs):\n        return super().post(request, *args, **kwargs)\n\n\ndef rate_limited_view(request, *args, **kwargs):\n    return HttpResponse(\"You are rate limited\", status=429)\n", "path": "website/thaliawebsite/views.py"}, {"content": "\"\"\"Thalia's root URL Configuration.\n\nThe ``urlpatterns`` list routes URLs to views. For more information please see:\nhttps://docs.djangoproject.com/en/dev/topics/http/urls/\n\nExamples:\n* Function views\n\n  1. Add an import: ``from my_app import views``\n  2. Add a URL to ``urlpatterns``: ``path('', views.home, name='home')``\n\n* Class-based views\n\n  1. Add an import: ``from other_app.views import Home``\n  2. Add a URL to urlpatterns: ``path('', Home.as_view(), name='home')``\n\n* Including another URLconf\n\n  1. Import the ``include()`` function::\n\n        from django.conf.urls import url, include\n\n  2. Add a URL to urlpatterns: ``path('blog/', include('blog.urls'))``\n\"\"\"\n\nimport os.path\n\nfrom django.conf import settings\nfrom django.conf.urls import include\nfrom django.conf.urls.static import static\nfrom django.contrib import admin\nfrom django.contrib.sitemaps.views import sitemap\nfrom django.urls import path, re_path\nfrom django.views.generic import RedirectView\nfrom django.views.i18n import JavaScriptCatalog\n\nimport debug_toolbar\nfrom oauth2_provider.urls import base_urlpatterns\nfrom oauth2_provider.views import (\n    AuthorizedTokenDeleteView,\n    AuthorizedTokensListView,\n    ConnectDiscoveryInfoView,\n    JwksInfoView,\n    UserInfoView,\n)\n\nfrom activemembers.sitemaps import sitemap as activemembers_sitemap\nfrom documents.sitemaps import sitemap as documents_sitemap\nfrom education.sitemaps import sitemap as education_sitemap\nfrom events.sitemaps import sitemap as events_sitemap\nfrom members.sitemaps import sitemap as members_sitemap\nfrom partners.sitemaps import sitemap as partners_sitemap\nfrom singlepages.sitemaps import sitemap as singlepages_sitemap\nfrom thabloid.sitemaps import sitemap as thabloid_sitemap\nfrom thaliawebsite.forms import AuthenticationForm\nfrom thaliawebsite.views import (\n    IndexView,\n    RateLimitedLoginView,\n    RateLimitedPasswordResetView,\n    TestCrashView,\n)\nfrom utils.media.views import private_media\n\nfrom .sitemaps import StaticViewSitemap\n\n__all__ = [\"urlpatterns\"]\n\nTHALIA_SITEMAP = {\n    \"main-static\": StaticViewSitemap,\n}\nTHALIA_SITEMAP.update(activemembers_sitemap)\nTHALIA_SITEMAP.update(members_sitemap)\nTHALIA_SITEMAP.update(documents_sitemap)\nTHALIA_SITEMAP.update(thabloid_sitemap)\nTHALIA_SITEMAP.update(partners_sitemap)\nTHALIA_SITEMAP.update(education_sitemap)\nTHALIA_SITEMAP.update(events_sitemap)\nTHALIA_SITEMAP.update(singlepages_sitemap)\n\nurlpatterns = [\n    path(\n        \"admin/login/\",\n        RedirectView.as_view(url=\"/user/login\", query_string=True),\n        name=\"login-redirect\",\n    ),\n    path(\"admin/\", admin.site.urls),\n    path(\"\", IndexView.as_view(), name=\"index\"),\n    # Default helpers\n    path(\n        \"\",\n        include(\n            (\n                [\n                    path(\n                        \"user/oauth/\",\n                        include(\n                            base_urlpatterns\n                            + [\n                                path(\n                                    \"authorised-apps/\",\n                                    AuthorizedTokensListView.as_view(),\n                                    name=\"authorized-token-list\",\n                                ),\n                                path(\n                                    \"authorised-apps/<int:pk>/delete/\",\n                                    AuthorizedTokenDeleteView.as_view(),\n                                    name=\"authorized-token-delete\",\n                                ),\n                                path(\n                                    \"keys/\",\n                                    JwksInfoView.as_view(),\n                                    name=\"jwks-info\",\n                                ),\n                                path(\n                                    \"info/\",\n                                    UserInfoView.as_view(),\n                                    name=\"user-info\",\n                                ),\n                            ]\n                        ),\n                    ),\n                    path(\n                        \".well-known/openid-configuration/\",\n                        ConnectDiscoveryInfoView.as_view(),\n                        name=\"oidc-connect-discovery-info\",\n                    ),\n                ],\n                \"oauth2_provider\",\n            ),\n            namespace=\"oauth2_provider\",\n        ),\n    ),\n    path(\n        \"user/\",\n        include(\n            [\n                path(\n                    \"login/\",\n                    RateLimitedLoginView.as_view(\n                        authentication_form=AuthenticationForm,\n                        redirect_authenticated_user=True,\n                    ),\n                    name=\"login\",\n                ),\n                path(\n                    \"password_reset/\",\n                    RateLimitedPasswordResetView.as_view(),\n                    name=\"password_reset\",\n                ),\n                path(\"\", include(\"django.contrib.auth.urls\")),\n            ]\n        ),\n    ),\n    path(\n        \"i18n/\",\n        include(\n            [\n                path(\"\", include(\"django.conf.urls.i18n\")),\n                path(\"js/\", JavaScriptCatalog.as_view(), name=\"javascript-catalog\"),\n            ]\n        ),\n    ),\n    # Apps\n    path(\"\", include(\"singlepages.urls\")),\n    path(\"\", include(\"merchandise.urls\")),\n    path(\"\", include(\"thabloid.urls\")),\n    path(\"\", include(\"registrations.urls\")),\n    path(\"\", include(\"newsletters.urls\")),\n    path(\"\", include(\"announcements.urls\")),\n    path(\"\", include(\"pushnotifications.urls\")),\n    path(\"\", include(\"facedetection.urls\")),\n    path(\"\", include(\"photos.urls\")),\n    path(\"\", include(\"members.urls\")),\n    path(\"\", include(\"payments.urls\")),\n    path(\"\", include(\"education.urls\")),\n    path(\"\", include(\"activemembers.urls\")),\n    path(\"\", include(\"documents.urls\")),\n    path(\"\", include(\"events.urls\")),\n    path(\"\", include(\"pizzas.urls\")),\n    path(\"\", include(\"partners.urls\")),\n    path(\"\", include(\"sales.urls\")),\n    path(\"api/\", include(\"thaliawebsite.api.urls\")),\n    # Sitemap\n    path(\n        \"sitemap.xml\",\n        sitemap,\n        {\"sitemaps\": THALIA_SITEMAP},\n        name=\"django.contrib.sitemaps.views.sitemap\",\n    ),\n    # Dependencies\n    path(\"tinymce/\", include(\"tinymce.urls\")),\n    path(\"__debug__/\", include(debug_toolbar.urls)),\n    # Provide something to test error handling. Limited to admins.\n    path(\"crash/\", TestCrashView.as_view()),\n    # Custom media paths\n    re_path(\n        r\"^media/private/(?P<request_path>.*)$\", private_media, name=\"private-media\"\n    ),\n    path(\"\", include(\"shortlinks.urls\")),\n    re_path(r\"^fp/\", include(\"django_drf_filepond.urls\")),\n] + static(\n    settings.PUBLIC_MEDIA_URL,\n    document_root=os.path.join(settings.MEDIA_ROOT, settings.PUBLIC_MEDIA_LOCATION),\n)\n", "path": "website/thaliawebsite/urls.py"}], "after_files": [{"content": "\"\"\"General views for the website.\"\"\"\n\nfrom django.contrib.admin.views.decorators import staff_member_required\nfrom django.contrib.auth.views import LoginView, PasswordResetView\nfrom django.core.exceptions import PermissionDenied\nfrom django.http import HttpResponse, HttpResponseForbidden\nfrom django.shortcuts import redirect\nfrom django.utils.decorators import method_decorator\nfrom django.views.generic import ListView, TemplateView\nfrom django.views.generic.base import View\n\nfrom django_ratelimit.decorators import ratelimit\n\n\nclass IndexView(TemplateView):\n    template_name = \"index.html\"\n\n\n@method_decorator(staff_member_required, \"dispatch\")\nclass TestCrashView(View):\n    \"\"\"Test view to intentionally crash to test the error handling.\"\"\"\n\n    def dispatch(self, request, *args, **kwargs) -> HttpResponse:\n        if not request.user.is_superuser:\n            return HttpResponseForbidden(\"This is not for you\")\n        raise Exception(\"Test exception\")\n\n\nclass PagedView(ListView):\n    \"\"\"A ListView with automatic pagination.\"\"\"\n\n    def get_context_data(self, **kwargs) -> dict:\n        context = super().get_context_data(**kwargs)\n        page = context[\"page_obj\"].number\n        paginator = context[\"paginator\"]\n\n        # Show the two pages before and after the current page\n        page_range_start = max(1, page - 2)\n        page_range_stop = min(page + 3, paginator.num_pages + 1)\n\n        # Add extra pages if we show less than 5 pages\n        page_range_start = min(page_range_start, page_range_stop - 5)\n        page_range_start = max(1, page_range_start)\n\n        # Add extra pages if we still show less than 5 pages\n        page_range_stop = max(page_range_stop, page_range_start + 5)\n        page_range_stop = min(page_range_stop, paginator.num_pages + 1)\n\n        page_range = range(page_range_start, page_range_stop)\n\n        context.update(\n            {\n                \"page_range\": page_range,\n            }\n        )\n\n        return context\n\n\nclass RateLimitedPasswordResetView(PasswordResetView):\n    @method_decorator(ratelimit(key=\"ip\", rate=\"5/h\"))\n    def post(self, request, *args, **kwargs):\n        return super().post(request, *args, **kwargs)\n\n\nclass RateLimitedLoginView(LoginView):\n    @method_decorator(ratelimit(key=\"ip\", rate=\"30/h\"))\n    @method_decorator(ratelimit(key=\"post:username\", rate=\"30/h\"))\n    def post(self, request, *args, **kwargs):\n        return super().post(request, *args, **kwargs)\n\n\ndef rate_limited_view(request, *args, **kwargs):\n    return HttpResponse(\"You are rate limited\", status=429)\n\n\ndef admin_unauthorized_view(request):\n    if not request.member:\n        url = \"/user/login\"\n        args = request.META.get(\"QUERY_STRING\", \"\")\n        if args:\n            url = f\"{url}?{args}\"\n        return redirect(url)\n    elif not request.member.is_staff:\n        # user is logged in but not authorized\n        raise PermissionDenied(\"You are not allowed to access the administration page.\")\n    else:\n        return redirect(request.GET.get(\"next\", \"/\"))\n", "path": "website/thaliawebsite/views.py"}, {"content": "\"\"\"Thalia's root URL Configuration.\n\nThe ``urlpatterns`` list routes URLs to views. For more information please see:\nhttps://docs.djangoproject.com/en/dev/topics/http/urls/\n\nExamples:\n* Function views\n\n  1. Add an import: ``from my_app import views``\n  2. Add a URL to ``urlpatterns``: ``path('', views.home, name='home')``\n\n* Class-based views\n\n  1. Add an import: ``from other_app.views import Home``\n  2. Add a URL to urlpatterns: ``path('', Home.as_view(), name='home')``\n\n* Including another URLconf\n\n  1. Import the ``include()`` function::\n\n        from django.conf.urls import url, include\n\n  2. Add a URL to urlpatterns: ``path('blog/', include('blog.urls'))``\n\"\"\"\n\nimport os.path\n\nfrom django.conf import settings\nfrom django.conf.urls import include\nfrom django.conf.urls.static import static\nfrom django.contrib import admin\nfrom django.contrib.sitemaps.views import sitemap\nfrom django.urls import path, re_path\nfrom django.views.i18n import JavaScriptCatalog\n\nimport debug_toolbar\nfrom oauth2_provider.urls import base_urlpatterns\nfrom oauth2_provider.views import (\n    AuthorizedTokenDeleteView,\n    AuthorizedTokensListView,\n    ConnectDiscoveryInfoView,\n    JwksInfoView,\n    UserInfoView,\n)\n\nfrom activemembers.sitemaps import sitemap as activemembers_sitemap\nfrom documents.sitemaps import sitemap as documents_sitemap\nfrom education.sitemaps import sitemap as education_sitemap\nfrom events.sitemaps import sitemap as events_sitemap\nfrom members.sitemaps import sitemap as members_sitemap\nfrom partners.sitemaps import sitemap as partners_sitemap\nfrom singlepages.sitemaps import sitemap as singlepages_sitemap\nfrom thabloid.sitemaps import sitemap as thabloid_sitemap\nfrom thaliawebsite.forms import AuthenticationForm\nfrom thaliawebsite.views import (\n    IndexView,\n    RateLimitedLoginView,\n    RateLimitedPasswordResetView,\n    TestCrashView,\n    admin_unauthorized_view,\n)\nfrom utils.media.views import private_media\n\nfrom .sitemaps import StaticViewSitemap\n\n__all__ = [\"urlpatterns\"]\n\nTHALIA_SITEMAP = {\n    \"main-static\": StaticViewSitemap,\n}\nTHALIA_SITEMAP.update(activemembers_sitemap)\nTHALIA_SITEMAP.update(members_sitemap)\nTHALIA_SITEMAP.update(documents_sitemap)\nTHALIA_SITEMAP.update(thabloid_sitemap)\nTHALIA_SITEMAP.update(partners_sitemap)\nTHALIA_SITEMAP.update(education_sitemap)\nTHALIA_SITEMAP.update(events_sitemap)\nTHALIA_SITEMAP.update(singlepages_sitemap)\n\nurlpatterns = [\n    path(\n        \"admin/login/\",\n        admin_unauthorized_view,\n        name=\"login-redirect\",\n    ),\n    path(\"admin/\", admin.site.urls),\n    path(\"\", IndexView.as_view(), name=\"index\"),\n    # Default helpers\n    path(\n        \"\",\n        include(\n            (\n                [\n                    path(\n                        \"user/oauth/\",\n                        include(\n                            base_urlpatterns\n                            + [\n                                path(\n                                    \"authorised-apps/\",\n                                    AuthorizedTokensListView.as_view(),\n                                    name=\"authorized-token-list\",\n                                ),\n                                path(\n                                    \"authorised-apps/<int:pk>/delete/\",\n                                    AuthorizedTokenDeleteView.as_view(),\n                                    name=\"authorized-token-delete\",\n                                ),\n                                path(\n                                    \"keys/\",\n                                    JwksInfoView.as_view(),\n                                    name=\"jwks-info\",\n                                ),\n                                path(\n                                    \"info/\",\n                                    UserInfoView.as_view(),\n                                    name=\"user-info\",\n                                ),\n                            ]\n                        ),\n                    ),\n                    path(\n                        \".well-known/openid-configuration/\",\n                        ConnectDiscoveryInfoView.as_view(),\n                        name=\"oidc-connect-discovery-info\",\n                    ),\n                ],\n                \"oauth2_provider\",\n            ),\n            namespace=\"oauth2_provider\",\n        ),\n    ),\n    path(\n        \"user/\",\n        include(\n            [\n                path(\n                    \"login/\",\n                    RateLimitedLoginView.as_view(\n                        authentication_form=AuthenticationForm,\n                        redirect_authenticated_user=True,\n                    ),\n                    name=\"login\",\n                ),\n                path(\n                    \"password_reset/\",\n                    RateLimitedPasswordResetView.as_view(),\n                    name=\"password_reset\",\n                ),\n                path(\"\", include(\"django.contrib.auth.urls\")),\n            ]\n        ),\n    ),\n    path(\n        \"i18n/\",\n        include(\n            [\n                path(\"\", include(\"django.conf.urls.i18n\")),\n                path(\"js/\", JavaScriptCatalog.as_view(), name=\"javascript-catalog\"),\n            ]\n        ),\n    ),\n    # Apps\n    path(\"\", include(\"singlepages.urls\")),\n    path(\"\", include(\"merchandise.urls\")),\n    path(\"\", include(\"thabloid.urls\")),\n    path(\"\", include(\"registrations.urls\")),\n    path(\"\", include(\"newsletters.urls\")),\n    path(\"\", include(\"announcements.urls\")),\n    path(\"\", include(\"pushnotifications.urls\")),\n    path(\"\", include(\"facedetection.urls\")),\n    path(\"\", include(\"photos.urls\")),\n    path(\"\", include(\"members.urls\")),\n    path(\"\", include(\"payments.urls\")),\n    path(\"\", include(\"education.urls\")),\n    path(\"\", include(\"activemembers.urls\")),\n    path(\"\", include(\"documents.urls\")),\n    path(\"\", include(\"events.urls\")),\n    path(\"\", include(\"pizzas.urls\")),\n    path(\"\", include(\"partners.urls\")),\n    path(\"\", include(\"sales.urls\")),\n    path(\"api/\", include(\"thaliawebsite.api.urls\")),\n    # Sitemap\n    path(\n        \"sitemap.xml\",\n        sitemap,\n        {\"sitemaps\": THALIA_SITEMAP},\n        name=\"django.contrib.sitemaps.views.sitemap\",\n    ),\n    # Dependencies\n    path(\"tinymce/\", include(\"tinymce.urls\")),\n    path(\"__debug__/\", include(debug_toolbar.urls)),\n    # Provide something to test error handling. Limited to admins.\n    path(\"crash/\", TestCrashView.as_view()),\n    # Custom media paths\n    re_path(\n        r\"^media/private/(?P<request_path>.*)$\", private_media, name=\"private-media\"\n    ),\n    path(\"\", include(\"shortlinks.urls\")),\n    re_path(r\"^fp/\", include(\"django_drf_filepond.urls\")),\n] + static(\n    settings.PUBLIC_MEDIA_URL,\n    document_root=os.path.join(settings.MEDIA_ROOT, settings.PUBLIC_MEDIA_LOCATION),\n)\n", "path": "website/thaliawebsite/urls.py"}]}