From 5c37aee79c04d9199dc47b9d60f9899a0f6571d9 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 19 May 2021 09:02:21 +0200 Subject: Dramatically simplify resources. We don't need _category_info, we don't need subcategories, we this will be much simpler now. Also, rglob is nice. --- pydis_site/apps/resources/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site/apps/resources/urls.py') diff --git a/pydis_site/apps/resources/urls.py b/pydis_site/apps/resources/urls.py index 19142081..cd4f53e7 100644 --- a/pydis_site/apps/resources/urls.py +++ b/pydis_site/apps/resources/urls.py @@ -5,5 +5,5 @@ from pydis_site.apps.resources import views app_name = "resources" urlpatterns = [ path("", views.ResourcesView.as_view(), name="index"), - path("/", views.ResourcesListView.as_view(), name="resources") + path("list/", views.ResourcesListView.as_view(), name="resources") ] -- cgit v1.2.3 From c781ed1f80188ffb274eeada974172f4aa07c0b5 Mon Sep 17 00:00:00 2001 From: fisher60 Date: Fri, 23 Jul 2021 20:20:10 -0500 Subject: change resources to prepare for smart resource search --- pydis_site/apps/resources/urls.py | 2 +- pydis_site/apps/resources/views/__init__.py | 4 +- pydis_site/apps/resources/views/resources.py | 10 +- pydis_site/static/css/resources/resources.css | 51 +++++----- pydis_site/templates/resources/resources.html | 131 ++++++++++++++------------ 5 files changed, 105 insertions(+), 93 deletions(-) (limited to 'pydis_site/apps/resources/urls.py') diff --git a/pydis_site/apps/resources/urls.py b/pydis_site/apps/resources/urls.py index cd4f53e7..c8d441df 100644 --- a/pydis_site/apps/resources/urls.py +++ b/pydis_site/apps/resources/urls.py @@ -4,6 +4,6 @@ from pydis_site.apps.resources import views app_name = "resources" urlpatterns = [ - path("", views.ResourcesView.as_view(), name="index"), + path("", views.resources.resource_view, name="index"), path("list/", views.ResourcesListView.as_view(), name="resources") ] diff --git a/pydis_site/apps/resources/views/__init__.py b/pydis_site/apps/resources/views/__init__.py index 8eb383b5..c89071c5 100644 --- a/pydis_site/apps/resources/views/__init__.py +++ b/pydis_site/apps/resources/views/__init__.py @@ -1,4 +1,4 @@ -from .resources import ResourcesView +from .resources import resource_view from .resources_list import ResourcesListView -__all__ = ["ResourcesView", "ResourcesListView"] +__all__ = ["resource_view", "ResourcesListView"] diff --git a/pydis_site/apps/resources/views/resources.py b/pydis_site/apps/resources/views/resources.py index 25ce3e50..dfd21682 100644 --- a/pydis_site/apps/resources/views/resources.py +++ b/pydis_site/apps/resources/views/resources.py @@ -1,7 +1,11 @@ from django.views.generic import TemplateView +from django.shortcuts import render +# class ResourcesView(TemplateView): +# """View for resources index page.""" +# +# template_name = "resources/resources.html" -class ResourcesView(TemplateView): - """View for resources index page.""" - template_name = "resources/resources.html" +def resource_view(request): + return render(request, template_name="resources/resources.html") diff --git a/pydis_site/static/css/resources/resources.css b/pydis_site/static/css/resources/resources.css index cf4cb472..a9226647 100644 --- a/pydis_site/static/css/resources/resources.css +++ b/pydis_site/static/css/resources/resources.css @@ -1,29 +1,30 @@ -.box, .tile.is-parent { - transition: 0.1s ease-out; -} -.box { - min-height: 15vh; -} -.tile.is-parent:hover .box { - box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); -} -.tile.is-parent:hover { - padding: 0.65rem 0.85rem 0.85rem 0.65rem; - filter: saturate(1.1) brightness(1.1); -} +/*.box, .tile.is-parent {*/ +/* transition: 0.1s ease-out;*/ +/*}*/ +/*.box {*/ +/* min-height: 15vh;*/ +/*}*/ +/*.tile.is-parent:hover .box {*/ +/* box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);*/ +/*}*/ +/*.tile.is-parent:hover {*/ +/* padding: 0.65rem 0.85rem 0.85rem 0.65rem;*/ +/* filter: saturate(1.1) brightness(1.1);*/ +/*}*/ -#readingBlock { - background-image: linear-gradient(141deg, #911eb4 0%, #b631de 71%, #cf4bf7 100%); -} +/*#readingBlock {*/ +/* background-image: linear-gradient(141deg, #911eb4 0%, #b631de 71%, #cf4bf7 100%);*/ +/*}*/ -#interactiveBlock { - background-image: linear-gradient(141deg, #d05600 0%, #da722a 71%, #e68846 100%); -} +/*#interactiveBlock {*/ +/* background-image: linear-gradient(141deg, #d05600 0%, #da722a 71%, #e68846 100%);*/ +/*}*/ -#communitiesBlock { - background-image: linear-gradient(141deg, #3b756f 0%, #3a847c 71%, #41948b 100%); -} +/*#communitiesBlock {*/ +/* background-image: linear-gradient(141deg, #3b756f 0%, #3a847c 71%, #41948b 100%);*/ +/*}*/ + +/*#podcastsBlock {*/ +/* background-image: linear-gradient(141deg, #232382 0%, #30309c 71%, #4343ad 100%);*/ +/*}*/ -#podcastsBlock { - background-image: linear-gradient(141deg, #232382 0%, #30309c 71%, #4343ad 100%); -} diff --git a/pydis_site/templates/resources/resources.html b/pydis_site/templates/resources/resources.html index 04744f90..7eb21432 100644 --- a/pydis_site/templates/resources/resources.html +++ b/pydis_site/templates/resources/resources.html @@ -12,79 +12,86 @@
-

