From 557dc279c7e15bd4f2c5f1faf689d6e48b6ddacd Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 15:28:57 +0000 Subject: [PATCH 01/28] Added basic readme file. --- README.rst | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 README.rst diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..d40de53 --- /dev/null +++ b/README.rst @@ -0,0 +1,8 @@ +For Cod sake do not use this project as is! +=========================================== + +I'm making this public on the off chance someone, at an early stage of learning Django, comes across it and finds something useful in the code. This project is designed specifically for my needs and is not intended to be portable to other people without considerable restructuring effort. Besides this, my coding skills are basic to say the least and as soon as the learner viewing the project improves their knowledge they'll be able to work out all the many errors I've made. + +I will decide on a license and add it in due course. + +Calum. \ No newline at end of file -- 2.39.5 From e7148362f0a7f058abc4e0185ced9f1e2c6e097d Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 21:55:49 +0000 Subject: [PATCH 02/28] Initial settings configuration. --- .env-template | 16 +++++++++++++ config/settings.py | 60 ++++++++++++++++++++++++++++++++++------------ pyproject.toml | 2 ++ uv.lock | 29 +++++++++++++++++++++- 4 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 .env-template diff --git a/.env-template b/.env-template new file mode 100644 index 0000000..49e9c15 --- /dev/null +++ b/.env-template @@ -0,0 +1,16 @@ +DJANGO_SECRET_KEY='' + +DEBUG='True' +ALLOWED_HOSTS=[] + +DB_NAME='' +DB_USER='' +DB_PASSWORD='' +DB_HOST='localhost' +DB_PORT=5432 + +EMAIL_HOST='' +EMAIL_PORT= +EMAIL_USE_TLS='True' +EMAIL_HOST_USER='' +EMAIL_HOST_PASSWORD='' \ No newline at end of file diff --git a/config/settings.py b/config/settings.py index 6cfe6a0..cef58bb 100644 --- a/config/settings.py +++ b/config/settings.py @@ -9,24 +9,26 @@ https://docs.djangoproject.com/en/5.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.1/ref/settings/ """ - +import os from pathlib import Path +from dotenv import load_dotenv + +load_dotenv() + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent - # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-e7m1a6dwy1^-^zp^*_(ql^c8y5!^0$kf4jwarw^3ny5b(d^t1r' +SECRET_KEY = os.getenv('DJANGO_SECRET_KEY') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] +DEBUG = os.getenv('DEBUG') +ALLOWED_HOSTS = eval(os.getenv('ALLOWED_HOSTS')) # Application definition @@ -54,8 +56,7 @@ ROOT_URLCONF = 'config.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [BASE_DIR / 'templates'] - , + 'DIRS': [BASE_DIR / 'templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -70,18 +71,20 @@ TEMPLATES = [ WSGI_APPLICATION = 'config.wsgi.application' - # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.getenv('DB_NAME'), + 'USER': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': os.getenv('DB_HOST'), + 'PORT': os.getenv('DB_PORT'), } } - # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators @@ -100,11 +103,10 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] - # Internationalization # https://docs.djangoproject.com/en/5.1/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = 'en-gb' TIME_ZONE = 'UTC' @@ -112,11 +114,39 @@ USE_I18N = True USE_TZ = True - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.1/howto/static-files/ STATIC_URL = 'static/' +STATIC_ROOT = BASE_DIR / 'static/' +MEDIA_URL = 'media/' +MEDIA_ROOT = BASE_DIR / 'media/' + +# User model and authentication +# LOGIN_REDIRECT_URL = '' +# LOGOUT_REDIRECT_URL = '' + +# Email backend configuration +# DEFAULT_FROM_EMAIL = 'calum@drulum.com' +# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +# EMAIL_HOST = os.getenv('EMAIL_HOST') +# EMAIL_PORT = os.getenv('EMAIL_PORT') +# EMAIL_USE_TLS = (os.getenv('EMAIL_USE_TLS') == 'True') +# EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER') +# EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') + +# Tagulous config +# SERIALIZATION_MODULES = { +# 'xml': 'tagulous.serializers.xml_serializer', +# 'json': 'tagulous.serializers.json', +# 'python': 'tagulous.serializers.python', +# 'yaml': 'tagulous.serializers.pyyaml', +# } + +# Markdownx config +# MARKDOWNX_MARKDOWN_EXTENSIONS = [ +# 'markdown.extensions.extra', +# ] # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field diff --git a/pyproject.toml b/pyproject.toml index d1349d1..7db15a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,4 +5,6 @@ description = "Add your description here" requires-python = ">=3.13" dependencies = [ "django>=5.1.6", + "psycopg>=3.2.4", + "python-dotenv>=1.0.1", ] diff --git a/uv.lock b/uv.lock index 4446a3c..bc23528 100644 --- a/uv.lock +++ b/uv.lock @@ -30,10 +30,37 @@ version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "django" }, + { name = "psycopg" }, + { name = "python-dotenv" }, ] [package.metadata] -requires-dist = [{ name = "django", specifier = ">=5.1.6" }] +requires-dist = [ + { name = "django", specifier = ">=5.1.6" }, + { name = "psycopg", specifier = ">=3.2.4" }, + { name = "python-dotenv", specifier = ">=1.0.1" }, +] + +[[package]] +name = "psycopg" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/f2/954b1467b3e2ca5945b83b5e320268be1f4df486c3e8ffc90f4e4b707979/psycopg-3.2.4.tar.gz", hash = "sha256:f26f1346d6bf1ef5f5ef1714dd405c67fb365cfd1c6cea07de1792747b167b92", size = 156109 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/49/15114d5f7ee68983f4e1a24d47e75334568960352a07c6f0e796e912685d/psycopg-3.2.4-py3-none-any.whl", hash = "sha256:43665368ccd48180744cab26b74332f46b63b7e06e8ce0775547a3533883d381", size = 198716 }, +] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, +] [[package]] name = "sqlparse" -- 2.39.5 From bad5592a9cb282925f4e6ab925e38390e39e731a Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 22:01:41 +0000 Subject: [PATCH 03/28] Created app for articles. --- articles/__init__.py | 0 articles/admin.py | 1 + articles/apps.py | 6 ++++++ articles/migrations/__init__.py | 0 articles/models.py | 1 + articles/tests.py | 1 + articles/views.py | 1 + config/settings.py | 1 + 8 files changed, 11 insertions(+) create mode 100644 articles/__init__.py create mode 100644 articles/admin.py create mode 100644 articles/apps.py create mode 100644 articles/migrations/__init__.py create mode 100644 articles/models.py create mode 100644 articles/tests.py create mode 100644 articles/views.py diff --git a/articles/__init__.py b/articles/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/articles/admin.py b/articles/admin.py new file mode 100644 index 0000000..846f6b4 --- /dev/null +++ b/articles/admin.py @@ -0,0 +1 @@ +# Register your models here. diff --git a/articles/apps.py b/articles/apps.py new file mode 100644 index 0000000..166ff9c --- /dev/null +++ b/articles/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ArticlesConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'articles' diff --git a/articles/migrations/__init__.py b/articles/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/articles/models.py b/articles/models.py new file mode 100644 index 0000000..6b20219 --- /dev/null +++ b/articles/models.py @@ -0,0 +1 @@ +# Create your models here. diff --git a/articles/tests.py b/articles/tests.py new file mode 100644 index 0000000..a39b155 --- /dev/null +++ b/articles/tests.py @@ -0,0 +1 @@ +# Create your tests here. diff --git a/articles/views.py b/articles/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/articles/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/config/settings.py b/config/settings.py index cef58bb..0851db1 100644 --- a/config/settings.py +++ b/config/settings.py @@ -39,6 +39,7 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'articles.apps.ArticlesConfig' ] MIDDLEWARE = [ -- 2.39.5 From 4dc5935374547806d75c4cf9d28c2235e42e0b50 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 22:26:19 +0000 Subject: [PATCH 04/28] Added required applications to uv config. --- pyproject.toml | 2 ++ uv.lock | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7db15a1..ad5e4e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,8 @@ description = "Add your description here" requires-python = ">=3.13" dependencies = [ "django>=5.1.6", + "django-markdownx>=4.0.7", + "django-tagulous>=2.1.0", "psycopg>=3.2.4", "python-dotenv>=1.0.1", ] diff --git a/uv.lock b/uv.lock index bc23528..8d56617 100644 --- a/uv.lock +++ b/uv.lock @@ -24,12 +24,40 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/75/6f/d2c216d00975e2604b10940937b0ba6b2c2d9b3cc0cc633e414ae3f14b2e/Django-5.1.6-py3-none-any.whl", hash = "sha256:8d203400bc2952fbfb287c2bbda630297d654920c72a73cc82a9ad7926feaad5", size = 8277066 }, ] +[[package]] +name = "django-markdownx" +version = "4.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "markdown" }, + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/b9/e33e16c721ca429c42cdf52f5f52f78023a576eb4f01294385446a3adaee/django-markdownx-4.0.7.tar.gz", hash = "sha256:38aa331c2ca0bee218b77f462361b5393e4727962bc6021939c09048363cb6ea", size = 35697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/0e/1413c3228ef262df351094621e0e1c550a68a7960c23719198b41a6097bc/django_markdownx-4.0.7-py2.py3-none-any.whl", hash = "sha256:c1975ae3053481d4c111abd38997a5b5bb89235a1e3215f995d835942925fe7b", size = 44136 }, +] + +[[package]] +name = "django-tagulous" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/97/f9a157716e3ce00b9d0ae596d9452bc6bb993066337a885a94c96ac7169c/django_tagulous-2.1.0.tar.gz", hash = "sha256:f629b54ad720052092785b0dce056dc6a68c7b63f8126075af9c25848b250bfd", size = 291575 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/7b/14c1c0a8e5378ccebe906676084ada7c7679ad76659a44cb5a55413ba5bc/django_tagulous-2.1.0-py3-none-any.whl", hash = "sha256:5ebba5a51f049f6df5f9d2a30eef431c0bf7cd35758aa0a42fc3351be4d239cd", size = 286294 }, +] + [[package]] name = "drulum" version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "django" }, + { name = "django-markdownx" }, + { name = "django-tagulous" }, { name = "psycopg" }, { name = "python-dotenv" }, ] @@ -37,10 +65,48 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "django", specifier = ">=5.1.6" }, + { name = "django-markdownx", specifier = ">=4.0.7" }, + { name = "django-tagulous", specifier = ">=2.1.0" }, { name = "psycopg", specifier = ">=3.2.4" }, { name = "python-dotenv", specifier = ">=1.0.1" }, ] +[[package]] +name = "markdown" +version = "3.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, +] + +[[package]] +name = "pillow" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 }, + { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 }, + { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 }, + { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 }, + { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 }, + { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 }, + { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 }, + { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 }, + { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 }, + { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 }, + { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 }, + { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 }, + { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 }, + { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 }, + { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 }, + { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 }, + { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, + { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, + { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, +] + [[package]] name = "psycopg" version = "3.2.4" -- 2.39.5 From 558757318f2d0f70e8befdedebb9e4ceb7374fac Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 22:31:51 +0000 Subject: [PATCH 05/28] Added article and category models to articles app. --- articles/models.py | 66 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/articles/models.py b/articles/models.py index 6b20219..816ea8b 100644 --- a/articles/models.py +++ b/articles/models.py @@ -1 +1,65 @@ -# Create your models here. +from datetime import datetime + +from django.db import models +from django.urls import reverse_lazy +from django.utils.text import slugify +from markdownx.models import MarkdownxField +from markdownx.utils import markdownify +from tagulous.models import TagField + + +class Category(models.Model): + title = models.CharField(max_length=40, unique=True) + slug = models.SlugField(max_length=40, unique=True) + introduction = models.TextField(blank=True, null=True) + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + if not self.slug: + self.slug = slugify(self.title) + return super(Category, self).save() + + def get_absolute_url(self): + return reverse_lazy('articles:list_category', args=[self.slug]) + + def __str__(self): + return self.title + + class Meta: + ordering = ['title'] + + +class Article(models.Model): + title = models.CharField(max_length=200) + subtitle = models.CharField(max_length=200, blank=True, null=True) + slug = models.SlugField(max_length=200, unique=True) + category = models.ForeignKey(Category, on_delete=models.PROTECT, related_name='articles') + introduction = models.TextField(blank=True, null=True) + body = MarkdownxField() + summary = MarkdownxField(blank=True, null=True) + tags = TagField(force_lowercase=True, + get_absolute_url=lambda tag: reverse_lazy('articles:list_tag', kwargs={'tag': tag.slug})) + created = models.DateTimeField(auto_now_add=True) + updated = models.DateTimeField(auto_now_add=True) + is_published = models.BooleanField(default=False) + is_updated = models.BooleanField(default=False) + is_featured = models.BooleanField(default=False) + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + if not self.slug: + self.slug = slugify(self.title) + if self.is_updated: + self.updated = datetime.now() + self.is_updated = False + return super(Article, self).save() + + def get_absolute_url(self): + return reverse_lazy('articles:detail', args=[self.category.slug, self.slug]) + + def body_as_markdown(self): + return markdownify(self.body) + + def __str__(self): + return self.title + + class Meta: + ordering = ['-created'] -- 2.39.5 From 8ff2bda49e320028f2349571e62f7d8c9dfc6290 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 22:32:14 +0000 Subject: [PATCH 06/28] Linked articles and categories into site admin. --- articles/admin.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/articles/admin.py b/articles/admin.py index 846f6b4..ddbbde0 100644 --- a/articles/admin.py +++ b/articles/admin.py @@ -1 +1,22 @@ -# Register your models here. +import tagulous.admin +from django.contrib import admin +from markdownx.admin import MarkdownxModelAdmin + +from .models import Article, Category + + +@admin.register(Category) +class CategoryAdmin(admin.ModelAdmin): + list_display = ['title', 'slug'] + prepopulated_fields = {'slug': ('title',)} + + +class ArticleAdmin(MarkdownxModelAdmin): + list_display = ['title', 'subtitle', 'category', 'tags', 'created', 'updated', 'is_published', 'is_featured'] + list_filter = ['is_published', 'is_featured', 'category', 'tags'] + ordering = ['is_published', '-created'] + prepopulated_fields = {'slug': ('title',)} + list_display_links = ['title', 'subtitle'] + + +tagulous.admin.register(Article, ArticleAdmin) -- 2.39.5 From 89ae91ac35646c380b9ce99824de4ac296af4f05 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 22:37:27 +0000 Subject: [PATCH 07/28] Corrected db storage of blank TextFields. --- articles/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/articles/models.py b/articles/models.py index 816ea8b..e044ff6 100644 --- a/articles/models.py +++ b/articles/models.py @@ -11,7 +11,7 @@ from tagulous.models import TagField class Category(models.Model): title = models.CharField(max_length=40, unique=True) slug = models.SlugField(max_length=40, unique=True) - introduction = models.TextField(blank=True, null=True) + introduction = models.TextField(blank=True) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if not self.slug: @@ -33,9 +33,9 @@ class Article(models.Model): subtitle = models.CharField(max_length=200, blank=True, null=True) slug = models.SlugField(max_length=200, unique=True) category = models.ForeignKey(Category, on_delete=models.PROTECT, related_name='articles') - introduction = models.TextField(blank=True, null=True) + introduction = models.TextField(blank=True) body = MarkdownxField() - summary = MarkdownxField(blank=True, null=True) + summary = MarkdownxField(blank=True) tags = TagField(force_lowercase=True, get_absolute_url=lambda tag: reverse_lazy('articles:list_tag', kwargs={'tag': tag.slug})) created = models.DateTimeField(auto_now_add=True) -- 2.39.5 From 2722fbfcd7bd0568441e07059c3e8907130a8fa6 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 22:38:59 +0000 Subject: [PATCH 08/28] Added article, category and taglist views. --- articles/views.py | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/articles/views.py b/articles/views.py index 60f00ef..a988861 100644 --- a/articles/views.py +++ b/articles/views.py @@ -1 +1,42 @@ -# Create your views here. +from django.shortcuts import get_object_or_404 +from django.views.generic import DetailView, ListView + +from .models import Article, Category + + +class ArticleList(ListView): + category = None + tag = None + paginate_by = 9 + + def get_queryset(self): + category_slug = self.kwargs.get('category', None) + self.tag = self.kwargs.get('tag', None) + if category_slug: + self.category = get_object_or_404(Category, slug=category_slug) + return Article.objects.filter(category=self.category).filter(is_published=True) + elif self.tag: + return Article.objects.filter(tags=self.tag).filter(is_published=True) + else: + return Article.objects.filter(is_published=True) + + def get_context_data(self, *, object_list=None, **kwargs): + context = super(ArticleList, self).get_context_data(**kwargs) + context['category'] = self.category + context['tag'] = self.tag + return context + + +class ArticleDetail(DetailView): + model = Article + + +class CategoryList(ListView): + model = Category + + +class TagList(ListView): + template_name = 'articles/tag_list.html' + + def get_queryset(self): + return Article.tags.tag_model.objects.all() -- 2.39.5 From c397468cc384fc55b12fcba5b9b65b8c391580da Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 22:46:31 +0000 Subject: [PATCH 09/28] Added urls for articles app. --- articles/urls.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 articles/urls.py diff --git a/articles/urls.py b/articles/urls.py new file mode 100644 index 0000000..e279bf8 --- /dev/null +++ b/articles/urls.py @@ -0,0 +1,14 @@ +from django.urls import path + +from . import views + +app_name = 'articles' + +urlpatterns = [ + path('', views.ArticleList.as_view(), name='list'), + path('categories/', views.CategoryList.as_view(), name='categories'), + path('categories//', views.ArticleList.as_view(), name='list_category'), + path('tags/', views.TagList.as_view(), name='tags'), + path('tags//', views.ArticleList.as_view(), name='list_tag'), + path('//', views.ArticleDetail.as_view(), name='detail'), +] -- 2.39.5 From 835de189528d8766fda925f437f95cfe74dbf7c9 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 22:49:48 +0000 Subject: [PATCH 10/28] Linked articles app and markdownx into project urls. --- config/urls.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/urls.py b/config/urls.py index 10a0e72..9f79c2d 100644 --- a/config/urls.py +++ b/config/urls.py @@ -15,8 +15,10 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path +from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), + path('articles/', include('articles.urls', namespace='articles')), + path('markdownx/', include('markdownx.urls')), ] -- 2.39.5 From 0a3ef1070f8ba3a9783a2c8914e655e0780d1584 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 23:09:43 +0000 Subject: [PATCH 11/28] Created initial templates for articles app with basic layout and no styling. --- .../templates/articles/article_detail.html | 33 +++++++++++ articles/templates/articles/article_list.html | 58 +++++++++++++++++++ .../templates/articles/category_list.html | 21 +++++++ articles/templates/articles/tag_list.html | 17 ++++++ 4 files changed, 129 insertions(+) create mode 100644 articles/templates/articles/article_detail.html create mode 100644 articles/templates/articles/article_list.html create mode 100644 articles/templates/articles/category_list.html create mode 100644 articles/templates/articles/tag_list.html diff --git a/articles/templates/articles/article_detail.html b/articles/templates/articles/article_detail.html new file mode 100644 index 0000000..a885c41 --- /dev/null +++ b/articles/templates/articles/article_detail.html @@ -0,0 +1,33 @@ +{% extends 'base.html' %} + +{% block content %} +
+

