-- 
cgit v1.2.3
From 8d7c655d5b52e15e569061c3fc57c535b90272ce Mon Sep 17 00:00:00 2001
From: ks129 <45097959+ks129@users.noreply.github.com>
Date: Fri, 30 Oct 2020 19:06:34 +0200
Subject: Use Django URL generation for category URL
---
 pydis_site/templates/content/article.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/templates/content/article.html b/pydis_site/templates/content/article.html
index f3c7c7a5..92b1b1ce 100644
--- a/pydis_site/templates/content/article.html
+++ b/pydis_site/templates/content/article.html
@@ -21,7 +21,7 @@
                 
-- 
cgit v1.2.3
From 7ccc33b39a5880a1879dbba4543d2b5e915b6f8e Mon Sep 17 00:00:00 2001
From: ks129 <45097959+ks129@users.noreply.github.com>
Date: Sat, 28 Nov 2020 09:59:35 +0200
Subject: Create categories and articles listing template
---
 pydis_site/templates/content/listing.html | 62 +++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 pydis_site/templates/content/listing.html
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/templates/content/listing.html b/pydis_site/templates/content/listing.html
new file mode 100644
index 00000000..8c06bccc
--- /dev/null
+++ b/pydis_site/templates/content/listing.html
@@ -0,0 +1,62 @@
+{% extends 'base/base.html' %}
+{% load static %}
+
+{% block title %}{{ category_info.name|default:"Articles" }}{% endblock %}
+{% block head %}
+    
+    
+    
+    
+{% endblock %}
+
+{% block content %}
+    {% include "base/navbar.html" %}
+
+    
+
+    
+        
+            
+                
{{ category_info.name|default:"Articles" }} 
+                {% for category, data in categories.items %}
+                    
+                {% endfor %}
+                {% for article, data in content.items %}
+                    
+                {% endfor %}
+            
+        
 
+     
+{% endblock %}
-- 
cgit v1.2.3
From 6989307606cfb36739469ca230799fbf8294456d Mon Sep 17 00:00:00 2001
From: ks129 <45097959+ks129@users.noreply.github.com>
Date: Sat, 28 Nov 2020 10:00:58 +0200
Subject: Change article.html to match with unlimited categories
---
 pydis_site/templates/content/article.html | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/templates/content/article.html b/pydis_site/templates/content/article.html
index 92b1b1ce..c7b85567 100644
--- a/pydis_site/templates/content/article.html
+++ b/pydis_site/templates/content/article.html
@@ -20,9 +20,9 @@
             
                 
              
-- 
cgit v1.2.3
From 0afa7f3eefc68c28aa6e75a59d8684075b6a74a7 Mon Sep 17 00:00:00 2001
From: ks129 <45097959+ks129@users.noreply.github.com>
Date: Sat, 28 Nov 2020 10:06:35 +0200
Subject: Remove old templates
---
 pydis_site/templates/content/articles.html | 53 ------------------------------
 pydis_site/templates/content/category.html | 44 -------------------------
 2 files changed, 97 deletions(-)
 delete mode 100644 pydis_site/templates/content/articles.html
 delete mode 100644 pydis_site/templates/content/category.html
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/templates/content/articles.html b/pydis_site/templates/content/articles.html
deleted file mode 100644
index 68d23611..00000000
--- a/pydis_site/templates/content/articles.html
+++ /dev/null
@@ -1,53 +0,0 @@
-{% extends 'base/base.html' %}
-{% load static %}
-
-{% block title %}Articles{% endblock %}
-{% block head %}
-  
-{% endblock %}
-
-{% block content %}
-    {% include "base/navbar.html" %}
-
-    
-
-    
-        
-            
-                
Articles 
-                {% for article, data in content.items %}
-                    
-                {% endfor %}
-                {% for category, data in categories.items %}
-                    
-                {% endfor %}
-            
-        
 
-     
-{% endblock %}
diff --git a/pydis_site/templates/content/category.html b/pydis_site/templates/content/category.html
deleted file mode 100644
index 3e45c433..00000000
--- a/pydis_site/templates/content/category.html
+++ /dev/null
@@ -1,44 +0,0 @@
-{% extends 'base/base.html' %}
-{% load static %}
-
-{% block title %}{{ category_info.name }}{% endblock %}
-{% block head %}
-  
-  
-  
-  
-{% endblock %}
-
-{% block content %}
-    {% include "base/navbar.html" %}
-
-    
-
-    
-        
-            
-                
{{ category_info.name }} 
-                {% for article, data in content.items %}
-                    
-                {% endfor %}
-            
-        
 
-     
-{% endblock %}
-- 
cgit v1.2.3
From 9723994c7044b554e3a0c5fe4e09aac728185c95 Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Tue, 23 Mar 2021 15:08:44 +0800
Subject: Update highlight.js script and use one dark style.
Unifies the highlighting with the events app.
---
 pydis_site/templates/content/article.html | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/templates/content/article.html b/pydis_site/templates/content/article.html
index c7b85567..fcb29f32 100644
--- a/pydis_site/templates/content/article.html
+++ b/pydis_site/templates/content/article.html
@@ -7,8 +7,9 @@
    
    
    
-   
-  
+   
+  
   
 {% endblock %}
 
-- 
cgit v1.2.3
From 7de2959402a048652bf6d4ec8e3bec1172bb11e8 Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Tue, 23 Mar 2021 17:17:22 +0800
Subject: Remove Github metadata feature.
This feature is still under implementation debate, so it will be further
discussed in another issue and implemeneted in a future PR.
---
 Pipfile                                           |   1 -
 Pipfile.lock                                      | 503 ++++++++++++----------
 pydis_site/apps/content/tests/test_utils.py       |  40 --
 pydis_site/apps/content/tests/test_views.py       |  10 +-
 pydis_site/apps/content/utils.py                  |  38 --
 pydis_site/apps/content/views/article_category.py |   1 -
 pydis_site/templates/content/article.html         |  18 -
 7 files changed, 283 insertions(+), 328 deletions(-)
(limited to 'pydis_site/templates/content')
diff --git a/Pipfile b/Pipfile
index dae75ea0..8af6e3ce 100644
--- a/Pipfile
+++ b/Pipfile
@@ -19,7 +19,6 @@ pyuwsgi = {version = "~=2.0", sys_platform = "!='win32'"}
 sentry-sdk = "~=0.14"
 gitpython = "~=3.1.7"
 markdown2 = "~=2.3.9"
-python-dateutil = "~=2.8.1"
 
 [dev-packages]
 coverage = "~=5.0"