Resources

+

Resources

+
+
+
+ {% endblock %} -- cgit v1.2.3 From 4ee8d527a2cf0ac7e4fd977ffd29e560b3cd48cb Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Thu, 27 Jan 2022 10:16:52 +0100 Subject: Greatly simplify the backend. Here we're getting rid of all filtering and search functionality on the backend. We'll be handling this on the client-side from now on. --- pydis_site/apps/resources/resource_search.py | 59 ---------------------- pydis_site/apps/resources/tests/test_views.py | 13 ----- pydis_site/apps/resources/urls.py | 3 +- pydis_site/apps/resources/views/__init__.py | 5 +- pydis_site/apps/resources/views/resources_list.py | 18 ------- pydis_site/templates/resources/resources_list.html | 52 ------------------- 6 files changed, 3 insertions(+), 147 deletions(-) delete mode 100644 pydis_site/apps/resources/resource_search.py delete mode 100644 pydis_site/apps/resources/views/resources_list.py delete mode 100644 pydis_site/templates/resources/resources_list.html (limited to 'pydis_site/apps/resources/urls.py') diff --git a/pydis_site/apps/resources/resource_search.py b/pydis_site/apps/resources/resource_search.py deleted file mode 100644 index 1e23089b..00000000 --- a/pydis_site/apps/resources/resource_search.py +++ /dev/null @@ -1,59 +0,0 @@ -import typing as t -from collections import defaultdict -from functools import reduce -from operator import and_, or_ -from pathlib import Path -from types import MappingProxyType - -import yaml -from django.conf import settings - - -def _transform_name(resource_name: str) -> str: - return resource_name.title().replace('And', 'and', -1) - - -Resource = dict[str, t.Union[str, list[dict[str, str]], dict[str, list[str]]]] - -RESOURCES_PATH = Path(settings.BASE_DIR, "pydis_site", "apps", "resources", "resources") - -RESOURCES: MappingProxyType[str, Resource] = MappingProxyType({ - path.stem: yaml.safe_load(path.read_text()) - for path in RESOURCES_PATH.rglob("*.yaml") -}) - -_resource_table = {category: defaultdict(set) for category in ( - "topics", - "payment_tiers", - "complexity", - "type" -)} - -for name, resource in RESOURCES.items(): - for category, tags in resource['tags'].items(): - for tag in tags: - _resource_table[category][_transform_name(tag)].add(name) - -# Freeze the resources table -RESOURCE_TABLE = MappingProxyType({ - category: MappingProxyType(d) - for category, d in _resource_table.items() -}) - -ALL_RESOURCE_NAMES = frozenset(RESOURCES.keys()) - - -def get_resources_from_search(search_categories: dict[str, set[str]]) -> list[Resource]: - """Returns a list of all resources that match the given search terms.""" - resource_names_that_match = reduce( - and_, - ( - reduce( - or_, - (RESOURCE_TABLE[category][label] for label in labels), - set() - ) or ALL_RESOURCE_NAMES - for category, labels in search_categories.items() - ) - ) - return [RESOURCES[name_] for name_ in resource_names_that_match] diff --git a/pydis_site/apps/resources/tests/test_views.py b/pydis_site/apps/resources/tests/test_views.py index 2e9efc1d..f96a04b0 100644 --- a/pydis_site/apps/resources/tests/test_views.py +++ b/pydis_site/apps/resources/tests/test_views.py @@ -16,16 +16,3 @@ class TestResourcesView(TestCase): url = reverse("resources:index") response = self.client.get(url) self.assertEqual(response.status_code, 200) - - -class TestResourcesListView(TestCase): - def test_valid_resource_list_200(self): - """Check does site return code 200 when visiting valid resource list.""" - url = reverse("resources:resources") - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - @patch("pydis_site.apps.resources.resource_search.RESOURCES_PATH", TESTING_RESOURCES_PATH) - def test_filter_resource_list(self): - """TODO: Check that we can correctly filter resources with GET parameters.""" - pass diff --git a/pydis_site/apps/resources/urls.py b/pydis_site/apps/resources/urls.py index c8d441df..3db26417 100644 --- a/pydis_site/apps/resources/urls.py +++ b/pydis_site/apps/resources/urls.py @@ -4,6 +4,5 @@ from pydis_site.apps.resources import views app_name = "resources" urlpatterns = [ - path("", views.resources.resource_view, name="index"), - path("list/", views.ResourcesListView.as_view(), name="resources") + path("", views.resources.ResourceView.as_view(), name="index"), ] diff --git a/pydis_site/apps/resources/views/__init__.py b/pydis_site/apps/resources/views/__init__.py index c89071c5..986f3e10 100644 --- a/pydis_site/apps/resources/views/__init__.py +++ b/pydis_site/apps/resources/views/__init__.py @@ -1,4 +1,3 @@ -from .resources import resource_view -from .resources_list import ResourcesListView +from .resources import ResourceView -__all__ = ["resource_view", "ResourcesListView"] +__all__ = ["ResourceView"] diff --git a/pydis_site/apps/resources/views/resources_list.py b/pydis_site/apps/resources/views/resources_list.py deleted file mode 100644 index 30498c8f..00000000 --- a/pydis_site/apps/resources/views/resources_list.py +++ /dev/null @@ -1,18 +0,0 @@ -from typing import Any, Dict - -from django.views.generic import TemplateView - -from pydis_site.apps.resources.resource_search import RESOURCES - - -class ResourcesListView(TemplateView): - """Shows specific resources list.""" - - template_name = "resources/resources_list.html" - - def get_context_data(self, **kwargs) -> Dict[str, Any]: - """Add resources and subcategories data into context.""" - context = super().get_context_data(**kwargs) - context["resources"] = RESOURCES - - return context diff --git a/pydis_site/templates/resources/resources_list.html b/pydis_site/templates/resources/resources_list.html deleted file mode 100644 index e2be3cb7..00000000 --- a/pydis_site/templates/resources/resources_list.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "base/base.html" %} -{% load as_icon %} -{% load static %} - -{% block title %}{{ category_info.name }}{% endblock %} -{% block head %} - -{% endblock %} - -{% block content %} - {% include "base/navbar.html" %} - - - -
-
-
-

