diff options
| author | 2021-04-03 15:32:20 +0800 | |
|---|---|---|
| committer | 2021-04-03 15:32:20 +0800 | |
| commit | f822d4fce05d228021450714a73cd84e143bbddc (patch) | |
| tree | 1c1d617891f6657766c9a2b15d4f6f199bfbf9e8 | |
| parent | Merge pull request #421 from ks129/resources-lists (diff) | |
| parent | Use metadata titles in Sub-Articles dropdown. (diff) | |
Merge pull request #468 from python-discord/content-app-improvements
Dewikification: Content app improvements.
| -rw-r--r-- | pydis_site/apps/content/resources/_info.yml | 2 | ||||
| -rw-r--r-- | pydis_site/apps/content/resources/guides/_info.yml | 2 | ||||
| -rw-r--r-- | pydis_site/apps/content/resources/guides/pydis-guides/_info.yml | 2 | ||||
| -rw-r--r-- | pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md | 88 | ||||
| -rw-r--r-- | pydis_site/apps/content/tests/helpers.py | 17 | ||||
| -rw-r--r-- | pydis_site/apps/content/tests/test_utils.py | 10 | ||||
| -rw-r--r-- | pydis_site/apps/content/tests/test_views.py | 53 | ||||
| -rw-r--r-- | pydis_site/apps/content/utils.py | 15 | ||||
| -rw-r--r-- | pydis_site/apps/content/views/page_category.py | 72 | ||||
| -rw-r--r-- | pydis_site/apps/events/tests/test_views.py | 4 | ||||
| -rw-r--r-- | pydis_site/apps/events/views/page.py | 7 | ||||
| -rw-r--r-- | pydis_site/settings.py | 9 | ||||
| -rw-r--r-- | pydis_site/static/css/content/page.css | 39 | ||||
| -rw-r--r-- | pydis_site/templates/content/base.html | 5 | ||||
| -rw-r--r-- | pydis_site/templates/content/dropdown.html | 34 | ||||
| -rw-r--r-- | pydis_site/templates/content/listing.html | 6 | ||||
| -rw-r--r-- | pydis_site/templates/content/page.html | 28 | 
17 files changed, 322 insertions, 71 deletions
| diff --git a/pydis_site/apps/content/resources/_info.yml b/pydis_site/apps/content/resources/_info.yml index 583cab18..6553dcc6 100644 --- a/pydis_site/apps/content/resources/_info.yml +++ b/pydis_site/apps/content/resources/_info.yml @@ -1,2 +1,2 @@ -name: Pages +title: Pages  description: Guides, articles, and pages hosted on the site. diff --git a/pydis_site/apps/content/resources/guides/_info.yml b/pydis_site/apps/content/resources/guides/_info.yml index 59c60a7b..2f65eaf9 100644 --- a/pydis_site/apps/content/resources/guides/_info.yml +++ b/pydis_site/apps/content/resources/guides/_info.yml @@ -1,2 +1,2 @@ -name: Guides +title: Guides  description: Made by us, for you. diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/_info.yml b/pydis_site/apps/content/resources/guides/pydis-guides/_info.yml index 7c9a2225..c126a68a 100644 --- a/pydis_site/apps/content/resources/guides/pydis-guides/_info.yml +++ b/pydis_site/apps/content/resources/guides/pydis-guides/_info.yml @@ -1,2 +1,2 @@ -name: Python Discord Guides +title: Python Discord Guides  description: Guides related to the Python Discord server and community. diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md b/pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md index f258ef74..726cb7b2 100644 --- a/pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md +++ b/pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md @@ -1,11 +1,11 @@  ---  title: How to Contribute a Page  description: Learn how to write and publish a page to this website. -icon_class: fas -icon: fa-info +icon: fas fa-info  relevant_links:      Contributing to Site: https://pythondiscord.com/pages/contributing/site/      Using Git: https://pythondiscord.com/pages/contributing/working-with-git/ +toc: 4  ---  Pages, which include guides, articles, and other static content, are stored in markdown files in the `site` repository on Github. @@ -22,17 +22,39 @@ As website changes require staff approval, discussing the page content beforehan  ## Creating the Page  All pages are located in the `site` repo, at the path `pydis_site/apps/content/resources/`. This is the root folder, which corresponds to the URL `www.pythondiscord.com/pages/`. +  For example, the file `pydis_site/apps/content/resources/hello-world.md` will result in a page available at `www.pythondiscord.com/pages/hello-world`. +#### Page Categories  Nested folders represent page categories on the website. Each folder under the root folder must include a `_info.yml` file with the following:  ```yml -name: Category name +title: Category name  description: Category description +icon: fas fa-folder # Optional  ```  All the markdown files in this folder will then be under this category. +#### Having the Category Also Be a Page +In order to make categories a page, just create a page **with the same name as the category folder** in the category's parent directory. + +```plaintext +guides +├── contributing.md +├── contributing +│   ├── _info.yml +│   └── bot.md +└── _info.yml +``` + +In the above example, `www.pythondiscord.com/guides/` will list `Contributing` as a category entry with information from `contributing/_info.yml`. + +However, `www.pythondiscord.com/guides/contributing` will render `contributing.md` rather than show the category contents. +A dropdown menu will be automatically generated in the top right corner of the page listing the children of the category page. + +Therefore, `www.pythondiscord.com/guides/contributing/bot` will then render `bot.md`, with backlinks to `contributing.md`. +  ## Writing the Page  Files representing pages are in `.md` (Markdown) format, with all-lowercase filenames and spaces replaced with `-` characters. @@ -44,8 +66,7 @@ The metadata is written in YAML, and should be enclosed in triple dashes `---` *  ---  title: How to Contribute a Page  description: Learn how to write and publish a page to this website. -icon_class: fas -icon: fa-info +icon: fas fa-info  relevant_links:      Contributing to Site: https://pythondiscord.com/pages/contributing/site/      Using Git: https://pythondiscord.com/pages/contributing/working-with-git/ @@ -59,9 +80,10 @@ Pages, which include guides, articles, and other static content,...  - **description:** Short, 1-2 line description of the page's content.  ### Optional Fields -- **icon_class:** Favicon class for the category entry for the page. Default: `fab` -- **icon:** Favicon for the category entry for the page. Default: `fa-python` <i class="fab fa-python is-black" aria-hidden="true"></i> +- **icon:** Icon for the category entry for the page. Default: `fab fa-python` <i class="fab fa-python is-black" aria-hidden="true"></i>  - **relevant_links:** A YAML dictionary containing `text:link` pairs. See the example above. +- **toc:** A number representing the smallest heading tag to show in the table of contents. +    See: [Table of Contents](#table-of-contents)  ## Extended Markdown @@ -141,3 +163,55 @@ import os  path = os.path.join("foo", "bar")  ``` + +--- + +### HTML Attributes +To add HTML attributes to certain lines/paragraphs, [see this page](https://python-markdown.github.io/extensions/attr_list/#the-list) for the format and where to put it. + +This can be useful for setting the image size when adding an image using markdown (see the [Image Captions](#image-captions) section for an example), or for adding bulma styles to certain elements (like the warning notification [here](/pages/guides/pydis-guides/contributing/sir-lancebot#setup-instructions)).   +**This should be used sparingly, as it reduces readability and simplicity of the article.** + +--- + +### Image Captions +To add an image caption, place a sentence with italics *right below* the image link + +**Markdown:** +```nohighlight +{: width="400" } +*Summmer Code Jam 2020 banner with event information.* +``` + +**Output:** + +{: width="400"} +*Summer Code Jam 2020 banner with event information.* + +> Note: To display a regular italicized line below an image, leave an empty line between the two. + +--- + +### Table of Contents +In order to show the table of contents on a page, simply define the `toc` key in the page metadata. + +The value of the `toc` key corresponds to the smallest heading to list in the table of contents. +For example, with markdown content like this: + +```markdown +# Header 1 +words +### Header 3 +more words +# Another Header 1 +## Header 2 +even more words +``` + +and `toc: 2` in the page metadata, only `Header 1`, `Another Header 1` and `Header 2` will be listed in the table of contents. + +To use a custom label in the table of contents for a heading, set the `data-toc-label` attribute in the heading line. See [HTML Attributes](#html-attributes) for more information. + +```markdown +# Header 1 {: data-toc-label="Header One" } +``` diff --git a/pydis_site/apps/content/tests/helpers.py b/pydis_site/apps/content/tests/helpers.py index 4e0cca34..29140375 100644 --- a/pydis_site/apps/content/tests/helpers.py +++ b/pydis_site/apps/content/tests/helpers.py @@ -8,6 +8,7 @@ description: TestDescription  relevant_links:      Python Discord: https://pythondiscord.com      Discord: https://discord.com +toc: 0  ---  # This is a header.  """ @@ -16,7 +17,7 @@ MARKDOWN_WITHOUT_METADATA = """#This is a header."""  # Valid YAML in a _info.yml file  CATEGORY_INFO = """ -name: Category Name +title: Category Name  description: Description  """ @@ -32,11 +33,12 @@ PARSED_METADATA = {      "relevant_links": {          "Python Discord": "https://pythondiscord.com",          "Discord": "https://discord.com" -    } +    }, +    "toc": 0  }  # The YAML data parsed from the above _info.yml file -PARSED_CATEGORY_INFO = {"name": "Category Name", "description": "Description"} +PARSED_CATEGORY_INFO = {"title": "Category Name", "description": "Description"}  class MockPagesTestCase(TestCase): @@ -48,9 +50,12 @@ class MockPagesTestCase(TestCase):      ├── root.md      ├── root_without_metadata.md      ├── not_a_page.md +    ├── tmp.md      ├── tmp      |   ├── _info.yml -    |   └── category_without_info +    |   └── category +    |       ├── _info.yml +    |       └── subcategory_without_info      └── category          ├── _info.yml          ├── with_metadata.md @@ -81,4 +86,6 @@ class MockPagesTestCase(TestCase):          # for testing purposes.          # See: https://jmcgeheeiv.github.io/pyfakefs/release/usage.html#os-temporary-directories          self.fs.create_file("tmp/_info.yml", contents=CATEGORY_INFO) -        self.fs.create_dir("tmp/category_without_info") +        self.fs.create_file("tmp.md", contents=MARKDOWN_WITH_METADATA) +        self.fs.create_file("tmp/category/_info.yml", contents=CATEGORY_INFO) +        self.fs.create_dir("tmp/category/subcategory_without_info") diff --git a/pydis_site/apps/content/tests/test_utils.py b/pydis_site/apps/content/tests/test_utils.py index 58175d6f..6612e44c 100644 --- a/pydis_site/apps/content/tests/test_utils.py +++ b/pydis_site/apps/content/tests/test_utils.py @@ -14,7 +14,7 @@ class GetCategoryTests(MockPagesTestCase):      def test_get_valid_category(self):          result = utils.get_category(Path("category")) -        self.assertEqual(result, {"name": "Category Name", "description": "Description"}) +        self.assertEqual(result, {"title": "Category Name", "description": "Description"})      def test_get_nonexistent_category(self):          with self.assertRaises(Http404): @@ -28,7 +28,7 @@ class GetCategoryTests(MockPagesTestCase):      def test_get_category_without_info_yml(self):          # Categories should provide an _info.yml file          with self.assertRaises(FileNotFoundError): -            utils.get_category(Path("tmp/category_without_info")) +            utils.get_category(Path("tmp/category/subcategory_without_info"))  class GetCategoriesTests(MockPagesTestCase): @@ -73,10 +73,12 @@ class GetPageTests(MockPagesTestCase):      """Tests for the get_page function."""      def test_get_page(self): +        # TOC is a special case because the markdown converter outputs the TOC as HTML +        updated_metadata = {**PARSED_METADATA, "toc": '<div class="toc">\n<ul></ul>\n</div>\n'}          cases = [ -            ("Root page with metadata", "root.md", PARSED_HTML, PARSED_METADATA), +            ("Root page with metadata", "root.md", PARSED_HTML, updated_metadata),              ("Root page without metadata", "root_without_metadata.md", PARSED_HTML, {}), -            ("Page with metadata", "category/with_metadata.md", PARSED_HTML, PARSED_METADATA), +            ("Page with metadata", "category/with_metadata.md", PARSED_HTML, updated_metadata),              ("Page without metadata", "category/subcategory/without_metadata.md", PARSED_HTML, {}),          ] diff --git a/pydis_site/apps/content/tests/test_views.py b/pydis_site/apps/content/tests/test_views.py index 560378bc..74d38f78 100644 --- a/pydis_site/apps/content/tests/test_views.py +++ b/pydis_site/apps/content/tests/test_views.py @@ -17,7 +17,23 @@ with fake_filesystem_unittest.Patcher() as _:      BASE_PATH = Path(".") -@override_settings(PAGES_PATH=BASE_PATH) +def patch_dispatch_attributes(view: PageOrCategoryView, location: str) -> None: +    """ +    Set the attributes set in the `dispatch` method manually. + +    This is necessary because it is never automatically called during tests. +    """ +    view.location = Path(location) + +    # URL location on the filesystem +    view.full_location = view.location + +    # Possible places to find page content information +    view.category_path = view.full_location +    view.page_path = view.full_location.with_suffix(".md") + + +@override_settings(CONTENT_PAGES_PATH=BASE_PATH)  class PageOrCategoryViewTests(MockPagesTestCase, SimpleTestCase, TestCase):      """Tests for the PageOrCategoryView class.""" @@ -63,16 +79,21 @@ class PageOrCategoryViewTests(MockPagesTestCase, SimpleTestCase, TestCase):          for path, expected_template in cases:              with self.subTest(path=path, expected_template=expected_template): -                self.ViewClass.full_location = Path(path) +                patch_dispatch_attributes(self.ViewClass, path)                  self.assertEqual(self.ViewClass.get_template_names(), [expected_template])      def test_get_template_names_with_nonexistent_paths_returns_404(self):          for path in ("invalid", "another_invalid", "nonexistent"):              with self.subTest(path=path): -                self.ViewClass.full_location = Path(path) +                patch_dispatch_attributes(self.ViewClass, path)                  with self.assertRaises(Http404):                      self.ViewClass.get_template_names() +    def test_get_template_names_returns_page_template_for_category_with_page(self): +        """Make sure the proper page is returned for category locations with pages.""" +        patch_dispatch_attributes(self.ViewClass, "tmp") +        self.assertEqual(self.ViewClass.get_template_names(), ["content/page.html"]) +      def test_get_context_data_with_valid_page(self):          """The method should return required fields in the template context."""          request = self.factory.get("/root") @@ -120,7 +141,7 @@ class PageOrCategoryViewTests(MockPagesTestCase, SimpleTestCase, TestCase):                  "page_description",                  PARSED_CATEGORY_INFO["description"]              ), -            ("Context includes page title", "page_title", PARSED_CATEGORY_INFO["name"]), +            ("Context includes page title", "page_title", PARSED_CATEGORY_INFO["title"]),          ]          context = self.ViewClass.get_context_data() @@ -128,6 +149,24 @@ class PageOrCategoryViewTests(MockPagesTestCase, SimpleTestCase, TestCase):              with self.subTest(msg=msg):                  self.assertEqual(context[key], expected_value) +    def test_get_context_data_for_category_with_page(self): +        """Make sure the proper page is returned for category locations with pages.""" +        request = self.factory.get("/category") +        self.ViewClass.setup(request) +        self.ViewClass.dispatch(request, location="tmp") + +        context = self.ViewClass.get_context_data() +        expected_page_context = { +            "page": PARSED_HTML, +            "page_title": PARSED_METADATA["title"], +            "page_description": PARSED_METADATA["description"], +            "relevant_links": PARSED_METADATA["relevant_links"], +            "subarticles": [{"path": "category", "name": "Category Name"}] +        } +        for key, expected_value in expected_page_context.items(): +            with self.subTest(): +                self.assertEqual(context[key], expected_value) +      def test_get_context_data_breadcrumbs(self):          """The method should return correct breadcrumbs."""          request = self.factory.get("/category/subcategory/with_metadata") @@ -138,8 +177,8 @@ class PageOrCategoryViewTests(MockPagesTestCase, SimpleTestCase, TestCase):          self.assertEquals(              context["breadcrumb_items"],              [ -                {"name": PARSED_CATEGORY_INFO["name"], "path": "."}, -                {"name": PARSED_CATEGORY_INFO["name"], "path": "category"}, -                {"name": PARSED_CATEGORY_INFO["name"], "path": "category/subcategory"}, +                {"name": PARSED_CATEGORY_INFO["title"], "path": "."}, +                {"name": PARSED_CATEGORY_INFO["title"], "path": "category"}, +                {"name": PARSED_CATEGORY_INFO["title"], "path": "category/subcategory"},              ]          ) diff --git a/pydis_site/apps/content/utils.py b/pydis_site/apps/content/utils.py index 726c991f..d3f270ff 100644 --- a/pydis_site/apps/content/utils.py +++ b/pydis_site/apps/content/utils.py @@ -32,7 +32,8 @@ def get_category_pages(path: Path) -> Dict[str, Dict]:      pages = {}      for item in path.glob("*.md"): -        if item.is_file(): +        # Only list page if there is no category with the same name +        if item.is_file() and not item.with_suffix("").is_dir():              pages[item.stem] = frontmatter.load(item).metadata      return pages @@ -44,14 +45,20 @@ def get_page(path: Path) -> Tuple[str, Dict]:          raise Http404("Page not found.")      metadata, content = frontmatter.parse(path.read_text(encoding="utf-8")) -    html = markdown.markdown( -        content, +    toc_depth = metadata.get("toc", 1) + +    md = markdown.Markdown(          extensions=[              "extra",              # Empty string for marker to disable text searching for [TOC]              # By using a metadata key instead, we save time on long markdown documents -            TocExtension(title="Table of Contents:", permalink=True, marker="") +            TocExtension(permalink=True, marker="", toc_depth=toc_depth)          ]      ) +    html = md.convert(content) + +    # Don't set the TOC if the metadata does not specify one +    if "toc" in metadata: +        metadata["toc"] = md.toc      return str(html), metadata diff --git a/pydis_site/apps/content/views/page_category.py b/pydis_site/apps/content/views/page_category.py index eec4e7e5..4031fde2 100644 --- a/pydis_site/apps/content/views/page_category.py +++ b/pydis_site/apps/content/views/page_category.py @@ -1,6 +1,7 @@  import typing as t  from pathlib import Path +import frontmatter  from django.conf import settings  from django.http import Http404  from django.views.generic import TemplateView @@ -14,16 +15,22 @@ class PageOrCategoryView(TemplateView):      def dispatch(self, request: t.Any, *args, **kwargs) -> t.Any:          """Conform URL path location to the filesystem path."""          self.location = Path(kwargs.get("location", "")) -        self.full_location = settings.PAGES_PATH / self.location + +        # URL location on the filesystem +        self.full_location = settings.CONTENT_PAGES_PATH / self.location + +        # Possible places to find page content information +        self.category_path = self.full_location +        self.page_path = self.full_location.with_suffix(".md")          return super().dispatch(request, *args, **kwargs)      def get_template_names(self) -> t.List[str]:          """Checks if the view uses the page template or listing template.""" -        if self.full_location.is_dir(): -            template_name = "content/listing.html" -        elif self.full_location.with_suffix(".md").is_file(): +        if self.page_path.is_file():              template_name = "content/page.html" +        elif self.category_path.is_dir(): +            template_name = "content/listing.html"          else:              raise Http404 @@ -33,29 +40,54 @@ class PageOrCategoryView(TemplateView):          """Assign proper context variables based on what resource user requests."""          context = super().get_context_data(**kwargs) -        if self.full_location.is_dir(): -            context["categories"] = utils.get_categories(self.full_location) -            context["pages"] = utils.get_category_pages(self.full_location) - -            category = utils.get_category(self.full_location) -            context["page_title"] = category["name"] -            context["page_description"] = category["description"] - -            context["path"] = f"{self.location}/"  # Add trailing slash here to simplify template -        elif self.full_location.with_suffix(".md").is_file(): -            page, metadata = utils.get_page(self.full_location.with_suffix(".md")) -            context["page"] = page -            context["page_title"] = metadata["title"] -            context["page_description"] = metadata["description"] -            context["relevant_links"] = metadata.get("relevant_links", {}) +        if self.page_path.is_file(): +            context.update(self._get_page_context(self.page_path)) +        elif self.category_path.is_dir(): +            context.update(self._get_category_context(self.category_path)) +            context["path"] = f"{self.location}/"  # Add trailing slash to simplify template          else:              raise Http404 +        # Add subarticle information for dropdown menu if the page is also a category +        if self.page_path.is_file() and self.category_path.is_dir(): +            context["subarticles"] = [] +            for entry in self.category_path.iterdir(): +                entry_info = {"path": entry.stem} +                if entry.suffix == ".md": +                    entry_info["name"] = frontmatter.load(entry).metadata["title"] +                elif entry.is_dir(): +                    entry_info["name"] = utils.get_category(entry)["title"] +                else: +                    continue +                context["subarticles"].append(entry_info) +          context["breadcrumb_items"] = [              { -                "name": utils.get_category(settings.PAGES_PATH / location)["name"], +                "name": utils.get_category(settings.CONTENT_PAGES_PATH / location)["title"],                  "path": str(location)              } for location in reversed(self.location.parents)          ]          return context + +    @staticmethod +    def _get_page_context(path: Path) -> t.Dict[str, t.Any]: +        page, metadata = utils.get_page(path) +        return { +            "page": page, +            "page_title": metadata["title"], +            "page_description": metadata["description"], +            "relevant_links": metadata.get("relevant_links", {}), +            "toc": metadata.get("toc") +        } + +    @staticmethod +    def _get_category_context(path: Path) -> t.Dict[str, t.Any]: +        category = utils.get_category(path) +        return { +            "categories": utils.get_categories(path), +            "pages": utils.get_category_pages(path), +            "page_title": category["title"], +            "page_description": category["description"], +            "icon": category.get("icon"), +        } diff --git a/pydis_site/apps/events/tests/test_views.py b/pydis_site/apps/events/tests/test_views.py index 0db0ef9a..23c9e596 100644 --- a/pydis_site/apps/events/tests/test_views.py +++ b/pydis_site/apps/events/tests/test_views.py @@ -17,7 +17,7 @@ class IndexTests(TestCase):  class PageTests(TestCase): -    @override_settings(PAGES_PATH=PAGES_PATH) +    @override_settings(EVENTS_PAGES_PATH=PAGES_PATH)      def test_valid_event_page_reponse_200(self):          """Should return response code 200 when visiting valid event page."""          pages = ( @@ -29,7 +29,7 @@ class PageTests(TestCase):                  resp = self.client.get(page)                  self.assertEqual(resp.status_code, 200) -    @override_settings(PAGES_PATH=PAGES_PATH) +    @override_settings(EVENTS_PAGES_PATH=PAGES_PATH)      def test_invalid_event_page_404(self):          """Should return response code 404 when visiting invalid event page."""          pages = ( diff --git a/pydis_site/apps/events/views/page.py b/pydis_site/apps/events/views/page.py index f4c37aeb..eab2f462 100644 --- a/pydis_site/apps/events/views/page.py +++ b/pydis_site/apps/events/views/page.py @@ -11,15 +11,16 @@ class PageView(TemplateView):      def get_template_names(self) -> List[str]:          """Get specific template names."""          path: str = self.kwargs['path'] -        page_path = settings.PAGES_PATH / path +        page_path = settings.EVENTS_PAGES_PATH / path          if page_path.is_dir():              page_path = page_path / "_index.html"              path = f"{path}/_index.html"          else: -            page_path = settings.PAGES_PATH / f"{path}.html" +            page_path = settings.EVENTS_PAGES_PATH / f"{path}.html"              path = f"{path}.html"          if not page_path.exists():              raise Http404 +        print(f"events/{settings.EVENTS_PAGES_PATH.name}/{path}") -        return [f"events/{settings.PAGES_PATH.name}/{path}"] +        return [f"events/{settings.EVENTS_PAGES_PATH.name}/{path}"] diff --git a/pydis_site/settings.py b/pydis_site/settings.py index 47750a47..d409bb21 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -119,9 +119,6 @@ MIDDLEWARE = [  ]  ROOT_URLCONF = 'pydis_site.urls' -# Path for events pages -PAGES_PATH = Path(BASE_DIR, "pydis_site", "templates", "events", "pages") -  TEMPLATES = [      {          'BACKEND': 'django.template.backends.django.DjangoTemplates', @@ -288,4 +285,8 @@ SITE_REPOSITORY_OWNER = "python-discord"  SITE_REPOSITORY_NAME = "site"  SITE_REPOSITORY_BRANCH = "master" -PAGES_PATH = Path(BASE_DIR, "pydis_site", "apps", "content", "resources") +# Path for events pages +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") diff --git a/pydis_site/static/css/content/page.css b/pydis_site/static/css/content/page.css index 57d7472b..3ac41d1b 100644 --- a/pydis_site/static/css/content/page.css +++ b/pydis_site/static/css/content/page.css @@ -29,3 +29,42 @@ code.hljs {  :is(h1, h2, h3, h4, h5, h6):hover > .headerlink {      display: inline;  } + +/* + * Display <em> tags immediately following <img> tags like figure subcaptions. + * Note: There must not be a newline between the image and the italicized line + * for this to work. Otherwise, it's regular markdown. + * + * Image caption: + * + *  + * *This is my caption.* + * + */ +img + em { +    /* Place the caption on it's own line */ +    display: block; +    white-space: pre; + +    /* Style */ +    font-size: .875em; +} + +/* + * Remove extra padding on the left of TOC items + */ +ul.menu-list.toc { +    margin-left: 0; +} + +/* + * Remove bullets set by the markdown extension, since bulma adds vertical + * lines to represent nesting + */ +.toc li { +    list-style-type: none; +} +/* ..but we still want bullets on the top <ul> items */ +.toc > ul > li { +    list-style-type: disc; +} diff --git a/pydis_site/templates/content/base.html b/pydis_site/templates/content/base.html index 19eec5d4..21895479 100644 --- a/pydis_site/templates/content/base.html +++ b/pydis_site/templates/content/base.html @@ -14,6 +14,7 @@      <section class="breadcrumb-section section">          <div class="container"> +            {# Article breadcrumb #}              <nav class="breadcrumb is-pulled-left" aria-label="breadcrumbs">                  <ul>                      {% for item in breadcrumb_items %} @@ -22,6 +23,10 @@                      <li class="is-active"><a href="#">{{ page_title }}</a></li>                  </ul>              </nav> +            {# Sub-Article dropdown for category pages #} +            {% if subarticles %} +                {% include "content/dropdown.html" %} +            {% endif %}          </div>      </section> diff --git a/pydis_site/templates/content/dropdown.html b/pydis_site/templates/content/dropdown.html new file mode 100644 index 00000000..c9491f3a --- /dev/null +++ b/pydis_site/templates/content/dropdown.html @@ -0,0 +1,34 @@ +<script> +    document.addEventListener("DOMContentLoaded", () => { +        const dropdown = document.querySelector("#dropdown"); +        // Show dropdown menu when clicked +        dropdown.addEventListener("click", () => { +            dropdown.classList.toggle("is-active"); +        }); + +        // Hide dropdown menu when anyhere on the page is clicked +        document.addEventListener("click", (e) => { +            if (!dropdown.contains(e.target) && dropdown.classList.contains("is-active")) { +                dropdown.classList.remove("is-active"); +            } +        }, false); +    }); +</script> + +<div class="dropdown is-pulled-right is-right" id="dropdown"> +    <div class="dropdown-trigger"> +        <a aria-haspopup="true" aria-controls="subarticle-menu"> +            <span>Sub-Articles</span> +            <span class="icon is-small"> +                <i class="fas fa-angle-down" aria-hidden="true"></i> +            </span> +        </a> +    </div> +    <div class="dropdown-menu" id="subarticle-menu" role="menu"> +        <div class="dropdown-content"> +            {% for page in subarticles|dictsort:"name" %} +                <a href="{{ page.path }}" class="dropdown-item">{{ page.name }}</a> +            {% endfor %} +        </div> +    </div> +</div> diff --git a/pydis_site/templates/content/listing.html b/pydis_site/templates/content/listing.html index 6de306b0..ef0ef919 100644 --- a/pydis_site/templates/content/listing.html +++ b/pydis_site/templates/content/listing.html @@ -4,11 +4,11 @@      {% for category, data in categories.items %}          <div class="box" style="max-width: 800px;">              <span class="icon is-size-4 is-medium"> -                <i class="fas fa-folder is-size-3 is-black has-icon-padding" aria-hidden="true"></i> +                <i class="{{ data.icon|default:"fas fa-folder" }} is-size-3 is-black has-icon-padding" aria-hidden="true"></i>              </span>              <a href="{% url "content:page_category" location=path|add:category %}"> -                <span class="is-size-4 has-text-weight-bold">{{ data.name }}</span> +                <span class="is-size-4 has-text-weight-bold">{{ data.title }}</span>              </a>              <p class="is-italic">{{ data.description }}</p>          </div> @@ -16,7 +16,7 @@      {% for page, data in pages.items %}          <div class="box" style="max-width: 800px;">              <span class="icon is-size-4 is-medium"> -                <i class="{{ data.icon_class|default:"fab" }} {{ data.icon|default:"fa-python" }} is-size-3 is-black has-icon-padding" aria-hidden="true"></i> +                <i class="{{ data.icon|default:"fab fa-python" }} is-size-3 is-black has-icon-padding" aria-hidden="true"></i>              </span>              <a href="{% url "content:page_category" location=path|add:page %}">                  <span class="is-size-4 has-text-weight-bold">{{ data.title }}</span> diff --git a/pydis_site/templates/content/page.html b/pydis_site/templates/content/page.html index 06d74208..45aa8221 100644 --- a/pydis_site/templates/content/page.html +++ b/pydis_site/templates/content/page.html @@ -9,20 +9,30 @@  {% endblock %}  {% block page_content %} -    {% if relevant_links|length > 0 %} +    {% if relevant_links or toc %}          <div class="columns is-variable is-8">              <div class="column is-two-thirds">                  {{ page|safe }}              </div>              <div class="column"> -                <div class="box"> -                    <p class="menu-label">Relevant links</p> -                    <ul class="menu-list"> -                        {% for value, link in relevant_links.items %} -                            <li><a class="has-text-link" href="{{link}}">{{ value }}</a></li> -                        {% endfor %} -                    </ul> -                </div> +                {% if toc %} +                    <div class="box"> +                        <p class="menu-label">Table of Contents</p> +                        <ul class="menu-list toc"> +                            {{ toc|safe }} +                        </ul> +                    </div> +                {% endif %} +                {% if relevant_links %} +                    <div class="box"> +                        <p class="menu-label">Relevant links</p> +                        <ul class="menu-list"> +                            {% for value, link in relevant_links.items %} +                                <li><a class="has-text-link" href="{{link}}">{{ value }}</a></li> +                            {% endfor %} +                        </ul> +                    </div> +                {% endif %}              </div>          </div>      {% else %} | 
