aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site
diff options
context:
space:
mode:
authorGravatar Bryan Kok <[email protected]>2021-03-04 11:58:05 +0800
committerGravatar Bryan Kok <[email protected]>2021-03-04 11:58:05 +0800
commitb5a7dc48cd1ffbb0471858660d58b8b2e6a115fa (patch)
tree3d9b98700df69ace83c90a31c8625710639427db /pydis_site
parentHide arrow to the right of More below the 1024px breakpoint (diff)
parentUpdate Dockerfile (diff)
Resolve conflicts
Diffstat (limited to 'pydis_site')
-rw-r--r--pydis_site/apps/api/models/bot/metricity.py53
-rw-r--r--pydis_site/apps/api/tests/test_infractions.py30
-rw-r--r--pydis_site/apps/api/tests/test_users.py16
-rw-r--r--pydis_site/apps/api/viewsets/bot/infraction.py16
-rw-r--r--pydis_site/apps/api/viewsets/bot/user.py7
-rw-r--r--pydis_site/apps/home/migrations/0002_auto_now_on_repository_metadata.py18
-rw-r--r--pydis_site/apps/home/models/repository_metadata.py15
-rw-r--r--pydis_site/apps/home/tests/mock_github_api_response.json2
-rw-r--r--pydis_site/apps/home/tests/test_repodata_helpers.py42
-rw-r--r--pydis_site/apps/home/views/home.py155
-rw-r--r--pydis_site/constants.py6
-rw-r--r--pydis_site/hosts.py3
-rw-r--r--pydis_site/settings.py28
-rw-r--r--pydis_site/static/css/base/base.css69
-rw-r--r--pydis_site/static/css/error_pages.css66
-rw-r--r--pydis_site/static/css/home/index.css231
-rw-r--r--pydis_site/static/css/home/timeline.css114
-rw-r--r--pydis_site/static/images/events/100k.pngbin0 -> 210477 bytes
-rw-r--r--pydis_site/static/images/frontpage/welcome.jpgbin0 -> 51725 bytes
-rw-r--r--pydis_site/static/images/navbar/discord.svg165
-rw-r--r--pydis_site/static/images/navbar/navbar_discordjoin.svg81
-rw-r--r--pydis_site/static/images/sponsors/adafruit.pngbin11705 -> 0 bytes
-rw-r--r--pydis_site/static/images/sponsors/notion.pngbin0 -> 38207 bytes
-rw-r--r--pydis_site/static/images/waves/wave_dark.svg73
-rw-r--r--pydis_site/static/images/waves/wave_white.svg77
-rw-r--r--pydis_site/static/js/timeline/main.js37
-rw-r--r--pydis_site/templates/404.html34
-rw-r--r--pydis_site/templates/500.html29
-rw-r--r--pydis_site/templates/base/footer.html2
-rw-r--r--pydis_site/templates/base/navbar.html14
-rw-r--r--pydis_site/templates/home/index.html193
-rw-r--r--pydis_site/templates/home/timeline.html39
32 files changed, 1261 insertions, 354 deletions
diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py
index 25b42fa2..cae630f1 100644
--- a/pydis_site/apps/api/models/bot/metricity.py
+++ b/pydis_site/apps/api/models/bot/metricity.py
@@ -1,5 +1,12 @@
from django.db import connections
+BLOCK_INTERVAL = 10 * 60 # 10 minute blocks
+
+EXCLUDE_CHANNELS = [
+ "267659945086812160", # Bot commands
+ "607247579608121354" # SeasonalBot commands
+]
+
class NotFound(Exception):
"""Raised when an entity cannot be found."""
@@ -21,7 +28,8 @@ class Metricity:
def user(self, user_id: str) -> dict:
"""Query a user's data."""
- columns = ["verified_at"]
+ # TODO: Swap this back to some sort of verified at date
+ columns = ["joined_at"]
query = f"SELECT {','.join(columns)} FROM users WHERE id = '%s'"
self.cursor.execute(query, [user_id])
values = self.cursor.fetchone()
@@ -33,7 +41,48 @@ class Metricity:
def total_messages(self, user_id: str) -> int:
"""Query total number of messages for a user."""
- self.cursor.execute("SELECT COUNT(*) FROM messages WHERE author_id = '%s'", [user_id])
+ self.cursor.execute(
+ """
+ SELECT
+ COUNT(*)
+ FROM messages
+ WHERE
+ author_id = '%s'
+ AND NOT is_deleted
+ AND NOT %s::varchar[] @> ARRAY[channel_id]
+ """,
+ [user_id, EXCLUDE_CHANNELS]
+ )
+ values = self.cursor.fetchone()
+
+ if not values:
+ raise NotFound()
+
+ return values[0]
+
+ def total_message_blocks(self, user_id: str) -> int:
+ """
+ Query number of 10 minute blocks during which the user has been active.
+
+ This metric prevents users from spamming to achieve the message total threshold.
+ """
+ self.cursor.execute(
+ """
+ SELECT
+ COUNT(*)
+ FROM (
+ SELECT
+ (floor((extract('epoch' from created_at) / %s )) * %s) AS interval
+ FROM messages
+ WHERE
+ author_id='%s'
+ AND NOT is_deleted
+ AND NOT %s::varchar[] @> ARRAY[channel_id]
+ GROUP BY interval
+ ) block_query;
+ """,
+ [BLOCK_INTERVAL, BLOCK_INTERVAL, user_id, EXCLUDE_CHANNELS]
+ )
values = self.cursor.fetchone()
if not values:
diff --git a/pydis_site/apps/api/tests/test_infractions.py b/pydis_site/apps/api/tests/test_infractions.py
index 93ef8171..82b497aa 100644
--- a/pydis_site/apps/api/tests/test_infractions.py
+++ b/pydis_site/apps/api/tests/test_infractions.py
@@ -512,6 +512,36 @@ class CreationTests(APISubdomainTestCase):
)
+class InfractionDeletionTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.user = User.objects.create(
+ id=9876,
+ name='Unknown user',
+ discriminator=9876,
+ )
+
+ cls.warning = Infraction.objects.create(
+ user_id=cls.user.id,
+ actor_id=cls.user.id,
+ type='warning',
+ active=False
+ )
+
+ def test_delete_unknown_infraction_returns_404(self):
+ url = reverse('bot:infraction-detail', args=('something',), host='api')
+ response = self.client.delete(url)
+
+ self.assertEqual(response.status_code, 404)
+
+ def test_delete_known_infraction_returns_204(self):
+ url = reverse('bot:infraction-detail', args=(self.warning.id,), host='api')
+ response = self.client.delete(url)
+
+ self.assertEqual(response.status_code, 204)
+ self.assertRaises(Infraction.DoesNotExist, Infraction.objects.get, id=self.warning.id)
+
+
class ExpandedTests(APISubdomainTestCase):
@classmethod
def setUpTestData(cls):
diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py
index 72ffcb3c..69bbfefc 100644
--- a/pydis_site/apps/api/tests/test_users.py
+++ b/pydis_site/apps/api/tests/test_users.py
@@ -407,9 +407,10 @@ class UserMetricityTests(APISubdomainTestCase):
def test_get_metricity_data(self):
# Given
- verified_at = "foo"
+ joined_at = "foo"
total_messages = 1
- self.mock_metricity_user(verified_at, total_messages)
+ total_blocks = 1
+ self.mock_metricity_user(joined_at, total_messages, total_blocks)
# When
url = reverse('bot:user-metricity-data', args=[0], host='api')
@@ -418,9 +419,10 @@ class UserMetricityTests(APISubdomainTestCase):
# Then
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {
- "verified_at": verified_at,
+ "joined_at": joined_at,
"total_messages": total_messages,
"voice_banned": False,
+ "activity_blocks": total_blocks
})
def test_no_metricity_user(self):
@@ -440,7 +442,7 @@ class UserMetricityTests(APISubdomainTestCase):
{'exception': ObjectDoesNotExist, 'voice_banned': False},
]
- self.mock_metricity_user("foo", 1)
+ self.mock_metricity_user("foo", 1, 1)
for case in cases:
with self.subTest(exception=case['exception'], voice_banned=case['voice_banned']):
@@ -453,13 +455,14 @@ class UserMetricityTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["voice_banned"], case["voice_banned"])
- def mock_metricity_user(self, verified_at, total_messages):
+ def mock_metricity_user(self, joined_at, total_messages, total_blocks):
patcher = patch("pydis_site.apps.api.viewsets.bot.user.Metricity")
self.metricity = patcher.start()
self.addCleanup(patcher.stop)
self.metricity = self.metricity.return_value.__enter__.return_value
- self.metricity.user.return_value = dict(verified_at=verified_at)
+ self.metricity.user.return_value = dict(joined_at=joined_at)
self.metricity.total_messages.return_value = total_messages
+ self.metricity.total_message_blocks.return_value = total_blocks
def mock_no_metricity_user(self):
patcher = patch("pydis_site.apps.api.viewsets.bot.user.Metricity")
@@ -468,3 +471,4 @@ class UserMetricityTests(APISubdomainTestCase):
self.metricity = self.metricity.return_value.__enter__.return_value
self.metricity.user.side_effect = NotFound()
self.metricity.total_messages.side_effect = NotFound()
+ self.metricity.total_message_blocks.side_effect = NotFound()
diff --git a/pydis_site/apps/api/viewsets/bot/infraction.py b/pydis_site/apps/api/viewsets/bot/infraction.py
index edec0a1e..423e806e 100644
--- a/pydis_site/apps/api/viewsets/bot/infraction.py
+++ b/pydis_site/apps/api/viewsets/bot/infraction.py
@@ -5,6 +5,7 @@ from rest_framework.exceptions import ValidationError
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.mixins import (
CreateModelMixin,
+ DestroyModelMixin,
ListModelMixin,
RetrieveModelMixin
)
@@ -18,7 +19,13 @@ from pydis_site.apps.api.serializers import (
)
-class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, GenericViewSet):
+class InfractionViewSet(
+ CreateModelMixin,
+ RetrieveModelMixin,
+ ListModelMixin,
+ GenericViewSet,
+ DestroyModelMixin
+):
"""
View providing CRUD operations on infractions for Discord users.
@@ -108,6 +115,13 @@ class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, Ge
- 400: if a field in the request body is invalid or disallowed
- 404: if an infraction with the given `id` could not be found
+ ### DELETE /bot/infractions/<id:int>
+ Delete the infraction with the given `id`.
+
+ #### Status codes
+ - 204: returned on success
+ - 404: if a infraction with the given `id` does not exist
+
### Expanded routes
All routes support expansion of `user` and `actor` in responses. To use an expanded route,
append `/expanded` to the end of the route e.g. `GET /bot/infractions/expanded`.
diff --git a/pydis_site/apps/api/viewsets/bot/user.py b/pydis_site/apps/api/viewsets/bot/user.py
index 5205dc97..829e2694 100644
--- a/pydis_site/apps/api/viewsets/bot/user.py
+++ b/pydis_site/apps/api/viewsets/bot/user.py
@@ -109,8 +109,10 @@ class UserViewSet(ModelViewSet):
#### Response format
>>> {
- ... "verified_at": "2020-10-06T21:54:23.540766",
- ... "total_messages": 2
+ ... "joined_at": "2020-10-06T21:54:23.540766",
+ ... "total_messages": 2,
+ ... "voice_banned": False,
+ ... "activity_blocks": 1
...}
#### Status codes
@@ -255,6 +257,7 @@ class UserViewSet(ModelViewSet):
data = metricity.user(user.id)
data["total_messages"] = metricity.total_messages(user.id)
data["voice_banned"] = voice_banned
+ data["activity_blocks"] = metricity.total_message_blocks(user.id)
return Response(data, status=status.HTTP_200_OK)
except NotFound:
return Response(dict(detail="User not found in metricity"),
diff --git a/pydis_site/apps/home/migrations/0002_auto_now_on_repository_metadata.py b/pydis_site/apps/home/migrations/0002_auto_now_on_repository_metadata.py
new file mode 100644
index 00000000..7e78045b
--- /dev/null
+++ b/pydis_site/apps/home/migrations/0002_auto_now_on_repository_metadata.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.11 on 2020-12-21 22:57
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('home', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='repositorymetadata',
+ name='last_updated',
+ field=models.DateTimeField(auto_now=True, help_text='The date and time this data was last fetched.'),
+ ),
+ ]
diff --git a/pydis_site/apps/home/models/repository_metadata.py b/pydis_site/apps/home/models/repository_metadata.py
index 92d2404d..00a83cd7 100644
--- a/pydis_site/apps/home/models/repository_metadata.py
+++ b/pydis_site/apps/home/models/repository_metadata.py
@@ -1,32 +1,31 @@
from django.db import models
-from django.utils import timezone
class RepositoryMetadata(models.Model):
"""Information about one of our repos fetched from the GitHub API."""
last_updated = models.DateTimeField(
- default=timezone.now,
- help_text="The date and time this data was last fetched."
+ help_text="The date and time this data was last fetched.",
+ auto_now=True,
)
repo_name = models.CharField(
primary_key=True,
max_length=40,
- help_text="The full name of the repo, e.g. python-discord/site"
+ help_text="The full name of the repo, e.g. python-discord/site",
)
description = models.CharField(
max_length=400,
- help_text="The description of the repo."
+ help_text="The description of the repo.",
)
forks = models.IntegerField(
- help_text="The number of forks of this repo"
+ help_text="The number of forks of this repo",
)
stargazers = models.IntegerField(
- help_text="The number of stargazers for this repo"
+ help_text="The number of stargazers for this repo",
)
language = models.CharField(
max_length=20,
- help_text="The primary programming language used for this repo."
+ help_text="The primary programming language used for this repo.",
)
def __str__(self):
diff --git a/pydis_site/apps/home/tests/mock_github_api_response.json b/pydis_site/apps/home/tests/mock_github_api_response.json
index 10be4f99..ddbffed8 100644
--- a/pydis_site/apps/home/tests/mock_github_api_response.json
+++ b/pydis_site/apps/home/tests/mock_github_api_response.json
@@ -35,7 +35,7 @@
"forks_count": 31
},
{
- "full_name": "python-discord/seasonalbot",
+ "full_name": "python-discord/sir-lancebot",
"description": "test",
"stargazers_count": 97,
"language": "Python",
diff --git a/pydis_site/apps/home/tests/test_repodata_helpers.py b/pydis_site/apps/home/tests/test_repodata_helpers.py
index 77b1a68d..5634bc9b 100644
--- a/pydis_site/apps/home/tests/test_repodata_helpers.py
+++ b/pydis_site/apps/home/tests/test_repodata_helpers.py
@@ -123,10 +123,38 @@ class TestRepositoryMetadataHelpers(TestCase):
mock_get.return_value.json.return_value = ['garbage']
metadata = self.home_view._get_repo_data()
- self.assertEquals(len(metadata), len(self.home_view.repos))
- for item in metadata:
- with self.subTest(item=item):
- self.assertEqual(item.description, "Not available.")
- self.assertEqual(item.forks, 999)
- self.assertEqual(item.stargazers, 999)
- self.assertEqual(item.language, "Python")
+ self.assertEquals(len(metadata), 0)
+
+ def test_cleans_up_stale_metadata(self):
+ """Tests that we clean up stale metadata when we start the HomeView."""
+ repo_data = RepositoryMetadata(
+ repo_name="python-discord/INVALID",
+ description="testrepo",
+ forks=42,
+ stargazers=42,
+ language="English",
+ last_updated=timezone.now() - timedelta(seconds=HomeView.repository_cache_ttl + 1),
+ )
+ repo_data.save()
+ self.home_view.__init__()
+ cached_repos = RepositoryMetadata.objects.all()
+ cached_names = [repo.repo_name for repo in cached_repos]
+
+ self.assertNotIn("python-discord/INVALID", cached_names)
+
+ def test_dont_clean_up_unstale_metadata(self):
+ """Tests that we don't clean up good metadata when we start the HomeView."""
+ repo_data = RepositoryMetadata(
+ repo_name="python-discord/site",
+ description="testrepo",
+ forks=42,
+ stargazers=42,
+ language="English",
+ last_updated=timezone.now() - timedelta(seconds=HomeView.repository_cache_ttl + 1),
+ )
+ repo_data.save()
+ self.home_view.__init__()
+ cached_repos = RepositoryMetadata.objects.all()
+ cached_names = [repo.repo_name for repo in cached_repos]
+
+ self.assertIn("python-discord/site", cached_names)
diff --git a/pydis_site/apps/home/views/home.py b/pydis_site/apps/home/views/home.py
index 09969f1d..e77772fb 100644
--- a/pydis_site/apps/home/views/home.py
+++ b/pydis_site/apps/home/views/home.py
@@ -1,4 +1,4 @@
-import datetime
+import logging
from typing import Dict, List
import requests
@@ -10,11 +10,13 @@ from django.views import View
from pydis_site.apps.home.models import RepositoryMetadata
+log = logging.getLogger(__name__)
+
class HomeView(View):
"""The main landing page for the website."""
- github_api = "https://api.github.com/users/python-discord/repos"
+ github_api = "https://api.github.com/users/python-discord/repos?per_page=100"
repository_cache_ttl = 3600
# Which of our GitHub repos should be displayed on the front page, and in which order?
@@ -22,82 +24,98 @@ class HomeView(View):
"python-discord/site",
"python-discord/bot",
"python-discord/snekbox",
- "python-discord/seasonalbot",
+ "python-discord/sir-lancebot",
"python-discord/metricity",
"python-discord/django-simple-bulma",
]
+ def __init__(self):
+ """Clean up stale RepositoryMetadata."""
+ RepositoryMetadata.objects.exclude(repo_name__in=self.repos).delete()
+
def _get_api_data(self) -> Dict[str, Dict[str, str]]:
- """Call the GitHub API and get information about our repos."""
- repo_dict: Dict[str, dict] = {repo_name: {} for repo_name in self.repos}
+ """
+ Call the GitHub API and get information about our repos.
+
+ If we're unable to get that info for any reason, return an empty dict.
+ """
+ repo_dict = {}
# Fetch the data from the GitHub API
api_data: List[dict] = requests.get(self.github_api).json()
# Process the API data into our dict
for repo in api_data:
- full_name = repo["full_name"]
-
- if full_name in self.repos:
- repo_dict[full_name] = {
- "full_name": repo["full_name"],
- "description": repo["description"],
- "language": repo["language"],
- "forks_count": repo["forks_count"],
- "stargazers_count": repo["stargazers_count"],
- }
+ try:
+ full_name = repo["full_name"]
+
+ if full_name in self.repos:
+ repo_dict[full_name] = {
+ "full_name": repo["full_name"],
+ "description": repo["description"],
+ "language": repo["language"],
+ "forks_count": repo["forks_count"],
+ "stargazers_count": repo["stargazers_count"],
+ }
+ # Something is not right about the API data we got back from GitHub.
+ except (TypeError, ConnectionError, KeyError) as e:
+ log.error(
+ "Unable to parse the GitHub repository metadata from response!",
+ extra={
+ 'api_data': api_data,
+ 'error': e
+ }
+ )
+ continue
return repo_dict
def _get_repo_data(self) -> List[RepositoryMetadata]:
"""Build a list of RepositoryMetadata objects that we can use to populate the front page."""
- # Try to get site data from the cache
- try:
- repo_data = RepositoryMetadata.objects.get(repo_name="python-discord/site")
+ database_repositories = []
- # If the data is stale, we should refresh it.
- if (timezone.now() - repo_data.last_updated).seconds > self.repository_cache_ttl:
+ # First, let's see if we have any metadata cached.
+ cached_data = RepositoryMetadata.objects.all()
- # Try to get new data from the API. If it fails, return the cached data.
- try:
- api_repositories = self._get_api_data()
- except (TypeError, ConnectionError):
- return RepositoryMetadata.objects.all()
- database_repositories = []
-
- # Update or create all RepoData objects in self.repos
- for repo_name, api_data in api_repositories.items():
- try:
- repo_data = RepositoryMetadata.objects.get(repo_name=repo_name)
- repo_data.description = api_data["description"]
- repo_data.language = api_data["language"]
- repo_data.forks = api_data["forks_count"]
- repo_data.stargazers = api_data["stargazers_count"]
- except RepositoryMetadata.DoesNotExist:
- repo_data = RepositoryMetadata(
- repo_name=api_data["full_name"],
- description=api_data["description"],
- forks=api_data["forks_count"],
- stargazers=api_data["stargazers_count"],
- language=api_data["language"],
- )
- repo_data.save()
- database_repositories.append(repo_data)
- return database_repositories
-
- # Otherwise, if the data is fresher than 2 minutes old, we should just return it.
- else:
- return RepositoryMetadata.objects.all()
+ # If we don't, we have to create some!
+ if not cached_data:
- # If this is raised, the database has no repodata at all, we will create them all.
- except RepositoryMetadata.DoesNotExist:
- database_repositories = []
- try:
- # Get new data from API
- api_repositories = self._get_api_data()
+ # Try to get new data from the API. If it fails, we'll return an empty list.
+ # In this case, we simply don't display our projects on the site.
+ api_repositories = self._get_api_data()
+
+ # Create all the repodata records in the database.
+ for api_data in api_repositories.values():
+ repo_data = RepositoryMetadata(
+ repo_name=api_data["full_name"],
+ description=api_data["description"],
+ forks=api_data["forks_count"],
+ stargazers=api_data["stargazers_count"],
+ language=api_data["language"],
+ )
+
+ repo_data.save()
+ database_repositories.append(repo_data)
- # Create all the repodata records in the database.
- for api_data in api_repositories.values():
+ return database_repositories
+
+ # If the data is stale, we should refresh it.
+ if (timezone.now() - cached_data[0].last_updated).seconds > self.repository_cache_ttl:
+ # Try to get new data from the API. If it fails, return the cached data.
+ api_repositories = self._get_api_data()
+
+ if not api_repositories:
+ return RepositoryMetadata.objects.all()
+
+ # Update or create all RepoData objects in self.repos
+ for repo_name, api_data in api_repositories.items():
+ try:
+ repo_data = RepositoryMetadata.objects.get(repo_name=repo_name)
+ repo_data.description = api_data["description"]
+ repo_data.language = api_data["language"]
+ repo_data.forks = api_data["forks_count"]
+ repo_data.stargazers = api_data["stargazers_count"]
+ except RepositoryMetadata.DoesNotExist:
repo_data = RepositoryMetadata(
repo_name=api_data["full_name"],
description=api_data["description"],
@@ -105,23 +123,14 @@ class HomeView(View):
stargazers=api_data["stargazers_count"],
language=api_data["language"],
)
- repo_data.save()
- database_repositories.append(repo_data)
- except TypeError:
- for repo_name in self.repos:
- repo_data = RepositoryMetadata(
- last_updated=timezone.now() - datetime.timedelta(minutes=50),
- repo_name=repo_name,
- description="Not available.",
- forks=999,
- stargazers=999,
- language="Python",
- )
- repo_data.save()
- database_repositories.append(repo_data)
-
+ repo_data.save()
+ database_repositories.append(repo_data)
return database_repositories
+ # Otherwise, if the data is fresher than 2 minutes old, we should just return it.
+ else:
+ return RepositoryMetadata.objects.all()
+
def get(self, request: WSGIRequest) -> HttpResponse:
"""Collect repo data and render the homepage view."""
repo_data = self._get_repo_data()
diff --git a/pydis_site/constants.py b/pydis_site/constants.py
index 0b76694a..c7ab5db0 100644
--- a/pydis_site/constants.py
+++ b/pydis_site/constants.py
@@ -1,5 +1,3 @@
-import git
+import os
-# Git SHA
-repo = git.Repo(search_parent_directories=True)
-GIT_SHA = repo.head.object.hexsha
+GIT_SHA = os.environ.get("GIT_SHA", "development")
diff --git a/pydis_site/hosts.py b/pydis_site/hosts.py
index 898e8cdc..5a837a8b 100644
--- a/pydis_site/hosts.py
+++ b/pydis_site/hosts.py
@@ -4,7 +4,10 @@ from django_hosts import host, patterns
host_patterns = patterns(
'',
host(r'admin', 'pydis_site.apps.admin.urls', name="admin"),
+ # External API ingress (over the net)
host(r'api', 'pydis_site.apps.api.urls', name='api'),
+ # Internal API ingress (cluster local)
+ host(r'pydis-api', 'pydis_site.apps.api.urls', name='internal_api'),
host(r'staff', 'pydis_site.apps.staff.urls', name='staff'),
host(r'.*', 'pydis_site.apps.home.urls', name=settings.DEFAULT_HOST)
)
diff --git a/pydis_site/settings.py b/pydis_site/settings.py
index 1ae97b86..300452fa 100644
--- a/pydis_site/settings.py
+++ b/pydis_site/settings.py
@@ -28,14 +28,14 @@ if typing.TYPE_CHECKING:
env = environ.Env(
DEBUG=(bool, False),
- SITE_SENTRY_DSN=(str, "")
+ SITE_DSN=(str, "")
)
sentry_sdk.init(
- dsn=env('SITE_SENTRY_DSN'),
+ dsn=env('SITE_DSN'),
integrations=[DjangoIntegration()],
send_default_pii=True,
- release=f"pydis-site@{GIT_SHA}"
+ release=f"site@{GIT_SHA}"
)
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
@@ -47,21 +47,7 @@ DEBUG = env('DEBUG')
# SECURITY WARNING: keep the secret key used in production secret!
if DEBUG:
- ALLOWED_HOSTS = env.list(
- 'ALLOWED_HOSTS',
- default=[
- 'pythondiscord.local',
- 'api.pythondiscord.local',
- 'admin.pythondiscord.local',
- 'staff.pythondiscord.local',
- '0.0.0.0', # noqa: S104
- 'localhost',
- 'web',
- 'api.web',
- 'admin.web',
- 'staff.web'
- ]
- )
+ ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['*'])
SECRET_KEY = "yellow polkadot bikini" # noqa: S105
elif 'CI' in os.environ:
@@ -76,10 +62,7 @@ else:
'admin.pythondiscord.com',
'api.pythondiscord.com',
'staff.pythondiscord.com',
- 'pydis.com',
- 'api.pydis.com',
- 'admin.pydis.com',
- 'staff.pydis.com',
+ 'pydis-api.default.svc.cluster.local',
]
)
SECRET_KEY = env('SECRET_KEY')
@@ -392,6 +375,7 @@ AUTHENTICATION_BACKENDS = (
ACCOUNT_ADAPTER = "pydis_site.utils.account.AccountAdapter"
ACCOUNT_EMAIL_REQUIRED = False # Undocumented allauth setting; don't require emails
ACCOUNT_EMAIL_VERIFICATION = "none" # No verification required; we don't use emails for anything
+ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https"
# We use this validator because Allauth won't let us actually supply a list with no validators
# in it, and we can't just give it a lambda - that'd be too easy, I suppose.
diff --git a/pydis_site/static/css/base/base.css b/pydis_site/static/css/base/base.css
index dc7c504d..a1d325f9 100644
--- a/pydis_site/static/css/base/base.css
+++ b/pydis_site/static/css/base/base.css
@@ -12,42 +12,69 @@ main.site-content {
flex: 1;
}
-div.card.has-equal-height {
+.card.has-equal-height {
height: 100%;
display: flex;
flex-direction: column;
}
-.navbar-item.is-fullsize {
- padding: 0;
+.navbar {
+ padding-right: 0.8em;
}
-.navbar-item.is-fullsize img {
- max-height: 4.75rem;
+.navbar-item .navbar-link {
+ padding-left: 1.5em;
+ padding-right: 2.5em;
+}
+
+.navbar-link:not(.is-arrowless)::after {
+ right: 1.125em;
+ margin-top: -0.455em;
}
.navbar-item.has-no-highlight:hover {
background-color: transparent;
}
-.navbar-item.has-left-margin-1 {
- margin-left: 1rem;
+#navbar-banner {
+ background-color: transparent;
}
-.navbar-item.has-left-margin-2 {
- margin-left: 2rem;
+#navbar-banner img {
+ max-height: 3rem;
}
-.navbar-item.has-left-margin-3 {
- margin-left: 3rem;
+#discord-btn a {
+ color: transparent;
+ background-image: url(../../images/navbar/discord.svg);
+ background-size: 200%;
+ background-position: 100% 50%;
+ background-repeat: no-repeat;
+ padding-left: 2.5rem;
+ padding-right: 2.5rem;
+ background-color: #697ec4ff;
+ margin-left: 0.5rem;
+ transition: all 0.2s cubic-bezier(.25,.8,.25,1);
+ overflow: hidden;
}
-#navbar-banner {
+#discord-btn:hover a {
+ box-shadow: 0 1px 4px rgba(0,0,0,0.16), 0 1px 6px rgba(0,0,0,0.23);
+ /*transform: scale(1.03) translate3d(0,0,0);*/
+ background-size: 200%;
+ background-position: 1% 50%;
+}
+
+#discord-btn:hover {
background-color: transparent;
}
-#navbar-banner img {
- max-height: 3rem;
+#linode-logo {
+ padding-left: 15px;
+ background: url(https://www.linode.com/wp-content/uploads/2021/01/Linode-Logo-Black.svg) no-repeat center;
+ filter: invert(1) grayscale(1);
+ background-size: 60px;
+ color: #00000000;
}
#django-logo {
@@ -111,3 +138,17 @@ button.is-size-navbar-menu, a.is-size-navbar-menu {
.codehilite-wrap {
margin-bottom: 1em;
}
+
+/* 16:9 aspect ratio fixing */
+.force-aspect-container {
+ position: relative;
+ padding-bottom: 56.25%;
+}
+
+.force-aspect-content {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+}
diff --git a/pydis_site/static/css/error_pages.css b/pydis_site/static/css/error_pages.css
new file mode 100644
index 00000000..77bb7e2b
--- /dev/null
+++ b/pydis_site/static/css/error_pages.css
@@ -0,0 +1,66 @@
+html {
+ height: 100%;
+}
+
+body {
+ background-color: #7289DA;
+ background-image: url("https://raw.githubusercontent.com/python-discord/branding/master/logos/banner_pattern/banner_pattern.svg");
+ background-size: 128px;
+ font-family: "Hind", "Helvetica", "Arial", sans-serif;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+}
+
+h1,
+p {
+ color: black;
+ padding: 0;
+ margin: 0;
+ margin-bottom: 10px;
+}
+
+h1 {
+ margin-bottom: 15px;
+ font-size: 26px;
+}
+
+p,
+li {
+ line-height: 125%;
+}
+
+a {
+ color: #7289DA;
+}
+
+ul {
+ margin-bottom: 0;
+}
+
+li {
+ margin-top: 10px;
+}
+
+.error-box {
+ display: flex;
+ flex-direction: column;
+ max-width: 512px;
+ background-color: white;
+ border-radius: 20px;
+ overflow: hidden;
+ box-shadow: 5px 7px 40px rgba(0, 0, 0, 0.432);
+}
+
+.logo-box {
+ display: flex;
+ justify-content: center;
+ height: 80px;
+ padding: 15px;
+ background-color: #758ad4;
+}
+
+.content-box {
+ padding: 25px;
+}
diff --git a/pydis_site/static/css/home/index.css b/pydis_site/static/css/home/index.css
index ba856a8e..58ca8888 100644
--- a/pydis_site/static/css/home/index.css
+++ b/pydis_site/static/css/home/index.css
@@ -1,87 +1,214 @@
-.discord-banner {
- border-radius: 0.5rem;
+h1 {
+ padding-bottom: 0.5em;
}
-.hero-image {
- width: 20rem;
- margin: auto;
+/* Mobile-only notice banner */
+
+#mobile-notice {
+ margin: 5px;
+ margin-bottom: -10px!important;
}
-.hero-body {
- padding-top: 1rem;
- padding-bottom: 1rem;
+/* Wave hero */
+
+#wave-hero {
+ position: relative;
+ background-color: #7289DA;
+ color: #fff;
+ height: 32vw;
+ min-height: 270px;
+ max-height: 500px;
+ overflow-x: hidden;
+ width: 100%;
+ padding: 0;
}
-.section-sp img {
- height: 5rem;
- margin-right: 2rem;
+#wave-hero .container {
+ z-index: 4; /* keep hero contents above wave animations */
}
-.video-container iframe,
-.video-container object,
-.video-container embed {
- width: 100%;
- height: calc(92vw * 0.5625);
- margin: 8px auto auto auto;
+@media screen and (min-width: 769px) and (max-width: 1023px) {
+ #wave-hero .columns {
+ margin: 0 1em 0 1em; /* Stop cards touching canvas edges in table-view */
+ }
+}
+
+#wave-hero iframe {
+ box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+ transition: all 0.3s cubic-bezier(.25,.8,.25,1);
+ border-radius: 10px;
+ margin-top: 1em;
+ border: none;
+}
+
+#wave-hero iframe:hover {
+ box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
+}
+
+#wave-hero-right img{
+ border-radius: 10px;
+ box-shadow: 0 1px 6px rgba(0,0,0,0.16), 0 1px 6px rgba(0,0,0,0.23);
+ margin-top: 1em;
+ text-align: right;
+}
+
+#wave-hero .wave {
+ background: url(../../images/waves/wave_dark.svg) repeat-x;
+ position: absolute;
+ bottom: 0;
+ width: 6400px;
+ animation-name: wave;
+ animation-timing-function: cubic-bezier(.36,.45,.63,.53);
+ animation-iteration-count: infinite;
+ transform: translate3d(0,0,0); /* Trigger 3D acceleration for smoother animation */
}
-div.card.github-card {
+#front-wave {
+ animation-duration: 60s;
+ animation-delay: -50s;
+ opacity: 0.5;
+ height: 178px;
+}
+
+#back-wave {
+ animation-duration: 65s;
+ height: 198px;
+}
+
+#bottom-wave {
+ animation-duration: 50s;
+ animation-delay: -10s;
+ background: url(../../images/waves/wave_white.svg) repeat-x !important;
+ height: 26px;
+ z-index: 3;
+}
+
+@keyframes wave {
+ 0% {
+ margin-left: 0;
+ }
+ 100% {
+ margin-left: -1600px;
+ }
+}
+
+/* Showcase */
+
+#showcase {
+ margin: 0 1em;
+}
+
+#showcase .mini-timeline {
+ height: 3px;
+ position: relative;
+ margin: 50px 0 50px 0;
+ background: linear-gradient(to right, #ffffff00, #666666ff, #ffffff00);
+ text-align: center;
+}
+
+#showcase .mini-timeline i {
+ display: inline-block;
+ vertical-align: middle;
+ width: 30px;
+ height: 30px;
+ border-radius: 50%;
+ position: relative;
+ top: -14px;
+ margin: 0 4% 0 4%;
+ background-color: #3EB2EF;
+ color: white;
+ font-size: 15px;
+ line-height: 33px;
+ border:none;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
+ transition: all 0.3s cubic-bezier(.25,.8,.25,1);
+}
+
+#showcase .mini-timeline i:hover {
+ box-shadow: 0 2px 5px rgba(0,0,0,0.16), 0 2px 5px rgba(0,0,0,0.23);
+ transform: scale(1.5);
+}
+
+/* Projects */
+
+#projects {
+ padding-top: 0;
+}
+
+#projects .card {
box-shadow: none;
border: #d1d5da 1px solid;
border-radius: 3px;
+ transition: all 0.2s cubic-bezier(.25,.8,.25,1);
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+#projects .card:hover {
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
}
-div.repo-headline {
+#projects .card-header {
+ box-shadow: none;
font-size: 1.25rem;
- margin-bottom: 8px;
+ padding: 1.5rem 1.5rem 0 1.5rem;
}
-span.repo-language-dot {
- border-radius: 50%;
- height: 12px;
- width: 12px;
- top: 1px;
- display: inline-block;
- position: relative;
+#projects .card-header-icon {
+ font-size: 1.5rem;
+ padding: 0 1rem 0 0;
}
-span.repo-language-dot.python {
- background-color: #3572A5;
+#projects .card-header-title {
+ padding: 0;
+ color: #7289DA;
}
-span.repo-language-dot.html {
- background-color: #e34c26;
+#projects .card:hover .card-header-title {
+ color: #363636;
}
-span.repo-language-dot.css {
- background-color: #563d7c;
+#projects .card-content {
+ padding-top: 8px;
+ padding-bottom: 1rem;
}
-span.repo-language-dot.javascript {
- background-color: #f1e05a;
+#projects .card-footer {
+ margin-top: auto;
+ border: none;
}
-#repo-footer-item {
- margin-left: 1.2rem;
+#projects .card-footer-item {
+ border: none;
}
-#sponsors-hero {
- padding-top: 2rem;
- padding-bottom: 3rem;
+#projects .card-footer-item i {
+ margin-right: 0.5rem;
}
-@media screen and (min-width: 1088px) {
- .video-container iframe {
- height: calc(42vw * 0.5625);
- max-height: 371px;
- max-width: 660px;
- }
+#projects .repo-language-dot {
+ border-radius: 50%;
+ height: 12px;
+ width: 12px;
+ top: -1px;
+ display: inline-block;
+ position: relative;
}
-@media screen and (max-width: 1087px) {
- .video-container iframe {
- height: calc(92vw * 0.5625);
- max-height: none;
- max-width: none;
- }
+#projects .repo-language-dot.python { background-color: #3572A5; }
+#projects .repo-language-dot.html { background-color: #e34c26; }
+#projects .repo-language-dot.css { background-color: #563d7c; }
+#projects .repo-language-dot.javascript { background-color: #f1e05a; }
+
+/* Sponsors */
+
+#sponsors .hero-body {
+ padding-top: 2rem;
+ padding-bottom: 3rem;
+}
+
+#sponsors img {
+ height: 5rem;
+ margin-right: 2rem;
}
diff --git a/pydis_site/static/css/home/timeline.css b/pydis_site/static/css/home/timeline.css
index 73698c7c..0a4dfbb6 100644
--- a/pydis_site/static/css/home/timeline.css
+++ b/pydis_site/static/css/home/timeline.css
@@ -61,20 +61,6 @@ button, input, textarea, select {
background-color: #576297 !important;
}
-.video-container {
- position: relative;
- width: 100%;
- height: 0;
- padding-bottom: 75%;
-}
-.video {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-}
-
.pydis-logo-banner {
background-color: #7289DA !important;
border-radius: 10px;
@@ -3497,8 +3483,8 @@ mark {
align-items: center;
-ms-flex-negative: 0;
flex-shrink: 0;
- width: 40px;
- height: 40px;
+ width: 30px;
+ height: 30px;
border-radius: 50%;
box-shadow: 0 0 0 4px hsl(0, 0%, 100%), inset 0 2px 0 rgba(0, 0, 0, 0.08), 0 3px 0 4px rgba(0, 0, 0, 0.05);
box-shadow: 0 0 0 4px var(--color-white), inset 0 2px 0 rgba(0, 0, 0, 0.08), 0 3px 0 4px rgba(0, 0, 0, 0.05)
@@ -3509,17 +3495,23 @@ mark {
color: white;
}
+@media (max-width: 64rem) {
+ .cd-timeline__img i {
+ font-size: 0.9em;
+ }
+}
+
.cd-timeline__img img {
- width: 50px;
- height: 50px;
+ width: 40px;
+ height: 40px;
margin-left: 2px;
margin-top: 2px;
}
@media (max-width: 64rem) {
.cd-timeline__img img {
- width: 30px;
- height: 30px;
+ width: 20px;
+ height: 20px;
margin-left: 2px;
margin-top: 2px;
}
@@ -3532,7 +3524,7 @@ mark {
-ms-flex-order: 1;
order: 1;
margin-left: calc(5% - 30px);
- will-change: transform
+ will-change: transform;
}
.cd-timeline__block:nth-child(even) .cd-timeline__img {
@@ -3646,6 +3638,14 @@ mark {
-webkit-animation-name: cd-bounce-2-inverse;
animation-name: cd-bounce-2-inverse
}
+ .cd-timeline__img--bounce-out {
+ -webkit-animation: cd-bounce-out-1 0.6s;
+ animation: cd-bounce-out-1 0.6s;
+ }
+ .cd-timeline__content--bounce-out {
+ -webkit-animation: cd-bounce-out-2 0.6s;
+ animation: cd-bounce-out-2 0.6s;
+ }
}
@-webkit-keyframes cd-bounce-1 {
@@ -3749,3 +3749,75 @@ mark {
transform: translateX(0)
}
}
+
+@-webkit-keyframes cd-bounce-out-1 {
+ 0% {
+ opacity: 1;
+ -webkit-transform: scale(1);
+ transform: scale(1)
+ }
+
+ 60% {
+ -webkit-transform: scale(1.2);
+ transform: scale(1.2)
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale(0.5);
+ transform: scale(0.5)
+ }
+}
+
+@keyframes cd-bounce-out-1 {
+ 0% {
+ opacity: 1;
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ }
+
+ 60% {
+ -webkit-transform: scale(1.2);
+ transform: scale(1.2);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale(0.5);
+ transform: scale(0.5);
+ }
+}
+
+@-webkit-keyframes cd-bounce-out-2 {
+ 0% {
+ opacity: 1;
+ -webkit-transform: translateX(0);
+ transform: translateX(0)
+ }
+ 60% {
+ -webkit-transform: translateX(20px);
+ transform: translateX(20px)
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translateX(-100px);
+ transform: translateX(-100px)
+ }
+}
+
+@keyframes cd-bounce-out-2 {
+ 0% {
+ opacity: 1;
+ -webkit-transform: translateX(0);
+ transform: translateX(0)
+ }
+ 60% {
+ -webkit-transform: translateX(20px);
+ transform: translateX(20px)
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translateX(-100px);
+ transform: translateX(-100px)
+ }
+}
diff --git a/pydis_site/static/images/events/100k.png b/pydis_site/static/images/events/100k.png
new file mode 100644
index 00000000..ae024d77
--- /dev/null
+++ b/pydis_site/static/images/events/100k.png
Binary files differ
diff --git a/pydis_site/static/images/frontpage/welcome.jpg b/pydis_site/static/images/frontpage/welcome.jpg
new file mode 100644
index 00000000..0eb8f672
--- /dev/null
+++ b/pydis_site/static/images/frontpage/welcome.jpg
Binary files differ
diff --git a/pydis_site/static/images/navbar/discord.svg b/pydis_site/static/images/navbar/discord.svg
new file mode 100644
index 00000000..406e3836
--- /dev/null
+++ b/pydis_site/static/images/navbar/discord.svg
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="120mm"
+ height="30mm"
+ viewBox="0 0 120 30"
+ version="1.1"
+ id="svg8"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
+ sodipodi:docname="discord.svg">
+ <defs
+ id="defs2">
+ <rect
+ x="75.819944"
+ y="98.265513"
+ width="25.123336"
+ height="7.8844509"
+ id="rect953" />
+ <rect
+ x="75.819946"
+ y="98.265511"
+ width="25.123337"
+ height="7.8844509"
+ id="rect953-0" />
+ <rect
+ x="75.819946"
+ y="98.265511"
+ width="25.123337"
+ height="7.8844509"
+ id="rect968" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8"
+ inkscape:cx="194.44623"
+ inkscape:cy="53.152927"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="2560"
+ inkscape:window-height="1413"
+ inkscape:window-x="4880"
+ inkscape:window-y="677"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:document-rotation="0" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-52.233408,-75.88169)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke-width:0.137677;paint-order:stroke fill markers;stop-color:#000000"
+ id="rect832"
+ width="61.511906"
+ height="30"
+ x="52.23341"
+ y="75.881691" />
+ <g
+ id="g910"
+ transform="matrix(0.90000009,0,0,0.90000009,17.445516,9.7980333)">
+ <g
+ id="g850"
+ transform="matrix(0.06491223,0,0,0.06491223,109.76284,82.07218)">
+ <path
+ class="st0"
+ d="m 142.8,120.1 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0,-6.1 -4.6,-11 -10.2,-11 z m -36.5,0 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0.1,-6.1 -4.5,-11 -10.2,-11 z"
+ id="path836" />
+ <path
+ class="st0"
+ d="m 191.4,36.9 h -134 c -11.3,0 -20.5,9.2 -20.5,20.5 v 134 c 0,11.3 9.2,20.5 20.5,20.5 h 113.4 l -5.3,-18.3 12.8,11.8 12.1,11.1 21.6,18.7 V 57.4 C 211.9,46.1 202.7,36.9 191.4,36.9 Z m -38.6,129.5 c 0,0 -3.6,-4.3 -6.6,-8 13.1,-3.7 18.1,-11.8 18.1,-11.8 -4.1,2.7 -8,4.6 -11.5,5.9 -5,2.1 -9.8,3.4 -14.5,4.3 -9.6,1.8 -18.4,1.3 -25.9,-0.1 -5.7,-1.1 -10.6,-2.6 -14.7,-4.3 -2.3,-0.9 -4.8,-2 -7.3,-3.4 -0.3,-0.2 -0.6,-0.3 -0.9,-0.5 -0.2,-0.1 -0.3,-0.2 -0.4,-0.2 -1.8,-1 -2.8,-1.7 -2.8,-1.7 0,0 4.8,7.9 17.5,11.7 -3,3.8 -6.7,8.2 -6.7,8.2 C 75,165.8 66.6,151.4 66.6,151.4 66.6,119.5 81,93.6 81,93.6 95.4,82.9 109,83.2 109,83.2 l 1,1.2 c -18,5.1 -26.2,13 -26.2,13 0,0 2.2,-1.2 5.9,-2.8 10.7,-4.7 19.2,-5.9 22.7,-6.3 0.6,-0.1 1.1,-0.2 1.7,-0.2 6.1,-0.8 13,-1 20.2,-0.2 9.5,1.1 19.7,3.9 30.1,9.5 0,0 -7.9,-7.5 -24.9,-12.6 l 1.4,-1.6 c 0,0 13.7,-0.3 28,10.4 0,0 14.4,25.9 14.4,57.8 0,-0.1 -8.4,14.3 -30.5,15 z m 151,-86.7 H 270.6 V 117 l 22.1,19.9 v -36.2 h 11.8 c 7.5,0 11.2,3.6 11.2,9.4 v 27.7 c 0,5.8 -3.5,9.7 -11.2,9.7 h -34 v 21.1 h 33.2 c 17.8,0.1 34.5,-8.8 34.5,-29.2 V 109.6 C 338.3,88.8 321.6,79.7 303.8,79.7 Z m 174,59.7 v -30.6 c 0,-11 19.8,-13.5 25.8,-2.5 l 18.3,-7.4 c -7.2,-15.8 -20.3,-20.4 -31.2,-20.4 -17.8,0 -35.4,10.3 -35.4,30.3 v 30.6 c 0,20.2 17.6,30.3 35,30.3 11.2,0 24.6,-5.5 32,-19.9 l -19.6,-9 c -4.8,12.3 -24.9,9.3 -24.9,-1.4 z M 417.3,113 c -6.9,-1.5 -11.5,-4 -11.8,-8.3 0.4,-10.3 16.3,-10.7 25.6,-0.8 l 14.7,-11.3 c -9.2,-11.2 -19.6,-14.2 -30.3,-14.2 -16.3,0 -32.1,9.2 -32.1,26.6 0,16.9 13,26 27.3,28.2 7.3,1 15.4,3.9 15.2,8.9 -0.6,9.5 -20.2,9 -29.1,-1.8 l -14.2,13.3 c 8.3,10.7 19.6,16.1 30.2,16.1 16.3,0 34.4,-9.4 35.1,-26.6 1,-21.7 -14.8,-27.2 -30.6,-30.1 z m -67,55.5 h 22.4 V 79.7 H 350.3 Z M 728,79.7 H 694.8 V 117 l 22.1,19.9 v -36.2 h 11.8 c 7.5,0 11.2,3.6 11.2,9.4 v 27.7 c 0,5.8 -3.5,9.7 -11.2,9.7 h -34 v 21.1 H 728 c 17.8,0.1 34.5,-8.8 34.5,-29.2 V 109.6 C 762.5,88.8 745.8,79.7 728,79.7 Z M 565.1,78.5 c -18.4,0 -36.7,10 -36.7,30.5 v 30.3 c 0,20.3 18.4,30.5 36.9,30.5 18.4,0 36.7,-10.2 36.7,-30.5 V 109 C 602,88.6 583.5,78.5 565.1,78.5 Z m 14.4,60.8 c 0,6.4 -7.2,9.7 -14.3,9.7 -7.2,0 -14.4,-3.1 -14.4,-9.7 V 109 c 0,-6.5 7,-10 14,-10 7.3,0 14.7,3.1 14.7,10 z M 682.4,109 c -0.5,-20.8 -14.7,-29.2 -33,-29.2 h -35.5 v 88.8 h 22.7 v -28.2 h 4 l 20.6,28.2 h 28 L 665,138.1 c 10.7,-3.4 17.4,-12.7 17.4,-29.1 z m -32.6,12 h -13.2 v -20.3 h 13.2 c 14.1,0 14.1,20.3 0,20.3 z"
+ id="path838" />
+ </g>
+ <path
+ id="path4789-6"
+ class=""
+ d="m 167.72059,90.383029 -3.19204,3.19205 c -0.15408,0.15408 -0.40352,0.15408 -0.55746,0 l -0.37229,-0.37231 c -0.15368,-0.15369 -0.15408,-0.40277 -4.9e-4,-0.55681 l 2.52975,-2.54167 -2.52975,-2.54164 c -0.15329,-0.15408 -0.15309,-0.40312 4.9e-4,-0.55681 l 0.37229,-0.37228 c 0.15408,-0.15408 0.40353,-0.15408 0.55746,0 l 3.19204,3.19201 c 0.15408,0.15407 0.15408,0.40354 0,0.55746 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:1;stroke-width:0.0164247" />
+ </g>
+ <g
+ id="g904"
+ transform="matrix(0.90000009,0,0,0.90000009,10.464254,9.7980333)">
+ <g
+ id="g850-3"
+ transform="matrix(0.06491223,0,0,0.06491223,52.083661,82.07218)">
+ <path
+ class="st0"
+ d="m 142.8,120.1 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0,-6.1 -4.6,-11 -10.2,-11 z m -36.5,0 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0.1,-6.1 -4.5,-11 -10.2,-11 z"
+ id="path836-5"
+ style="fill:#7289da;fill-opacity:1" />
+ <path
+ class="st0"
+ d="m 191.4,36.9 h -134 c -11.3,0 -20.5,9.2 -20.5,20.5 v 134 c 0,11.3 9.2,20.5 20.5,20.5 h 113.4 l -5.3,-18.3 12.8,11.8 12.1,11.1 21.6,18.7 V 57.4 C 211.9,46.1 202.7,36.9 191.4,36.9 Z m -38.6,129.5 c 0,0 -3.6,-4.3 -6.6,-8 13.1,-3.7 18.1,-11.8 18.1,-11.8 -4.1,2.7 -8,4.6 -11.5,5.9 -5,2.1 -9.8,3.4 -14.5,4.3 -9.6,1.8 -18.4,1.3 -25.9,-0.1 -5.7,-1.1 -10.6,-2.6 -14.7,-4.3 -2.3,-0.9 -4.8,-2 -7.3,-3.4 -0.3,-0.2 -0.6,-0.3 -0.9,-0.5 -0.2,-0.1 -0.3,-0.2 -0.4,-0.2 -1.8,-1 -2.8,-1.7 -2.8,-1.7 0,0 4.8,7.9 17.5,11.7 -3,3.8 -6.7,8.2 -6.7,8.2 C 75,165.8 66.6,151.4 66.6,151.4 66.6,119.5 81,93.6 81,93.6 95.4,82.9 109,83.2 109,83.2 l 1,1.2 c -18,5.1 -26.2,13 -26.2,13 0,0 2.2,-1.2 5.9,-2.8 10.7,-4.7 19.2,-5.9 22.7,-6.3 0.6,-0.1 1.1,-0.2 1.7,-0.2 6.1,-0.8 13,-1 20.2,-0.2 9.5,1.1 19.7,3.9 30.1,9.5 0,0 -7.9,-7.5 -24.9,-12.6 l 1.4,-1.6 c 0,0 13.7,-0.3 28,10.4 0,0 14.4,25.9 14.4,57.8 0,-0.1 -8.4,14.3 -30.5,15 z"
+ id="path838-6"
+ style="fill:#7289da;fill-opacity:1"
+ sodipodi:nodetypes="sssssccccccscccccccccccccccccccccccccccc" />
+ </g>
+ <path
+ id="path4789-6-2"
+ class=""
+ d="m 107.16039,90.382629 -3.19204,3.19205 c -0.15408,0.15408 -0.40352,0.15408 -0.55746,0 l -0.37229,-0.37231 c -0.15368,-0.15369 -0.15408,-0.40277 -5.3e-4,-0.55681 l 2.52975,-2.54167 -2.52975,-2.54164 c -0.15329,-0.15408 -0.15309,-0.40312 5.3e-4,-0.55681 l 0.37229,-0.37228 c 0.15408,-0.15408 0.40353,-0.15408 0.55746,0 l 3.19204,3.19201 c 0.15408,0.15407 0.15408,0.40354 0,0.55746 z"
+ inkscape:connector-curvature="0"
+ style="fill:#7289da;fill-opacity:1;stroke-width:0.0164247" />
+ <g
+ aria-label="JOIN US"
+ transform="matrix(1.2501707,0,0,1.2501707,-25.160061,-36.966352)"
+ id="text951"
+ style="font-style:normal;font-weight:normal;font-size:6.35px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect953-0);fill:#7289da;fill-opacity:1;stroke:none">
+ <path
+ d="m 75.839362,102.56309 c 0.127,0.9525 0.89535,1.3843 1.67005,1.3843 0.85725,0 1.7145,-0.55245 1.7145,-1.53035 v -3.028953 h -2.1463 v 1.028703 h 1.02235 v 2.00025 c 0,0.26035 -0.2667,0.4318 -0.5461,0.4318 -0.2794,0 -0.57785,-0.14605 -0.64135,-0.508 z"
+ style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-family:'Uni Sans';-inkscape-font-specification:'Uni Sans Heavy';fill:#7289da;fill-opacity:1"
+ id="path850" />
+ <path
+ d="m 79.795412,102.40434 c 0,1.0287 0.93345,1.54305 1.8669,1.54305 0.93345,0 1.86055,-0.51435 1.86055,-1.54305 v -1.5367 c 0,-1.028703 -0.93345,-1.543053 -1.8669,-1.543053 -0.93345,0 -1.86055,0.508 -1.86055,1.543053 z m 1.13665,-1.5367 c 0,-0.3302 0.3556,-0.508 0.7112,-0.508 0.3683,0 0.74295,0.15875 0.74295,0.508 v 1.5367 c 0,0.32385 -0.36195,0.48895 -0.7239,0.48895 -0.36195,0 -0.73025,-0.15875 -0.73025,-0.48895 z"
+ style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-family:'Uni Sans';-inkscape-font-specification:'Uni Sans Heavy';fill:#7289da;fill-opacity:1"
+ id="path852" />
+ <path
+ d="m 85.262755,99.388087 h -1.13665 v 4.495803 h 1.13665 z"
+ style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-family:'Uni Sans';-inkscape-font-specification:'Uni Sans Heavy';fill:#7289da;fill-opacity:1"
+ id="path854" />
+ <path
+ d="m 85.973945,103.88389 h 1.13665 v -1.79705 l -0.14605,-0.86995 0.03175,-0.006 0.3937,0.9017 1.016,1.77165 h 1.14935 v -4.495803 h -1.1303 v 2.038353 c 0.0063,0 0.12065,0.7747 0.127,0.7747 l -0.03175,0.006 -0.381,-0.9017 -1.08585,-1.917703 h -1.0795 z"
+ style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-family:'Uni Sans';-inkscape-font-specification:'Uni Sans Heavy';fill:#7289da;fill-opacity:1"
+ id="path856" />
+ <path
+ d="m 92.546182,99.388087 h -1.14935 v 2.990853 c -0.0063,2.1082 3.5814,2.1082 3.58775,0 v -2.990853 h -1.14935 v 2.990853 c -0.0064,0.7239 -1.28905,0.7239 -1.28905,0 z"
+ style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-family:'Uni Sans';-inkscape-font-specification:'Uni Sans Heavy';fill:#7289da;fill-opacity:1"
+ id="path858" />
+ <path
+ d="m 95.44178,103.13459 c 0.4191,0.53975 0.9906,0.8128 1.53035,0.8128 0.8255,0 1.7399,-0.47625 1.778,-1.3462 0.0508,-1.1049 -0.7493,-1.3843 -1.5494,-1.53035 -0.34925,-0.0762 -0.5842,-0.2032 -0.5969,-0.4191 0.01905,-0.5207 0.8255,-0.53975 1.2954,-0.0381 l 0.74295,-0.5715 c -0.46355,-0.565153 -0.9906,-0.717553 -1.5367,-0.717553 -0.8255,0 -1.6256,0.46355 -1.6256,1.346203 0,0.85725 0.6604,1.31445 1.3843,1.42875 0.3683,0.0508 0.78105,0.19685 0.76835,0.45085 -0.03175,0.4826 -1.02235,0.4572 -1.4732,-0.0889 z"
+ style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-family:'Uni Sans';-inkscape-font-specification:'Uni Sans Heavy';fill:#7289da;fill-opacity:1"
+ id="path860" />
+ </g>
+ </g>
+ </g>
+ <style
+ id="style834">.st0{fill:#FFFFFF;}</style>
+</svg>
diff --git a/pydis_site/static/images/navbar/navbar_discordjoin.svg b/pydis_site/static/images/navbar/navbar_discordjoin.svg
deleted file mode 100644
index 75e6b102..00000000
--- a/pydis_site/static/images/navbar/navbar_discordjoin.svg
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="114.70044mm"
- height="61.897388mm"
- viewBox="0 0 114.70044 61.897388"
- version="1.1"
- id="svg8"
- inkscape:version="0.92.4 5da689c313, 2019-01-14"
- sodipodi:docname="discordjoin.svg">
- <defs
- id="defs2" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.98994949"
- inkscape:cx="-404.01729"
- inkscape:cy="34.494854"
- inkscape:document-units="mm"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="3440"
- inkscape:window-height="1409"
- inkscape:window-x="2560"
- inkscape:window-y="31"
- inkscape:window-maximized="1"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0" />
- <metadata
- id="metadata5">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(-52.233408,-75.88169)">
- <path
- style="opacity:1;vector-effect:none;fill:#697ec4;fill-opacity:1;stroke:none;stroke-width:0.81460673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- d="m 60.360377,75.88169 -8.126969,61.89739 H 166.93385 V 75.88169 Z"
- id="rect4758-3"
- inkscape:connector-curvature="0" />
- <path
- id="path4789-6"
- class=""
- d="m 157.65213,107.11299 -4.99428,4.9943 c -0.24107,0.24107 -0.63135,0.24107 -0.8722,0 l -0.58249,-0.58252 c -0.24045,-0.24046 -0.24107,-0.63017 -8.3e-4,-0.87119 l 3.95805,-3.9767 -3.95805,-3.97665 c -0.23984,-0.24107 -0.23953,-0.63072 8.3e-4,-0.87118 l 0.58249,-0.58248 c 0.24107,-0.24108 0.63137,-0.24108 0.8722,0 l 4.99428,4.99422 c 0.24107,0.24107 0.24107,0.63138 0,0.8722 z"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-opacity:1;stroke-width:0.02569815" />
- <image
- y="94.290833"
- x="67.190086"
- id="image4856"
- xlink:href="data:image/png;base64,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 "
- preserveAspectRatio="none"
- height="26.987499"
- width="79.375" />
- </g>
-</svg>
diff --git a/pydis_site/static/images/sponsors/adafruit.png b/pydis_site/static/images/sponsors/adafruit.png
deleted file mode 100644
index eb14cf5d..00000000
--- a/pydis_site/static/images/sponsors/adafruit.png
+++ /dev/null
Binary files differ
diff --git a/pydis_site/static/images/sponsors/notion.png b/pydis_site/static/images/sponsors/notion.png
new file mode 100644
index 00000000..44ae9244
--- /dev/null
+++ b/pydis_site/static/images/sponsors/notion.png
Binary files differ
diff --git a/pydis_site/static/images/waves/wave_dark.svg b/pydis_site/static/images/waves/wave_dark.svg
new file mode 100644
index 00000000..35174c47
--- /dev/null
+++ b/pydis_site/static/images/waves/wave_dark.svg
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1600"
+ height="198"
+ version="1.1"
+ id="svg11"
+ sodipodi:docname="wave.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
+ <metadata
+ id="metadata15">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1409"
+ id="namedview13"
+ showgrid="false"
+ inkscape:zoom="1.44625"
+ inkscape:cx="757.49384"
+ inkscape:cy="107.38903"
+ inkscape:window-x="4880"
+ inkscape:window-y="677"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg11" />
+ <defs
+ id="defs7">
+ <linearGradient
+ id="a"
+ x1="50%"
+ x2="50%"
+ y1="-10.959%"
+ y2="100%">
+ <stop
+ stop-color="#57BBC1"
+ stop-opacity=".25"
+ offset="0%"
+ id="stop2" />
+ <stop
+ stop-color="#015871"
+ offset="100%"
+ id="stop4" />
+ </linearGradient>
+ </defs>
+ <path
+ fill="url(#a)"
+ fill-rule="evenodd"
+ d="M.005 121C311 121 409.898-.25 811 0c400 0 500 121 789 121v77H0s.005-48 .005-77z"
+ transform="matrix(-1 0 0 1 1600 0)"
+ id="path9"
+ style="fill:#5b6daf;fill-opacity:1" />
+</svg>
diff --git a/pydis_site/static/images/waves/wave_white.svg b/pydis_site/static/images/waves/wave_white.svg
new file mode 100644
index 00000000..441dacff
--- /dev/null
+++ b/pydis_site/static/images/waves/wave_white.svg
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1600"
+ height="28.745832"
+ version="1.1"
+ id="svg11"
+ sodipodi:docname="wavew.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
+ <metadata
+ id="metadata15">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1409"
+ id="namedview13"
+ showgrid="false"
+ inkscape:zoom="1.44625"
+ inkscape:cx="884.40031"
+ inkscape:cy="-61.865141"
+ inkscape:window-x="4880"
+ inkscape:window-y="677"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg11"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <defs
+ id="defs7">
+ <linearGradient
+ id="a"
+ x1="0.5"
+ x2="0.5"
+ y1="-0.10958999"
+ y2="1">
+ <stop
+ stop-color="#57BBC1"
+ stop-opacity=".25"
+ offset="0%"
+ id="stop2" />
+ <stop
+ stop-color="#015871"
+ offset="100%"
+ id="stop4" />
+ </linearGradient>
+ </defs>
+ <path
+ fill="url(#a)"
+ fill-rule="evenodd"
+ d="M 1599.995,17.566918 C 1289,17.566918 1190.102,-0.03623696 789,5.6042811e-5 389,5.6042811e-5 289,17.566918 0,17.566918 v 11.178914 h 1600 c 0,0 -0.01,-6.968673 -0.01,-11.178914 z"
+ id="path9"
+ style="fill:#ffffff;fill-opacity:1;stroke-width:0.381026" />
+</svg>
diff --git a/pydis_site/static/js/timeline/main.js b/pydis_site/static/js/timeline/main.js
index a4bf4f31..2ff7df57 100644
--- a/pydis_site/static/js/timeline/main.js
+++ b/pydis_site/static/js/timeline/main.js
@@ -1,5 +1,5 @@
(function(){
- // Vertical Timeline - by CodyHouse.co
+ // Vertical Timeline - by CodyHouse.co (modified)
function VerticalTimeline( element ) {
this.element = element;
this.blocks = this.element.getElementsByClassName("cd-timeline__block");
@@ -32,17 +32,36 @@
var self = this;
for( var i = 0; i < this.blocks.length; i++) {
(function(i){
- if( self.contents[i].classList.contains("cd-timeline__content--hidden") && self.blocks[i].getBoundingClientRect().top <= window.innerHeight*self.offset ) {
+ if((self.contents[i].classList.contains("cd-timeline__content--hidden") || self.contents[i].classList.contains("cd-timeline__content--bounce-out")) && self.blocks[i].getBoundingClientRect().top <= window.innerHeight*self.offset ) {
// add bounce-in animation
self.images[i].classList.add("cd-timeline__img--bounce-in");
self.contents[i].classList.add("cd-timeline__content--bounce-in");
self.images[i].classList.remove("cd-timeline__img--hidden");
self.contents[i].classList.remove("cd-timeline__content--hidden");
+ self.images[i].classList.remove("cd-timeline__img--bounce-out");
+ self.contents[i].classList.remove("cd-timeline__content--bounce-out");
}
})(i);
}
};
+ VerticalTimeline.prototype.hideBlocksScroll = function () {
+ if ( ! "classList" in document.documentElement ) {
+ return;
+ }
+ var self = this;
+ for( var i = 0; i < this.blocks.length; i++) {
+ (function(i){
+ if(self.contents[i].classList.contains("cd-timeline__content--bounce-in") && self.blocks[i].getBoundingClientRect().top > window.innerHeight*self.offset ) {
+ self.images[i].classList.remove("cd-timeline__img--bounce-in");
+ self.contents[i].classList.remove("cd-timeline__content--bounce-in");
+ self.images[i].classList.add("cd-timeline__img--bounce-out");
+ self.contents[i].classList.add("cd-timeline__content--bounce-out");
+ }
+ })(i);
+ }
+ }
+
var verticalTimelines = document.getElementsByClassName("js-cd-timeline"),
verticalTimelinesArray = [],
scrolling = false;
@@ -60,11 +79,25 @@
(!window.requestAnimationFrame) ? setTimeout(checkTimelineScroll, 250) : window.requestAnimationFrame(checkTimelineScroll);
}
});
+
+ function animationEnd(event) {
+ if (event.target.classList.contains("cd-timeline__img--bounce-out")) {
+ event.target.classList.add("cd-timeline__img--hidden");
+ event.target.classList.remove("cd-timeline__img--bounce-out");
+ } else if (event.target.classList.contains("cd-timeline__content--bounce-out")) {
+ event.target.classList.add("cd-timeline__content--hidden");
+ event.target.classList.remove("cd-timeline__content--bounce-out");
+ }
+ }
+
+ window.addEventListener("animationend", animationEnd);
+ window.addEventListener("webkitAnimationEnd", animationEnd);
}
function checkTimelineScroll() {
verticalTimelinesArray.forEach(function(timeline){
timeline.showBlocks();
+ timeline.hideBlocksScroll();
});
scrolling = false;
};
diff --git a/pydis_site/templates/404.html b/pydis_site/templates/404.html
new file mode 100644
index 00000000..42e317d2
--- /dev/null
+++ b/pydis_site/templates/404.html
@@ -0,0 +1,34 @@
+{% load static %}
+
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <title>Python Discord | 404</title>
+
+ <meta charset="UTF-8">
+
+ <link rel="preconnect" href="https://fonts.gstatic.com">
+ <link href="https://fonts.googleapis.com/css2?family=Hind:wght@400;600&display=swap" rel="stylesheet">
+ <link rel="stylesheet" href="{% static "css/error_pages.css" %}">
+</head>
+
+<body>
+ <div class="error-box">
+ <div class="logo-box">
+ <img src="https://raw.githubusercontent.com/python-discord/branding/b67897df93e572c1576a9026eb78c785a794d226/logos/logo_banner/logo_site_banner.svg"
+ alt="Python Discord banner" />
+ </div>
+ <div class="content-box">
+ <h1>404 — Not Found</h1>
+ <p>We couldn't find the page you're looking for. Here are a few things to try out:</p>
+ <ul>
+ <li>Double check the URL. Are you sure you typed it out correctly?
+ <li>Come join <a href="https://discord.gg/python">our Discord Server</a>. Maybe we can help you out over
+ there
+ </ul>
+ </div>
+ </div>
+</body>
+
+</html>
diff --git a/pydis_site/templates/500.html b/pydis_site/templates/500.html
new file mode 100644
index 00000000..869892ec
--- /dev/null
+++ b/pydis_site/templates/500.html
@@ -0,0 +1,29 @@
+{% load static %}
+
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <title>Python Discord | 500</title>
+
+ <meta charset="UTF-8">
+
+ <link rel="preconnect" href="https://fonts.gstatic.com">
+ <link href="https://fonts.googleapis.com/css2?family=Hind:wght@400;600&display=swap" rel="stylesheet">
+ <link rel="stylesheet" href="{% static "css/error_pages.css" %}">
+</head>
+
+<body>
+ <div class="error-box">
+ <div class="logo-box">
+ <img src="https://raw.githubusercontent.com/python-discord/branding/b67897df93e572c1576a9026eb78c785a794d226/logos/logo_banner/logo_site_banner.svg"
+ alt="Python Discord banner" />
+ </div>
+ <div class="content-box">
+ <h1>500 — Internal Server Error</h1>
+ <p>Something went wrong at our end. Please try again shortly, or if the problem persists, please let us know <a href="https://discord.gg/python">on Discord</a>.</p>
+ </div>
+ </div>
+</body>
+
+</html>
diff --git a/pydis_site/templates/base/footer.html b/pydis_site/templates/base/footer.html
index 90f06f3c..bca43b5d 100644
--- a/pydis_site/templates/base/footer.html
+++ b/pydis_site/templates/base/footer.html
@@ -1,7 +1,7 @@
<footer class="footer has-background-dark has-text-light">
<div class="content has-text-centered">
<p>
- Built with <a href="https://www.djangoproject.com/"><span id="django-logo">django</span></a> and <a href="https://bulma.io"><span id="bulma-logo">Bulma</span></a> <br/> &copy; {% now "Y" %} <span id="pydis-text">Python Discord</span>
+ Powered by <a href="https://www.linode.com/?r=3bc18ce876ff43ea31f201b91e8e119c9753f085"><span id="linode-logo">Linode</span></a><br>Built with <a href="https://www.djangoproject.com/"><span id="django-logo">django</span></a> and <a href="https://bulma.io"><span id="bulma-logo">Bulma</span></a> <br/> &copy; {% now "Y" %} <span id="pydis-text">Python Discord</span>
</p>
</div>
</footer>
diff --git a/pydis_site/templates/base/navbar.html b/pydis_site/templates/base/navbar.html
index 1bf5b7aa..64e3654b 100644
--- a/pydis_site/templates/base/navbar.html
+++ b/pydis_site/templates/base/navbar.html
@@ -20,7 +20,7 @@
<div class="navbar-menu is-paddingless" id="navbar_menu">
<div class="navbar-end">
- {# Discord invite - only visible in the hamburger on mobile sizes. #}
+ {# Burger-menu Discord #}
<a class="navbar-item is-hidden-desktop" href="https://discord.gg/python">
<span class="icon is-size-4 is-medium"><i class="fab fa-discord"></i></span>
<span>&nbsp;Discord</span>
@@ -57,7 +57,7 @@
</a>
{# More #}
- <div class="navbar-item has-dropdown is-hoverable has-left-margin-1">
+ <div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link is-hidden-touch">
More
</a>
@@ -125,12 +125,14 @@
</div>
</div>
+
+ {# Desktop Nav Discord #}
+ <div id="discord-btn" class="buttons is-hidden-touch">
+ <a href="https://discord.gg/python" class="button is-large is-primary">Discord</a>
+ </div>
+
</div>
- {# Join us on Discord! #}
- <a class="navbar-item is-fullsize has-no-highlight has-left-margin-1" href="https://discord.gg/python">
- <img class="is-hidden-touch" src="{% static "images/navbar/navbar_discordjoin.svg" %}" alt="Join us on Discord!"/>
- </a>
</div>
</nav>
diff --git a/pydis_site/templates/home/index.html b/pydis_site/templates/home/index.html
index f31363a4..04815b7f 100644
--- a/pydis_site/templates/home/index.html
+++ b/pydis_site/templates/home/index.html
@@ -9,12 +9,69 @@
{% block content %}
{% include "base/navbar.html" %}
- <section class="section">
+ <!-- Mobile-only Notice -->
+ <section id="mobile-notice" class="message is-primary is-hidden-tablet">
+ <div class="message-header">
+ <p>100K Member Milestone!</p>
+ </div>
+ <div class="message-body">
+ Thanks to all our members for helping us create this friendly and helpful community!
+ <br><br>
+ As a nice treat, we've created a <a href="{% url 'timeline' %}">Timeline page</a> for people
+ to discover the events that made our community what it is today. Be sure to check it out!
+ </div>
+ </section>
+
+ <!-- Wave Hero -->
+ <section id="wave-hero" class="section is-hidden-mobile">
+
+ <div class="container">
+ <div class="columns is-variable is-8">
+
+ {# Embedded Welcome video #}
+ <div id="wave-hero-left" class="column is-half">
+ <div class="force-aspect-container">
+ <iframe
+ class="force-aspect-content"
+ src="https://www.youtube.com/embed/ZH26PuX3re0"
+ srcdoc="
+ <style>
+ *{padding:0;margin:0;overflow:hidden}
+ html,body{height:100%}
+ img,span{position:absolute;width:100%;top:0;bottom:0;margin:auto}
+ span{height:1.5em;text-align:center;font:68px/1.5 sans-serif;color:#FFFFFFEE;text-shadow:0 0 0.1em #00000020}
+ </style>
+ <a href=https://www.youtube.com/embed/ZH26PuX3re0?autoplay=1>
+ <img src='{% static "images/frontpage/welcome.jpg" %}' alt='Welcome to Python Discord'>
+ <span>▶</span>
+ </a>"
+ allow="autoplay; accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
+ allowfullscreen
+ ></iframe>
+ </div>
+ </div>
+
+ {# Right side content #}
+ <div id="wave-hero-right" class="column is-half">
+ <img src="{% static "images/events/100k.png" %}" alt="100K members!">
+ </div>
- {# Who are we? #}
- <div class="container is-spaced">
+ </div>
+ </div>
+
+ {# Animated wave elements #}
+ <span id="front-wave" class="wave"></span>
+ <span id="back-wave" class="wave"></span>
+ <span id="bottom-wave" class="wave"></span>
+
+ </section>
+
+ <!-- Main Body -->
+ <section id="body" class="section">
+
+ <div class="container">
<h1 class="is-size-1">Who are we?</h1>
- <br>
+
<div class="columns is-desktop">
<div class="column is-half-desktop content">
<p>
@@ -31,68 +88,122 @@
</p>
<p>
You can find help with most Python-related problems in one of our help channels.
- Our staff of over 50 dedicated expert Helpers are available around the clock
+ Our staff of over 90 dedicated expert Helpers are available around the clock
in every timezone. Whether you're looking to learn the language or working on a
complex project, we've got someone who can help you if you get stuck.
</p>
</div>
- {# Right column container #}
- <div class="column is-half-desktop">
- <iframe width="560" height="315" src="https://www.youtube.com/embed/ZH26PuX3re0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
- </div>
- </div>
+ {# Showcase box #}
+ <section id="showcase" class="column is-half-desktop has-text-centered">
+ <article class="box">
+
+ <header class="title">New Timeline!</header>
- {# Projects #}
- <h1 class="is-size-1">Projects</h1>
- <br>
- <div class="columns is-multiline is-tablet">
-
- {# Display projects from HomeView.repos #}
- {% for repo in repo_data %}
- <div class="column is-one-third-desktop is-half-tablet">
- <div class="card has-equal-height github-card">
- <div class="card-content">
- <div class="repo-headline">
- <i class="fab fa-github"></i>
- <a href="https://github.com/{{ repo.repo_name }}"> <strong>{{ repo.repo_name }}</strong></a>
- </div>
- <div>
- {{ repo.description }}
- <br><br>
- </span><span class="repo-language-dot {{ repo.language | lower }}"></span> {{ repo.language }}
- <span id="repo-footer-item"><i class="fas fa-star"></i> {{ repo.stargazers }}</span>
- <span id="repo-footer-item"><i class="fas fa-code-branch"></i> {{ repo.forks }}</span>
- </div>
- </div>
+ <div class="mini-timeline">
+ <i class="fa fa-asterisk"></i>
+ <i class="fa fa-code"></i>
+ <i class="fab fa-python"></i>
+ <i class="fa fa-alien-monster"></i>
+ <i class="fa fa-duck"></i>
+ <i class="fa fa-bug"></i>
</div>
- </div>
- {% endfor %}
+
+ <p class="subtitle">
+ Start from our humble beginnings to discover the events that made our community what it is today.
+ </p>
+
+ <div class="buttons are-large is-centered">
+ <a href="{% url 'timeline' %}" class="button is-primary">
+ <span>Check it out!</span>
+ <span class="icon">
+ <i class="fas fa-arrow-right"></i>
+ </span>
+ </a>
+ </div>
+
+ </article>
+ </section>
</div>
</div>
</section>
- {# Sponsors #}
- <section class="section-sp hero is-light">
- <div id="sponsors-hero" class="hero-body">
+ <!-- Projects -->
+ {% if repo_data %}
+ <section id="projects" class="section">
+ <div class="container">
+ <h1 class="is-size-1">Projects</h1>
+
+ <div class="columns is-multiline is-tablet">
+
+ {# Generate project data from HomeView.repos #}
+ {% for repo in repo_data %}
+ <div class="column is-one-third-desktop is-half-tablet">
+
+ <a href="https://github.com/{{ repo.repo_name }}">
+ <article class="card">
+
+ <header class="card-header">
+ <span class="card-header-icon">
+ <span class="icon"><i class="fab fa-github"></i></span>
+ </span>
+ <div class="card-header-title">
+ {{ repo.repo_name|cut:"python-discord/" }}
+ </div>
+ </header>
+
+ <p class="card-content">
+ {{ repo.description }}
+ </p>
+
+ <footer class="card-footer">
+ <div class="card-footer-item">
+ <i class="repo-language-dot {{ repo.language | lower }}"></i>
+ {{ repo.language }}
+ </div>
+ <div class="card-footer-item">
+ <i class="fas fa-star"></i>
+ {{ repo.stargazers }}
+ </div>
+ <div class="card-footer-item">
+ <i class="fas fa-code-branch"></i>
+ {{ repo.forks }}
+ </div>
+ </footer>
+
+ </article>
+ </a>
+
+ </div>
+ {% endfor %}
+
+ </div>
+
+ </div>
+ </section>
+ {% endif %}
+
+ <!-- Sponsors -->
+ <section id="sponsors" class="hero is-light">
+ <div class="hero-body">
<div class="container">
<h1 class="title is-6 has-text-grey">
Sponsors
</h1>
<div class="columns is-mobile is-multiline">
- <a href="https://linode.com" class="column is-narrow">
+ <a href="https://www.linode.com/?r=3bc18ce876ff43ea31f201b91e8e119c9753f085" class="column is-narrow">
<img src="{% static "images/sponsors/linode.png" %}" alt="Linode"/>
</a>
<a href="https://jetbrains.com" class="column is-narrow">
<img src="{% static "images/sponsors/jetbrains.png" %}" alt="JetBrains"/>
</a>
- <a href="https://adafruit.com" class="column is-narrow">
- <img src="{% static "images/sponsors/adafruit.png" %}" alt="Adafruit"/>
- </a>
<a href="https://sentry.io" class="column is-narrow">
<img src="{% static "images/sponsors/sentry.png" %}" alt="Sentry"/>
</a>
+ <a href="https://notion.so" class="column is-narrow">
+ <img src="{% static "images/sponsors/notion.png" %}" alt="Notion"/>
+ </a>
</div>
</div>
</div>
diff --git a/pydis_site/templates/home/timeline.html b/pydis_site/templates/home/timeline.html
index 5c71f3a7..f3c58fc2 100644
--- a/pydis_site/templates/home/timeline.html
+++ b/pydis_site/templates/home/timeline.html
@@ -14,7 +14,7 @@
<div class="container max-width-lg cd-timeline__container">
<div class="cd-timeline__block">
<div class="cd-timeline__img cd-timeline__img--picture">
- <img src={% static "images/timeline/cd-icon-picture.svg" %} alt="Picture">
+ <img src="{% static "images/timeline/cd-icon-picture.svg" %}" alt="Picture">
</div>
<div class="cd-timeline__content text-component">
@@ -231,8 +231,8 @@
<div class="cd-timeline__content text-component">
<h2>PyDis hits 15,000 members; the “hot ones special” video is released</h2>
- <div class="video-container">
- <iframe class="video" src="https://www.youtube.com/embed/DIBXg8Qh7bA" frameborder="0"
+ <div class="force-aspect-container">
+ <iframe class="force-aspect-content" src="https://www.youtube.com/embed/DIBXg8Qh7bA" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
@@ -319,8 +319,8 @@
developers join us to judge the event and help out our members during the event. One of them,
@tshirtman, even joins our staff!</p>
- <div class="video-container">
- <iframe class="video" src="https://www.youtube.com/embed/8fbZsGrqBzo" frameborder="0"
+ <div class="force-aspect-container">
+ <iframe class="force-aspect-content" src="https://www.youtube.com/embed/8fbZsGrqBzo" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
@@ -377,8 +377,8 @@
Several of the Code Jam participants also end up getting involved contributing to the Arcade
repository.</p>
- <div class="video-container">
- <iframe class="video" src="https://www.youtube.com/embed/KkLXMvKfEgs" frameborder="0"
+ <div class="force-aspect-container">
+ <iframe class="force-aspect-content" src="https://www.youtube.com/embed/KkLXMvKfEgs" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
@@ -450,8 +450,8 @@
Code Jam for 2020 attracts hundreds of participants, and sees the creation of some fantastic
projects. Check them out in our judge stream below:</p>
- <div class="video-container">
- <iframe class="video" src="https://www.youtube.com/embed/OFtm8f2iu6c" frameborder="0"
+ <div class="force-aspect-container">
+ <iframe class="force-aspect-content" src="https://www.youtube.com/embed/OFtm8f2iu6c" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
@@ -479,6 +479,25 @@
</div>
</div>
+ <div class="cd-timeline__block">
+ <div class="cd-timeline__img cd-timeline__img--picture">
+ <img src="{% static "images/timeline/cd-icon-picture.svg" %}" alt="Picture">
+ </div>
+
+ <div class="cd-timeline__content text-component">
+ <h2>Python Discord hosts the 2020 CPython Core Developer Q&A</h2>
+ <div class="force-aspect-container">
+ <iframe class="force-aspect-content" src="https://www.youtube.com/embed/gXMdfBTcOfQ" frameborder="0"
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
+ allowfullscreen></iframe>
+ </div>
+
+ <div class="flex justify-between items-center">
+ <span class="cd-timeline__date">Oct 21st, 2020</span>
+ </div>
+ </div>
+ </div>
+
<div class="cd-timeline__block">
<div class="cd-timeline__img pastel-dark-blue cd-timeline__img--picture">
<i class="fa fa-users"></i>
@@ -490,7 +509,7 @@
and one we're very proud of. To commemorate it, we create this timeline.</p>
<div class="flex justify-between items-center">
- <span class="cd-timeline__date">Sep ??, 2020</span>
+ <span class="cd-timeline__date">Oct 22nd, 2020</span>
</div>
</div>
</div>