{{ category_info.name }}

-

{{ category_info.description|safe }}

-
- {% for resource in resources|dictsort:"position" %} - {% include "resources/resource_box.html" %} - {% endfor %} - - {% for subcategory in subcategories|dictsort:"category_info.position" %} -

- - {{ subcategory.category_info.name }} - -

-

{{ subcategory.category_info.description|safe }}

- - {% for resource in subcategory.resources|dictsort:"position" %} - {% with category_info=subcategory.category_info %} - {% include "resources/resource_box.html" %} - {% endwith %} - {% endfor %} - {% endfor %} -
-
-
-
-{% endblock %} -- cgit v1.2.3 From 473a15a3dbaf0ff2ccc7fdbeaafc96e0e853949e Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 29 Jan 2022 12:42:12 +0100 Subject: Distill the resources page. This should allow Netlify Deploy Previews. --- pydis_site/apps/resources/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'pydis_site/apps/resources/urls.py') diff --git a/pydis_site/apps/resources/urls.py b/pydis_site/apps/resources/urls.py index 3db26417..5d5ae7fb 100644 --- a/pydis_site/apps/resources/urls.py +++ b/pydis_site/apps/resources/urls.py @@ -1,8 +1,8 @@ -from django.urls import path +from django_distill import distill_path from pydis_site.apps.resources import views app_name = "resources" urlpatterns = [ - path("", views.resources.ResourceView.as_view(), name="index"), + distill_path("", views.resources.ResourceView.as_view(), name="index"), ] -- cgit v1.2.3 From e53a3a15d0213b3854a3c9619390f0a0e35c4bf6 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 30 Jan 2022 11:41:44 +0100 Subject: Redirects from old endpoints now filter correctly. For example, navigating to pydis.com/resources/communities will now correctly redirect to pydis.com/resources/?type=community. --- pydis_site/apps/redirect/redirects.yaml | 10 ++++++---- pydis_site/apps/resources/urls.py | 1 + pydis_site/apps/resources/views/resources.py | 25 ++++++++----------------- pydis_site/static/js/resources.js | 8 ++++++++ pydis_site/templates/resources/resources.html | 1 + 5 files changed, 24 insertions(+), 21 deletions(-) (limited to 'pydis_site/apps/resources/urls.py') diff --git a/pydis_site/apps/redirect/redirects.yaml b/pydis_site/apps/redirect/redirects.yaml index bee65103..533b9e25 100644 --- a/pydis_site/apps/redirect/redirects.yaml +++ b/pydis_site/apps/redirect/redirects.yaml @@ -90,30 +90,32 @@ resources_index_redirect: resources_reading_redirect: original_path: resources/reading/ redirect_route: "resources:index" + redirect_arguments: ["book"] resources_videos_redirect: original_path: resources/videos/ redirect_route: "resources:index" - -resources_interactive_redirect: - original_path: resources/interactive/ - redirect_route: "resources:index" + redirect_arguments: ["video"] resources_courses_redirect: original_path: resources/courses/ redirect_route: "resources:index" + redirect_arguments: ["course"] resources_communities_redirect: original_path: resources/communities/ redirect_route: "resources:index" + redirect_arguments: ["community"] resources_podcasts_redirect: original_path: resources/podcasts/ redirect_route: "resources:index" + redirect_arguments: ["podcast"] resources_tools_redirect: original_path: resources/tools/ redirect_route: "resources:index" + redirect_arguments: ["tool"] # Events events_index_redirect: diff --git a/pydis_site/apps/resources/urls.py b/pydis_site/apps/resources/urls.py index 5d5ae7fb..ed24dc99 100644 --- a/pydis_site/apps/resources/urls.py +++ b/pydis_site/apps/resources/urls.py @@ -5,4 +5,5 @@ from pydis_site.apps.resources import views app_name = "resources" urlpatterns = [ distill_path("", views.resources.ResourceView.as_view(), name="index"), + distill_path("/", views.resources.ResourceView.as_view(), name="index"), ] diff --git a/pydis_site/apps/resources/views/resources.py b/pydis_site/apps/resources/views/resources.py index d0b8bae7..b828d89a 100644 --- a/pydis_site/apps/resources/views/resources.py +++ b/pydis_site/apps/resources/views/resources.py @@ -1,8 +1,9 @@ from pathlib import Path +import typing as t import yaml from django.core.handlers.wsgi import WSGIRequest -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseNotFound from django.shortcuts import render from django.views import View @@ -78,22 +79,12 @@ class ResourceView(View): } } - @staticmethod - def _get_filter_options(request: WSGIRequest) -> dict[str, set]: - """Get the requested filter options out of the request object.""" - return { - option: set(request.GET.get(url_param, "").split(",")[:-1]) - for option, url_param in ( - ('topics', 'topics'), - ('type', 'type'), - ('payment_tiers', 'payment'), - ('difficulty', 'difficulty'), - ) - } - - def get(self, request: WSGIRequest) -> HttpResponse: + def get(self, request: WSGIRequest, resource_type: t.Optional[str] = None) -> HttpResponse: """List out all the resources, and any filtering options from the URL.""" - filter_options = self._get_filter_options(request) + + # Add type filtering if the request is made to somewhere like /resources/video + if resource_type and resource_type.title() not in self.filters['Type']['filters']: + return HttpResponseNotFound() return render( request, @@ -101,6 +92,6 @@ class ResourceView(View): context={ "resources": self.resources, "filters": self.filters, - "filter_options": filter_options, + "resource_type": resource_type, } ) diff --git a/pydis_site/static/js/resources.js b/pydis_site/static/js/resources.js index eaca3978..bf570097 100644 --- a/pydis_site/static/js/resources.js +++ b/pydis_site/static/js/resources.js @@ -160,6 +160,14 @@ function updateUI() { // Executed when the page has finished loading. document.addEventListener("DOMContentLoaded", function () { + /* Check if the user has navigated to one of the old resource pages, + like pydis.com/resources/communities. In this case, we'll rewrite + the URL before we do anything else. */ + let resourceTypeInput = $("#resource-type-input").val(); + if (resourceTypeInput.length !== 0) { + window.history.replaceState(null, document.title, `../?type=${resourceTypeInput}`); + } + // Update the filters on page load to reflect URL parameters. $('.filter-box-tag').hide(); deserializeURLParams(); diff --git a/pydis_site/templates/resources/resources.html b/pydis_site/templates/resources/resources.html index 9ebebe1f..7a284fd6 100644 --- a/pydis_site/templates/resources/resources.html +++ b/pydis_site/templates/resources/resources.html @@ -16,6 +16,7 @@ {% block content %} {% include "base/navbar.html" %} +
{# Filtering toolbox #} -- cgit v1.2.3 From 4b17d714fb0cb7d1fd199a2ca123b6d18fe377e7 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 30 Jan 2022 14:02:19 +0100 Subject: Fix resource redirects for deploy previews. This could probably be improved by making it less hardcoded, but since there's a bunch of special cases, and since it only affects deploy previews, I'm choosing to solve this with a simple solution. I don't think realistically that we're gonna be adding a ton of resource types down the line anyway. --- pydis_site/apps/content/urls.py | 2 +- pydis_site/apps/resources/urls.py | 40 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) (limited to 'pydis_site/apps/resources/urls.py') diff --git a/pydis_site/apps/content/urls.py b/pydis_site/apps/content/urls.py index fe7c2852..f8496095 100644 --- a/pydis_site/apps/content/urls.py +++ b/pydis_site/apps/content/urls.py @@ -30,7 +30,7 @@ def __get_all_files(root: Path, folder: typing.Optional[Path] = None) -> list[st def get_all_pages() -> typing.Iterator[dict[str, str]]: - """Yield a dict of all pag categories.""" + """Yield a dict of all page categories.""" for location in __get_all_files(Path("pydis_site", "apps", "content", "resources")): yield {"location": location} diff --git a/pydis_site/apps/resources/urls.py b/pydis_site/apps/resources/urls.py index ed24dc99..aa3892b6 100644 --- a/pydis_site/apps/resources/urls.py +++ b/pydis_site/apps/resources/urls.py @@ -1,9 +1,47 @@ +import typing + from django_distill import distill_path from pydis_site.apps.resources import views +# This is only used for `distill_path`, so that the Netlify Deploy Previews +# can successfully redirect to static pages. This could probably be improved by +# making it less hardcoded, but since there's a bunch of special cases, and since +# it only affects deploy previews, I'm choosing to solve this with a simple solution. +VALID_RESOURCE_TYPES = [ + "book", + "books", + "reading", + "podcast", + "podcasts", + "interactive", + "videos", + "video", + "courses", + "course", + "communities", + "community", + "tutorial", + "tutorials", + "tool", + "tools", + "project ideas", + "project-ideas", +] + + +def get_all_pages() -> typing.Iterator[dict[str, str]]: + for resource_type in VALID_RESOURCE_TYPES: + yield {"resource_type": resource_type} + + app_name = "resources" urlpatterns = [ distill_path("", views.resources.ResourceView.as_view(), name="index"), - distill_path("/", views.resources.ResourceView.as_view(), name="index"), + distill_path( + "/", + views.resources.ResourceView.as_view(), + name="index", + distill_func=get_all_pages, + ), ] -- cgit v1.2.3 From dda3e355ac31dc4b9629f2e9e63474bbba69d740 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 30 Jan 2022 14:29:48 +0100 Subject: Refactor: collapsibles as a stand-alone component --- pydis_site/apps/resources/urls.py | 1 + pydis_site/static/css/collapsibles.css | 12 ++ pydis_site/static/css/content/page.css | 13 -- pydis_site/static/js/collapsibles.js | 13 ++ pydis_site/static/js/content/page.js | 13 -- pydis_site/static/js/resources.js | 236 -------------------------- pydis_site/static/js/resources/resources.js | 236 ++++++++++++++++++++++++++ pydis_site/templates/content/base.html | 3 +- pydis_site/templates/resources/resources.html | 6 +- 9 files changed, 267 insertions(+), 266 deletions(-) create mode 100644 pydis_site/static/css/collapsibles.css create mode 100644 pydis_site/static/js/collapsibles.js delete mode 100644 pydis_site/static/js/content/page.js delete mode 100644 pydis_site/static/js/resources.js create mode 100644 pydis_site/static/js/resources/resources.js (limited to 'pydis_site/apps/resources/urls.py') diff --git a/pydis_site/apps/resources/urls.py b/pydis_site/apps/resources/urls.py index aa3892b6..044f7072 100644 --- a/pydis_site/apps/resources/urls.py +++ b/pydis_site/apps/resources/urls.py @@ -31,6 +31,7 @@ VALID_RESOURCE_TYPES = [ def get_all_pages() -> typing.Iterator[dict[str, str]]: + """Get all the valid options for the resource_type path selector.""" for resource_type in VALID_RESOURCE_TYPES: yield {"resource_type": resource_type} diff --git a/pydis_site/static/css/collapsibles.css b/pydis_site/static/css/collapsibles.css new file mode 100644 index 00000000..7b76d8d5 --- /dev/null +++ b/pydis_site/static/css/collapsibles.css @@ -0,0 +1,12 @@ +.collapsible { + cursor: pointer; + width: 100%; + border: none; + outline: none; +} + +.collapsible-content { + overflow: hidden; + max-height: 0; + transition: max-height 0.2s ease-out; +} \ No newline at end of file diff --git a/pydis_site/static/css/content/page.css b/pydis_site/static/css/content/page.css index 2d4bd325..d831f86d 100644 --- a/pydis_site/static/css/content/page.css +++ b/pydis_site/static/css/content/page.css @@ -77,16 +77,3 @@ ul.menu-list.toc { li img { margin-top: 0.5em; } - -.collapsible { - cursor: pointer; - width: 100%; - border: none; - outline: none; -} - -.collapsible-content { - overflow: hidden; - max-height: 0; - transition: max-height 0.2s ease-out; -} diff --git a/pydis_site/static/js/collapsibles.js b/pydis_site/static/js/collapsibles.js new file mode 100644 index 00000000..366a033c --- /dev/null +++ b/pydis_site/static/js/collapsibles.js @@ -0,0 +1,13 @@ +document.addEventListener("DOMContentLoaded", () => { + const headers = document.getElementsByClassName("collapsible"); + for (const header of headers) { + header.addEventListener("click", () => { + var content = header.nextElementSibling; + if (content.style.maxHeight){ + content.style.maxHeight = null; + } else { + content.style.maxHeight = content.scrollHeight + "px"; + } + }); + } +}); diff --git a/pydis_site/static/js/content/page.js b/pydis_site/static/js/content/page.js deleted file mode 100644 index 366a033c..00000000 --- a/pydis_site/static/js/content/page.js +++ /dev/null @@ -1,13 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const headers = document.getElementsByClassName("collapsible"); - for (const header of headers) { - header.addEventListener("click", () => { - var content = header.nextElementSibling; - if (content.style.maxHeight){ - content.style.maxHeight = null; - } else { - content.style.maxHeight = content.scrollHeight + "px"; - } - }); - } -}); diff --git a/pydis_site/static/js/resources.js b/pydis_site/static/js/resources.js deleted file mode 100644 index 718e1e88..00000000 --- a/pydis_site/static/js/resources.js +++ /dev/null @@ -1,236 +0,0 @@ -"use strict"; - -// Filters that are currently selected -var activeFilters = { - topics: [], - type: [], - "payment-tiers": [], - difficulty: [] -}; - -function addFilter(filterName, filterItem) { - // Push the filter into the stack - var filterIndex = activeFilters[filterName].indexOf(filterItem); - if (filterIndex === -1) { - activeFilters[filterName].push(filterItem); - } - updateUI(); - - // Show a corresponding filter box tag - $(`.filter-box-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).show(); - - // Make corresponding resource tags active - $(`.resource-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).addClass("active"); - - // Hide the "No filters selected" tag. - $(".no-tags-selected.tag").hide(); -} - -function removeFilter(filterName, filterItem) { - // Remove the filter from the stack - var filterIndex = activeFilters[filterName].indexOf(filterItem); - if (filterIndex !== -1) { - activeFilters[filterName].splice(filterIndex, 1); - } - updateUI(); - - // Hide the corresponding filter box tag - $(`.filter-box-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).hide(); - - // Make corresponding resource tags inactive - $(`.resource-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).removeClass("active"); - - // Show "No filters selected" tag, if there are no filters active - if (noFilters()) { - $(".no-tags-selected.tag").show(); - } -} - -/* Check if there are no filters */ -function noFilters() { - return ( - activeFilters.topics.length === 0 && - activeFilters.type.length === 0 && - activeFilters["payment-tiers"].length === 0 && - activeFilters.difficulty.length === 0 - ); -} - -/* Get the params out of the URL and use them. This is run when the page loads. */ -function deserializeURLParams() { - let searchParams = new window.URLSearchParams(window.location.search); - - // Work through the parameters and add them to the filter object - $.each(Object.keys(activeFilters), function(_, filterType) { - let paramFilterContent = searchParams.get(filterType); - - if (paramFilterContent !== null) { - // We use split here because we always want an array, not a string. - let paramFilterArray = paramFilterContent.split(","); - activeFilters[filterType] = paramFilterArray; - - // Update the corresponding filter UI, so it reflects the internal state. - $(paramFilterArray).each(function(_, filter) { - let checkbox = $(`.filter-checkbox[data-filter-name='${filterType}'][data-filter-item='${filter}']`); - let filterTag = $(`.filter-box-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); - let resourceTags = $(`.resource-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); - checkbox.prop("checked", true); - filterTag.show(); - resourceTags.addClass("active"); - }); - } - }); -} - -/* Update the URL with new parameters */ -function updateURL() { - // If there's nothing in the filters, we don't want anything in the URL. - if (noFilters()) { - window.history.replaceState(null, document.title, './'); - return; - } - - // Iterate through and get rid of empty ones - let searchParams = new URLSearchParams(activeFilters); - $.each(activeFilters, function(filterType, filters) { - if (filters.length === 0) { - searchParams.delete(filterType); - } - }); - - // Now update the URL - window.history.replaceState(null, document.title, `?${searchParams.toString()}`); -} - -/* Update the resources to match 'active_filters' */ -function updateUI() { - let resources = $('.resource-box'); - let filterTags = $('.filter-box-tag'); - - // Update the URL to match the new filters. - updateURL(); - - // If there's nothing in the filters, show everything and return. - if (noFilters()) { - resources.show(); - filterTags.hide(); - return; - } - - // Otherwise, hide everything and then filter the resources to decide what to show. - let hasMatches = false; - resources.hide(); - resources.filter(function() { - let validation = { - topics: false, - type: false, - 'payment-tiers': false, - difficulty: false - }; - let resourceBox = $(this); - - // Validate the filters - $.each(activeFilters, function(filterType, filters) { - // If the filter list is empty, this passes validation. - if (filters.length === 0) { - validation[filterType] = true; - return; - } - - // Otherwise, we need to check if one of the classes exist. - $.each(filters, function(index, filter) { - if (resourceBox.hasClass(`${filterType}-${filter}`)) { - validation[filterType] = true; - } - }); - }); - - // If validation passes, show the resource. - if (Object.values(validation).every(Boolean)) { - hasMatches = true; - return true; - } else { - return false; - } - }).show(); - - // If there are no matches, show the no matches message - if (!hasMatches) { - $(".no-resources-found").show(); - } else { - $(".no-resources-found").hide(); - } -} - -// Executed when the page has finished loading. -document.addEventListener("DOMContentLoaded", function () { - /* Check if the user has navigated to one of the old resource pages, - like pydis.com/resources/communities. In this case, we'll rewrite - the URL before we do anything else. */ - let resourceTypeInput = $("#resource-type-input").val(); - if (resourceTypeInput !== "None") { - window.history.replaceState(null, document.title, `../?type=${resourceTypeInput}`); - } - - // Update the filters on page load to reflect URL parameters. - $('.filter-box-tag').hide(); - deserializeURLParams(); - updateUI(); - - // If you collapse or uncollapse a filter group, swap the icon. - $('button.collapsible').click(function() { - let icon = $(this).find(".card-header-icon i"); - - if ($(icon).hasClass("fa-window-minimize")) { - $(icon).removeClass(["far", "fa-window-minimize"]); - $(icon).addClass(["fas", "fa-angle-down"]); - } else { - $(icon).removeClass(["fas", "fa-angle-down"]); - $(icon).addClass(["far", "fa-window-minimize"]); - } - }); - - // If you click on the div surrounding the filter checkbox, it clicks the corresponding checkbox. - $('.filter-panel').click(function() { - let checkbox = $(this).find(".filter-checkbox"); - checkbox.prop("checked", !checkbox.prop("checked")); - checkbox.change(); - }); - - // If you click on one of the tags in the filter box, it unchecks the corresponding checkbox. - $('.filter-box-tag').click(function() { - let filterItem = this.dataset.filterItem; - let filterName = this.dataset.filterName; - let checkbox = $(`.filter-checkbox[data-filter-name='${filterName}'][data-filter-item='${filterItem}']`); - - removeFilter(filterName, filterItem); - checkbox.prop("checked", false); - }); - - // If you click on one of the tags in the resource cards, it clicks the corresponding checkbox. - $('.resource-tag').click(function() { - let filterItem = this.dataset.filterItem; - let filterName = this.dataset.filterName; - let checkbox = $(`.filter-checkbox[data-filter-name='${filterName}'][data-filter-item='${filterItem}']`); - - if (!$(this).hasClass("active")) { - addFilter(filterName, filterItem); - checkbox.prop("checked", true); - } else { - removeFilter(filterName, filterItem); - checkbox.prop("checked", false); - } - }); - - // When checkboxes are toggled, trigger a filter update. - $('.filter-checkbox').change(function () { - let filterItem = this.dataset.filterItem; - let filterName = this.dataset.filterName; - - if (this.checked) { - addFilter(filterName, filterItem); - } else { - removeFilter(filterName, filterItem); - } - }); -}); diff --git a/pydis_site/static/js/resources/resources.js b/pydis_site/static/js/resources/resources.js new file mode 100644 index 00000000..718e1e88 --- /dev/null +++ b/pydis_site/static/js/resources/resources.js @@ -0,0 +1,236 @@ +"use strict"; + +// Filters that are currently selected +var activeFilters = { + topics: [], + type: [], + "payment-tiers": [], + difficulty: [] +}; + +function addFilter(filterName, filterItem) { + // Push the filter into the stack + var filterIndex = activeFilters[filterName].indexOf(filterItem); + if (filterIndex === -1) { + activeFilters[filterName].push(filterItem); + } + updateUI(); + + // Show a corresponding filter box tag + $(`.filter-box-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).show(); + + // Make corresponding resource tags active + $(`.resource-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).addClass("active"); + + // Hide the "No filters selected" tag. + $(".no-tags-selected.tag").hide(); +} + +function removeFilter(filterName, filterItem) { + // Remove the filter from the stack + var filterIndex = activeFilters[filterName].indexOf(filterItem); + if (filterIndex !== -1) { + activeFilters[filterName].splice(filterIndex, 1); + } + updateUI(); + + // Hide the corresponding filter box tag + $(`.filter-box-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).hide(); + + // Make corresponding resource tags inactive + $(`.resource-tag[data-filter-name=${filterName}][data-filter-item=${filterItem}]`).removeClass("active"); + + // Show "No filters selected" tag, if there are no filters active + if (noFilters()) { + $(".no-tags-selected.tag").show(); + } +} + +/* Check if there are no filters */ +function noFilters() { + return ( + activeFilters.topics.length === 0 && + activeFilters.type.length === 0 && + activeFilters["payment-tiers"].length === 0 && + activeFilters.difficulty.length === 0 + ); +} + +/* Get the params out of the URL and use them. This is run when the page loads. */ +function deserializeURLParams() { + let searchParams = new window.URLSearchParams(window.location.search); + + // Work through the parameters and add them to the filter object + $.each(Object.keys(activeFilters), function(_, filterType) { + let paramFilterContent = searchParams.get(filterType); + + if (paramFilterContent !== null) { + // We use split here because we always want an array, not a string. + let paramFilterArray = paramFilterContent.split(","); + activeFilters[filterType] = paramFilterArray; + + // Update the corresponding filter UI, so it reflects the internal state. + $(paramFilterArray).each(function(_, filter) { + let checkbox = $(`.filter-checkbox[data-filter-name='${filterType}'][data-filter-item='${filter}']`); + let filterTag = $(`.filter-box-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); + let resourceTags = $(`.resource-tag[data-filter-name='${filterType}'][data-filter-item='${filter}']`); + checkbox.prop("checked", true); + filterTag.show(); + resourceTags.addClass("active"); + }); + } + }); +} + +/* Update the URL with new parameters */ +function updateURL() { + // If there's nothing in the filters, we don't want anything in the URL. + if (noFilters()) { + window.history.replaceState(null, document.title, './'); + return; + } + + // Iterate through and get rid of empty ones + let searchParams = new URLSearchParams(activeFilters); + $.each(activeFilters, function(filterType, filters) { + if (filters.length === 0) { + searchParams.delete(filterType); + } + }); + + // Now update the URL + window.history.replaceState(null, document.title, `?${searchParams.toString()}`); +} + +/* Update the resources to match 'active_filters' */ +function updateUI() { + let resources = $('.resource-box'); + let filterTags = $('.filter-box-tag'); + + // Update the URL to match the new filters. + updateURL(); + + // If there's nothing in the filters, show everything and return. + if (noFilters()) { + resources.show(); + filterTags.hide(); + return; + } + + // Otherwise, hide everything and then filter the resources to decide what to show. + let hasMatches = false; + resources.hide(); + resources.filter(function() { + let validation = { + topics: false, + type: false, + 'payment-tiers': false, + difficulty: false + }; + let resourceBox = $(this); + + // Validate the filters + $.each(activeFilters, function(filterType, filters) { + // If the filter list is empty, this passes validation. + if (filters.length === 0) { + validation[filterType] = true; + return; + } + + // Otherwise, we need to check if one of the classes exist. + $.each(filters, function(index, filter) { + if (resourceBox.hasClass(`${filterType}-${filter}`)) { + validation[filterType] = true; + } + }); + }); + + // If validation passes, show the resource. + if (Object.values(validation).every(Boolean)) { + hasMatches = true; + return true; + } else { + return false; + } + }).show(); + + // If there are no matches, show the no matches message + if (!hasMatches) { + $(".no-resources-found").show(); + } else { + $(".no-resources-found").hide(); + } +} + +// Executed when the page has finished loading. +document.addEventListener("DOMContentLoaded", function () { + /* Check if the user has navigated to one of the old resource pages, + like pydis.com/resources/communities. In this case, we'll rewrite + the URL before we do anything else. */ + let resourceTypeInput = $("#resource-type-input").val(); + if (resourceTypeInput !== "None") { + window.history.replaceState(null, document.title, `../?type=${resourceTypeInput}`); + } + + // Update the filters on page load to reflect URL parameters. + $('.filter-box-tag').hide(); + deserializeURLParams(); + updateUI(); + + // If you collapse or uncollapse a filter group, swap the icon. + $('button.collapsible').click(function() { + let icon = $(this).find(".card-header-icon i"); + + if ($(icon).hasClass("fa-window-minimize")) { + $(icon).removeClass(["far", "fa-window-minimize"]); + $(icon).addClass(["fas", "fa-angle-down"]); + } else { + $(icon).removeClass(["fas", "fa-angle-down"]); + $(icon).addClass(["far", "fa-window-minimize"]); + } + }); + + // If you click on the div surrounding the filter checkbox, it clicks the corresponding checkbox. + $('.filter-panel').click(function() { + let checkbox = $(this).find(".filter-checkbox"); + checkbox.prop("checked", !checkbox.prop("checked")); + checkbox.change(); + }); + + // If you click on one of the tags in the filter box, it unchecks the corresponding checkbox. + $('.filter-box-tag').click(function() { + let filterItem = this.dataset.filterItem; + let filterName = this.dataset.filterName; + let checkbox = $(`.filter-checkbox[data-filter-name='${filterName}'][data-filter-item='${filterItem}']`); + + removeFilter(filterName, filterItem); + checkbox.prop("checked", false); + }); + + // If you click on one of the tags in the resource cards, it clicks the corresponding checkbox. + $('.resource-tag').click(function() { + let filterItem = this.dataset.filterItem; + let filterName = this.dataset.filterName; + let checkbox = $(`.filter-checkbox[data-filter-name='${filterName}'][data-filter-item='${filterItem}']`); + + if (!$(this).hasClass("active")) { + addFilter(filterName, filterItem); + checkbox.prop("checked", true); + } else { + removeFilter(filterName, filterItem); + checkbox.prop("checked", false); + } + }); + + // When checkboxes are toggled, trigger a filter update. + $('.filter-checkbox').change(function () { + let filterItem = this.dataset.filterItem; + let filterName = this.dataset.filterName; + + if (this.checked) { + addFilter(filterName, filterItem); + } else { + removeFilter(filterName, filterItem); + } + }); +}); diff --git a/pydis_site/templates/content/base.html b/pydis_site/templates/content/base.html index 00f4fce4..4a19a275 100644 --- a/pydis_site/templates/content/base.html +++ b/pydis_site/templates/content/base.html @@ -7,7 +7,8 @@ - + + {% endblock %} {% block content %} diff --git a/pydis_site/templates/resources/resources.html b/pydis_site/templates/resources/resources.html index c3881092..80577c2b 100644 --- a/pydis_site/templates/resources/resources.html +++ b/pydis_site/templates/resources/resources.html @@ -7,10 +7,10 @@ {% block title %}Resources{% endblock %} {% block head %} - + - - + + {% endblock %} {% block content %} -- cgit v1.2.3 From 3b213bbdfcdf2a17c4b29692bd75094a47440cd6 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 30 Jan 2022 21:15:32 +0100 Subject: Revert ugly deploy preview redirect hack. --- pydis_site/apps/resources/urls.py | 41 +-------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) (limited to 'pydis_site/apps/resources/urls.py') diff --git a/pydis_site/apps/resources/urls.py b/pydis_site/apps/resources/urls.py index 044f7072..ed24dc99 100644 --- a/pydis_site/apps/resources/urls.py +++ b/pydis_site/apps/resources/urls.py @@ -1,48 +1,9 @@ -import typing - from django_distill import distill_path from pydis_site.apps.resources import views -# This is only used for `distill_path`, so that the Netlify Deploy Previews -# can successfully redirect to static pages. This could probably be improved by -# making it less hardcoded, but since there's a bunch of special cases, and since -# it only affects deploy previews, I'm choosing to solve this with a simple solution. -VALID_RESOURCE_TYPES = [ - "book", - "books", - "reading", - "podcast", - "podcasts", - "interactive", - "videos", - "video", - "courses", - "course", - "communities", - "community", - "tutorial", - "tutorials", - "tool", - "tools", - "project ideas", - "project-ideas", -] - - -def get_all_pages() -> typing.Iterator[dict[str, str]]: - """Get all the valid options for the resource_type path selector.""" - for resource_type in VALID_RESOURCE_TYPES: - yield {"resource_type": resource_type} - - app_name = "resources" urlpatterns = [ distill_path("", views.resources.ResourceView.as_view(), name="index"), - distill_path( - "/", - views.resources.ResourceView.as_view(), - name="index", - distill_func=get_all_pages, - ), + distill_path("/", views.resources.ResourceView.as_view(), name="index"), ] -- cgit v1.2.3