diff options
author | 2021-05-14 12:34:19 +0800 | |
---|---|---|
committer | 2021-05-14 12:34:19 +0800 | |
commit | 3506646251b03a13880f0a8e95256439dcdddab9 (patch) | |
tree | 975644cd1842760c7dfeb524b8237611f34f24e7 | |
parent | Merge pull request #478 from python-discord/content-migration (diff) | |
parent | Make tests expect 302. (diff) |
Merge pull request #485 from python-discord/ks129/dewikification/redirection
Dewikification - Implement redirections app
-rw-r--r-- | docs/README.md | 4 | ||||
-rw-r--r-- | pydis_site/apps/content/resources/privacy.md | 7 | ||||
-rw-r--r-- | pydis_site/apps/home/urls.py | 1 | ||||
-rw-r--r-- | pydis_site/apps/redirect/__init__.py | 0 | ||||
-rw-r--r-- | pydis_site/apps/redirect/apps.py | 7 | ||||
-rw-r--r-- | pydis_site/apps/redirect/migrations/__init__.py | 0 | ||||
-rw-r--r-- | pydis_site/apps/redirect/redirects.yaml | 194 | ||||
-rw-r--r-- | pydis_site/apps/redirect/tests.py | 58 | ||||
-rw-r--r-- | pydis_site/apps/redirect/urls.py | 19 | ||||
-rw-r--r-- | pydis_site/apps/redirect/views.py | 26 | ||||
-rw-r--r-- | pydis_site/settings.py | 3 | ||||
-rw-r--r-- | pydis_site/templates/base/navbar.html | 18 |
12 files changed, 328 insertions, 9 deletions
diff --git a/docs/README.md b/docs/README.md index 2e9f15a1..5af383b4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,3 +11,7 @@ This directory contains useful documentation for working with and using our site * [Development with Docker](setup.md#development-with-docker) * [Development with `pip`](setup.md#development-with-pip) + +> Note: If you're looking to add redirects to the site, the `redirects` app is **NOT** the way to go. +> Convenience redirects should use our [Cloudflare Worker](https://github.com/python-discord/workers/tree/main/short-urls). +> Ask in the server if you have any questions! diff --git a/pydis_site/apps/content/resources/privacy.md b/pydis_site/apps/content/resources/privacy.md new file mode 100644 index 00000000..88916b79 --- /dev/null +++ b/pydis_site/apps/content/resources/privacy.md @@ -0,0 +1,7 @@ +--- +title: Privacy Policy +description: Our server's privacy policy. +icon: fab fa-discord +--- + +You should be redirected. If you are not, [please click here](https://www.notion.so/pythondiscord/Python-Discord-Privacy-ee2581fea4854ddcb1ebc06c1dbb9fbd). diff --git a/pydis_site/apps/home/urls.py b/pydis_site/apps/home/urls.py index 3c716875..e475c491 100644 --- a/pydis_site/apps/home/urls.py +++ b/pydis_site/apps/home/urls.py @@ -6,6 +6,7 @@ from .views import HomeView app_name = 'home' urlpatterns = [ path('', HomeView.as_view(), name='home'), + path('', include('pydis_site.apps.redirect.urls')), path('admin/', admin.site.urls), path('resources/', include('pydis_site.apps.resources.urls')), path('pages/', include('pydis_site.apps.content.urls')), diff --git a/pydis_site/apps/redirect/__init__.py b/pydis_site/apps/redirect/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pydis_site/apps/redirect/__init__.py diff --git a/pydis_site/apps/redirect/apps.py b/pydis_site/apps/redirect/apps.py new file mode 100644 index 00000000..9b70d169 --- /dev/null +++ b/pydis_site/apps/redirect/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class RedirectConfig(AppConfig): + """AppConfig instance for Redirect app.""" + + name = 'redirect' diff --git a/pydis_site/apps/redirect/migrations/__init__.py b/pydis_site/apps/redirect/migrations/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pydis_site/apps/redirect/migrations/__init__.py diff --git a/pydis_site/apps/redirect/redirects.yaml b/pydis_site/apps/redirect/redirects.yaml new file mode 100644 index 00000000..ce789b61 --- /dev/null +++ b/pydis_site/apps/redirect/redirects.yaml @@ -0,0 +1,194 @@ +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# The redirects here are for dewikification backwards compatibility +# and SHOULD NOT be used for adding new redirects for convenience. +# +# Convenience redirects should be added using our cloudflare worker +# at https://github.com/python-discord/workers/tree/main/short-urls +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +# Root pages +roles_redirect: + original_path: pages/roles/ + redirect_route: "content:page_category" + redirect_arguments: ["server-info/roles"] + +roles_expectations_redirect: + original_path: pages/roles/staff-role-expectations/ + redirect_route: "content:page_category" + redirect_arguments: ["server-info/staff-role-expectations"] + +contributing_redirect: + original_path: pages/contributing/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/pydis-guides/contributing"] + +# Guides +guides_redirect: + original_path: pages/resources/guides/ + redirect_route: "content:page_category" + redirect_arguments: ["guides"] + +# - Python guides +discord_py_redirect: + original_path: pages/resources/guides/discordpy/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/python-guides/discordpy"] + +mutability_redirect: + original_path: pages/resources/guides/core-concepts/mutability/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/python-guides/mutability"] + +function_params_redirect: + original_path: pages/resources/guides/core-concepts/parameters-and-arguments/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/python-guides/parameters-and-arguments"] + +# - Pydis guides +help_channel_redirect: + original_path: pages/resources/guides/help-channels/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/pydis-guides/help-channel-guide"] + +good_questions_redirect: + original_path: pages/resources/guides/asking-good-questions/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/pydis-guides/asking-good-questions"] + +helping_others_redirect: + original_path: pages/resources/guides/helping-others/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/pydis-guides/helping-others"] + +code_review_redirect: + original_path: pages/resources/guides/code-reviews-primer/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/pydis-guides/code-reviews-primer"] + +off-topic_redirect: + original_path: pages/resources/guides/off-topic-etiquette/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/pydis-guides/off-topic-etiquette"] + +# Resources +resources_index_redirect: + original_path: pages/resources/ + redirect_route: "resources:index" + +resources_resources_redirect: + original_path: pages/resources/<str:category>/ + redirect_route: "resources:resources" + +# Events +events_index_redirect: + original_path: pages/events/ + redirect_route: "events:index" + +events_code_jams_index_redirect: + original_path: pages/code-jams/ + redirect_route: "events:page" + redirect_arguments: ["code-jams"] + +events_code_jams_one_redirect: + original_path: pages/code-jams/code-jam-1-snakes-bot/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/1"] + +events_code_jams_two_redirect: + original_path: pages/code-jams/code-jam-2/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/2"] + +events_code_jams_three_redirect: + original_path: pages/code-jams/code-jam-3/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/3"] + +events_code_jams_four_redirect: + original_path: pages/code-jams/code-jam-4/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/4"] + +events_code_jams_five_redirect: + original_path: pages/code-jams/code-jam-5/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/5"] + +events_code_jams_six_redirect: + original_path: pages/code-jams/code-jam-6/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/6"] + +events_code_jams_six_rules_redirect: + original_path: pages/code-jams/code-jam-6/rules/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/6/rules"] + +events_code_jams_seven_redirect: + original_path: pages/code-jams/code-jam-7/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/7"] + +events_code_jams_seven_rules_redirect: + original_path: pages/code-jams/code-jam-7/rules/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/7/rules"] + +events_code_jams_how_to_use_git_redirect: + original_path: pages/code-jams/using-git/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/using-git"] + +events_code_jams_judging_redirect: + original_path: pages/code-jams/judging/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/judging"] + +events_code_jams_pull_request_redirect: + original_path: pages/code-jams/pull-request/ + redirect_route: "events:page" + redirect_arguments: ["code-jams/pull-request"] + +events_game_jams_twenty_twenty_index_redirect: + original_path: pages/events/game-jam-2020/ + redirect_route: "events:page" + redirect_arguments: ["game-jams/2020"] + +events_game_jams_twenty_twenty_judging_redirect: + original_path: pages/events/game-jam-2020/judging/ + redirect_route: "events:page" + redirect_arguments: ["game-jams/2020/judging"] + +events_game_jams_twenty_twenty_project_setup_redirect: + original_path: pages/events/game-jam-2020/project-setup/ + redirect_route: "events:page" + redirect_arguments: ["game-jams/2020/project-setup"] + +events_game_jams_twenty_twenty_rules_redirect: + original_path: pages/events/game-jam-2020/rules/ + redirect_route: "events:page" + redirect_arguments: ["game-jams/2020/rules"] + +events_game_jams_twenty_twenty_technical_requirements_redirect: + original_path: pages/events/game-jam-2020/technical-requirements + redirect_route: "events:page" + redirect_arguments: ["game-jams/2020/technical-requirements"] + +# This are overrides for the contributing prefix redirect +security_notice_redirect: + original_path: pages/contributing/security-notice/ + redirect_route: "content:page_category" + redirect_arguments: ["security-notice"] + +sir-lancebot_env_var_redirect: + original_path: pages/contributing/sir-lancebot/sir-lancebot-env-var-reference/ + redirect_route: "content:page_category" + redirect_arguments: ["guides/pydis-guides/contributing/sir-lancebot/env-var-reference"] + +# Prefix redirects +# Prefix redirects must be last in each group. +guides_pydis_guides_contributing_prefix_redirect: + original_path: pages/contributing/<path:path>/ # path:path will be joined together with static arguments. + redirect_route: "content:page_category" + redirect_arguments: ["guides/pydis-guides/contributing/"] # It is important to put / at end in prefix redirect! + prefix_redirect: true diff --git a/pydis_site/apps/redirect/tests.py b/pydis_site/apps/redirect/tests.py new file mode 100644 index 00000000..c145ecda --- /dev/null +++ b/pydis_site/apps/redirect/tests.py @@ -0,0 +1,58 @@ +import yaml +from django.conf import settings +from django.test import TestCase +from django.urls import reverse + +TESTING_ARGUMENTS = { + "resources_resources_redirect": ("reading",), + "guides_pydis_guides_contributing_prefix_redirect": ("sir-lancebot/env-var-reference",), +} + + +class RedirectTests(TestCase): + """Survival tests for redirects.""" + + def test_redirects(self) -> None: + """ + Should redirect to given route based on redirect rules. + + Makes sure that every redirect: + 1. Redirects only once. + 2. Redirects to right URL. + 3. Resulting page status code is 200. + """ + for name, data in yaml.safe_load(settings.REDIRECTIONS_PATH.read_text()).items(): + with self.subTest( + original_path=data["original_path"], + redirect_route=data["redirect_route"], + name=name, + redirect_arguments=tuple(data.get("redirect_arguments", ())), + args=TESTING_ARGUMENTS.get(name, ()) + ): + resp = self.client.get( + reverse( + f"home:redirect:{name}", + args=TESTING_ARGUMENTS.get(name, ()) + ), + follow=True + ) + + if data.get("prefix_redirect", False): + expected_args = ( + "".join( + tuple(data.get("redirect_arguments", ())) + TESTING_ARGUMENTS.get(name, ()) + ), + ) + else: + expected_args = TESTING_ARGUMENTS.get(name, ()) + tuple(data.get("redirect_arguments", ())) + + self.assertEqual(1, len(resp.redirect_chain)) + self.assertRedirects( + resp, + reverse( + f"home:{data['redirect_route']}", + args=expected_args + ), + status_code=302 + ) + self.assertEqual(resp.status_code, 200) diff --git a/pydis_site/apps/redirect/urls.py b/pydis_site/apps/redirect/urls.py new file mode 100644 index 00000000..6187af17 --- /dev/null +++ b/pydis_site/apps/redirect/urls.py @@ -0,0 +1,19 @@ +import yaml +from django.conf import settings +from django.urls import path + +from pydis_site.apps.redirect.views import CustomRedirectView + +app_name = "redirect" +urlpatterns = [ + path( + data["original_path"], + CustomRedirectView.as_view( + pattern_name=data["redirect_route"], + static_args=tuple(data.get("redirect_arguments", ())), + prefix_redirect=data.get("prefix_redirect", False) + ), + name=name + ) + for name, data in yaml.safe_load(settings.REDIRECTIONS_PATH.read_text()).items() +] diff --git a/pydis_site/apps/redirect/views.py b/pydis_site/apps/redirect/views.py new file mode 100644 index 00000000..9dc9881a --- /dev/null +++ b/pydis_site/apps/redirect/views.py @@ -0,0 +1,26 @@ +import typing as t + +from django.views.generic import RedirectView + + +class CustomRedirectView(RedirectView): + """Extended RedirectView for manual route args.""" + + # We want temporary redirects for the time being, after this is running on prod and + # stable we can enable permanent redirects. + permanent = False + static_args = () + prefix_redirect = False + + @classmethod + def as_view(cls, **initkwargs): + """Overwrites original as_view to add static args.""" + return super().as_view(**initkwargs) + + def get_redirect_url(self, *args, **kwargs) -> t.Optional[str]: + """Extends default behaviour to use static args.""" + args = self.static_args + args + tuple(kwargs.values()) + if self.prefix_redirect: + args = ("".join(args),) + + return super().get_redirect_url(*args) diff --git a/pydis_site/settings.py b/pydis_site/settings.py index d409bb21..65bd8e7a 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -87,6 +87,7 @@ INSTALLED_APPS = [ 'pydis_site.apps.resources', 'pydis_site.apps.content', 'pydis_site.apps.events', + 'pydis_site.apps.redirect', 'django.contrib.admin', 'django.contrib.auth', @@ -290,3 +291,5 @@ EVENTS_PAGES_PATH = Path(BASE_DIR, "pydis_site", "templates", "events", "pages") # Path for content pages CONTENT_PAGES_PATH = Path(BASE_DIR, "pydis_site", "apps", "content", "resources") + +REDIRECTIONS_PATH = Path(BASE_DIR, "pydis_site", "apps", "redirect", "redirects.yaml") diff --git a/pydis_site/templates/base/navbar.html b/pydis_site/templates/base/navbar.html index 6c8d52a1..ebafa269 100644 --- a/pydis_site/templates/base/navbar.html +++ b/pydis_site/templates/base/navbar.html @@ -61,35 +61,35 @@ More </a> <div class="navbar-dropdown"> - <a class="navbar-item" href="#"> + <a class="navbar-item" href="{% url "resources:index" %}"> Resources </a> - <a class="navbar-item" href="#"> + <a class="navbar-item" href="{% url "resources:resources" category="tools" %}"> Tools </a> - <a class="navbar-item" href="#"> + <a class="navbar-item" href="{% url "content:page_category" location="guides/pydis-guides/contributing"%}"> Contributing </a> - <a class="navbar-item" href="#"> + <a class="navbar-item" href="{% url "content:page_category" location="frequently-asked-questions" %}"> FAQ </a> - <a class="navbar-item" href="#"> + <a class="navbar-item" href="{% url "content:page_category" location="rules" %}"> Rules </a> - <a class="navbar-item" href="#"> + <a class="navbar-item" href="{% url "content:page_category" location="code-of-conduct" %}"> Code of Conduct </a> - <a class="navbar-item" href="#"> + <a class="navbar-item" href="{% url "content:page_category" location="privacy" %}"> Privacy </a> <hr class="navbar-divider"> <div class="navbar-item"> <strong>Events</strong> </div> - <a class="navbar-item" href="#"> + <a class="navbar-item" href="{% url "events:page" path="code-jams/7" %}"> Most Recent: Code Jam 7 </a> - <a class="navbar-item" href="#"> + <a class="navbar-item" href="{% url "events:index" %}"> All events </a> </div> |