diff --git a/Pipfile.lock b/Pipfile.lock
index 18b0d582..3dfb7ec6 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "c0e53fd7b7c3d2fc62331078d4ba9301a7e848cd39ec8b41f0f7bc6d911a4d88"
+            "sha256": "893ab5cc0b64f7bb87c0304c9fb6eb24b1856c40286fbbdb1a2bf4a0d7a6d39f"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -18,33 +18,34 @@
     "default": {
         "asgiref": {
             "hashes": [
-                "sha256:a5098bc870b80e7b872bff60bb363c7f2c2c89078759f6c47b53ff8c525a152e",
-                "sha256:cd88907ecaec59d78e4ac00ea665b03e571cb37e3a0e37b3702af1a9e86c365a"
+                "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17",
+                "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0"
             ],
             "markers": "python_version >= '3.5'",
-            "version": "==3.3.0"
+            "version": "==3.3.1"
         },
         "certifi": {
             "hashes": [
-                "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
-                "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
+                "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
+                "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
             ],
-            "version": "==2020.6.20"
+            "version": "==2020.12.5"
         },
         "chardet": {
             "hashes": [
-                "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
-                "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+                "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
+                "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
             ],
-            "version": "==3.0.4"
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+            "version": "==4.0.0"
         },
         "django": {
             "hashes": [
-                "sha256:2d14be521c3ae24960e5e83d4575e156a8c479a75c935224b671b1c6e66eddaf",
-                "sha256:313d0b8f96685e99327785cc600a5178ca855f8e6f4ed162e671e8c3cf749739"
+                "sha256:2afe4900667bcceac792fa34b4fb25448c4fd950d8b32c5508b3442c4b10442a",
+                "sha256:6f13c3e8109236129c49d65a42fbf30c928e66b05ca6862246061b9343ecbaf2"
             ],
             "index": "pypi",
-            "version": "==3.0.10"
+            "version": "==3.0.13"
         },
         "django-environ": {
             "hashes": [
@@ -103,11 +104,11 @@
         },
         "gitpython": {
             "hashes": [
-                "sha256:6eea89b655917b500437e9668e4a12eabdcf00229a0df1762aabd692ef9b746b",
-                "sha256:befa4d101f91bad1b632df4308ec64555db684c360bd7d2130b4807d49ce86b8"
+                "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b",
+                "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"
             ],
             "index": "pypi",
-            "version": "==3.1.11"
+            "version": "==3.1.14"
         },
         "idna": {
             "hashes": [
@@ -145,61 +146,56 @@
         },
         "psycopg2-binary": {
             "hashes": [
-                "sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52",
+                "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c",
+                "sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67",
                 "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0",
-                "sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729",
-                "sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da",
+                "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6",
+                "sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db",
                 "sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94",
-                "sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c",
-                "sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25",
-                "sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1",
-                "sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2",
+                "sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52",
+                "sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056",
+                "sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b",
+                "sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd",
+                "sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550",
+                "sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679",
                 "sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83",
-                "sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71",
+                "sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77",
+                "sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2",
+                "sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77",
+                "sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2",
                 "sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd",
-                "sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd",
                 "sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859",
+                "sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1",
+                "sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25",
+                "sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152",
+                "sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf",
+                "sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f",
+                "sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729",
+                "sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71",
                 "sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66",
-                "sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77",
-                "sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5",
                 "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4",
-                "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c",
-                "sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b",
                 "sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449",
-                "sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4",
-                "sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152",
-                "sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f",
-                "sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77",
-                "sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550",
-                "sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2",
-                "sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf",
-                "sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb",
-                "sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67",
-                "sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db",
+                "sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da",
                 "sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a",
-                "sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679"
+                "sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c",
+                "sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb",
+                "sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4",
+                "sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5"
             ],
             "index": "pypi",
             "version": "==2.8.6"
         },
-        "python-dateutil": {
-            "hashes": [
-                "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
-                "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
-            ],
-            "index": "pypi",
-            "version": "==2.8.1"
-        },
         "pytz": {
             "hashes": [
-                "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed",
-                "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"
+                "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
+                "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
             ],
-            "version": "==2020.1"
+            "version": "==2021.1"
         },
         "pyuwsgi": {
             "hashes": [
                 "sha256:0bd14517398f494d828d77a9bf72b5a6cbef0112e1cc05e9a0080fa8828ccfa0",
+                "sha256:149675b2e020b0e833e8b871a545751ca346cbfed85c8fd2b320a01d40dc3d8f",
                 "sha256:285e263a9094389f13cfdefd033a4e99fbed3ad120dba9ac5093846cc03ac5ab",
                 "sha256:297d1d0b8c472374b12eda7f17a9f5de67cf516612e42b71a7636afb9d1e3974",
                 "sha256:5439f0f3ef5d6bf1622f341662d04c1d92b88889db40b295419e5fe75a7c7d45",
@@ -209,6 +205,7 @@
                 "sha256:90e4235020048456ad867aefc383cdf5528b7f6e327555ceec579c428a828759",
                 "sha256:94d4287b155aa789ce4b6f671c981f7d6c58fc3113330e2f29ac7926cb854645",
                 "sha256:a425f562f382a097ca49df26b70d47d12f0cf0abf233610f3f58b1f7f780355e",
+                "sha256:ac79dead0685beab5ecfe0926426849a44c5572528f89bb17f6ecf5eb561024e",
                 "sha256:bddeb8df77010d0f842068765a0b3155fdcfd847f14bc1ba89fc7e37914a13d2",
                 "sha256:dac4a04dc0f69d641dba984e83214d2c2cc098496c5d5585e7d3f4e7a9190f84"
             ],
@@ -218,36 +215,54 @@
         },
         "pyyaml": {
             "hashes": [
-                "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
-                "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
-                "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
-                "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
-                "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
-                "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
-                "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
-                "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
-                "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
-                "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
-                "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
-            ],
-            "index": "pypi",
-            "version": "==5.3.1"
+                "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf",
+                "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696",
+                "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393",
+                "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77",
+                "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922",
+                "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5",
+                "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8",
+                "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10",
+                "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc",
+                "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018",
+                "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e",
+                "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253",
+                "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347",
+                "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183",
+                "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541",
+                "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb",
+                "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185",
+                "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc",
+                "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db",
+                "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa",
+                "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46",
+                "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122",
+                "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b",
+                "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63",
+                "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df",
+                "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc",
+                "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247",
+                "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6",
+                "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"
+            ],
+            "index": "pypi",
+            "version": "==5.4.1"
         },
         "requests": {
             "hashes": [
-                "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
-                "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
+                "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
+                "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
             ],
             "index": "pypi",
-            "version": "==2.24.0"
+            "version": "==2.25.1"
         },
         "sentry-sdk": {
             "hashes": [
-                "sha256:0eea248408d36e8e7037c7b73827bea20b13a4375bf1719c406cae6fcbc094e3",
-                "sha256:5cf36eb6b1dc62d55f3c64289792cbaebc8ffa5a9da14474f49b46d20caa7fc8"
+                "sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237",
+                "sha256:e75c8c58932bda8cd293ea8e4b242527129e1caaec91433d21b8b2f20fee030b"
             ],
             "index": "pypi",
-            "version": "==0.19.1"
+            "version": "==0.20.3"
         },
         "six": {
             "hashes": [
@@ -259,11 +274,11 @@
         },
         "smmap": {
             "hashes": [
-                "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4",
-                "sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24"
+                "sha256:7bfcf367828031dc893530a29cb35eb8c8f2d7c8f2d0989354d75d24c8573714",
+                "sha256:84c2751ef3072d4f6b2785ec7ee40244c6f45eb934d9e543e2c51f1bd3d54c50"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==3.0.4"
+            "version": "==3.0.5"
         },
         "sqlparse": {
             "hashes": [
@@ -275,11 +290,11 @@
         },
         "urllib3": {
             "hashes": [
-                "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2",
-                "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"
+                "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
+                "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
-            "version": "==1.25.11"
+            "version": "==1.26.4"
         },
         "whitenoise": {
             "hashes": [
@@ -300,18 +315,18 @@
         },
         "attrs": {
             "hashes": [
-                "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
-                "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
+                "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
+                "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==20.2.0"
+            "version": "==20.3.0"
         },
         "bandit": {
             "hashes": [
-                "sha256:336620e220cf2d3115877685e264477ff9d9abaeb0afe3dc7264f55fa17a3952",
-                "sha256:41e75315853507aa145d62a78a2a6c5e3240fe14ee7c601459d0df9418196065"
+                "sha256:216be4d044209fa06cf2a3e51b319769a51be8318140659719aa7a115c35ed07",
+                "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"
             ],
-            "version": "==1.6.2"
+            "version": "==1.7.0"
         },
         "cfgv": {
             "hashes": [
@@ -323,43 +338,61 @@
         },
         "coverage": {
             "hashes": [
-                "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516",
-                "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259",
-                "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9",
-                "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097",
-                "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0",
-                "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f",
-                "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7",
-                "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c",
-                "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5",
-                "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7",
-                "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729",
-                "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978",
-                "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9",
-                "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f",
-                "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9",
-                "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822",
-                "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418",
-                "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82",
-                "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f",
-                "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d",
-                "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221",
-                "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4",
-                "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21",
-                "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709",
-                "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54",
-                "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d",
-                "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270",
-                "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24",
-                "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751",
-                "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a",
-                "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237",
-                "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7",
-                "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636",
-                "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"
-            ],
-            "index": "pypi",
-            "version": "==5.3"
+                "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c",
+                "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6",
+                "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45",
+                "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a",
+                "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03",
+                "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529",
+                "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a",
+                "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a",
+                "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2",
+                "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6",
+                "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759",
+                "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53",
+                "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a",
+                "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4",
+                "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff",
+                "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502",
+                "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793",
+                "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb",
+                "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905",
+                "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821",
+                "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b",
+                "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81",
+                "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0",
+                "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b",
+                "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3",
+                "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184",
+                "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701",
+                "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a",
+                "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82",
+                "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638",
+                "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5",
+                "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083",
+                "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6",
+                "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90",
+                "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465",
+                "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a",
+                "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3",
+                "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e",
+                "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066",
+                "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf",
+                "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b",
+                "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae",
+                "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669",
+                "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873",
+                "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b",
+                "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6",
+                "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb",
+                "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160",
+                "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c",
+                "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079",
+                "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d",
+                "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"
+            ],
+            "index": "pypi",
+            "version": "==5.5"
         },
         "distlib": {
             "hashes": [
@@ -377,19 +410,19 @@
         },
         "flake8": {
             "hashes": [
-                "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839",
-                "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"
+                "sha256:12d05ab02614b6aee8df7c36b97d1a3b2372761222b19b58621355e82acddcff",
+                "sha256:78873e372b12b093da7b5e5ed302e8ad9e988b38b063b61ad937f26ca58fc5f0"
             ],
             "index": "pypi",
-            "version": "==3.8.4"
+            "version": "==3.9.0"
         },
         "flake8-annotations": {
             "hashes": [
-                "sha256:0bcebb0792f1f96d617ded674dca7bf64181870bfe5dace353a1483551f8e5f1",
-                "sha256:bebd11a850f6987a943ce8cdff4159767e0f5f89b3c88aca64680c2175ee02df"
+                "sha256:40a4d504cdf64126ea0bdca39edab1608bc6d515e96569b7e7c3c59c84f66c36",
+                "sha256:eabbfb2dd59ae0e9835f509f930e79cd99fa4ff1026fe6ca073503a57407037c"
             ],
             "index": "pypi",
-            "version": "==2.4.1"
+            "version": "==2.6.1"
         },
         "flake8-bandit": {
             "hashes": [
@@ -400,19 +433,19 @@
         },
         "flake8-bugbear": {
             "hashes": [
-                "sha256:a3ddc03ec28ba2296fc6f89444d1c946a6b76460f859795b35b77d4920a51b63",
-                "sha256:bd02e4b009fb153fe6072c31c52aeab5b133d508095befb2ffcf3b41c4823162"
+                "sha256:528020129fea2dea33a466b9d64ab650aa3e5f9ffc788b70ea4bc6cf18283538",
+                "sha256:f35b8135ece7a014bc0aee5b5d485334ac30a6da48494998cc1fabf7ec70d703"
             ],
             "index": "pypi",
-            "version": "==20.1.4"
+            "version": "==20.11.1"
         },
         "flake8-docstrings": {
             "hashes": [
-                "sha256:3d5a31c7ec6b7367ea6506a87ec293b94a0a46c0bce2bb4975b7f1d09b6f3717",
-                "sha256:a256ba91bc52307bef1de59e2a009c3cf61c3d0952dbe035d6ff7208940c2edc"
+                "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde",
+                "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"
             ],
             "index": "pypi",
-            "version": "==1.5.0"
+            "version": "==1.6.0"
         },
         "flake8-import-order": {
             "hashes": [
@@ -439,11 +472,11 @@
         },
         "flake8-tidy-imports": {
             "hashes": [
-                "sha256:62059ca07d8a4926b561d392cbab7f09ee042350214a25cf12823384a45d27dd",
-                "sha256:c30b40337a2e6802ba3bb611c26611154a27e94c53fc45639e3e282169574fd3"
+                "sha256:52e5f2f987d3d5597538d5941153409ebcab571635835b78f522c7bf03ca23bc",
+                "sha256:76e36fbbfdc8e3c5017f9a216c2855a298be85bc0631e66777f4e6a07a859dc4"
             ],
             "index": "pypi",
-            "version": "==4.1.0"
+            "version": "==4.2.1"
         },
         "flake8-todo": {
             "hashes": [
@@ -462,27 +495,27 @@
         },
         "gitpython": {
             "hashes": [
-                "sha256:6eea89b655917b500437e9668e4a12eabdcf00229a0df1762aabd692ef9b746b",
-                "sha256:befa4d101f91bad1b632df4308ec64555db684c360bd7d2130b4807d49ce86b8"
+                "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b",
+                "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"
             ],
             "index": "pypi",
-            "version": "==3.1.11"
+            "version": "==3.1.14"
         },
         "identify": {
             "hashes": [
-                "sha256:3139bf72d81dfd785b0a464e2776bd59bdc725b4cc10e6cf46b56a0db931c82e",
-                "sha256:969d844b7a85d32a5f9ac4e163df6e846d73c87c8b75847494ee8f4bd2186421"
+                "sha256:39c0b110c9d0cd2391b6c38cd0ff679ee4b4e98f8db8b06c5d9d9e502711a1e1",
+                "sha256:efbf090a619255bc31c4fbba709e2805f7d30913fd4854ad84ace52bd276e2f6"
             ],
-            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==1.5.6"
+            "markers": "python_full_version >= '3.6.1'",
+            "version": "==2.2.0"
         },
         "importlib-metadata": {
             "hashes": [
-                "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da",
-                "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3"
+                "sha256:742add720a20d0467df2f444ae41704000f50e1234f46174b51f9c6031a1bd71",
+                "sha256:b74159469b464a99cb8cc3e21973e4d96e05d3024d337313fedb618a6e86e6f4"
             ],
             "markers": "python_version < '3.8'",
-            "version": "==2.0.0"
+            "version": "==3.7.3"
         },
         "mccabe": {
             "hashes": [
@@ -517,52 +550,70 @@
         },
         "pre-commit": {
             "hashes": [
-                "sha256:7eadaa7f4547a8a19b83230ce430ba81bbe4797bd41c8d7fb54b246164628d1f",
-                "sha256:8fb2037c404ef8c87125e72564f316cf2bc94fc9c1cb184b8352117de747e164"
+                "sha256:94c82f1bf5899d56edb1d926732f4e75a7df29a0c8c092559c77420c9d62428b",
+                "sha256:de55c5c72ce80d79106e48beb1b54104d16495ce7f95b0c7b13d4784193a00af"
             ],
             "index": "pypi",
-            "version": "==2.8.1"
+            "version": "==2.11.1"
         },
         "pycodestyle": {
             "hashes": [
-                "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
-                "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
+                "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068",
+                "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==2.6.0"
+            "version": "==2.7.0"
         },
         "pydocstyle": {
             "hashes": [
-                "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325",
-                "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"
+                "sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f",
+                "sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d"
             ],
-            "markers": "python_version >= '3.5'",
-            "version": "==5.1.1"
+            "markers": "python_version >= '3.6'",
+            "version": "==6.0.0"
         },
         "pyflakes": {
             "hashes": [
-                "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
-                "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
+                "sha256:910208209dcea632721cb58363d0f72913d9e8cf64dc6f8ae2e02a3609aba40d",
+                "sha256:e59fd8e750e588358f1b8885e5a4751203a0516e0ee6d34811089ac294c8806f"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==2.2.0"
+            "version": "==2.3.0"
         },
         "pyyaml": {
             "hashes": [
-                "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
-                "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
-                "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
-                "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
-                "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
-                "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
-                "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
-                "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
-                "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
-                "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
-                "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
-            ],
-            "index": "pypi",
-            "version": "==5.3.1"
+                "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf",
+                "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696",
+                "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393",
+                "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77",
+                "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922",
+                "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5",
+                "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8",
+                "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10",
+                "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc",
+                "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018",
+                "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e",
+                "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253",
+                "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347",
+                "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183",
+                "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541",
+                "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb",
+                "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185",
+                "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc",
+                "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db",
+                "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa",
+                "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46",
+                "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122",
+                "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b",
+                "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63",
+                "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df",
+                "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc",
+                "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247",
+                "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6",
+                "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"
+            ],
+            "index": "pypi",
+            "version": "==5.4.1"
         },
         "six": {
             "hashes": [
@@ -574,69 +625,79 @@
         },
         "smmap": {
             "hashes": [
-                "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4",
-                "sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24"
+                "sha256:7bfcf367828031dc893530a29cb35eb8c8f2d7c8f2d0989354d75d24c8573714",
+                "sha256:84c2751ef3072d4f6b2785ec7ee40244c6f45eb934d9e543e2c51f1bd3d54c50"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==3.0.4"
+            "version": "==3.0.5"
         },
         "snowballstemmer": {
             "hashes": [
-                "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0",
-                "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"
+                "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2",
+                "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"
             ],
-            "version": "==2.0.0"
+            "version": "==2.1.0"
         },
         "stevedore": {
             "hashes": [
-                "sha256:5e1ab03eaae06ef6ce23859402de785f08d97780ed774948ef16c4652c41bc62",
-                "sha256:f845868b3a3a77a2489d226568abe7328b5c2d4f6a011cc759dfa99144a521f0"
+                "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee",
+                "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"
             ],
             "markers": "python_version >= '3.6'",
-            "version": "==3.2.2"
+            "version": "==3.3.0"
         },
         "toml": {
             "hashes": [
-                "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
-                "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+                "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
+                "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
             ],
-            "version": "==0.10.1"
+            "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+            "version": "==0.10.2"
         },
         "typed-ast": {
             "hashes": [
-                "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
-                "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
-                "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d",
-                "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
-                "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
-                "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
-                "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c",
-                "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
-                "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
-                "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
-                "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
-                "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
-                "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
-                "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d",
-                "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
-                "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
-                "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c",
-                "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
-                "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395",
-                "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
-                "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
-                "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
-                "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
-                "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
-                "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072",
-                "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298",
-                "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91",
-                "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
-                "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f",
-                "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
+                "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1",
+                "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d",
+                "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6",
+                "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd",
+                "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37",
+                "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151",
+                "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07",
+                "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440",
+                "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70",
+                "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496",
+                "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea",
+                "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400",
+                "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc",
+                "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606",
+                "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc",
+                "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581",
+                "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412",
+                "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a",
+                "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2",
+                "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787",
+                "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f",
+                "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937",
+                "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64",
+                "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487",
+                "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b",
+                "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41",
+                "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a",
+                "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3",
+                "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166",
+                "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"
+            ],
+            "markers": "python_version < '3.8'",
+            "version": "==1.4.2"
+        },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
+                "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
+                "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
             ],
             "markers": "python_version < '3.8'",
-            "version": "==1.4.1"
+            "version": "==3.7.4.3"
         },
         "unittest-xml-reporting": {
             "hashes": [
@@ -648,19 +709,19 @@
         },
         "virtualenv": {
             "hashes": [
-                "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2",
-                "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"
+                "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107",
+                "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==20.1.0"
+            "version": "==20.4.3"
         },
         "zipp": {
             "hashes": [
-                "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108",
-                "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"
+                "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76",
+                "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"
             ],
             "markers": "python_version >= '3.6'",
-            "version": "==3.4.0"
+            "version": "==3.4.1"
         }
     }
 }
diff --git a/pydis_site/apps/content/tests/test_utils.py b/pydis_site/apps/content/tests/test_utils.py
index 85f1139a..26b2bba9 100644
--- a/pydis_site/apps/content/tests/test_utils.py
+++ b/pydis_site/apps/content/tests/test_utils.py
@@ -146,43 +146,3 @@ class TestGetArticle(TestCase):
         """Check does this raise Http404 when category don't exist."""
         with self.assertRaises(Http404):
             utils.get_article(["invalid", "some-guide"])
-
-
-class GetGitHubInformationTests(TestCase):
-    @patch("pydis_site.apps.content.utils.requests.get")
-    @patch("pydis_site.apps.content.utils.COMMITS_URL", "foobar")
-    def test_call_get_github_information_requests_get(self, requests_get_mock):
-        """Check does this call requests.get function with proper URL."""
-        utils.get_github_information(["foo"])
-        requests_get_mock.assert_called_once_with("foobar")
-
-    @patch("pydis_site.apps.content.utils.requests.get")
-    def test_github_status_code_200_response(self, requests_get_mock):
-        """Check does this return provided modified date and contributors."""
-        requests_get_mock.return_value = MagicMock(status_code=200)
-        requests_get_mock.return_value.json.return_value = [{
-            "commit": {
-                "committer": {
-                    "date": datetime(2020, 10, 1).isoformat(),
-                    "name": "foobar",
-                }
-            },
-            "committer": {
-                "html_url": "abc1234"
-            }
-        }]
-        result = utils.get_github_information(["foo"])
-        self.assertEqual(result, {
-            "last_modified": datetime(2020, 10, 1).strftime("%dth %B %Y"),
-            "contributors": {"foobar": "abc1234"}
-        })
-
-    @patch("pydis_site.apps.content.utils.requests.get")
-    def test_github_other_status_code_response(self, requests_get_mock):
-        """Check does this return provided modified date and contributors."""
-        requests_get_mock.return_value = MagicMock(status_code=404)
-        result = utils.get_github_information(["foo"])
-        self.assertEqual(result, {
-            "last_modified": "N/A",
-            "contributors": {}
-        })
diff --git a/pydis_site/apps/content/tests/test_views.py b/pydis_site/apps/content/tests/test_views.py
index 98b99b83..55bfb8ea 100644
--- a/pydis_site/apps/content/tests/test_views.py
+++ b/pydis_site/apps/content/tests/test_views.py
@@ -30,8 +30,7 @@ class TestArticleOrCategoryView(TestCase):
     @override_settings(ARTICLES_PATH=BASE_PATH)
     @patch("pydis_site.apps.content.views.article_category.utils.get_article")
     @patch("pydis_site.apps.content.views.article_category.utils.get_category")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_github_information")
-    def test_article_return_code_200(self, gh_info_mock, get_category_mock, get_article_mock):
+    def test_article_return_code_200(self, get_category_mock, get_article_mock):
         get_article_mock.return_value = {"guide": "test", "metadata": {}}
 
         url = reverse("content:article_category", args=["test2"])
@@ -39,7 +38,6 @@ class TestArticleOrCategoryView(TestCase):
         self.assertEqual(response.status_code, 200)
         get_category_mock.assert_not_called()
         get_article_mock.assert_called_once()
-        gh_info_mock.assert_called_once()
 
     @patch("pydis_site.apps.content.views.article_category.utils.get_article")
     @patch("pydis_site.apps.content.views.article_category.utils.get_category")
@@ -99,11 +97,9 @@ class TestArticleOrCategoryView(TestCase):
 
     @patch("pydis_site.apps.content.views.article_category.utils.get_article")
     @patch("pydis_site.apps.content.views.article_category.utils.get_category")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_github_information")
     @override_settings(ARTICLES_PATH=BASE_PATH)
     def test_valid_category_article_code_200(
             self,
-            gh_info_mock,
             get_category_mock,
             get_article_mock
     ):
@@ -115,15 +111,12 @@ class TestArticleOrCategoryView(TestCase):
         self.assertEqual(response.status_code, 200)
         get_article_mock.assert_called_once()
         self.assertEqual(get_category_mock.call_count, 2)
-        gh_info_mock.assert_called_once()
 
     @patch("pydis_site.apps.content.views.article_category.utils.get_article")
     @patch("pydis_site.apps.content.views.article_category.utils.get_category")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_github_information")
     @override_settings(ARTICLES_PATH=BASE_PATH)
     def test_invalid_category_article_code_404(
             self,
-            gh_info_mock,
             get_category_mock,
             get_article_mock
     ):
@@ -135,7 +128,6 @@ class TestArticleOrCategoryView(TestCase):
         self.assertEqual(response.status_code, 404)
         get_article_mock.assert_not_called()
         get_category_mock.assert_not_called()
-        gh_info_mock.assert_not_called()
 
     @override_settings(ARTICLES_PATH=BASE_PATH)
     def test_article_category_template_names(self):
diff --git a/pydis_site/apps/content/utils.py b/pydis_site/apps/content/utils.py
index a89db83c..305d26b5 100644
--- a/pydis_site/apps/content/utils.py
+++ b/pydis_site/apps/content/utils.py
@@ -1,16 +1,11 @@
 import os
 from typing import Dict, List, Optional, Union
 
-import requests
 import yaml
-from dateutil import parser
 from django.conf import settings
 from django.http import Http404
 from markdown2 import markdown
 
-COMMITS_URL = "https://api.github.com/repos/{owner}/{name}/commits?path={path}&sha={branch}"
-BASE_ARTICLES_LOCATION = "pydis_site/apps/content/resources/content/"
-
 
 def get_category(path: List[str]) -> Dict[str, str]:
     """Load category information by name from _info.yml."""
@@ -77,36 +72,3 @@ def get_article(path: List[str]) -> Dict[str, Union[str, Dict]]:
     )
 
     return {"article": str(html), "metadata": html.metadata}
-
-
-def get_github_information(
-        path: List[str]
-) -> Dict[str, Union[List[str], str]]:
-    """Get article last modified date and contributors from GitHub."""
-    result = requests.get(
-        COMMITS_URL.format(
-            owner=settings.SITE_REPOSITORY_OWNER,
-            name=settings.SITE_REPOSITORY_NAME,
-            branch=settings.SITE_REPOSITORY_BRANCH,
-            path=(
-                f"{BASE_ARTICLES_LOCATION}{'/'.join(path[:-1])}"
-                f"{'/' if len(path) > 1 else ''}{path[-1]}.md"
-            )
-        )
-    )
-
-    if result.status_code == 200 and len(result.json()):
-        data = result.json()
-        return {
-            "last_modified": parser.isoparse(
-                data[0]["commit"]["committer"]["date"]
-            ).strftime("%dth %B %Y"),
-            "contributors": {
-                c["commit"]["committer"]["name"]: c["committer"]["html_url"] for c in data
-            }
-        }
-    else:
-        return {
-            "last_modified": "N/A",
-            "contributors": {}
-        }
diff --git a/pydis_site/apps/content/views/article_category.py b/pydis_site/apps/content/views/article_category.py
index 0c22b5e8..51b1def2 100644
--- a/pydis_site/apps/content/views/article_category.py
+++ b/pydis_site/apps/content/views/article_category.py
@@ -54,7 +54,6 @@ class ArticleOrCategoryView(TemplateView):
                     article_result["metadata"].get("relevant_link_values", "").split(",")
                 ) if link != "" and value != ""
             }
-            context["github_data"] = utils.get_github_information(location)
         else:
             raise Http404
 
diff --git a/pydis_site/templates/content/article.html b/pydis_site/templates/content/article.html
index fcb29f32..bcdee95a 100644
--- a/pydis_site/templates/content/article.html
+++ b/pydis_site/templates/content/article.html
@@ -34,29 +34,11 @@
         
             
                 {{ article.metadata.title }} 
-                
Last modified:  {{ github_data.last_modified }}
                 
                     
                         {{ article.article|safe }}
                     
                     
-                        
-                            
-                            
-                                {% if github_data.contributors|length %}
-                                    
-                                {% else %}
-                                
N/A
-                                {% endif %}
-                            
-                        
-
                         {% if relevant_links|length > 0 %}
                         
                             
-- 
cgit v1.2.3
From 102ac2be5523bb4a26157168e426fae8f15cf28c Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Tue, 23 Mar 2021 17:34:34 +0800
Subject: Simplify relevant_links configuration in markdown.
---
 pydis_site/apps/content/views/article_category.py | 7 +------
 pydis_site/templates/content/article.html         | 2 +-
 2 files changed, 2 insertions(+), 7 deletions(-)
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/apps/content/views/article_category.py b/pydis_site/apps/content/views/article_category.py
index 51b1def2..2a407b99 100644
--- a/pydis_site/apps/content/views/article_category.py
+++ b/pydis_site/apps/content/views/article_category.py
@@ -48,12 +48,7 @@ class ArticleOrCategoryView(TemplateView):
                 context["category_data"] = {"name": None, "raw_name": None}
 
             context["article"] = article_result
-            context["relevant_links"] = {
-                link: value for link, value in zip(
-                    article_result["metadata"].get("relevant_links", "").split(","),
-                    article_result["metadata"].get("relevant_link_values", "").split(",")
-                ) if link != "" and value != ""
-            }
+            context["relevant_links"] = article_result["metadata"].get("relevant_links", {})
         else:
             raise Http404
 
diff --git a/pydis_site/templates/content/article.html b/pydis_site/templates/content/article.html
index bcdee95a..69d01a8d 100644
--- a/pydis_site/templates/content/article.html
+++ b/pydis_site/templates/content/article.html
@@ -43,7 +43,7 @@
                         
                             
                             
-- 
cgit v1.2.3
From 367ba267c4dbc6d406922f80bc4337ee9a0139a0 Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Tue, 23 Mar 2021 18:24:56 +0800
Subject: Rename `articles` to `pages`.
Articles was a good name, but we want an `articles` category in
the future. `/pages/guides/` and `/pages/articles/` are clearer in name
than `/articles/guides/` and `/articles/articles/`.
---
 pydis_site/apps/content/tests/test_utils.py       |  70 ++++++------
 pydis_site/apps/content/tests/test_views.py       | 127 +++++++++++-----------
 pydis_site/apps/content/urls.py                   |   4 +-
 pydis_site/apps/content/utils.py                  |  36 +++---
 pydis_site/apps/content/views/__init__.py         |   6 +-
 pydis_site/apps/content/views/article_category.py |  69 ------------
 pydis_site/apps/content/views/articles.py         |  16 ---
 pydis_site/apps/content/views/page_category.py    |  69 ++++++++++++
 pydis_site/apps/content/views/pages.py            |  16 +++
 pydis_site/apps/events/urls.py                    |   4 +-
 pydis_site/apps/events/views/__init__.py          |   4 +-
 pydis_site/apps/events/views/page.py              |   2 +-
 pydis_site/apps/home/urls.py                      |   2 +-
 pydis_site/settings.py                            |   2 +-
 pydis_site/static/css/content/articles.css        |  16 ---
 pydis_site/static/css/content/page.css            |  16 +++
 pydis_site/templates/content/article.html         |  58 ----------
 pydis_site/templates/content/listing.html         |  20 ++--
 pydis_site/templates/content/page.html            |  58 ++++++++++
 pydis_site/templates/resources/resources.html     |   2 +-
 20 files changed, 299 insertions(+), 298 deletions(-)
 delete mode 100644 pydis_site/apps/content/views/article_category.py
 delete mode 100644 pydis_site/apps/content/views/articles.py
 create mode 100644 pydis_site/apps/content/views/page_category.py
 create mode 100644 pydis_site/apps/content/views/pages.py
 delete mode 100644 pydis_site/static/css/content/articles.css
 create mode 100644 pydis_site/static/css/content/page.css
 delete mode 100644 pydis_site/templates/content/article.html
 create mode 100644 pydis_site/templates/content/page.html
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/apps/content/tests/test_utils.py b/pydis_site/apps/content/tests/test_utils.py
index 26b2bba9..e1f1b92a 100644
--- a/pydis_site/apps/content/tests/test_utils.py
+++ b/pydis_site/apps/content/tests/test_utils.py
@@ -13,20 +13,20 @@ BASE_PATH = Path(settings.BASE_DIR, "pydis_site", "apps", "content", "tests", "t
 
 
 class TestGetCategory(TestCase):
-    @override_settings(ARTICLES_PATH=BASE_PATH)
+    @override_settings(PAGES_PATH=BASE_PATH)
     def test_get_category_successfully(self):
         """Check does this get right data from category data file."""
         result = utils.get_category(["category"])
 
         self.assertEqual(result, {"name": "My Category", "description": "My Description"})
 
-    @override_settings(ARTICLES_PATH=BASE_PATH)
+    @override_settings(PAGES_PATH=BASE_PATH)
     def test_get_category_not_exists(self):
         """Check does this raise 404 error when category don't exists."""
         with self.assertRaises(Http404):
             utils.get_category(["invalid"])
 
-    @override_settings(ARTICLES_PATH=BASE_PATH)
+    @override_settings(PAGES_PATH=BASE_PATH)
     def test_get_category_not_directory(self):
         """Check does this raise 404 error when category isn't directory."""
         with self.assertRaises(Http404):
@@ -34,7 +34,7 @@ class TestGetCategory(TestCase):
 
 
 class TestGetCategories(TestCase):
-    @override_settings(ARTICLES_PATH=BASE_PATH)
+    @override_settings(PAGES_PATH=BASE_PATH)
     @patch("pydis_site.apps.content.utils.get_category")
     def test_get_categories(self, get_category_mock):
         """Check does this return test content categories."""
@@ -47,7 +47,7 @@ class TestGetCategories(TestCase):
             result, {"category": {"name": "My Category", "description": "My Description"}}
         )
 
-    @override_settings(ARTICLES_PATH=BASE_PATH)
+    @override_settings(PAGES_PATH=BASE_PATH)
     def test_get_categories_root_path(self):
         """Check does this doesn't call joinpath when getting root categories."""
         result = utils.get_categories()
@@ -55,7 +55,7 @@ class TestGetCategories(TestCase):
             result, {"category": {"name": "My Category", "description": "My Description"}}
         )
 
-    @override_settings(ARTICLES_PATH=BASE_PATH)
+    @override_settings(PAGES_PATH=BASE_PATH)
     def test_get_categories_in_category(self):
         """Check does this call joinpath when getting subcategories."""
         result = utils.get_categories(["category"])
@@ -64,11 +64,11 @@ class TestGetCategories(TestCase):
         )
 
 
-class TestGetArticles(TestCase):
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_get_all_root_articles(self):
+class TestGetPages(TestCase):
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_get_all_root_pages(self):
         """Check does this return all root level testing content."""
-        result = utils.get_articles()
+        result = utils.get_pages()
 
         for case in ["test", "test2"]:
             with self.subTest(guide=case):
@@ -77,10 +77,10 @@ class TestGetArticles(TestCase):
                 self.assertIn(case, result)
                 self.assertEqual(md.metadata, result[case])
 
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_get_all_category_articles(self):
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_get_all_category_pages(self):
         """Check does this return all category testing content."""
-        result = utils.get_articles(["category"])
+        result = utils.get_pages(["category"])
 
         md = markdown(BASE_PATH.joinpath("category", "test3.md").read_text(), extras=["metadata"])
 
@@ -88,11 +88,11 @@ class TestGetArticles(TestCase):
         self.assertEqual(md.metadata, result["test3"])
 
 
-class TestGetArticle(TestCase):
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_get_root_article_success(self):
-        """Check does this return article HTML and metadata when root article exist."""
-        result = utils.get_article(["test"])
+class TestGetPage(TestCase):
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_get_root_page_success(self):
+        """Check does this return page HTML and metadata when root page exist."""
+        result = utils.get_page(["test"])
 
         md = markdown(
             BASE_PATH.joinpath("test.md").read_text(),
@@ -107,18 +107,18 @@ class TestGetArticle(TestCase):
             ]
         )
 
-        self.assertEqual(result, {"article": str(md), "metadata": md.metadata})
+        self.assertEqual(result, {"page": str(md), "metadata": md.metadata})
 
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_get_root_article_dont_exist(self):
-        """Check does this raise Http404 when root article don't exist."""
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_get_root_page_dont_exist(self):
+        """Check does this raise Http404 when root page don't exist."""
         with self.assertRaises(Http404):
-            utils.get_article(["invalid"])
+            utils.get_page(["invalid"])
 
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_get_category_article_success(self):
-        """Check does this return article HTML and metadata when category guide exist."""
-        result = utils.get_article(["category", "test3"])
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_get_category_page_success(self):
+        """Check does this return page HTML and metadata when category guide exist."""
+        result = utils.get_page(["category", "test3"])
 
         md = markdown(
             BASE_PATH.joinpath("category", "test3.md").read_text(),
@@ -133,16 +133,16 @@ class TestGetArticle(TestCase):
             ]
         )
 
-        self.assertEqual(result, {"article": str(md), "metadata": md.metadata})
+        self.assertEqual(result, {"page": str(md), "metadata": md.metadata})
 
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_get_category_article_dont_exist(self):
-        """Check does this raise Http404 when category article don't exist."""
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_get_category_page_dont_exist(self):
+        """Check does this raise Http404 when category page don't exist."""
         with self.assertRaises(Http404):
-            utils.get_article(["category", "invalid"])
+            utils.get_page(["category", "invalid"])
 
-    @patch("pydis_site.settings.ARTICLES_PATH", new=BASE_PATH)
-    def test_get_category_article_category_dont_exist(self):
+    @patch("pydis_site.settings.PAGES_PATH", new=BASE_PATH)
+    def test_get_category_page_category_dont_exist(self):
         """Check does this raise Http404 when category don't exist."""
         with self.assertRaises(Http404):
-            utils.get_article(["invalid", "some-guide"])
+            utils.get_page(["invalid", "some-guide"])
diff --git a/pydis_site/apps/content/tests/test_views.py b/pydis_site/apps/content/tests/test_views.py
index 55bfb8ea..5ce18afb 100644
--- a/pydis_site/apps/content/tests/test_views.py
+++ b/pydis_site/apps/content/tests/test_views.py
@@ -6,143 +6,144 @@ from django.http import Http404
 from django.test import RequestFactory, TestCase, override_settings
 from django_hosts.resolvers import reverse
 
-from pydis_site.apps.content.views import ArticleOrCategoryView
+from pydis_site.apps.content.views import PageOrCategoryView
 
 BASE_PATH = Path(settings.BASE_DIR, "pydis_site", "apps", "content", "tests", "test_content")
 
 
-class TestArticlesIndexView(TestCase):
-    @patch("pydis_site.apps.content.views.articles.get_articles")
-    @patch("pydis_site.apps.content.views.articles.get_categories")
-    def test_articles_index_return_200(self, get_categories_mock, get_articles_mock):
+class TestPagesIndexView(TestCase):
+    @patch("pydis_site.apps.content.views.pages.get_pages")
+    @patch("pydis_site.apps.content.views.pages.get_categories")
+    def test_pages_index_return_200(self, get_categories_mock, get_page_mock):
         """Check that content index return HTTP code 200."""
         get_categories_mock.return_value = {}
-        get_articles_mock.return_value = {}
+        get_page_mock.return_value = {}
 
-        url = reverse('content:articles')
+        url = reverse('content:pages')
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)
-        get_articles_mock.assert_called_once()
+        get_page_mock.assert_called_once()
         get_categories_mock.assert_called_once()
 
 
-class TestArticleOrCategoryView(TestCase):
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    @patch("pydis_site.apps.content.views.article_category.utils.get_article")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_category")
-    def test_article_return_code_200(self, get_category_mock, get_article_mock):
-        get_article_mock.return_value = {"guide": "test", "metadata": {}}
+class TestPageOrCategoryView(TestCase):
+    @override_settings(PAGES_PATH=BASE_PATH)
+    @patch("pydis_site.apps.content.views.page_category.utils.get_page")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_category")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_github_information")
+    def test_page_return_code_200(self, get_category_mock, get_page_mock):
+        get_page_mock.return_value = {"guide": "test", "metadata": {}}
 
-        url = reverse("content:article_category", args=["test2"])
+        url = reverse("content:page_category", args=["test2"])
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)
         get_category_mock.assert_not_called()
-        get_article_mock.assert_called_once()
+        get_page_mock.assert_called_once()
 
-    @patch("pydis_site.apps.content.views.article_category.utils.get_article")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_category")
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_article_return_404(self, get_category_mock, get_article_mock):
-        """Check that return code is 404 when invalid article provided."""
-        get_article_mock.side_effect = Http404("Article not found.")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_page")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_category")
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_page_return_404(self, get_category_mock, get_page_mock):
+        """Check that return code is 404 when invalid page provided."""
+        get_page_mock.side_effect = Http404("Page not found.")
 
-        url = reverse("content:article_category", args=["invalid-guide"])
+        url = reverse("content:page_category", args=["invalid-guide"])
         response = self.client.get(url)
         self.assertEqual(response.status_code, 404)
-        get_article_mock.assert_not_called()
+        get_page_mock.assert_not_called()
         get_category_mock.assert_not_called()
 
-    @patch("pydis_site.apps.content.views.article_category.utils.get_category")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_articles")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_categories")
-    @override_settings(ARTICLES_PATH=BASE_PATH)
+    @patch("pydis_site.apps.content.views.page_category.utils.get_category")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_pages")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_categories")
+    @override_settings(PAGES_PATH=BASE_PATH)
     def test_valid_category_code_200(
             self,
             get_categories_mock,
-            get_articles_mock,
+            get_pages_mock,
             get_category_mock
     ):
         """Check that return code is 200 when visiting valid category."""
         get_category_mock.return_value = {"name": "test", "description": "test"}
-        get_articles_mock.return_value = {}
+        get_pages_mock.return_value = {}
 
-        url = reverse("content:article_category", args=["category"])
+        url = reverse("content:page_category", args=["category"])
         response = self.client.get(url)
 
         self.assertEqual(response.status_code, 200)
-        get_articles_mock.assert_called_once()
+        get_pages_mock.assert_called_once()
         get_category_mock.assert_called_once()
         get_categories_mock.assert_called_once()
 
-    @patch("pydis_site.apps.content.views.article_category.utils.get_category")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_articles")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_categories")
-    @override_settings(ARTICLES_PATH=BASE_PATH)
+    @patch("pydis_site.apps.content.views.page_category.utils.get_category")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_pages")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_categories")
+    @override_settings(PAGES_PATH=BASE_PATH)
     def test_invalid_category_code_404(
             self,
             get_categories_mock,
-            get_articles_mock,
+            get_pages_mock,
             get_category_mock
     ):
         """Check that return code is 404 when trying to visit invalid category."""
         get_category_mock.side_effect = Http404("Category not found.")
 
