diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c641598
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,304 @@
+
+# Created by https://www.toptal.com/developers/gitignore/api/python,node
+# Edit at https://www.toptal.com/developers/gitignore?templates=python,node
+
+### Node ###
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional stylelint cache
+.stylelintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+.temp
+
+# Docusaurus cache and generated files
+.docusaurus
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+
+### Node Patch ###
+# Serverless Webpack directories
+.webpack/
+
+# Optional stylelint cache
+
+# SvelteKit build / generate output
+.svelte-kit
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+# End of https://www.toptal.com/developers/gitignore/api/python,node
diff --git a/manage.py b/manage.py
new file mode 100755
index 0000000..5c6e784
--- /dev/null
+++ b/manage.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == "__main__":
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "website.settings")
+
+ from django.core.management import execute_from_command_line
+
+ execute_from_command_line(sys.argv)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..17b8a9b
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+Django==4.0.5
+wagtail==3.0
diff --git a/static/css/website.css b/static/css/website.css
new file mode 100644
index 0000000..e69de29
diff --git a/static/js/website.js b/static/js/website.js
new file mode 100644
index 0000000..e69de29
diff --git a/templates/404.html b/templates/404.html
new file mode 100644
index 0000000..f19ab95
--- /dev/null
+++ b/templates/404.html
@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+
+{% block title %}Page not found{% endblock %}
+
+{% block body_class %}template-404{% endblock %}
+
+{% block content %}
+
Page not found
+
+Sorry, this page could not be found.
+{% endblock %}
diff --git a/templates/500.html b/templates/500.html
new file mode 100644
index 0000000..77379e5
--- /dev/null
+++ b/templates/500.html
@@ -0,0 +1,13 @@
+
+
+
+
+ Internal server error
+
+
+
+ Internal server error
+
+ Sorry, there seems to be an error. Please try again soon.
+
+
diff --git a/templates/base.html b/templates/base.html
new file mode 100644
index 0000000..a385e41
--- /dev/null
+++ b/templates/base.html
@@ -0,0 +1,39 @@
+{% load static wagtailcore_tags wagtailuserbar %}
+
+
+
+
+
+
+ {% block title %}
+ {% if page.seo_title %}{{ page.seo_title }}{% else %}{{ page.title }}{% endif %}
+ {% endblock %}
+ {% block title_suffix %}
+ {% wagtail_site as current_site %}
+ {% if current_site and current_site.site_name %}- {{ current_site.site_name }}{% endif %}
+ {% endblock %}
+
+
+
+
+ {# Global stylesheets #}
+
+
+ {% block extra_css %}
+ {# Override this in templates to add extra stylesheets #}
+ {% endblock %}
+
+
+
+ {% wagtailuserbar %}
+
+ {% block content %}{% endblock %}
+
+ {# Global javascript #}
+
+
+ {% block extra_js %}
+ {# Override this in templates to add extra javascript #}
+ {% endblock %}
+
+
diff --git a/website/__init__.py b/website/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/website/home/__init__.py b/website/home/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/website/home/migrations/0001_initial.py b/website/home/migrations/0001_initial.py
new file mode 100644
index 0000000..77b74b9
--- /dev/null
+++ b/website/home/migrations/0001_initial.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("wagtailcore", "0040_page_draft_title"),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="HomePage",
+ fields=[
+ (
+ "page_ptr",
+ models.OneToOneField(
+ on_delete=models.CASCADE,
+ parent_link=True,
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.Page",
+ ),
+ ),
+ ],
+ options={
+ "abstract": False,
+ },
+ bases=("wagtailcore.page",),
+ ),
+ ]
diff --git a/website/home/migrations/0002_create_homepage.py b/website/home/migrations/0002_create_homepage.py
new file mode 100644
index 0000000..ca328fa
--- /dev/null
+++ b/website/home/migrations/0002_create_homepage.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+from django.db import migrations
+
+
+def create_homepage(apps, schema_editor):
+ # Get models
+ ContentType = apps.get_model("contenttypes.ContentType")
+ Page = apps.get_model("wagtailcore.Page")
+ Site = apps.get_model("wagtailcore.Site")
+ HomePage = apps.get_model("home.HomePage")
+
+ # Delete the default homepage
+ # If migration is run multiple times, it may have already been deleted
+ Page.objects.filter(id=2).delete()
+
+ # Create content type for homepage model
+ homepage_content_type, __ = ContentType.objects.get_or_create(
+ model="homepage", app_label="home"
+ )
+
+ # Create a new homepage
+ homepage = HomePage.objects.create(
+ title="Home",
+ draft_title="Home",
+ slug="home",
+ content_type=homepage_content_type,
+ path="00010001",
+ depth=2,
+ numchild=0,
+ url_path="/home/",
+ )
+
+ # Create a site with the new homepage set as the root
+ Site.objects.create(hostname="localhost", root_page=homepage, is_default_site=True)
+
+
+def remove_homepage(apps, schema_editor):
+ # Get models
+ ContentType = apps.get_model("contenttypes.ContentType")
+ HomePage = apps.get_model("home.HomePage")
+
+ # Delete the default homepage
+ # Page and Site objects CASCADE
+ HomePage.objects.filter(slug="home", depth=2).delete()
+
+ # Delete content type for homepage model
+ ContentType.objects.filter(model="homepage", app_label="home").delete()
+
+
+class Migration(migrations.Migration):
+
+ run_before = [
+ ("wagtailcore", "0053_locale_model"),
+ ]
+
+ dependencies = [
+ ("home", "0001_initial"),
+ ]
+
+ operations = [
+ migrations.RunPython(create_homepage, remove_homepage),
+ ]
diff --git a/website/home/migrations/__init__.py b/website/home/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/website/home/models.py b/website/home/models.py
new file mode 100644
index 0000000..5076f57
--- /dev/null
+++ b/website/home/models.py
@@ -0,0 +1,7 @@
+from django.db import models
+
+from wagtail.models import Page
+
+
+class HomePage(Page):
+ pass
diff --git a/website/home/static/css/welcome_page.css b/website/home/static/css/welcome_page.css
new file mode 100644
index 0000000..2379395
--- /dev/null
+++ b/website/home/static/css/welcome_page.css
@@ -0,0 +1,187 @@
+html {
+ box-sizing: border-box;
+}
+
+*,
+*:before,
+*:after {
+ box-sizing: inherit;
+}
+
+body {
+ max-width: 960px;
+ min-height: 100vh;
+ margin: 0 auto;
+ padding: 0 15px;
+ color: #231f20;
+ font-family: 'Helvetica Neue', 'Segoe UI', Arial, sans-serif;
+ line-height: 1.25;
+}
+
+a {
+ background-color: transparent;
+ color: #308282;
+ text-decoration: underline;
+}
+
+a:hover {
+ color: #ea1b10;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+p,
+ul {
+ padding: 0;
+ margin: 0;
+ font-weight: 400;
+}
+
+svg:not(:root) {
+ overflow: hidden;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-top: 20px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #e6e6e6;
+}
+
+.logo {
+ width: 150px;
+ margin-inline-end: 20px;
+}
+
+.logo a {
+ display: block;
+}
+
+.figure-logo {
+ max-width: 150px;
+ max-height: 55.1px;
+}
+
+.release-notes {
+ font-size: 14px;
+}
+
+.main {
+ padding: 40px 0;
+ margin: 0 auto;
+ text-align: center;
+}
+
+.figure-space {
+ max-width: 265px;
+}
+
+@keyframes pos {
+ 0%, 100% {
+ transform: rotate(-6deg);
+ }
+ 50% {
+ transform: rotate(6deg);
+ }
+}
+
+.egg {
+ fill: #43b1b0;
+ animation: pos 3s ease infinite;
+ transform: translateY(50px);
+ transform-origin: 50% 80%;
+}
+
+.main-text {
+ max-width: 400px;
+ margin: 5px auto;
+}
+
+.main-text h1 {
+ font-size: 22px;
+}
+
+.main-text p {
+ margin: 15px auto 0;
+}
+
+.footer {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ border-top: 1px solid #e6e6e6;
+ padding: 10px;
+}
+
+.option {
+ display: block;
+ padding: 10px 10px 10px 34px;
+ position: relative;
+ text-decoration: none;
+}
+
+.option svg {
+ width: 24px;
+ height: 24px;
+ fill: gray;
+ border: 1px solid #d9d9d9;
+ padding: 5px;
+ border-radius: 100%;
+ top: 10px;
+ /* Remove once we drop support for Safari 13. */
+ /* stylelint-disable-next-line property-disallowed-list */
+ left: 0;
+ inset-inline-start: 0;
+ position: absolute;
+}
+
+.option h2 {
+ font-size: 19px;
+ text-decoration: underline;
+}
+
+.option p {
+ padding-top: 3px;
+ color: #231f20;
+ font-size: 15px;
+ font-weight: 300;
+}
+
+@media (max-width: 996px) {
+ body {
+ max-width: 780px;
+ }
+}
+
+@media (max-width: 767px) {
+ .option {
+ flex: 0 0 50%;
+ }
+}
+
+@media (max-width: 599px) {
+ .main {
+ padding: 20px 0;
+ }
+
+ .figure-space {
+ max-width: 200px;
+ }
+
+ .footer {
+ display: block;
+ width: 300px;
+ margin: 0 auto;
+ }
+}
+
+@media (max-width: 360px) {
+ .header-link {
+ max-width: 100px;
+ }
+}
diff --git a/website/home/templates/home/home_page.html b/website/home/templates/home/home_page.html
new file mode 100644
index 0000000..db9e9b0
--- /dev/null
+++ b/website/home/templates/home/home_page.html
@@ -0,0 +1,21 @@
+{% extends "base.html" %}
+{% load static %}
+
+{% block body_class %}template-homepage{% endblock %}
+
+{% block extra_css %}
+
+{% comment %}
+Delete the line below if you're just getting started and want to remove the welcome screen!
+{% endcomment %}
+
+{% endblock extra_css %}
+
+{% block content %}
+
+{% comment %}
+Delete the line below if you're just getting started and want to remove the welcome screen!
+{% endcomment %}
+{% include 'home/welcome_page.html' %}
+
+{% endblock content %}
diff --git a/website/home/templates/home/welcome_page.html b/website/home/templates/home/welcome_page.html
new file mode 100644
index 0000000..dcacaf3
--- /dev/null
+++ b/website/home/templates/home/welcome_page.html
@@ -0,0 +1,52 @@
+{% load i18n wagtailcore_tags %}
+
+
+
+
+
+
{% trans "Welcome to your new Wagtail site!" %}
+
{% trans 'Please feel free to join our community on Slack, or get started with one of the links below.' %}
+
+
+
diff --git a/website/search/__init__.py b/website/search/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/website/search/templates/search/search.html b/website/search/templates/search/search.html
new file mode 100644
index 0000000..476427f
--- /dev/null
+++ b/website/search/templates/search/search.html
@@ -0,0 +1,38 @@
+{% extends "base.html" %}
+{% load static wagtailcore_tags %}
+
+{% block body_class %}template-searchresults{% endblock %}
+
+{% block title %}Search{% endblock %}
+
+{% block content %}
+Search
+
+
+
+{% if search_results %}
+
+ {% for result in search_results %}
+ -
+
+ {% if result.search_description %}
+ {{ result.search_description }}
+ {% endif %}
+
+ {% endfor %}
+
+
+{% if search_results.has_previous %}
+Previous
+{% endif %}
+
+{% if search_results.has_next %}
+Next
+{% endif %}
+{% elif search_query %}
+No results found
+{% endif %}
+{% endblock %}
diff --git a/website/search/views.py b/website/search/views.py
new file mode 100644
index 0000000..703c4dd
--- /dev/null
+++ b/website/search/views.py
@@ -0,0 +1,38 @@
+from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
+from django.template.response import TemplateResponse
+
+from wagtail.models import Page
+from wagtail.search.models import Query
+
+
+def search(request):
+ search_query = request.GET.get("query", None)
+ page = request.GET.get("page", 1)
+
+ # Search
+ if search_query:
+ search_results = Page.objects.live().search(search_query)
+ query = Query.get(search_query)
+
+ # Record hit
+ query.add_hit()
+ else:
+ search_results = Page.objects.none()
+
+ # Pagination
+ paginator = Paginator(search_results, 10)
+ try:
+ search_results = paginator.page(page)
+ except PageNotAnInteger:
+ search_results = paginator.page(1)
+ except EmptyPage:
+ search_results = paginator.page(paginator.num_pages)
+
+ return TemplateResponse(
+ request,
+ "search/search.html",
+ {
+ "search_query": search_query,
+ "search_results": search_results,
+ },
+ )
diff --git a/website/settings.py b/website/settings.py
new file mode 100644
index 0000000..e760264
--- /dev/null
+++ b/website/settings.py
@@ -0,0 +1,176 @@
+"""
+Django settings for website project.
+
+Generated by 'django-admin startproject' using Django 4.0.5.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.0/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/4.0/ref/settings/
+"""
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+import os
+
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = "django-insecure-_w9@x(3f5xaa+3n%!a#v7!i9!n4dh0%hn2nb9@c49=2r2664u*"
+
+# SECURITY WARNING: define the correct hosts in production!
+ALLOWED_HOSTS = ["*"]
+
+EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ "website.home",
+ "website.search",
+ "wagtail.contrib.forms",
+ "wagtail.contrib.redirects",
+ "wagtail.embeds",
+ "wagtail.sites",
+ "wagtail.users",
+ "wagtail.snippets",
+ "wagtail.documents",
+ "wagtail.images",
+ "wagtail.search",
+ "wagtail.admin",
+ "wagtail",
+ "modelcluster",
+ "taggit",
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.messages",
+ "django.contrib.staticfiles",
+]
+
+MIDDLEWARE = [
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
+ "django.middleware.security.SecurityMiddleware",
+ "wagtail.contrib.redirects.middleware.RedirectMiddleware",
+]
+
+ROOT_URLCONF = "website.urls"
+
+TEMPLATES = [
+ {
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [
+ os.path.join(BASE_DIR, "templates"),
+ ],
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": [
+ "django.template.context_processors.debug",
+ "django.template.context_processors.request",
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = "website.wsgi.application"
+
+
+# Database
+# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
+
+DATABASES = {
+ "default": {
+ "ENGINE": "django.db.backends.sqlite3",
+ "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
+ },
+ {
+ "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
+ },
+ {
+ "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
+ },
+ {
+ "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/4.0/topics/i18n/
+
+LANGUAGE_CODE = "en-gb"
+
+TIME_ZONE = "UTC"
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/4.0/howto/static-files/
+
+STATICFILES_FINDERS = [
+ "django.contrib.staticfiles.finders.FileSystemFinder",
+ "django.contrib.staticfiles.finders.AppDirectoriesFinder",
+]
+
+STATICFILES_DIRS = [
+ os.path.join(BASE_DIR, "static"),
+]
+
+# ManifestStaticFilesStorage is recommended in production, to prevent outdated
+# JavaScript / CSS assets being served from cache (e.g. after a Wagtail upgrade).
+# See https://docs.djangoproject.com/en/4.0/ref/contrib/staticfiles/#manifeststaticfilesstorage
+STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
+
+STATIC_ROOT = os.path.join(BASE_DIR, "collected-static")
+STATIC_URL = "/static/"
+
+MEDIA_ROOT = os.path.join(BASE_DIR, "media")
+MEDIA_URL = "/media/"
+
+
+# Wagtail settings
+
+WAGTAIL_SITE_NAME = "website"
+
+# Search
+# https://docs.wagtail.org/en/stable/topics/search/backends.html
+WAGTAILSEARCH_BACKENDS = {
+ "default": {
+ "BACKEND": "wagtail.search.backends.database",
+ }
+}
+
+# Base URL to use when referring to full URLs within the Wagtail admin backend -
+# e.g. in notification emails. Don't include '/admin' or a trailing slash
+WAGTAILADMIN_BASE_URL = "http://example.com"
diff --git a/website/urls.py b/website/urls.py
new file mode 100644
index 0000000..c65d781
--- /dev/null
+++ b/website/urls.py
@@ -0,0 +1,35 @@
+from django.conf import settings
+from django.urls import include, path
+from django.contrib import admin
+
+from wagtail.admin import urls as wagtailadmin_urls
+from wagtail import urls as wagtail_urls
+from wagtail.documents import urls as wagtaildocs_urls
+
+from website.search import views as search_views
+
+urlpatterns = [
+ path("django-admin/", admin.site.urls),
+ path("admin/", include(wagtailadmin_urls)),
+ path("documents/", include(wagtaildocs_urls)),
+ path("search/", search_views.search, name="search"),
+]
+
+
+if settings.DEBUG:
+ from django.conf.urls.static import static
+ from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+
+ # Serve static and media files from development server
+ urlpatterns += staticfiles_urlpatterns()
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+
+urlpatterns = urlpatterns + [
+ # For anything not caught by a more specific rule above, hand over to
+ # Wagtail's page serving mechanism. This should be the last pattern in
+ # the list:
+ path("", include(wagtail_urls)),
+ # Alternatively, if you want Wagtail pages to be served from a subpath
+ # of your site, rather than the site root:
+ # path("pages/", include(wagtail_urls)),
+]
diff --git a/website/wsgi.py b/website/wsgi.py
new file mode 100644
index 0000000..63b5a1c
--- /dev/null
+++ b/website/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for website project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "website.settings.dev")
+
+application = get_wsgi_application()