{{ article.title }}

+ {% if article.subtitle %} +
{{ article.subtitle }}
+ {% endif %} +

Added on the {{ article.created|date:'jS \o\f F, Y' }} + {% if article.created.date != article.updated.date %} + and updated on the {{ article.updated|date:'jS \o\f F, Y' }} + {% endif %} +

+
+ {% if article.introduction %} +

{{ article.introduction|linebreaksbr }}

+
+ {% endif %} +
+ {{ article.body_as_markdown|safe }} +
+
+
+ Posted in the {{ article.category }} category + + {% for tag in article.tags.all %} + {{ tag }}{% if tag != article.tags.last %},{% endif %} + {% endfor %} + +
+
+{% endblock %} \ No newline at end of file diff --git a/articles/templates/articles/article_list.html b/articles/templates/articles/article_list.html new file mode 100644 index 0000000..01727cb --- /dev/null +++ b/articles/templates/articles/article_list.html @@ -0,0 +1,58 @@ +{% extends 'base.html' %} + +{% block content %} +
+ {% if object_list %} + {% if category %} +

{{ category }} Articles

+ {% if category.introduction %} +
+

{{ category.introduction }}

+
+ {% endif %} + {% elif tag %} +

Articles for tag: {{ tag }}

+ {% else %} +