-        url = reverse("content:article_category", args=["invalid-category"])
+        url = reverse("content:page_category", args=["invalid-category"])
         response = self.client.get(url)
 
         self.assertEqual(response.status_code, 404)
         get_category_mock.assert_not_called()
-        get_articles_mock.assert_not_called()
+        get_pages_mock.assert_not_called()
         get_categories_mock.assert_not_called()
 
-    @patch("pydis_site.apps.content.views.article_category.utils.get_article")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_category")
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_valid_category_article_code_200(
+    @patch("pydis_site.apps.content.views.page_category.utils.get_page")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_category")
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_valid_category_page_code_200(
             self,
             get_category_mock,
-            get_article_mock
+            get_page_mock
     ):
-        """Check that return code is 200 when visiting valid category article."""
-        get_article_mock.return_value = {"guide": "test", "metadata": {}}
+        """Check that return code is 200 when visiting valid category page."""
+        get_page_mock.return_value = {"guide": "test", "metadata": {}}
 
-        url = reverse("content:article_category", args=["category/test3"])
+        url = reverse("content:page_category", args=["category/test3"])
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)
-        get_article_mock.assert_called_once()
+        get_page_mock.assert_called_once()
         self.assertEqual(get_category_mock.call_count, 2)
 
-    @patch("pydis_site.apps.content.views.article_category.utils.get_article")
-    @patch("pydis_site.apps.content.views.article_category.utils.get_category")
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_invalid_category_article_code_404(
+    @patch("pydis_site.apps.content.views.page_category.utils.get_page")
+    @patch("pydis_site.apps.content.views.page_category.utils.get_category")
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_invalid_category_page_code_404(
             self,
             get_category_mock,
-            get_article_mock
+            get_page_mock
     ):
-        """Check that return code is 200 when trying to visit invalid category article."""
-        get_article_mock.side_effect = Http404("Article not found.")
+        """Check that return code is 200 when trying to visit invalid category page."""
+        get_page_mock.side_effect = Http404("Page not found.")
 
-        url = reverse("content:article_category", args=["category/invalid"])
+        url = reverse("content:page_category", args=["category/invalid"])
         response = self.client.get(url)
         self.assertEqual(response.status_code, 404)
-        get_article_mock.assert_not_called()
+        get_page_mock.assert_not_called()
         get_category_mock.assert_not_called()
 
-    @override_settings(ARTICLES_PATH=BASE_PATH)
-    def test_article_category_template_names(self):
-        """Check that this return category, article template or raise Http404."""
+    @override_settings(PAGES_PATH=BASE_PATH)
+    def test_page_category_template_names(self):
+        """Check that this return category, page template or raise Http404."""
         factory = RequestFactory()
         cases = [
             {"location": "category", "output": ["content/listing.html"]},
-            {"location": "test", "output": ["content/article.html"]},
+            {"location": "test", "output": ["content/page.html"]},
             {"location": "invalid", "output": None, "raises": Http404}
         ]
 
         for case in cases:
             with self.subTest(location=case["location"], output=case["output"]):
-                request = factory.get(f"/articles/{case['location']}")
-                instance = ArticleOrCategoryView()
+                request = factory.get(f"/pages/{case['location']}")
+                instance = PageOrCategoryView()
                 instance.request = request
                 instance.kwargs = {"location": case["location"]}
 
diff --git a/pydis_site/apps/content/urls.py b/pydis_site/apps/content/urls.py
index 49a5a2ef..1406f672 100644
--- a/pydis_site/apps/content/urls.py
+++ b/pydis_site/apps/content/urls.py
@@ -4,6 +4,6 @@ from . import views
 
 app_name = "content"
 urlpatterns = [
-    path("", views.ArticlesView.as_view(), name='articles'),
-    path("/", views.ArticleOrCategoryView.as_view(), name='article_category'),
+    path("", views.PagesView.as_view(), name='pages'),
+    path("/", views.PageOrCategoryView.as_view(), name='page_category'),
 ]
diff --git a/pydis_site/apps/content/utils.py b/pydis_site/apps/content/utils.py
index 305d26b5..db502a71 100644
--- a/pydis_site/apps/content/utils.py
+++ b/pydis_site/apps/content/utils.py
@@ -9,7 +9,7 @@ from markdown2 import markdown
 
 def get_category(path: List[str]) -> Dict[str, str]:
     """Load category information by name from _info.yml."""
-    path = settings.ARTICLES_PATH.joinpath(*path)
+    path = settings.PAGES_PATH.joinpath(*path)
     if not path.exists() or not path.is_dir():
         raise Http404("Category not found.")
 
@@ -20,10 +20,10 @@ def get_categories(path: Optional[List[str]] = None) -> Dict[str, Dict]:
     """Get all categories information."""
     categories = {}
     if path is None:
-        categories_path = settings.ARTICLES_PATH
+        categories_path = settings.PAGES_PATH
         path = []
     else:
-        categories_path = settings.ARTICLES_PATH.joinpath(*path)
+        categories_path = settings.PAGES_PATH.joinpath(*path)
 
     for name in categories_path.iterdir():
         if name.is_dir():
@@ -32,34 +32,34 @@ def get_categories(path: Optional[List[str]] = None) -> Dict[str, Dict]:
     return categories
 
 
-def get_articles(path: Optional[List[str]] = None) -> Dict[str, Dict]:
-    """Get all root or category articles."""
+def get_pages(path: Optional[List[str]] = None) -> Dict[str, Dict]:
+    """Get all root or category pages."""
     if path is None:
-        base_dir = settings.ARTICLES_PATH
+        base_dir = settings.PAGES_PATH
     else:
-        base_dir = settings.ARTICLES_PATH.joinpath(*path)
+        base_dir = settings.PAGES_PATH.joinpath(*path)
 
-    articles = {}
+    pages = {}
 
     for item in base_dir.iterdir():
         if item.is_file() and item.name.endswith(".md"):
             md = markdown(item.read_text(), extras=["metadata"])
-            articles[os.path.splitext(item.name)[0]] = md.metadata
+            pages[os.path.splitext(item.name)[0]] = md.metadata
 
-    return articles
+    return pages
 
 
-def get_article(path: List[str]) -> Dict[str, Union[str, Dict]]:
-    """Get one specific article. When category is specified, get it from there."""
-    article_path = settings.ARTICLES_PATH.joinpath(*path[:-1])
+def get_page(path: List[str]) -> Dict[str, Union[str, Dict]]:
+    """Get one specific page. When category is specified, get it from there."""
+    page_path = settings.PAGES_PATH.joinpath(*path[:-1])
 
     # We need to include extension MD
-    article_path = article_path.joinpath(f"{path[-1]}.md")
-    if not article_path.exists() or not article_path.is_file():
-        raise Http404("Article not found.")
+    page_path = page_path.joinpath(f"{path[-1]}.md")
+    if not page_path.exists() or not page_path.is_file():
+        raise Http404("Page not found.")
 
     html = markdown(
-        article_path.read_text(),
+        page_path.read_text(),
         extras=[
             "metadata",
             "fenced-code-blocks",
@@ -71,4 +71,4 @@ def get_article(path: List[str]) -> Dict[str, Union[str, Dict]]:
         ]
     )
 
-    return {"article": str(html), "metadata": html.metadata}
+    return {"page": str(html), "metadata": html.metadata}
diff --git a/pydis_site/apps/content/views/__init__.py b/pydis_site/apps/content/views/__init__.py
index f92660d6..740d98e9 100644
--- a/pydis_site/apps/content/views/__init__.py
+++ b/pydis_site/apps/content/views/__init__.py
@@ -1,4 +1,4 @@
-from .article_category import ArticleOrCategoryView
-from .articles import ArticlesView
+from .page_category import PageOrCategoryView
+from .pages import PagesView
 
-__all__ = ["ArticleOrCategoryView", "ArticlesView"]
+__all__ = ["PageOrCategoryView", "PagesView"]
diff --git a/pydis_site/apps/content/views/article_category.py b/pydis_site/apps/content/views/article_category.py
deleted file mode 100644
index 2a407b99..00000000
--- a/pydis_site/apps/content/views/article_category.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import typing as t
-
-from django.conf import settings
-from django.http import Http404
-from django.views.generic import TemplateView
-
-from pydis_site.apps.content import utils
-
-
-class ArticleOrCategoryView(TemplateView):
-    """Handles article and category pages."""
-
-    def get_template_names(self) -> t.List[str]:
-        """Checks does this use article template or listing template."""
-        location = self.kwargs["location"].split("/")
-        full_location = settings.ARTICLES_PATH.joinpath(*location)
-
-        if full_location.is_dir():
-            template_name = "content/listing.html"
-        elif full_location.with_suffix(".md").is_file():
-            template_name = "content/article.html"
-        else:
-            raise Http404
-
-        return [template_name]
-
-    def get_context_data(self, **kwargs) -> t.Dict[str, t.Any]:
-        """Assign proper context variables based on what resource user requests."""
-        context = super().get_context_data(**kwargs)
-
-        location: list = self.kwargs["location"].split("/")
-        full_location = settings.ARTICLES_PATH.joinpath(*location)
-
-        if full_location.is_dir():
-            context["category_info"] = utils.get_category(location)
-            context["content"] = utils.get_articles(location)
-            context["categories"] = utils.get_categories(location)
-            # Add trailing slash here to simplify template
-            context["path"] = "/".join(location) + "/"
-            context["in_category"] = True
-        elif full_location.with_suffix(".md").is_file():
-            article_result = utils.get_article(location)
-
-            if len(location) > 1:
-                context["category_data"] = utils.get_category(location[:-1])
-                context["category_data"]["raw_name"] = location[:-1][-1]
-            else:
-                context["category_data"] = {"name": None, "raw_name": None}
-
-            context["article"] = article_result
-            context["relevant_links"] = article_result["metadata"].get("relevant_links", {})
-        else:
-            raise Http404
-
-        location.pop()
-        breadcrumb_items = []
-        while len(location):
-            breadcrumb_items.insert(
-                0,
-                {
-                    "name": utils.get_category(location)["name"],
-                    "path": "/".join(location)
-                }
-            )
-            location.pop()
-
-        context["breadcrumb_items"] = breadcrumb_items
-
-        return context
diff --git a/pydis_site/apps/content/views/articles.py b/pydis_site/apps/content/views/articles.py
deleted file mode 100644
index 999002d0..00000000
--- a/pydis_site/apps/content/views/articles.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from django.views.generic import TemplateView
-
-from pydis_site.apps.content.utils import get_articles, get_categories
-
-
-class ArticlesView(TemplateView):
-    """Shows all content and categories."""
-
-    template_name = "content/listing.html"
-
-    def get_context_data(self, **kwargs) -> dict:
-        """Add articles and categories data to template context."""
-        context = super().get_context_data(**kwargs)
-        context["content"] = get_articles()
-        context["categories"] = get_categories()
-        return context
diff --git a/pydis_site/apps/content/views/page_category.py b/pydis_site/apps/content/views/page_category.py
new file mode 100644
index 00000000..f00a79ee
--- /dev/null
+++ b/pydis_site/apps/content/views/page_category.py
@@ -0,0 +1,69 @@
+import typing as t
+
+from django.conf import settings
+from django.http import Http404
+from django.views.generic import TemplateView
+
+from pydis_site.apps.content import utils
+
+
+class PageOrCategoryView(TemplateView):
+    """Handles pages and page categories."""
+
+    def get_template_names(self) -> t.List[str]:
+        """Checks does this use page template or listing template."""
+        location = self.kwargs["location"].split("/")
+        full_location = settings.PAGES_PATH.joinpath(*location)
+
+        if full_location.is_dir():
+            template_name = "content/listing.html"
+        elif full_location.with_suffix(".md").is_file():
+            template_name = "content/page.html"
+        else:
+            raise Http404
+
+        return [template_name]
+
+    def get_context_data(self, **kwargs) -> t.Dict[str, t.Any]:
+        """Assign proper context variables based on what resource user requests."""
+        context = super().get_context_data(**kwargs)
+
+        location: list = self.kwargs["location"].split("/")
+        full_location = settings.PAGES_PATH.joinpath(*location)
+
+        if full_location.is_dir():
+            context["category_info"] = utils.get_category(location)
+            context["content"] = utils.get_pages(location)
+            context["categories"] = utils.get_categories(location)
+            # Add trailing slash here to simplify template
+            context["path"] = "/".join(location) + "/"
+            context["in_category"] = True
+        elif full_location.with_suffix(".md").is_file():
+            page_result = utils.get_page(location)
+
+            if len(location) > 1:
+                context["category_data"] = utils.get_category(location[:-1])
+                context["category_data"]["raw_name"] = location[:-1][-1]
+            else:
+                context["category_data"] = {"name": None, "raw_name": None}
+
+            context["page"] = page_result
+            context["relevant_links"] = page_result["metadata"].get("relevant_links", {})
+        else:
+            raise Http404
+
+        location.pop()
+        breadcrumb_items = []
+        while len(location):
+            breadcrumb_items.insert(
+                0,
+                {
+                    "name": utils.get_category(location)["name"],
+                    "path": "/".join(location)
+                }
+            )
+            location.pop()
+
+        context["breadcrumb_items"] = breadcrumb_items
+
+        return context
diff --git a/pydis_site/apps/content/views/pages.py b/pydis_site/apps/content/views/pages.py
new file mode 100644
index 00000000..11ac0eeb
--- /dev/null
+++ b/pydis_site/apps/content/views/pages.py
@@ -0,0 +1,16 @@
+from django.views.generic import TemplateView
+
+from pydis_site.apps.content.utils import get_pages, get_categories
+
+
+class PagesView(TemplateView):
+    """Shows all pages and categories."""
+
+    template_name = "content/listing.html"
+
+    def get_context_data(self, **kwargs) -> dict:
+        """Add page and category data to template context."""
+        context = super().get_context_data(**kwargs)
+        context["content"] = get_pages()
+        context["categories"] = get_categories()
+        return context
diff --git a/pydis_site/apps/events/urls.py b/pydis_site/apps/events/urls.py
index 9a65cf1f..601451da 100644
--- a/pydis_site/apps/events/urls.py
+++ b/pydis_site/apps/events/urls.py
@@ -1,9 +1,9 @@
 from django.urls import path
 
-from pydis_site.apps.events.views import IndexView, PageView
+from pydis_site.apps.events.views import IndexView, PagesView
 
 app_name = "events"
 urlpatterns = [
     path("", IndexView.as_view(), name="index"),
-    path("/", PageView.as_view(), name="page"),
+    path("/", PagesView.as_view(), name="page"),
 ]
diff --git a/pydis_site/apps/events/views/__init__.py b/pydis_site/apps/events/views/__init__.py
index 8a107e2f..6188f723 100644
--- a/pydis_site/apps/events/views/__init__.py
+++ b/pydis_site/apps/events/views/__init__.py
@@ -1,4 +1,4 @@
 from .index import IndexView
-from .page import PageView
+from .page import PagesView
 
-__all__ = ["IndexView", "PageView"]
+__all__ = ["IndexView", "PagesView"]
diff --git a/pydis_site/apps/events/views/page.py b/pydis_site/apps/events/views/page.py
index f4c37aeb..fbedd4e8 100644
--- a/pydis_site/apps/events/views/page.py
+++ b/pydis_site/apps/events/views/page.py
@@ -5,7 +5,7 @@ from django.http import Http404
 from django.views.generic import TemplateView
 
 
-class PageView(TemplateView):
+class PagesView(TemplateView):
     """Handles event pages showing."""
 
     def get_template_names(self) -> List[str]:
diff --git a/pydis_site/apps/home/urls.py b/pydis_site/apps/home/urls.py
index bd7c0625..3c716875 100644
--- a/pydis_site/apps/home/urls.py
+++ b/pydis_site/apps/home/urls.py
@@ -8,6 +8,6 @@ urlpatterns = [
     path('', HomeView.as_view(), name='home'),
     path('admin/', admin.site.urls),
     path('resources/', include('pydis_site.apps.resources.urls')),
-    path('articles/', include('pydis_site.apps.content.urls')),
+    path('pages/', include('pydis_site.apps.content.urls')),
     path('events/', include('pydis_site.apps.events.urls', namespace='events')),
 ]
diff --git a/pydis_site/settings.py b/pydis_site/settings.py
index d509d980..0b4d9fd1 100644
--- a/pydis_site/settings.py
+++ b/pydis_site/settings.py
@@ -287,4 +287,4 @@ SITE_REPOSITORY_OWNER = "python-discord"
 SITE_REPOSITORY_NAME = "site"
 SITE_REPOSITORY_BRANCH = "master"
 
-ARTICLES_PATH = Path(BASE_DIR, "pydis_site", "apps", "content", "resources", "content")
+PAGES_PATH = Path(BASE_DIR, "pydis_site", "apps", "content", "resources", "content")
diff --git a/pydis_site/static/css/content/articles.css b/pydis_site/static/css/content/articles.css
deleted file mode 100644
index f46d6b15..00000000
--- a/pydis_site/static/css/content/articles.css
+++ /dev/null
@@ -1,16 +0,0 @@
-.breadcrumb-section {
-    padding: 1rem;
-}
-
-i.has-icon-padding {
-    padding: 0 10px 25px 0;
-}
-
-pre {
-    /*
-     * Style it the same as the  tag, since highlight.js does not style
-     * backgrounds of  tags but bulma does, resulting in a weird off-white
-     * border.
-     */
-    background-color: #282c34;
-}
diff --git a/pydis_site/static/css/content/page.css b/pydis_site/static/css/content/page.css
new file mode 100644
index 00000000..f46d6b15
--- /dev/null
+++ b/pydis_site/static/css/content/page.css
@@ -0,0 +1,16 @@
+.breadcrumb-section {
+    padding: 1rem;
+}
+
+i.has-icon-padding {
+    padding: 0 10px 25px 0;
+}
+
+pre {
+    /*
+     * Style it the same as the  tag, since highlight.js does not style
+     * backgrounds of  tags but bulma does, resulting in a weird off-white
+     * border.
+     */
+    background-color: #282c34;
+}
diff --git a/pydis_site/templates/content/article.html b/pydis_site/templates/content/article.html
deleted file mode 100644
index 69d01a8d..00000000
--- a/pydis_site/templates/content/article.html
+++ /dev/null
@@ -1,58 +0,0 @@
-{% extends 'base/base.html' %}
-{% load static %}
-
-{% block title %}{{ article.metadata.title }}{% endblock %}
-{% block head %}
-   
-   
-   
-   
-   
-  
-  
-{% endblock %}
-
-{% block content %}
-    {% include "base/navbar.html" %}
-
-    
-
-    
-        
-            
-                
{{ article.metadata.title }} 
-                
-                    
-                        {{ article.article|safe }}
-                    
-                    
-                        {% if relevant_links|length > 0 %}
-                        
-                            
-                            
-                        
-                        {% endif %}
-                    
-                
-            
-        
 
-     
-
-{% endblock %}
diff --git a/pydis_site/templates/content/listing.html b/pydis_site/templates/content/listing.html
index 8c06bccc..39eae1c2 100644
--- a/pydis_site/templates/content/listing.html
+++ b/pydis_site/templates/content/listing.html
@@ -1,12 +1,12 @@
 {% extends 'base/base.html' %}
 {% load static %}
 
-{% block title %}{{ category_info.name|default:"Articles" }}{% endblock %}
+{% block title %}{{ category_info.name|default:"Pages" }}{% endblock %}
 {% block head %}
-     
+     
      
      
-     
+     
 {% endblock %}
 
 {% block content %}
@@ -17,12 +17,12 @@
             
                 
              
                 
@@ -31,7 +31,7 @@
     
         
             
-                
{{ category_info.name|default:"Articles" }} 
+                
{{ category_info.name|default:"Pages" }} 
                 {% for category, data in categories.items %}
                     
                 {% endfor %}
-                {% for article, data in content.items %}
+                {% for page, data in content.items %}
                     
                         
                              
                          
-                        
+                         
                                 {{ data.title }} 
                          
                         {{ data.short_description }}
diff --git a/pydis_site/templates/content/page.html b/pydis_site/templates/content/page.html
new file mode 100644
index 00000000..3b0ebb5f
--- /dev/null
+++ b/pydis_site/templates/content/page.html
@@ -0,0 +1,58 @@
+{% extends 'base/base.html' %}
+{% load static %}
+
+{% block title %}{{ page.metadata.title }}{% endblock %}
+{% block head %}
+  
+  
+  
+  
+  
+  
+  
+{% endblock %}
+
+{% block content %}
+    {% include "base/navbar.html" %}
+
+    
+
+    
+        
+            
+                
{{ page.metadata.title }} 
+                
+                    
+                        {{ page.page|safe }}
+                    
+                    
+                        {% if relevant_links|length > 0 %}
+                        
+                            
+                            
+                        
+                        {% endif %}
+                    
+                
+            
+        
 
+     
+
+{% endblock %}
diff --git a/pydis_site/templates/resources/resources.html b/pydis_site/templates/resources/resources.html
index 2dc88a8c..491bc55e 100644
--- a/pydis_site/templates/resources/resources.html
+++ b/pydis_site/templates/resources/resources.html
@@ -15,7 +15,7 @@
               
Resources 
 
               
-                  
+                   
                       
                             Guides
                           Made by us, for you
-- 
cgit v1.2.3
From 82daedc766dc3986dc9ac17ea8a6b3da87a6b1ac Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Tue, 23 Mar 2021 23:17:55 +0800
Subject: Simplify content app.
Rather than having two views for the base page and all other pages, all
pages now use the same view.
The view context handler is simplified to take advantage of pathlib
features.
The markdown folder is now /content/resources/* rather than
/content/resources/content/*, as the latter is unnecessary nesting.
---
 pydis_site/apps/content/resources/_info.yml        |  2 +
 .../content/resources/content/guides/_info.yml     |  2 -
 .../content/guides/pydis-guides/_info.yml          |  2 -
 .../guides/pydis-guides/how-to-write-a-article.md  | 80 ----------------------
 pydis_site/apps/content/resources/guides/_info.yml |  2 +
 .../resources/guides/pydis-guides/_info.yml        |  2 +
 .../guides/pydis-guides/how-to-write-a-article.md  | 80 ++++++++++++++++++++++
 pydis_site/apps/content/urls.py                    |  2 +-
 pydis_site/apps/content/utils.py                   | 42 ++++--------
 pydis_site/apps/content/views/__init__.py          |  3 +-
 pydis_site/apps/content/views/page_category.py     | 61 +++++++----------
 pydis_site/apps/content/views/pages.py             | 16 -----
 pydis_site/settings.py                             |  2 +-
 pydis_site/templates/content/listing.html          |  5 +-
 pydis_site/templates/content/page.html             |  1 -
 15 files changed, 127 insertions(+), 175 deletions(-)
 create mode 100644 pydis_site/apps/content/resources/_info.yml
 delete mode 100644 pydis_site/apps/content/resources/content/guides/_info.yml
 delete mode 100644 pydis_site/apps/content/resources/content/guides/pydis-guides/_info.yml
 delete mode 100644 pydis_site/apps/content/resources/content/guides/pydis-guides/how-to-write-a-article.md
 create mode 100644 pydis_site/apps/content/resources/guides/_info.yml
 create mode 100644 pydis_site/apps/content/resources/guides/pydis-guides/_info.yml
 create mode 100644 pydis_site/apps/content/resources/guides/pydis-guides/how-to-write-a-article.md
 delete mode 100644 pydis_site/apps/content/views/pages.py
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/apps/content/resources/_info.yml b/pydis_site/apps/content/resources/_info.yml
new file mode 100644
index 00000000..4ccdd7e1
--- /dev/null
+++ b/pydis_site/apps/content/resources/_info.yml
@@ -0,0 +1,2 @@
+name: Pages
+description: Guides, articles and pages hosted on the site.
diff --git a/pydis_site/apps/content/resources/content/guides/_info.yml b/pydis_site/apps/content/resources/content/guides/_info.yml
deleted file mode 100644
index 369f05d4..00000000
--- a/pydis_site/apps/content/resources/content/guides/_info.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-name: Guides
-description: Python and PyDis guides.
diff --git a/pydis_site/apps/content/resources/content/guides/pydis-guides/_info.yml b/pydis_site/apps/content/resources/content/guides/pydis-guides/_info.yml
deleted file mode 100644
index 64111a83..00000000
--- a/pydis_site/apps/content/resources/content/guides/pydis-guides/_info.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-name: Python Discord Guides
-description: Python Discord server and community guides.
diff --git a/pydis_site/apps/content/resources/content/guides/pydis-guides/how-to-write-a-article.md b/pydis_site/apps/content/resources/content/guides/pydis-guides/how-to-write-a-article.md
deleted file mode 100644
index ec89988c..00000000
--- a/pydis_site/apps/content/resources/content/guides/pydis-guides/how-to-write-a-article.md
+++ /dev/null
@@ -1,80 +0,0 @@
----
-title: How to Write a Article
-short_description: Learn how to write a article for this website
-icon_class: fas
-icon: fa-info
----
-
-When you are interested about how to write articles for this site (like this), then you can learn about it here.
-PyDis use Markdown (GitHub Markdown) files for articles.
-
-## Getting Started
-Before you can get started with writing a article, you need idea.
-Best way to find out is your idea good is to discuss about it in #dev-contrib channel. There can other peoples give their opinion about your idea. Even better, open issue in site repository first, then PyDis staff can see it and approve/decline this idea.
-It's good idea to wait for staff decision before starting to write guide to avoid case when you write a long long article, but then this don't get approved.
-
-To start with contributing, you should read [how to contribute to site](https://pythondiscord.com/pages/contributing/site/).
-You should also read our [Git workflow](https://pythondiscord.com/pages/contributing/working-with-git/), because you need to push your guide to GitHub.
-
-## Creating a File
-All articles is located at `site` repository, in `pydis_site/apps/content/resources/content`. Under this is root level articles (.md files) and categories (directories). Learn more about categories in [categories section](#categories).
-
-When you are writing guides, then these are located under `guides` category.
-
-At this point, you will need your article name for filename. Replace all your article name spaces with `-` and make all lowercase. Save this as `.md` (Markdown) file. This name (without Markdown extension) is path of article in URL.
-
-## Markdown Metadata
-Article files have some required metadata, like title, description, relevant pages. Metadata is first thing in file, YAML-like key-value pairs:
-
-```md
----
-title: My Article
-short_description: This is my short description.
-relevant_links: url1,url2,url3
-relevant_link_values: Text for url1,Text for url2,Text for url3
----
-
-Here comes content of article...
-```
-
-You can read more about Markdown metadata [here](https://github.com/trentm/python-markdown2/wiki/metadata).
-
-### Fields
-- **Name:** Easily-readable name for your article.
-- **Short Description:** Small, 1-2 line description that describe what your article explain.
-- **Relevant Links and Values:** URLs and values is under different fields, separated with comma.
-- **Icon class:** `icon_class` field have one of the favicons classes. Default is `fab`.
-- **Icon:** `icon` field have favicon name. Default `fa-python`.
-
-## Content
-For content, mostly you can use standard markdown, but there is a few addition that is available.
-
-### IDs for quick jumps
-System automatically assign IDs to headers, so like this header will get ID `ids-for-quick-jumps`.
-
-### Tables
-Tables like in GitHub is supported too:
-
-| This is header | This is too header |
-| -------------- | ------------------ |
-| My item        | My item too        |
-
-### Codeblocks
-Also this system supports codeblocks and provides syntax highlighting with `highlight.js`.
-To activate syntax highlight, just put language directly after starting backticks.
-
-```py
-import os
-
-path = os.path.join("foo", "bar")
-```
-
-## Categories
-To have some systematic sorting of guides, site support guides categories. Currently this system support only 1 level of categories. Categories live at `site` repo in `pydis_site/apps/content/resources/content` subdirectories. Directory name is path of category in URL. Inside category directory, there is 1 file required: `_info.yml`. This file need 2 key-value pairs defined:
-
-```yml
-name: Category name
-description: Category description
-```
-
-Then all Markdown files in this folder will be under this category.
diff --git a/pydis_site/apps/content/resources/guides/_info.yml b/pydis_site/apps/content/resources/guides/_info.yml
new file mode 100644
index 00000000..369f05d4
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/_info.yml
@@ -0,0 +1,2 @@
+name: Guides
+description: Python and PyDis guides.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/_info.yml b/pydis_site/apps/content/resources/guides/pydis-guides/_info.yml
new file mode 100644
index 00000000..64111a83
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/_info.yml
@@ -0,0 +1,2 @@
+name: Python Discord Guides
+description: Python Discord server and community guides.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/how-to-write-a-article.md b/pydis_site/apps/content/resources/guides/pydis-guides/how-to-write-a-article.md
new file mode 100644
index 00000000..ec89988c
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/how-to-write-a-article.md
@@ -0,0 +1,80 @@
+---
+title: How to Write a Article
+short_description: Learn how to write a article for this website
+icon_class: fas
+icon: fa-info
+---
+
+When you are interested about how to write articles for this site (like this), then you can learn about it here.
+PyDis use Markdown (GitHub Markdown) files for articles.
+
+## Getting Started
+Before you can get started with writing a article, you need idea.
+Best way to find out is your idea good is to discuss about it in #dev-contrib channel. There can other peoples give their opinion about your idea. Even better, open issue in site repository first, then PyDis staff can see it and approve/decline this idea.
+It's good idea to wait for staff decision before starting to write guide to avoid case when you write a long long article, but then this don't get approved.
+
+To start with contributing, you should read [how to contribute to site](https://pythondiscord.com/pages/contributing/site/).
+You should also read our [Git workflow](https://pythondiscord.com/pages/contributing/working-with-git/), because you need to push your guide to GitHub.
+
+## Creating a File
+All articles is located at `site` repository, in `pydis_site/apps/content/resources/content`. Under this is root level articles (.md files) and categories (directories). Learn more about categories in [categories section](#categories).
+
+When you are writing guides, then these are located under `guides` category.
+
+At this point, you will need your article name for filename. Replace all your article name spaces with `-` and make all lowercase. Save this as `.md` (Markdown) file. This name (without Markdown extension) is path of article in URL.
+
+## Markdown Metadata
+Article files have some required metadata, like title, description, relevant pages. Metadata is first thing in file, YAML-like key-value pairs:
+
+```md
+---
+title: My Article
+short_description: This is my short description.
+relevant_links: url1,url2,url3
+relevant_link_values: Text for url1,Text for url2,Text for url3
+---
+
+Here comes content of article...
+```
+
+You can read more about Markdown metadata [here](https://github.com/trentm/python-markdown2/wiki/metadata).
+
+### Fields
+- **Name:** Easily-readable name for your article.
+- **Short Description:** Small, 1-2 line description that describe what your article explain.
+- **Relevant Links and Values:** URLs and values is under different fields, separated with comma.
+- **Icon class:** `icon_class` field have one of the favicons classes. Default is `fab`.
+- **Icon:** `icon` field have favicon name. Default `fa-python`.
+
+## Content
+For content, mostly you can use standard markdown, but there is a few addition that is available.
+
+### IDs for quick jumps
+System automatically assign IDs to headers, so like this header will get ID `ids-for-quick-jumps`.
+
+### Tables
+Tables like in GitHub is supported too:
+
+| This is header | This is too header |
+| -------------- | ------------------ |
+| My item        | My item too        |
+
+### Codeblocks
+Also this system supports codeblocks and provides syntax highlighting with `highlight.js`.
+To activate syntax highlight, just put language directly after starting backticks.
+
+```py
+import os
+
+path = os.path.join("foo", "bar")
+```
+
+## Categories
+To have some systematic sorting of guides, site support guides categories. Currently this system support only 1 level of categories. Categories live at `site` repo in `pydis_site/apps/content/resources/content` subdirectories. Directory name is path of category in URL. Inside category directory, there is 1 file required: `_info.yml`. This file need 2 key-value pairs defined:
+
+```yml
+name: Category name
+description: Category description
+```
+
+Then all Markdown files in this folder will be under this category.
diff --git a/pydis_site/apps/content/urls.py b/pydis_site/apps/content/urls.py
index 1406f672..c11b222a 100644
--- a/pydis_site/apps/content/urls.py
+++ b/pydis_site/apps/content/urls.py
@@ -4,6 +4,6 @@ from . import views
 
 app_name = "content"
 urlpatterns = [
-    path("", views.PagesView.as_view(), name='pages'),
+    path("", views.PageOrCategoryView.as_view(), name='pages'),
     path("/", views.PageOrCategoryView.as_view(), name='page_category'),
 ]
diff --git a/pydis_site/apps/content/utils.py b/pydis_site/apps/content/utils.py
index db502a71..ef02a8cb 100644
--- a/pydis_site/apps/content/utils.py
+++ b/pydis_site/apps/content/utils.py
@@ -1,65 +1,49 @@
-import os
-from typing import Dict, List, Optional, Union
+from pathlib import Path
+from typing import Dict, Union
 
 import yaml
-from django.conf import settings
 from django.http import Http404
 from markdown2 import markdown
 
 
-def get_category(path: List[str]) -> Dict[str, str]:
+def get_category(path: Path) -> Dict[str, str]:
     """Load category information by name from _info.yml."""
-    path = settings.PAGES_PATH.joinpath(*path)
     if not path.exists() or not path.is_dir():
         raise Http404("Category not found.")
 
     return yaml.safe_load(path.joinpath("_info.yml").read_text())
 
 
-def get_categories(path: Optional[List[str]] = None) -> Dict[str, Dict]:
+def get_categories(path: Path) -> Dict[str, Dict]:
     """Get all categories information."""
     categories = {}
-    if path is None:
-        categories_path = settings.PAGES_PATH
-        path = []
-    else:
-        categories_path = settings.PAGES_PATH.joinpath(*path)
 
-    for name in categories_path.iterdir():
+    for name in path.iterdir():
         if name.is_dir():
-            categories[name.name] = get_category([*path, name.name])
+            categories[name.name] = get_category(path.joinpath(name.name))
 
     return categories
 
 
-def get_pages(path: Optional[List[str]] = None) -> Dict[str, Dict]:
-    """Get all root or category pages."""
-    if path is None:
-        base_dir = settings.PAGES_PATH
-    else:
-        base_dir = settings.PAGES_PATH.joinpath(*path)
-
+def get_pages(path: Path) -> Dict[str, Dict]:
+    """Get all root or category page names and their metadata."""
     pages = {}
 
-    for item in base_dir.iterdir():
+    for item in path.iterdir():
         if item.is_file() and item.name.endswith(".md"):
             md = markdown(item.read_text(), extras=["metadata"])
-            pages[os.path.splitext(item.name)[0]] = md.metadata
+            pages[item.stem] = md.metadata
 
     return pages
 
 
-def get_page(path: List[str]) -> Dict[str, Union[str, Dict]]:
+def get_page(path: Path) -> Dict[str, Union[str, Dict]]:
     """Get one specific page. When category is specified, get it from there."""
-    page_path = settings.PAGES_PATH.joinpath(*path[:-1])
-
-    # We need to include extension MD
-    page_path = page_path.joinpath(f"{path[-1]}.md")
-    if not page_path.exists() or not page_path.is_file():
+    if not path.exists() or not path.is_file():
         raise Http404("Page not found.")
 
     html = markdown(
-        page_path.read_text(),
+        path.read_text(),
         extras=[
             "metadata",
             "fenced-code-blocks",
diff --git a/pydis_site/apps/content/views/__init__.py b/pydis_site/apps/content/views/__init__.py
index 740d98e9..70ea1c7a 100644
--- a/pydis_site/apps/content/views/__init__.py
+++ b/pydis_site/apps/content/views/__init__.py
@@ -1,4 +1,3 @@
 from .page_category import PageOrCategoryView
-from .pages import PagesView
 
-__all__ = ["PageOrCategoryView", "PagesView"]
+__all__ = ["PageOrCategoryView"]
diff --git a/pydis_site/apps/content/views/page_category.py b/pydis_site/apps/content/views/page_category.py
index f00a79ee..7e04e2f3 100644
--- a/pydis_site/apps/content/views/page_category.py
+++ b/pydis_site/apps/content/views/page_category.py
@@ -1,4 +1,5 @@
 import typing as t
+from pathlib import Path
 
 from django.conf import settings
 from django.http import Http404
@@ -10,14 +11,18 @@ from pydis_site.apps.content import utils
 class PageOrCategoryView(TemplateView):
     """Handles pages and page categories."""
 
+    def dispatch(self, request: t.Any, *args, **kwargs) -> t.Any:
+        """Conform URL path location to the filesystem path."""
+        self.location = Path(self.kwargs.get("location", ""))
+        self.full_location = settings.PAGES_PATH / self.location
+
+        return super().dispatch(request, *args, **kwargs)
+
     def get_template_names(self) -> t.List[str]:
         """Checks does this use page template or listing template."""
-        location = self.kwargs["location"].split("/")
-        full_location = settings.PAGES_PATH.joinpath(*location)
-
-        if full_location.is_dir():
+        if self.full_location.is_dir():
             template_name = "content/listing.html"
-        elif full_location.with_suffix(".md").is_file():
+        elif self.full_location.with_suffix(".md").is_file():
             template_name = "content/page.html"
         else:
             raise Http404
@@ -28,42 +33,24 @@ class PageOrCategoryView(TemplateView):
         """Assign proper context variables based on what resource user requests."""
         context = super().get_context_data(**kwargs)
 
-        location: list = self.kwargs["location"].split("/")
-        full_location = settings.PAGES_PATH.joinpath(*location)
-
-        if full_location.is_dir():
-            context["category_info"] = utils.get_category(location)
-            context["content"] = utils.get_pages(location)
-            context["categories"] = utils.get_categories(location)
-            # Add trailing slash here to simplify template
-            context["path"] = "/".join(location) + "/"
-            context["in_category"] = True
-        elif full_location.with_suffix(".md").is_file():
-            page_result = utils.get_page(location)
-
-            if len(location) > 1:
-                context["category_data"] = utils.get_category(location[:-1])
-                context["category_data"]["raw_name"] = location[:-1][-1]
-            else:
-                context["category_data"] = {"name": None, "raw_name": None}
-
+        if self.full_location.is_dir():
+            context["categories"] = utils.get_categories(self.full_location)
+            context["category_info"] = utils.get_category(self.full_location)
+            context["content"] = utils.get_pages(self.full_location)
+            context["path"] = f"{self.location}/"  # Add trailing slash here to simplify template
+        elif self.full_location.with_suffix(".md").is_file():
+            page_result = utils.get_page(self.full_location.with_suffix(".md"))
             context["page"] = page_result
             context["relevant_links"] = page_result["metadata"].get("relevant_links", {})
         else:
             raise Http404
 
-        location.pop()
-        breadcrumb_items = []
-        while len(location):
-            breadcrumb_items.insert(
-                0,
-                {
-                    "name": utils.get_category(location)["name"],
-                    "path": "/".join(location)
-                }
-            )
-            location.pop()
-
-        context["breadcrumb_items"] = breadcrumb_items
+        breadcrumb_items = [
+            {
+                "name": utils.get_category(settings.PAGES_PATH / location)["name"],
+                "path": str(location)
+            } for location in self.location.parents
+        ]
+        context["breadcrumb_items"] = reversed(breadcrumb_items)
 
         return context
diff --git a/pydis_site/apps/content/views/pages.py b/pydis_site/apps/content/views/pages.py
deleted file mode 100644
index 11ac0eeb..00000000
--- a/pydis_site/apps/content/views/pages.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from django.views.generic import TemplateView
-
-from pydis_site.apps.content.utils import get_pages, get_categories
-
-
-class PagesView(TemplateView):
-    """Shows all pages and categories."""
-
-    template_name = "content/listing.html"
-
-    def get_context_data(self, **kwargs) -> dict:
-        """Add page and category data to template context."""
-        context = super().get_context_data(**kwargs)
-        context["content"] = get_pages()
-        context["categories"] = get_categories()
-        return context
diff --git a/pydis_site/settings.py b/pydis_site/settings.py
index 0b4d9fd1..3abf556a 100644
--- a/pydis_site/settings.py
+++ b/pydis_site/settings.py
@@ -287,4 +287,4 @@ SITE_REPOSITORY_OWNER = "python-discord"
 SITE_REPOSITORY_NAME = "site"
 SITE_REPOSITORY_BRANCH = "master"
 
-PAGES_PATH = Path(BASE_DIR, "pydis_site", "apps", "content", "resources", "content")
+PAGES_PATH = Path(BASE_DIR, "pydis_site", "apps", "content", "resources")
diff --git a/pydis_site/templates/content/listing.html b/pydis_site/templates/content/listing.html
index 39eae1c2..097cac4f 100644
--- a/pydis_site/templates/content/listing.html
+++ b/pydis_site/templates/content/listing.html
@@ -16,13 +16,10 @@
         
diff --git a/pydis_site/templates/content/page.html b/pydis_site/templates/content/page.html
index 3b0ebb5f..433baa69 100644
--- a/pydis_site/templates/content/page.html
+++ b/pydis_site/templates/content/page.html
@@ -20,7 +20,6 @@
         
             
                 
-                    Pages  
                     {% for item in breadcrumb_items %}
                         {{ item.name }}  
                     {% endfor %}
-- 
cgit v1.2.3
From b3a48b8b9056a7620f06282cb45a4a9eef9616fa Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Wed, 24 Mar 2021 00:54:04 +0800
Subject: Fix content columns when there is no sidebar.
If the `relevant_links` field is not provided in the markdown, do not
restrict contents to 2/3 columns.
---
 pydis_site/templates/content/page.html | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/templates/content/page.html b/pydis_site/templates/content/page.html
index 433baa69..a687933d 100644
--- a/pydis_site/templates/content/page.html
+++ b/pydis_site/templates/content/page.html
@@ -33,23 +33,26 @@
         
             
                 {{ page.metadata.title }} 
-                
-                    
-                        {{ page.page|safe }}
-                    
-                    
-                        {% if relevant_links|length > 0 %}
-                        
-                            
-                            
+
+                {% if relevant_links|length > 0 %}
+                    
+                        
+                            {{ page.page|safe }}
+                        
+                        
-                        {% endif %}
                     
-                
+                {% else %}
+                    
{{ page.page|safe }}
+                {% endif %}
             
 
         
     
-- 
cgit v1.2.3
From dd16d63b3a6f1f646879c35e09fadfcbe39a03d1 Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Wed, 24 Mar 2021 00:55:28 +0800
Subject: Rename `short_description` to `description`.
Given that there is no "long description", just `description` conveys
the same information with brevity.
---
 .../guides/pydis-guides/how-to-write-a-article.md  | 80 ----------------------
 .../test_content/category/subcategory/test4.md     |  2 +-
 .../content/tests/test_content/category/test3.md   |  2 +-
 pydis_site/apps/content/tests/test_content/test.md |  2 +-
 .../apps/content/tests/test_content/test2.md       |  2 +-
 pydis_site/templates/content/listing.html          |  2 +-
 pydis_site/templates/content/page.html             |  2 +-
 7 files changed, 6 insertions(+), 86 deletions(-)
 delete mode 100644 pydis_site/apps/content/resources/guides/pydis-guides/how-to-write-a-article.md
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/how-to-write-a-article.md b/pydis_site/apps/content/resources/guides/pydis-guides/how-to-write-a-article.md
deleted file mode 100644
index ec89988c..00000000
--- a/pydis_site/apps/content/resources/guides/pydis-guides/how-to-write-a-article.md
+++ /dev/null
@@ -1,80 +0,0 @@
----
-title: How to Write a Article
-short_description: Learn how to write a article for this website
-icon_class: fas
-icon: fa-info
----
-
-When you are interested about how to write articles for this site (like this), then you can learn about it here.
-PyDis use Markdown (GitHub Markdown) files for articles.
-
-## Getting Started
-Before you can get started with writing a article, you need idea.
-Best way to find out is your idea good is to discuss about it in #dev-contrib channel. There can other peoples give their opinion about your idea. Even better, open issue in site repository first, then PyDis staff can see it and approve/decline this idea.
-It's good idea to wait for staff decision before starting to write guide to avoid case when you write a long long article, but then this don't get approved.
-
-To start with contributing, you should read [how to contribute to site](https://pythondiscord.com/pages/contributing/site/).
-You should also read our [Git workflow](https://pythondiscord.com/pages/contributing/working-with-git/), because you need to push your guide to GitHub.
-
-## Creating a File
-All articles is located at `site` repository, in `pydis_site/apps/content/resources/content`. Under this is root level articles (.md files) and categories (directories). Learn more about categories in [categories section](#categories).
-
-When you are writing guides, then these are located under `guides` category.
-
-At this point, you will need your article name for filename. Replace all your article name spaces with `-` and make all lowercase. Save this as `.md` (Markdown) file. This name (without Markdown extension) is path of article in URL.
-
-## Markdown Metadata
-Article files have some required metadata, like title, description, relevant pages. Metadata is first thing in file, YAML-like key-value pairs:
-
-```md
----
-title: My Article
-short_description: This is my short description.
-relevant_links: url1,url2,url3
-relevant_link_values: Text for url1,Text for url2,Text for url3
----
-
-Here comes content of article...
-```
-
-You can read more about Markdown metadata [here](https://github.com/trentm/python-markdown2/wiki/metadata).
-
-### Fields
-- **Name:** Easily-readable name for your article.
-- **Short Description:** Small, 1-2 line description that describe what your article explain.
-- **Relevant Links and Values:** URLs and values is under different fields, separated with comma.
-- **Icon class:** `icon_class` field have one of the favicons classes. Default is `fab`.
-- **Icon:** `icon` field have favicon name. Default `fa-python`.
-
-## Content
-For content, mostly you can use standard markdown, but there is a few addition that is available.
-
-### IDs for quick jumps
-System automatically assign IDs to headers, so like this header will get ID `ids-for-quick-jumps`.
-
-### Tables
-Tables like in GitHub is supported too:
-
-| This is header | This is too header |
-| -------------- | ------------------ |
-| My item        | My item too        |
-
-### Codeblocks
-Also this system supports codeblocks and provides syntax highlighting with `highlight.js`.
-To activate syntax highlight, just put language directly after starting backticks.
-
-```py
-import os
-
-path = os.path.join("foo", "bar")
-```
-
-## Categories
-To have some systematic sorting of guides, site support guides categories. Currently this system support only 1 level of categories. Categories live at `site` repo in `pydis_site/apps/content/resources/content` subdirectories. Directory name is path of category in URL. Inside category directory, there is 1 file required: `_info.yml`. This file need 2 key-value pairs defined:
-
-```yml
-name: Category name
-description: Category description
-```
-
-Then all Markdown files in this folder will be under this category.
diff --git a/pydis_site/apps/content/tests/test_content/category/subcategory/test4.md b/pydis_site/apps/content/tests/test_content/category/subcategory/test4.md
index 8031131d..1763a869 100644
--- a/pydis_site/apps/content/tests/test_content/category/subcategory/test4.md
+++ b/pydis_site/apps/content/tests/test_content/category/subcategory/test4.md
@@ -1,6 +1,6 @@
 ---
 title: Test 4
-short_description: Testing 4
+description: Testing 4
 ---
 
 This is also test content and in subcategory.
diff --git a/pydis_site/apps/content/tests/test_content/category/test3.md b/pydis_site/apps/content/tests/test_content/category/test3.md
index 03ddd67b..9f294130 100644
--- a/pydis_site/apps/content/tests/test_content/category/test3.md
+++ b/pydis_site/apps/content/tests/test_content/category/test3.md
@@ -1,6 +1,6 @@
 ---
 title: Test 3
-short_description: Testing 3
+description: Testing 3
 ---
 
 This is too test content, but in category.
diff --git a/pydis_site/apps/content/tests/test_content/test.md b/pydis_site/apps/content/tests/test_content/test.md
index ca06c1d5..709860d1 100644
--- a/pydis_site/apps/content/tests/test_content/test.md
+++ b/pydis_site/apps/content/tests/test_content/test.md
@@ -1,6 +1,6 @@
 ---
 title: Test
-short_description: Testing
+description: Testing
 relevant_links:
     Asking Good Questions: https://pythondiscord.com/pages/resources/guides/asking-good-questions/
     Help Channel Guide: https://pythondiscord.com/pages/resources/guides/help-channels/
diff --git a/pydis_site/apps/content/tests/test_content/test2.md b/pydis_site/apps/content/tests/test_content/test2.md
index 14d8a54b..0e57c3cd 100644
--- a/pydis_site/apps/content/tests/test_content/test2.md
+++ b/pydis_site/apps/content/tests/test_content/test2.md
@@ -1,6 +1,6 @@
 ---
 title: Test 2
-short_description: Testing 2
+description: Testing 2
 ---
 
 This is too test content.
diff --git a/pydis_site/templates/content/listing.html b/pydis_site/templates/content/listing.html
index 097cac4f..6c84a38c 100644
--- a/pydis_site/templates/content/listing.html
+++ b/pydis_site/templates/content/listing.html
@@ -50,7 +50,7 @@
                         
                                 {{ data.title }} 
                          
-                        {{ data.short_description }}
+                        {{ data.description }}
                       
                 {% endfor %}
             
 
diff --git a/pydis_site/templates/content/page.html b/pydis_site/templates/content/page.html
index a687933d..c3b2285b 100644
--- a/pydis_site/templates/content/page.html
+++ b/pydis_site/templates/content/page.html
@@ -5,7 +5,7 @@
 {% block head %}
    
    
-   
+   
    
    
-- 
cgit v1.2.3
From baebc131ed3be8d015acaad589b697983435037e Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Wed, 24 Mar 2021 14:09:32 +0800
Subject: Refactor common HTML into a base template.
---
 pydis_site/apps/content/views/page_category.py |  7 ++-
 pydis_site/templates/content/base.html         | 36 ++++++++++++
 pydis_site/templates/content/listing.html      | 76 ++++++++-----------------
 pydis_site/templates/content/page.html         | 77 ++++++++------------------
 4 files changed, 88 insertions(+), 108 deletions(-)
 create mode 100644 pydis_site/templates/content/base.html
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/apps/content/views/page_category.py b/pydis_site/apps/content/views/page_category.py
index 7e8fe07d..623c2596 100644
--- a/pydis_site/apps/content/views/page_category.py
+++ b/pydis_site/apps/content/views/page_category.py
@@ -35,12 +35,17 @@ class PageOrCategoryView(TemplateView):
 
         if self.full_location.is_dir():
             context["categories"] = utils.get_categories(self.full_location)
-            context["category_info"] = utils.get_category(self.full_location)
+            category = utils.get_category(self.full_location)
+            context["category_info"] = category
+            context["page_title"] = category["name"]
+            context["page_description"] = category["description"]
             context["content"] = utils.get_pages(self.full_location)
             context["path"] = f"{self.location}/"  # Add trailing slash here to simplify template
         elif self.full_location.with_suffix(".md").is_file():
             page_result = utils.get_page(self.full_location.with_suffix(".md"))
             context["page"] = page_result
+            context["page_title"] = page_result["metadata"]["title"]
+            context["page_description"] = page_result["metadata"]["description"]
             context["relevant_links"] = page_result["metadata"].get("relevant_links", {})
         else:
             raise Http404
diff --git a/pydis_site/templates/content/base.html b/pydis_site/templates/content/base.html
new file mode 100644
index 00000000..1508dfb3
--- /dev/null
+++ b/pydis_site/templates/content/base.html
@@ -0,0 +1,36 @@
+{% extends 'base/base.html' %}
+{% load static %}
+
+{% block title %}{{ page_title }}{% endblock %}
+{% block head %}
+ 
+     
+     
+     
+{% endblock %}
+
+{% block content %}
+    {% include "base/navbar.html" %}
+
+    
+
+    
+        
+            
+                
{{ page_title }} 
+                {% block page_content %}{% endblock %}
+            
+        
 
+     
+{% endblock %}
diff --git a/pydis_site/templates/content/listing.html b/pydis_site/templates/content/listing.html
index 6c84a38c..9271f3ab 100644
--- a/pydis_site/templates/content/listing.html
+++ b/pydis_site/templates/content/listing.html
@@ -1,59 +1,27 @@
-{% extends 'base/base.html' %}
-{% load static %}
+{% extends 'content/base.html' %}
 
-{% block title %}{{ category_info.name|default:"Pages" }}{% endblock %}
-{% block head %}
-     
-     
-     
-     
-{% endblock %}
-
-{% block content %}
-    {% include "base/navbar.html" %}
+{% block page_content %}
+    {% for category, data in categories.items %}
+        
+            
+                 
+             
 
-    
-
-    
-        
-            
-                
{{ category_info.name|default:"Pages" }} 
-                {% for category, data in categories.items %}
-                    
-                {% endfor %}
-                {% for page, data in content.items %}
-                    
-                {% endfor %}
-            
+    {% endfor %}
+    {% for page, data in content.items %}
+        
-    
+    {% endfor %}
 {% endblock %}
diff --git a/pydis_site/templates/content/page.html b/pydis_site/templates/content/page.html
index c3b2285b..2b366e50 100644
--- a/pydis_site/templates/content/page.html
+++ b/pydis_site/templates/content/page.html
@@ -1,60 +1,31 @@
-{% extends 'base/base.html' %}
-{% load static %}
+{% extends 'content/base.html' %}
 
-{% block title %}{{ page.metadata.title }}{% endblock %}
 {% block head %}
-  
-  
-  
-  
-  
-  
-  
+    {{ block.super }}
+    
+    
+    
 {% endblock %}
 
-{% block content %}
-    {% include "base/navbar.html" %}
-
-    
-
-    
-        
-            
-                
{{ page.metadata.title }} 
-
-                {% if relevant_links|length > 0 %}
-                    
-                        
-                            {{ page.page|safe }}
-                        
-                        
-                    
-                {% else %}
-                    
{{ page.page|safe }}
-                {% endif %}
+{% block page_content %}
+    {% if relevant_links|length > 0 %}
+        
+            
+                {{ page.page|safe }}
+            
+            
         
-    
-
+    {% else %}
+        
{{ page.page|safe }}
+    {% endif %}
 {% endblock %}
-- 
cgit v1.2.3
From dbac69c7a58938fb24128a787971ea1bc1892110 Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Wed, 24 Mar 2021 14:14:43 +0800
Subject: Improve variable and key names for page utils.
---
 pydis_site/apps/content/utils.py               | 6 +++---
 pydis_site/apps/content/views/page_category.py | 2 +-
 pydis_site/templates/content/listing.html      | 2 +-
 pydis_site/templates/content/page.html         | 4 ++--
 4 files changed, 7 insertions(+), 7 deletions(-)
(limited to 'pydis_site/templates/content')
diff --git a/pydis_site/apps/content/utils.py b/pydis_site/apps/content/utils.py
index 6cb15581..8cbcad91 100644
--- a/pydis_site/apps/content/utils.py
+++ b/pydis_site/apps/content/utils.py
@@ -25,8 +25,8 @@ def get_categories(path: Path) -> Dict[str, Dict]:
     return categories
 
 
-def get_pages(path: Path) -> Dict[str, Dict]:
-    """Get all root or category page names and their metadata."""
+def get_category_pages(path: Path) -> Dict[str, Dict]:
+    """Get all page names and their metadata at a category path."""
     pages = {}
 
     for item in path.iterdir():
@@ -56,4 +56,4 @@ def get_page(path: Path) -> Dict[str, Union[str, Dict]]:
         ]
     )
 
-    return {"page": str(html), "metadata": html.metadata}
+    return {"content": str(html), "metadata": html.metadata}
diff --git a/pydis_site/apps/content/views/page_category.py b/pydis_site/apps/content/views/page_category.py
index 623c2596..0bef82d0 100644
--- a/pydis_site/apps/content/views/page_category.py
+++ b/pydis_site/apps/content/views/page_category.py
@@ -39,7 +39,7 @@ class PageOrCategoryView(TemplateView):
             context["category_info"] = category
             context["page_title"] = category["name"]
             context["page_description"] = category["description"]
-            context["content"] = utils.get_pages(self.full_location)
+            context["pages"] = utils.get_category_pages(self.full_location)
             context["path"] = f"{self.location}/"  # Add trailing slash here to simplify template
         elif self.full_location.with_suffix(".md").is_file():
             page_result = utils.get_page(self.full_location.with_suffix(".md"))
diff --git a/pydis_site/templates/content/listing.html b/pydis_site/templates/content/listing.html
index 9271f3ab..6de306b0 100644
--- a/pydis_site/templates/content/listing.html
+++ b/pydis_site/templates/content/listing.html
@@ -13,7 +13,7 @@
             {{ data.description }}
          
     {% endfor %}
-    {% for page, data in content.items %}
+    {% for page, data in pages.items %}
         
             
                  
diff --git a/pydis_site/templates/content/page.html b/pydis_site/templates/content/page.html
index 2b366e50..5e820c26 100644
--- a/pydis_site/templates/content/page.html
+++ b/pydis_site/templates/content/page.html
@@ -12,7 +12,7 @@
     {% if relevant_links|length > 0 %}
         
             
-                {{ page.page|safe }}
+                {{ page.content|safe }}
             
             
     {% else %}
-        
{{ page.page|safe }}
+        
{{ page.content|safe }}
     {% endif %}
 {% endblock %}
-- 
cgit v1.2.3
From 6ccec0d866c44cd7f9789a396ef6ec6cd2cd5df8 Mon Sep 17 00:00:00 2001
From: kosayoda 
Date: Wed, 24 Mar 2021 19:55:16 +0800
Subject: Replace `markdown2` with `markdown` and `python-frontmatter`.
This allows us to properly escape codeblocks within markdown, permalink
to headers on a page, and decouples getting metadata from a file and
getting generated HTML from the Markdown content.
---
 Pipfile                                            |  3 +-
 Pipfile.lock                                       | 43 ++++++++++++++--
 .../pydis-guides/how-to-contribute-a-page.md       | 59 +++++++++++++++++++++-
 pydis_site/apps/content/utils.py                   | 32 ++++++------
 pydis_site/apps/content/views/page_category.py     | 15 +++---
 pydis_site/static/css/content/page.css             | 29 ++++++++---
 pydis_site/templates/content/base.html             |  2 +-
 pydis_site/templates/content/page.html             |  4 +-
 8 files changed, 146 insertions(+), 41 deletions(-)
(limited to 'pydis_site/templates/content')
diff --git a/Pipfile b/Pipfile
index 8af6e3ce..c2b87ab4 100644
--- a/Pipfile
+++ b/Pipfile
@@ -18,7 +18,8 @@ pyyaml = "~=5.1"
 pyuwsgi = {version = "~=2.0", sys_platform = "!='win32'"}
 sentry-sdk = "~=0.14"
 gitpython = "~=3.1.7"
-markdown2 = "~=2.3.9"
+markdown = "~=3.3.4"
+python-frontmatter = "~=1.0"
 
 [dev-packages]
 coverage = "~=5.0"
diff --git a/Pipfile.lock b/Pipfile.lock
index 3dfb7ec6..4f8bd0d3 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "893ab5cc0b64f7bb87c0304c9fb6eb24b1856c40286fbbdb1a2bf4a0d7a6d39f"
+            "sha256": "a338a377b64a5d25bf925646c97d932a2d1c783fc8f40d91b6a9ab8f30c2b14e"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -118,6 +118,14 @@
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
             "version": "==2.10"
         },
+        "importlib-metadata": {
+            "hashes": [
+                "sha256:742add720a20d0467df2f444ae41704000f50e1234f46174b51f9c6031a1bd71",
+                "sha256:b74159469b464a99cb8cc3e21973e4d96e05d3024d337313fedb618a6e86e6f4"
+            ],
+            "markers": "python_version < '3.8'",
+            "version": "==3.7.3"
+        },
         "libsass": {
             "hashes": [
                 "sha256:1521d2a8d4b397c6ec90640a1f6b5529077035efc48ef1c2e53095544e713d1b",
@@ -136,13 +144,13 @@
             ],
             "version": "==0.20.1"
         },
-        "markdown2": {
+        "markdown": {
             "hashes": [
-                "sha256:85956d8119fa6378156fef65545d66705a842819d2e1b50379a2b9d2aaa17cf0",
-                "sha256:fef148e5fd68d4532286c3e2943e9d2c076a8ad781b0a70a9d599a0ffe91652d"
+                "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49",
+                "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"
             ],
             "index": "pypi",
-            "version": "==2.3.10"
+            "version": "==3.3.4"
         },
         "psycopg2-binary": {
             "hashes": [
@@ -185,6 +193,14 @@
             "index": "pypi",
             "version": "==2.8.6"
         },
+        "python-frontmatter": {
+            "hashes": [
+                "sha256:766ae75f1b301ffc5fe3494339147e0fd80bc3deff3d7590a93991978b579b08",
+                "sha256:e98152e977225ddafea6f01f40b4b0f1de175766322004c826ca99842d19a7cd"
+            ],
+            "index": "pypi",
+            "version": "==1.0.0"
+        },
         "pytz": {
             "hashes": [
                 "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
@@ -288,6 +304,15 @@
             "markers": "python_version >= '3.5'",
             "version": "==0.4.1"
         },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
+                "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
+                "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
+            ],
+            "markers": "python_version < '3.8'",
+            "version": "==3.7.4.3"
+        },
         "urllib3": {
             "hashes": [
                 "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
@@ -303,6 +328,14 @@
             ],
             "index": "pypi",
             "version": "==5.2.0"
+        },
+        "zipp": {
+            "hashes": [
+                "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76",
+                "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==3.4.1"
         }
     },
     "develop": {
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 8967e7da..12969ba2 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
@@ -69,18 +69,75 @@ You can learn more about Markdown metadata [here](https://github.com/trentm/pyth
 
 Apart from standard Markdown, certain additions are available:
 
+### Abbreviations
+HTML `` tags can be used in markdown using this format:
+
+**Markdown:**
+```nohighlight
+This website is HTML generated from YAML and Markdown.
+
+*[HTML]: Hyper Text Markup Language
+*[YAML]: YAML Ain't Markup Language
+```
+
+**Output:**
+
+This website is HTML 
+generated from YAML  and Markdown.
+
+---
+
+### Footnotes
+**Markdown:**
+```nohighlight
+This footnote[^1] links to the bottom[^custom_label] of the page[^3].
+
+[^1]: Footnote labels start with a caret `^`.
+[^3]: The footnote link is numbered based on the order of the labels.
+[^custom label]: Footnote labels can contain any text within square brackets.
+```
+
+**Output:**
+
+This footnote[^1] links to the bottom[^custom label] of the page[^3].
+
+[^1]: Footnote labels start with a caret `^`.
+[^3]: The footnote link is numbered based on the order of the labels.
+[^custom label]: Footnote labels can contain any text within square brackets.
+
+---
+
 ### Tables
 
+**Markdown:**
+```nohighlight
+| This is header | This is another header |
+| -------------- | ---------------------- |
+| An item        | Another item           |
+```
+
+**Output:**
+
 | This is header | This is another header |
 | -------------- | ---------------------- |
 | An item        | Another item           |
-| Big item       | Small item             |
 
+---
 
 ### Codeblock Syntax Highlighting
 Syntax highlighting is provided by `highlight.js`.
 To activate syntax highlighting, put the language directly after the starting backticks.
 
+**Markdown:**
+````nohighlight
+```python
+import os
+
+path = os.path.join("foo", "bar")
+```
+````
+
+**Output:**
 ```python
 import os
 
diff --git a/pydis_site/apps/content/utils.py b/pydis_site/apps/content/utils.py
index 0faf722c..11d2b792 100644
--- a/pydis_site/apps/content/utils.py
+++ b/pydis_site/apps/content/utils.py
@@ -1,9 +1,11 @@
 from pathlib import Path
-from typing import Dict, Union
+from typing import Dict, Tuple
 
+import frontmatter
+import markdown
 import yaml
 from django.http import Http404
-from markdown2 import markdown
+from markdown.extensions.toc import TocExtension
 
 
 def get_category(path: Path) -> Dict[str, str]:
@@ -31,29 +33,25 @@ def get_category_pages(path: Path) -> Dict[str, Dict]:
 
     for item in path.glob("*.md"):
         if item.is_file():
-            md = markdown(item.read_text(), extras=["metadata"])
-            pages[item.stem] = md.metadata
+            pages[item.stem] = frontmatter.load(item)
 
     return pages
 
 
-def get_page(path: Path) -> Dict[str, Union[str, Dict]]:
+def get_page(path: Path) -> Tuple[str, Dict]:
     """Get one specific page."""
     if not path.is_file():
         raise Http404("Page not found.")
 
-    html = markdown(
-        path.read_text(encoding="utf-8"),
-        extras=[
-            "metadata",
-            "fenced-code-blocks",
-            "highlightjs-lang",
-            "header-ids",
-            "strike",
-            "target-blank-links",
-            "tables",
-            "task_list"
+    metadata, content = frontmatter.parse(path.read_text(encoding="utf-8"))
+    html = markdown.markdown(
+        content,
+        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="")
         ]
     )
 
-    return {"content": str(html), "metadata": html.metadata}
+    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 91aed7f0..4a2ed2d6 100644
--- a/pydis_site/apps/content/views/page_category.py
+++ b/pydis_site/apps/content/views/page_category.py
@@ -35,18 +35,19 @@ class PageOrCategoryView(TemplateView):
 
         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["category_info"] = category
             context["page_title"] = category["name"]
             context["page_description"] = category["description"]
-            context["pages"] = utils.get_category_pages(self.full_location)
+
             context["path"] = f"{self.location}/"  # Add trailing slash here to simplify template
         elif self.full_location.with_suffix(".md").is_file():
-            page_result = utils.get_page(self.full_location.with_suffix(".md"))
-            context["page"] = page_result
-            context["page_title"] = page_result["metadata"]["title"]
-            context["page_description"] = page_result["metadata"]["description"]
-            context["relevant_links"] = page_result["metadata"].get("relevant_links", {})
+            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", {})
         else:
             raise Http404
 
diff --git a/pydis_site/static/css/content/page.css b/pydis_site/static/css/content/page.css
index f46d6b15..57d7472b 100644
--- a/pydis_site/static/css/content/page.css
+++ b/pydis_site/static/css/content/page.css
@@ -6,11 +6,26 @@ i.has-icon-padding {
     padding: 0 10px 25px 0;
 }
 
-pre {
-    /*
-     * Style it the same as the  tag, since highlight.js does not style
-     * backgrounds of  tags but bulma does, resulting in a weird off-white
-     * border.
-     */
-    background-color: #282c34;
+/*
+ * Move padding padding from  tag to hljs  tags so the padding
+ * space is colored the same as the background of hljs  blocks.
+ */
+.content pre {
+    padding: 0;
+}
+
+code.hljs {
+    padding: 1.75em 2em;
+}
+
+/*
+ * Show header permalink on hover.
+ */
+.headerlink {
+    display: none;
+    padding-left: 0.5em;
+}
+
+:is(h1, h2, h3, h4, h5, h6):hover > .headerlink {
+    display: inline;
 }
diff --git a/pydis_site/templates/content/base.html b/pydis_site/templates/content/base.html
index 1508dfb3..19eec5d4 100644
--- a/pydis_site/templates/content/base.html
+++ b/pydis_site/templates/content/base.html
@@ -3,7 +3,7 @@
 
 {% block title %}{{ page_title }}{% endblock %}
 {% block head %}
- 
+     
      
      
      
diff --git a/pydis_site/templates/content/page.html b/pydis_site/templates/content/page.html
index 5e820c26..06d74208 100644
--- a/pydis_site/templates/content/page.html
+++ b/pydis_site/templates/content/page.html
@@ -12,7 +12,7 @@
     {% if relevant_links|length > 0 %}
         
             
-                {{ page.content|safe }}
+                {{ page|safe }}
             
             
     {% else %}
-        
{{ page.content|safe }}
+        
{{ page|safe }}
     {% endif %}
 {% endblock %}
-- 
cgit v1.2.3