diff options
| -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> | 