Recent Articles

+ {% endif %} +
+
+ {% for article in object_list %} +
+ {{ article.title }} + {% if article.subtitle %} +
{{ article.subtitle }}
+ {% endif %} + {% if article.introduction %} +
+

{{ article.introduction|linebreaksbr }}

+ {% endif %} +
+
+ + {{ article.category }} + • {{ article.created|date:'jS \o\f F, Y' }} + + + {% for tag in article.tags.all %} + {{ tag }}{% if tag != article.tags.last %}, + {% endif %} + {% endfor %} + +
+
+ {% endfor %} +
+ {% else %} +
+

Oops!

+
It looks like there are no articles for that {% if category %}category{% elif tag %} tag{% endif %} + yet.
+

(sorry not sorry)

+
+ {% endif %} + {% if page_obj.has_previous or page_obj.has_next %} + {% include 'pagination.html' %} + {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/articles/templates/articles/category_list.html b/articles/templates/articles/category_list.html new file mode 100644 index 0000000..5df0b7e --- /dev/null +++ b/articles/templates/articles/category_list.html @@ -0,0 +1,21 @@ +{% extends 'base.html' %} + +{% block content %} +
+ {% if object_list %} +

Category List

+
+
    + {% for category in object_list %} +
  • + {{ category.title }} + {% if category.introduction %} +
    +

    {{ category.introduction }}

    + {% endif %} +
  • + {% endfor %} +
+ {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/articles/templates/articles/tag_list.html b/articles/templates/articles/tag_list.html new file mode 100644 index 0000000..8f75d8a --- /dev/null +++ b/articles/templates/articles/tag_list.html @@ -0,0 +1,17 @@ +{% extends 'base.html' %} + +{% block content %} +
+ {% if object_list %} +

List of Tags

+
+
    + {% for tag in object_list %} +
  • + {{ tag }} +
  • + {% endfor %} +
+ {% endif %} +
+{% endblock %} \ No newline at end of file -- 2.39.5 From 6aec5a5659785d160160b0f14b0d29d254861537 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 23:31:05 +0000 Subject: [PATCH 12/28] Migrated articles app. --- articles/migrations/0001_initial.py | 72 +++++++++++++++++++++++++++++ config/settings.py | 3 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 articles/migrations/0001_initial.py diff --git a/articles/migrations/0001_initial.py b/articles/migrations/0001_initial.py new file mode 100644 index 0000000..c08bf10 --- /dev/null +++ b/articles/migrations/0001_initial.py @@ -0,0 +1,72 @@ +# Generated by Django 5.1.6 on 2025-02-16 23:10 + +import django.db.models.deletion +import markdownx.models +import tagulous.models.fields +import tagulous.models.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=40, unique=True)), + ('slug', models.SlugField(max_length=40, unique=True)), + ('introduction', models.TextField(blank=True)), + ], + options={ + 'ordering': ['title'], + }, + ), + migrations.CreateModel( + name='Tagulous_Article_tags', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True)), + ('slug', models.SlugField()), + ('count', + models.IntegerField(default=0, help_text='Internal counter of how many times this tag is in use')), + ('protected', + models.BooleanField(default=False, help_text='Will not be deleted when the count reaches 0')), + ], + options={ + 'ordering': ('name',), + 'abstract': False, + 'unique_together': {('slug',)}, + }, + bases=(tagulous.models.models.BaseTagModel, models.Model), + ), + migrations.CreateModel( + name='Article', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('subtitle', models.CharField(blank=True, max_length=200, null=True)), + ('slug', models.SlugField(max_length=200, unique=True)), + ('introduction', models.TextField(blank=True)), + ('body', markdownx.models.MarkdownxField()), + ('summary', markdownx.models.MarkdownxField(blank=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now_add=True)), + ('is_published', models.BooleanField(default=False)), + ('is_updated', models.BooleanField(default=False)), + ('is_featured', models.BooleanField(default=False)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='articles', + to='articles.category')), + ('tags', tagulous.models.fields.TagField(_set_tag_meta=True, force_lowercase=True, + help_text='Enter a comma-separated tag string', + to='articles.tagulous_article_tags')), + ], + options={ + 'ordering': ['-created'], + }, + ), + ] diff --git a/config/settings.py b/config/settings.py index 0851db1..3bcb6e0 100644 --- a/config/settings.py +++ b/config/settings.py @@ -39,7 +39,8 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'articles.apps.ArticlesConfig' + 'articles.apps.ArticlesConfig', + 'markdownx', ] MIDDLEWARE = [ -- 2.39.5 From d665992b225fec40ee800aa117432d1cb1a81304 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 23:33:27 +0000 Subject: [PATCH 13/28] Created core app for homepage and generic templates. --- config/settings.py | 1 + core/__init__.py | 0 core/admin.py | 1 + core/apps.py | 6 ++++++ core/migrations/__init__.py | 0 core/models.py | 1 + core/tests.py | 1 + core/views.py | 1 + 8 files changed, 11 insertions(+) create mode 100644 core/__init__.py create mode 100644 core/admin.py create mode 100644 core/apps.py create mode 100644 core/migrations/__init__.py create mode 100644 core/models.py create mode 100644 core/tests.py create mode 100644 core/views.py diff --git a/config/settings.py b/config/settings.py index 3bcb6e0..ef235cc 100644 --- a/config/settings.py +++ b/config/settings.py @@ -39,6 +39,7 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'core.apps.CoreConfig', 'articles.apps.ArticlesConfig', 'markdownx', ] diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/admin.py b/core/admin.py new file mode 100644 index 0000000..846f6b4 --- /dev/null +++ b/core/admin.py @@ -0,0 +1 @@ +# Register your models here. diff --git a/core/apps.py b/core/apps.py new file mode 100644 index 0000000..8115ae6 --- /dev/null +++ b/core/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'core' diff --git a/core/migrations/__init__.py b/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/models.py b/core/models.py new file mode 100644 index 0000000..6b20219 --- /dev/null +++ b/core/models.py @@ -0,0 +1 @@ +# Create your models here. diff --git a/core/tests.py b/core/tests.py new file mode 100644 index 0000000..a39b155 --- /dev/null +++ b/core/tests.py @@ -0,0 +1 @@ +# Create your tests here. diff --git a/core/views.py b/core/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/core/views.py @@ -0,0 +1 @@ +# Create your views here. -- 2.39.5 From c6b834e5b7fb29920d370d95222eec0909be505e Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 23:39:00 +0000 Subject: [PATCH 14/28] Added initial homepage view. --- core/views.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/core/views.py b/core/views.py index 60f00ef..d59612b 100644 --- a/core/views.py +++ b/core/views.py @@ -1 +1,14 @@ -# Create your views here. +from django.views.generic import TemplateView + +from articles.models import Article + + +class Homepage(TemplateView): + template_name = 'core/homepage.html' + + def get_context_data(self, **kwargs): + context = super(Homepage, self).get_context_data() + context['featured_articles'] = Article.objects.filter(is_featured=True) + return context + +# TODO: place a random website link on the homepage -- 2.39.5 From 7b8a57fc926075055218e278ba2a9a4f2984e9d1 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 23:47:37 +0000 Subject: [PATCH 15/28] Added homepage template. --- core/templates/core/homepage.html | 47 +++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 core/templates/core/homepage.html diff --git a/core/templates/core/homepage.html b/core/templates/core/homepage.html new file mode 100644 index 0000000..5bedf29 --- /dev/null +++ b/core/templates/core/homepage.html @@ -0,0 +1,47 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+

Oh my word, this is basic!

+

I got bored and decided to develop this site from scratch, again. See the About This Website page if you're at all interested in + what I'm doing and why. The short version is that I'll be developing features for the website as I + create content for it. In other words, it's going to take a long time before it's pretty.

+

Longer term this site will present my own photographs and photography tuition services along with + articles on photography, philosophy, politics, health & mental health, and whatever else I feel + like.

+
+
+ {% if featured_articles %} +

Featured Articles

+
+
+ {% for article in featured_articles %} +
+ {{ article.title }} + {% if article.subtitle %} +
{{ article.subtitle }}
+ {% endif %} + {% if article.introduction %} +
+

{{ article.introduction|linebreaksbr }}

+ {% endif %} +
+
+ + {{ article.category }} • {{ article.created|date:'jS \o\f F, Y' }} + + + {% for tag in article.tags.all %} + {{ tag }}{% if tag != article.tags.last %}, + {% endif %} + {% endfor %} + +
+
+ {% endfor %} +
+ {% endif %} +
+{% endblock %} \ No newline at end of file -- 2.39.5 From 5ccb53fb5dcd96431aa514d4144b66a6aaf38ed3 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 23:48:56 +0000 Subject: [PATCH 16/28] Added base template. --- core/templates/base.html | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/templates/base.html diff --git a/core/templates/base.html b/core/templates/base.html new file mode 100644 index 0000000..56d5e56 --- /dev/null +++ b/core/templates/base.html @@ -0,0 +1,18 @@ +{% load static %} + + + + + + + {% block title %}Drulum{% endblock %} + {# #} + + +{% block page %} + {% block header %}{% include 'header.html' %}{% endblock %} + {% block content %}{% endblock %} + {% block footer %}{% include 'footer.html' %}{% endblock %} +{% endblock %} + + \ No newline at end of file -- 2.39.5 From bd4c7cab70fb039ff8f8da28f0d0c8840de7ec9b Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Sun, 16 Feb 2025 23:49:43 +0000 Subject: [PATCH 17/28] Added footer template. --- core/templates/footer.html | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 core/templates/footer.html diff --git a/core/templates/footer.html b/core/templates/footer.html new file mode 100644 index 0000000..06e921d --- /dev/null +++ b/core/templates/footer.html @@ -0,0 +1,3 @@ +
+

All content © Calum Andrew Morrell

+
\ No newline at end of file -- 2.39.5 From 017aad2edeb5c7bb8189b61fda93e86e99af7a34 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Mon, 17 Feb 2025 00:01:27 +0000 Subject: [PATCH 18/28] Added header template. --- core/templates/header.html | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 core/templates/header.html diff --git a/core/templates/header.html b/core/templates/header.html new file mode 100644 index 0000000..7419b0a --- /dev/null +++ b/core/templates/header.html @@ -0,0 +1,19 @@ +
+ + +
\ No newline at end of file -- 2.39.5 From c30f39ca2460369dc66d22dd86bfcd7dccb2365a Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Mon, 17 Feb 2025 00:02:51 +0000 Subject: [PATCH 19/28] Added pagination template. --- core/templates/pagination.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 core/templates/pagination.html diff --git a/core/templates/pagination.html b/core/templates/pagination.html new file mode 100644 index 0000000..55c735f --- /dev/null +++ b/core/templates/pagination.html @@ -0,0 +1,16 @@ + \ No newline at end of file -- 2.39.5 From 3f4188e4b7f4e473c36c5ca4796cdfc52b1fdd60 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Mon, 17 Feb 2025 00:05:23 +0000 Subject: [PATCH 20/28] Added urls for the core app. --- core/urls.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 core/urls.py diff --git a/core/urls.py b/core/urls.py new file mode 100644 index 0000000..138c8b8 --- /dev/null +++ b/core/urls.py @@ -0,0 +1,13 @@ +from django.contrib.flatpages.views import flatpage +from django.urls import path + +from . import views + +app_name = 'core' + +urlpatterns = [ + path('', views.Homepage.as_view(), name='homepage'), + path('about/me/', flatpage, {'url': '/about/me/'}, name='about-me'), + path('about/website/', flatpage, {'url': '/about/website/'}, name='about-website'), + path('about/contact/', flatpage, {'url': '/about/contact/'}, name='contact-me'), +] -- 2.39.5 From 1d7d4ff4420c4ec2f4f3fabaaf2e46c9ca66f5b8 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Mon, 17 Feb 2025 00:06:43 +0000 Subject: [PATCH 21/28] Added default flatpage template. --- core/templates/flatpages/default.html | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 core/templates/flatpages/default.html diff --git a/core/templates/flatpages/default.html b/core/templates/flatpages/default.html new file mode 100644 index 0000000..ef8426e --- /dev/null +++ b/core/templates/flatpages/default.html @@ -0,0 +1,8 @@ +{% extends 'base.html' %} + +{% block content %} +

{{ flatpage.title }}

+
+ {{ flatpage.content }} +
+{% endblock %} \ No newline at end of file -- 2.39.5 From 22148202b2a846721d3b6f40ee9ffeb1ff24956f Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Mon, 17 Feb 2025 00:08:02 +0000 Subject: [PATCH 22/28] Linked core urls into project. --- config/urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/config/urls.py b/config/urls.py index 9f79c2d..1f70949 100644 --- a/config/urls.py +++ b/config/urls.py @@ -19,6 +19,7 @@ from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), + path('', include('core.urls', namespace='core')), path('articles/', include('articles.urls', namespace='articles')), path('markdownx/', include('markdownx.urls')), ] -- 2.39.5 From c135a81f973ac5f1b523f40f5d7875a3bd5d5f36 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Mon, 17 Feb 2025 00:13:07 +0000 Subject: [PATCH 23/28] Added sites & flatpages into project settings and uncommented entries for tagulous & markdownx. --- config/settings.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/config/settings.py b/config/settings.py index ef235cc..be82070 100644 --- a/config/settings.py +++ b/config/settings.py @@ -39,6 +39,8 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django.contrib.sites', + 'django.contrib.flatpages', 'core.apps.CoreConfig', 'articles.apps.ArticlesConfig', 'markdownx', @@ -125,6 +127,9 @@ STATIC_ROOT = BASE_DIR / 'static/' MEDIA_URL = 'media/' MEDIA_ROOT = BASE_DIR / 'media/' +# Site id to allow flatpages to function correctly +SITE_ID = 1 + # User model and authentication # LOGIN_REDIRECT_URL = '' # LOGOUT_REDIRECT_URL = '' @@ -139,17 +144,17 @@ MEDIA_ROOT = BASE_DIR / 'media/' # EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') # Tagulous config -# SERIALIZATION_MODULES = { -# 'xml': 'tagulous.serializers.xml_serializer', -# 'json': 'tagulous.serializers.json', -# 'python': 'tagulous.serializers.python', -# 'yaml': 'tagulous.serializers.pyyaml', -# } +SERIALIZATION_MODULES = { + 'xml': 'tagulous.serializers.xml_serializer', + 'json': 'tagulous.serializers.json', + 'python': 'tagulous.serializers.python', + 'yaml': 'tagulous.serializers.pyyaml', +} # Markdownx config -# MARKDOWNX_MARKDOWN_EXTENSIONS = [ -# 'markdown.extensions.extra', -# ] +MARKDOWNX_MARKDOWN_EXTENSIONS = [ + 'markdown.extensions.extra', +] # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field -- 2.39.5 From f7f45160d6e2c2a4de0398764f7e4354b2c0d00a Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Mon, 17 Feb 2025 00:15:24 +0000 Subject: [PATCH 24/28] Added a fixme to articles model. --- articles/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/articles/models.py b/articles/models.py index e044ff6..618c3b2 100644 --- a/articles/models.py +++ b/articles/models.py @@ -36,6 +36,7 @@ class Article(models.Model): introduction = models.TextField(blank=True) body = MarkdownxField() summary = MarkdownxField(blank=True) + # FIXME: tags with spaces do not match any articles when clicked on. tags = TagField(force_lowercase=True, get_absolute_url=lambda tag: reverse_lazy('articles:list_tag', kwargs={'tag': tag.slug})) created = models.DateTimeField(auto_now_add=True) -- 2.39.5 From 4c9b8d6c386b10f848de93dc553fe0214881f6c9 Mon Sep 17 00:00:00 2001 From: Calum Andrew Morrell Date: Mon, 5 May 2025 19:27:43 +0100 Subject: [PATCH 25/28] Updated template files for the core website to include tailwind styling. --- core/static/css/base.css | 1840 +++++++++++++++++++++++++++++ core/templates/base.html | 4 +- core/templates/core/homepage.html | 53 +- core/templates/footer.html | 4 +- core/templates/header.html | 41 +- core/templates/pagination.html | 5 +- 6 files changed, 1900 insertions(+), 47 deletions(-) create mode 100644 core/static/css/base.css diff --git a/core/static/css/base.css b/core/static/css/base.css new file mode 100644 index 0000000..4a7fbda --- /dev/null +++ b/core/static/css/base.css @@ -0,0 +1,1840 @@ +/* +! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.prose { + color: var(--tw-prose-body); + max-width: 65ch; +} + +.prose :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; + margin-bottom: 1.25em; +} + +.prose :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-lead); + font-size: 1.25em; + line-height: 1.6; + margin-top: 1.2em; + margin-bottom: 1.2em; +} + +.prose :where(a):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-links); + text-decoration: underline; + font-weight: 500; +} + +.prose :where(strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-bold); + font-weight: 600; +} + +.prose :where(a strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(blockquote strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(thead th strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: decimal; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-left: 1.625em; +} + +.prose :where(ol[type="A"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-alpha; +} + +.prose :where(ol[type="a"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-alpha; +} + +.prose :where(ol[type="A" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-alpha; +} + +.prose :where(ol[type="a" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-alpha; +} + +.prose :where(ol[type="I"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-roman; +} + +.prose :where(ol[type="i"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-roman; +} + +.prose :where(ol[type="I" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-roman; +} + +.prose :where(ol[type="i" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-roman; +} + +.prose :where(ol[type="1"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: decimal; +} + +.prose :where(ul):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: disc; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-left: 1.625em; +} + +.prose :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *))::marker { + font-weight: 400; + color: var(--tw-prose-counters); +} + +.prose :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *))::marker { + color: var(--tw-prose-bullets); +} + +.prose :where(dt):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.25em; +} + +.prose :where(hr):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-color: var(--tw-prose-hr); + border-top-width: 1px; + margin-top: 3em; + margin-bottom: 3em; +} + +.prose :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 500; + font-style: italic; + color: var(--tw-prose-quotes); + border-left-width: 0.25rem; + border-left-color: var(--tw-prose-quote-borders); + quotes: "\201C""\201D""\2018""\2019"; + margin-top: 1.6em; + margin-bottom: 1.6em; + padding-left: 1em; +} + +.prose :where(blockquote p:first-of-type):not(:where([class~="not-prose"],[class~="not-prose"] *))::before { + content: open-quote; +} + +.prose :where(blockquote p:last-of-type):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { + content: close-quote; +} + +.prose :where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 800; + font-size: 2.25em; + margin-top: 0; + margin-bottom: 0.8888889em; + line-height: 1.1111111; +} + +.prose :where(h1 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 900; + color: inherit; +} + +.prose :where(h2):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 700; + font-size: 1.5em; + margin-top: 2em; + margin-bottom: 1em; + line-height: 1.3333333; +} + +.prose :where(h2 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 800; + color: inherit; +} + +.prose :where(h3):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + font-size: 1.25em; + margin-top: 1.6em; + margin-bottom: 0.6em; + line-height: 1.6; +} + +.prose :where(h3 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 700; + color: inherit; +} + +.prose :where(h4):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.5em; + margin-bottom: 0.5em; + line-height: 1.5; +} + +.prose :where(h4 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 700; + color: inherit; +} + +.prose :where(img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(picture):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + display: block; + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(kbd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 500; + font-family: inherit; + color: var(--tw-prose-kbd); + box-shadow: 0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%), 0 3px 0 rgb(var(--tw-prose-kbd-shadows) / 10%); + font-size: 0.875em; + border-radius: 0.3125rem; + padding-top: 0.1875em; + padding-right: 0.375em; + padding-bottom: 0.1875em; + padding-left: 0.375em; +} + +.prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-code); + font-weight: 600; + font-size: 0.875em; +} + +.prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::before { + content: "`"; +} + +.prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { + content: "`"; +} + +.prose :where(a code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(h1 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(h2 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; + font-size: 0.875em; +} + +.prose :where(h3 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; + font-size: 0.9em; +} + +.prose :where(h4 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(blockquote code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(thead th code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(pre):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-pre-code); + background-color: var(--tw-prose-pre-bg); + overflow-x: auto; + font-weight: 400; + font-size: 0.875em; + line-height: 1.7142857; + margin-top: 1.7142857em; + margin-bottom: 1.7142857em; + border-radius: 0.375rem; + padding-top: 0.8571429em; + padding-right: 1.1428571em; + padding-bottom: 0.8571429em; + padding-left: 1.1428571em; +} + +.prose :where(pre code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + background-color: transparent; + border-width: 0; + border-radius: 0; + padding: 0; + font-weight: inherit; + color: inherit; + font-size: inherit; + font-family: inherit; + line-height: inherit; +} + +.prose :where(pre code):not(:where([class~="not-prose"],[class~="not-prose"] *))::before { + content: none; +} + +.prose :where(pre code):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { + content: none; +} + +.prose :where(table):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + width: 100%; + table-layout: auto; + text-align: left; + margin-top: 2em; + margin-bottom: 2em; + font-size: 0.875em; + line-height: 1.7142857; +} + +.prose :where(thead):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-th-borders); +} + +.prose :where(thead th):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + vertical-align: bottom; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} + +.prose :where(tbody tr):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-td-borders); +} + +.prose :where(tbody tr:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-bottom-width: 0; +} + +.prose :where(tbody td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + vertical-align: baseline; +} + +.prose :where(tfoot):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-top-width: 1px; + border-top-color: var(--tw-prose-th-borders); +} + +.prose :where(tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + vertical-align: top; +} + +.prose :where(figure > *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; +} + +.prose :where(figcaption):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-captions); + font-size: 0.875em; + line-height: 1.4285714; + margin-top: 0.8571429em; +} + +.prose { + --tw-prose-body: #374151; + --tw-prose-headings: #111827; + --tw-prose-lead: #4b5563; + --tw-prose-links: #111827; + --tw-prose-bold: #111827; + --tw-prose-counters: #6b7280; + --tw-prose-bullets: #d1d5db; + --tw-prose-hr: #e5e7eb; + --tw-prose-quotes: #111827; + --tw-prose-quote-borders: #e5e7eb; + --tw-prose-captions: #6b7280; + --tw-prose-kbd: #111827; + --tw-prose-kbd-shadows: 17 24 39; + --tw-prose-code: #111827; + --tw-prose-pre-code: #e5e7eb; + --tw-prose-pre-bg: #1f2937; + --tw-prose-th-borders: #d1d5db; + --tw-prose-td-borders: #e5e7eb; + --tw-prose-invert-body: #d1d5db; + --tw-prose-invert-headings: #fff; + --tw-prose-invert-lead: #9ca3af; + --tw-prose-invert-links: #fff; + --tw-prose-invert-bold: #fff; + --tw-prose-invert-counters: #9ca3af; + --tw-prose-invert-bullets: #4b5563; + --tw-prose-invert-hr: #374151; + --tw-prose-invert-quotes: #f3f4f6; + --tw-prose-invert-quote-borders: #374151; + --tw-prose-invert-captions: #9ca3af; + --tw-prose-invert-kbd: #fff; + --tw-prose-invert-kbd-shadows: 255 255 255; + --tw-prose-invert-code: #fff; + --tw-prose-invert-pre-code: #d1d5db; + --tw-prose-invert-pre-bg: rgb(0 0 0 / 50%); + --tw-prose-invert-th-borders: #4b5563; + --tw-prose-invert-td-borders: #374151; + font-size: 1rem; + line-height: 1.75; +} + +.prose :where(picture > img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; +} + +.prose :where(video):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +.prose :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0.375em; +} + +.prose :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0.375em; +} + +.prose :where(.prose > ul > li p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} + +.prose :where(.prose > ul > li > *:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; +} + +.prose :where(.prose > ul > li > *:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.25em; +} + +.prose :where(.prose > ol > li > *:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; +} + +.prose :where(.prose > ol > li > *:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.25em; +} + +.prose :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} + +.prose :where(dl):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; + margin-bottom: 1.25em; +} + +.prose :where(dd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.5em; + padding-left: 1.625em; +} + +.prose :where(hr + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(h2 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(h3 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(h4 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(thead th:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0; +} + +.prose :where(thead th:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-right: 0; +} + +.prose :where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-top: 0.5714286em; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} + +.prose :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0; +} + +.prose :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-right: 0; +} + +.prose :where(figure):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(.prose > :first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(.prose > :last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 0; +} + +.static { + position: static; +} + +.row-span-3 { + grid-row: span 3 / span 3; +} + +.m-8 { + margin: 2rem; +} + +.mx-8 { + margin-left: 2rem; + margin-right: 2rem; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} + +.my-8 { + margin-top: 2rem; + margin-bottom: 2rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.block { + display: block; +} + +.flex { + display: flex; +} + +.grid { + display: grid; +} + +.max-w-2xl { + max-width: 42rem; +} + +.max-w-3xl { + max-width: 48rem; +} + +.max-w-5xl { + max-width: 64rem; +} + +.max-w-none { + max-width: none; +} + +.break-inside-avoid-column { + -moz-column-break-inside: avoid; + break-inside: avoid-column; +} + +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.content-center { + align-content: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-12 { + gap: 3rem; +} + +.gap-x-2 { + -moz-column-gap: 0.5rem; + column-gap: 0.5rem; +} + +.gap-x-4 { + -moz-column-gap: 1rem; + column-gap: 1rem; +} + +.bg-blue-900 { + --tw-bg-opacity: 1; + background-color: rgb(30 58 138 / var(--tw-bg-opacity)); +} + +.p-8 { + padding: 2rem; +} + +.px-8 { + padding-left: 2rem; + padding-right: 2rem; +} + +.pb-8 { + padding-bottom: 2rem; +} + +.text-center { + text-align: center; +} + +.text-justify { + text-align: justify; +} + +.font-mono { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +.font-sans { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +.font-serif { + font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + +.text-6xl { + font-size: 3.75rem; + line-height: 1; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.font-bold { + font-weight: 700; +} + +.font-extrabold { + font-weight: 800; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.uppercase { + text-transform: uppercase; +} + +.italic { + font-style: italic; +} + +.text-blue-600 { + --tw-text-opacity: 1; + color: rgb(37 99 235 / var(--tw-text-opacity)); +} + +.text-cyan-400 { + --tw-text-opacity: 1; + color: rgb(34 211 238 / var(--tw-text-opacity)); +} + +.text-neutral-400 { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +@media (min-width: 768px) { + .md\:prose-lg { + font-size: 1.125rem; + line-height: 1.7777778; + } + + .md\:prose-lg :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; + } + + .md\:prose-lg :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 1.2222222em; + line-height: 1.4545455; + margin-top: 1.0909091em; + margin-bottom: 1.0909091em; + } + + .md\:prose-lg :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.6666667em; + margin-bottom: 1.6666667em; + padding-left: 1em; + } + + .md\:prose-lg :where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 2.6666667em; + margin-top: 0; + margin-bottom: 0.8333333em; + line-height: 1; + } + + .md\:prose-lg :where(h2):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 1.6666667em; + margin-top: 1.8666667em; + margin-bottom: 1.0666667em; + line-height: 1.3333333; + } + + .md\:prose-lg :where(h3):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 1.3333333em; + margin-top: 1.6666667em; + margin-bottom: 0.6666667em; + line-height: 1.5; + } + + .md\:prose-lg :where(h4):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.7777778em; + margin-bottom: 0.4444444em; + line-height: 1.5555556; + } + + .md\:prose-lg :where(img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; + } + + .md\:prose-lg :where(picture):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; + } + + .md\:prose-lg :where(picture > img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; + } + + .md\:prose-lg :where(video):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; + } + + .md\:prose-lg :where(kbd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.8888889em; + border-radius: 0.3125rem; + padding-top: 0.2222222em; + padding-right: 0.4444444em; + padding-bottom: 0.2222222em; + padding-left: 0.4444444em; + } + + .md\:prose-lg :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.8888889em; + } + + .md\:prose-lg :where(h2 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.8666667em; + } + + .md\:prose-lg :where(h3 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.875em; + } + + .md\:prose-lg :where(pre):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.8888889em; + line-height: 1.75; + margin-top: 2em; + margin-bottom: 2em; + border-radius: 0.375rem; + padding-top: 1em; + padding-right: 1.5em; + padding-bottom: 1em; + padding-left: 1.5em; + } + + .md\:prose-lg :where(ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; + padding-left: 1.5555556em; + } + + .md\:prose-lg :where(ul):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; + padding-left: 1.5555556em; + } + + .md\:prose-lg :where(li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.6666667em; + margin-bottom: 0.6666667em; + } + + .md\:prose-lg :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0.4444444em; + } + + .md\:prose-lg :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0.4444444em; + } + + .md\:prose-lg :where(.md\:prose-lg > ul > li p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.8888889em; + margin-bottom: 0.8888889em; + } + + .md\:prose-lg :where(.md\:prose-lg > ul > li > *:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.3333333em; + } + + .md\:prose-lg :where(.md\:prose-lg > ul > li > *:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.3333333em; + } + + .md\:prose-lg :where(.md\:prose-lg > ol > li > *:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.3333333em; + } + + .md\:prose-lg :where(.md\:prose-lg > ol > li > *:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.3333333em; + } + + .md\:prose-lg :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.8888889em; + margin-bottom: 0.8888889em; + } + + .md\:prose-lg :where(dl):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; + } + + .md\:prose-lg :where(dt):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.3333333em; + } + + .md\:prose-lg :where(dd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.6666667em; + padding-left: 1.5555556em; + } + + .md\:prose-lg :where(hr):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 3.1111111em; + margin-bottom: 3.1111111em; + } + + .md\:prose-lg :where(hr + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .md\:prose-lg :where(h2 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .md\:prose-lg :where(h3 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .md\:prose-lg :where(h4 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .md\:prose-lg :where(table):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.8888889em; + line-height: 1.5; + } + + .md\:prose-lg :where(thead th):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-right: 0.75em; + padding-bottom: 0.75em; + padding-left: 0.75em; + } + + .md\:prose-lg :where(thead th:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0; + } + + .md\:prose-lg :where(thead th:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-right: 0; + } + + .md\:prose-lg :where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-top: 0.75em; + padding-right: 0.75em; + padding-bottom: 0.75em; + padding-left: 0.75em; + } + + .md\:prose-lg :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0; + } + + .md\:prose-lg :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-right: 0; + } + + .md\:prose-lg :where(figure):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; + } + + .md\:prose-lg :where(figure > *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; + } + + .md\:prose-lg :where(figcaption):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.8888889em; + line-height: 1.5; + margin-top: 1em; + } + + .md\:prose-lg :where(.md\:prose-lg > :first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .md\:prose-lg :where(.md\:prose-lg > :last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 0; + } +} + +@media (min-width: 1024px) { + .lg\:prose-xl { + font-size: 1.25rem; + line-height: 1.8; + } + + .lg\:prose-xl :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.2em; + margin-bottom: 1.2em; + } + + .lg\:prose-xl :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 1.2em; + line-height: 1.5; + margin-top: 1em; + margin-bottom: 1em; + } + + .lg\:prose-xl :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.6em; + margin-bottom: 1.6em; + padding-left: 1.0666667em; + } + + .lg\:prose-xl :where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 2.8em; + margin-top: 0; + margin-bottom: 0.8571429em; + line-height: 1; + } + + .lg\:prose-xl :where(h2):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 1.8em; + margin-top: 1.5555556em; + margin-bottom: 0.8888889em; + line-height: 1.1111111; + } + + .lg\:prose-xl :where(h3):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 1.5em; + margin-top: 1.6em; + margin-bottom: 0.6666667em; + line-height: 1.3333333; + } + + .lg\:prose-xl :where(h4):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.8em; + margin-bottom: 0.6em; + line-height: 1.6; + } + + .lg\:prose-xl :where(img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; + } + + .lg\:prose-xl :where(picture):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; + } + + .lg\:prose-xl :where(picture > img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; + } + + .lg\:prose-xl :where(video):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; + } + + .lg\:prose-xl :where(kbd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.9em; + border-radius: 0.3125rem; + padding-top: 0.25em; + padding-right: 0.4em; + padding-bottom: 0.25em; + padding-left: 0.4em; + } + + .lg\:prose-xl :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.9em; + } + + .lg\:prose-xl :where(h2 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.8611111em; + } + + .lg\:prose-xl :where(h3 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.9em; + } + + .lg\:prose-xl :where(pre):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.9em; + line-height: 1.7777778; + margin-top: 2em; + margin-bottom: 2em; + border-radius: 0.5rem; + padding-top: 1.1111111em; + padding-right: 1.3333333em; + padding-bottom: 1.1111111em; + padding-left: 1.3333333em; + } + + .lg\:prose-xl :where(ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.2em; + margin-bottom: 1.2em; + padding-left: 1.6em; + } + + .lg\:prose-xl :where(ul):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.2em; + margin-bottom: 1.2em; + padding-left: 1.6em; + } + + .lg\:prose-xl :where(li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.6em; + margin-bottom: 0.6em; + } + + .lg\:prose-xl :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0.4em; + } + + .lg\:prose-xl :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0.4em; + } + + .lg\:prose-xl :where(.lg\:prose-xl > ul > li p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.8em; + margin-bottom: 0.8em; + } + + .lg\:prose-xl :where(.lg\:prose-xl > ul > li > *:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.2em; + } + + .lg\:prose-xl :where(.lg\:prose-xl > ul > li > *:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.2em; + } + + .lg\:prose-xl :where(.lg\:prose-xl > ol > li > *:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.2em; + } + + .lg\:prose-xl :where(.lg\:prose-xl > ol > li > *:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.2em; + } + + .lg\:prose-xl :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.8em; + margin-bottom: 0.8em; + } + + .lg\:prose-xl :where(dl):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.2em; + margin-bottom: 1.2em; + } + + .lg\:prose-xl :where(dt):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.2em; + } + + .lg\:prose-xl :where(dd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.6em; + padding-left: 1.6em; + } + + .lg\:prose-xl :where(hr):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2.8em; + margin-bottom: 2.8em; + } + + .lg\:prose-xl :where(hr + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .lg\:prose-xl :where(h2 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .lg\:prose-xl :where(h3 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .lg\:prose-xl :where(h4 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .lg\:prose-xl :where(table):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.9em; + line-height: 1.5555556; + } + + .lg\:prose-xl :where(thead th):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-right: 0.6666667em; + padding-bottom: 0.8888889em; + padding-left: 0.6666667em; + } + + .lg\:prose-xl :where(thead th:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0; + } + + .lg\:prose-xl :where(thead th:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-right: 0; + } + + .lg\:prose-xl :where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-top: 0.8888889em; + padding-right: 0.6666667em; + padding-bottom: 0.8888889em; + padding-left: 0.6666667em; + } + + .lg\:prose-xl :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-left: 0; + } + + .lg\:prose-xl :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-right: 0; + } + + .lg\:prose-xl :where(figure):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; + } + + .lg\:prose-xl :where(figure > *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; + } + + .lg\:prose-xl :where(figcaption):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-size: 0.9em; + line-height: 1.5555556; + margin-top: 1em; + } + + .lg\:prose-xl :where(.lg\:prose-xl > :first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + } + + .lg\:prose-xl :where(.lg\:prose-xl > :last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 0; + } +} + +.hover\:text-red-400:hover { + --tw-text-opacity: 1; + color: rgb(248 113 113 / var(--tw-text-opacity)); +} + +.prose-img\:rounded-2xl :is(:where(img):not(:where([class~="not-prose"],[class~="not-prose"] *))) { + border-radius: 1rem; +} + +@media (min-width: 640px) { + .sm\:w-1\/2 { + width: 50%; + } + + .sm\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } +} + +@media (min-width: 768px) { + .md\:row-span-2 { + grid-row: span 2 / span 2; + } + + .md\:row-span-3 { + grid-row: span 3 / span 3; + } + + .md\:grid { + display: grid; + } + + .md\:grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:grid-cols-\[400px_1fr\] { + grid-template-columns: 400px 1fr; + } + + .md\:items-end { + align-items: flex-end; + } + + .md\:justify-end { + justify-content: flex-end; + } + + .md\:gap-x-4 { + -moz-column-gap: 1rem; + column-gap: 1rem; + } +} + +@media (min-width: 1024px) { + .lg\:w-1\/3 { + width: 33.333333%; + } + + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .lg\:text-6xl { + font-size: 3.75rem; + line-height: 1; + } +} + +@media (min-width: 1280px) { + .xl\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } +} \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 56d5e56..1c10008 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -6,9 +6,9 @@ {% block title %}Drulum{% endblock %} - {# #} + - + {% block page %} {% block header %}{% include 'header.html' %}{% endblock %} {% block content %}{% endblock %} diff --git a/core/templates/core/homepage.html b/core/templates/core/homepage.html index 5bedf29..4774505 100644 --- a/core/templates/core/homepage.html +++ b/core/templates/core/homepage.html @@ -2,42 +2,41 @@ {% block content %}
-
-

Oh my word, this is basic!

-

I got bored and decided to develop this site from scratch, again. See the About This Website page if you're at all interested in - what I'm doing and why. The short version is that I'll be developing features for the website as I - create content for it. In other words, it's going to take a long time before it's pretty.

-

Longer term this site will present my own photographs and photography tuition services along with - articles on photography, philosophy, politics, health & mental health, and whatever else I feel - like.

+
+

Oh my word, this is basic!

+

I got bored and decided to develop this site from scratch, again. See the About This Website page if + you're at all interested in what I'm doing and why. The short version is that I'll be developing + features for the website as I create content for it. In other words, it's going to take a long time + before it's pretty.

+

Longer term this site will present my own photographs and photography + tuition services along with articles on photography, philosophy, politics, health & mental health, and + whatever else I feel like.

-
+
{% if featured_articles %} -

Featured Articles

-
-
+

Featured Articles

+
+
{% for article in featured_articles %} -
- {{ article.title }} +
+ {{ article.title }} {% if article.subtitle %} -
{{ article.subtitle }}
+
{{ article.subtitle }}
{% endif %} {% if article.introduction %} -
-

{{ article.introduction|linebreaksbr }}

+
+

{{ article.introduction|linebreaksbr }}

{% endif %} -
-
+
+
- {{ article.category }} • {{ article.created|date:'jS \o\f F, Y' }} - - - {% for tag in article.tags.all %} - {{ tag }}{% if tag != article.tags.last %}, - {% endif %} - {% endfor %} + {{ article.category }} + • {{ article.created | date:'jS \o\f F, Y' }} +{# {% include 'articles/includes/display_tags.html' %}#}
{% endfor %} diff --git a/core/templates/footer.html b/core/templates/footer.html index 06e921d..ac40002 100644 --- a/core/templates/footer.html +++ b/core/templates/footer.html @@ -1,3 +1,3 @@ -