fucked things up with egit gui but now its cool.
39
.dockerignore
Normal file
@ -0,0 +1,39 @@
|
||||
# Django project
|
||||
/media/
|
||||
/static/
|
||||
*.sqlite3
|
||||
|
||||
# Python and others
|
||||
__pycache__
|
||||
*.pyc
|
||||
.DS_Store
|
||||
*.swp
|
||||
/venv/
|
||||
/tmp/
|
||||
/.vagrant/
|
||||
/Vagrantfile.local
|
||||
node_modules/
|
||||
/npm-debug.log
|
||||
/.idea/
|
||||
.vscode
|
||||
coverage
|
||||
.python-version
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
18
.project
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>jacksbastards</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.python.pydev.PyDevBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.python.pydev.pythonNature</nature>
|
||||
<nature>org.python.pydev.django.djangoNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
166
.pydevproject
Normal file
@ -0,0 +1,166 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?eclipse-pydev version="1.0"?><pydev_project>
|
||||
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
||||
<path>/${PROJECT_DIR_NAME}</path>
|
||||
</pydev_pathproperty>
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
||||
<pydev_variables_property name="org.python.pydev.PROJECT_VARIABLE_SUBSTITUTION">
|
||||
<key>env_var:PS1</key>
|
||||
<value>[📦 $FLATPAK_ID \W]\$ </value>
|
||||
<key>env_var:INVOCATION_ID</key>
|
||||
<value>ec08f22997324076b6f99c10ec5d1f64</value>
|
||||
<key>PY</key>
|
||||
<value>314</value>
|
||||
<key>env_var:SWT_GTK4</key>
|
||||
<value>0</value>
|
||||
<key>env_var:HOSTNAME</key>
|
||||
<value>studio-desktop</value>
|
||||
<key>env_var:LANG</key>
|
||||
<value>en_GB.UTF-8</value>
|
||||
<key>env_var:GDM_LANG</key>
|
||||
<value>en_GB.UTF-8</value>
|
||||
<key>env_var:XDG_CONFIG_DIRS</key>
|
||||
<value>/app/etc/xdg:/etc/xdg</value>
|
||||
<key>env_var:GI_TYPELIB_PATH</key>
|
||||
<value>/app/lib/girepository-1.0</value>
|
||||
<key>env_var:PWD</key>
|
||||
<value>/home/maarten</value>
|
||||
<key>env_var:XDG_MENU_PREFIX</key>
|
||||
<value>gnome-</value>
|
||||
<key>env_var:DBUS_SESSION_BUS_ADDRESS</key>
|
||||
<value>unix:path=/run/flatpak/bus</value>
|
||||
<key>env_var:HOME</key>
|
||||
<value>/home/maarten</value>
|
||||
<key>env_var:WAYLAND_DISPLAY</key>
|
||||
<value>wayland-0</value>
|
||||
<key>env_var:XDG_DATA_DIRS</key>
|
||||
<value>/app/share:/usr/share:/usr/share/runtime/share:/run/host/user-share:/run/host/share</value>
|
||||
<key>env_var:GDK_CORE_DEVICE_EVENTS</key>
|
||||
<value>1</value>
|
||||
<key>env_var:MANAGERPID</key>
|
||||
<value>1277</value>
|
||||
<key>env_var:XDG_DATA_HOME</key>
|
||||
<value>/home/maarten/.var/app/org.eclipse.Java/data</value>
|
||||
<key>env_var:XDG_CURRENT_DESKTOP</key>
|
||||
<value>GNOME</value>
|
||||
<key>env_var:FLATPAK_ID</key>
|
||||
<value>org.eclipse.Java</value>
|
||||
<key>env_var:SYSTEMD_EXEC_PID</key>
|
||||
<value>4524</value>
|
||||
<key>env_var:LOGNAME</key>
|
||||
<value>maarten</value>
|
||||
<key>env_var:PATH</key>
|
||||
<value>/app/bin:/usr/bin</value>
|
||||
<key>env_var:XDG_SESSION_CLASS</key>
|
||||
<value>user</value>
|
||||
<key>env_var:GPG_TTY</key>
|
||||
<value>not a tty</value>
|
||||
<key>env_var:GDK_GL</key>
|
||||
<value>gles</value>
|
||||
<key>env_var:XDG_SESSION_TYPE</key>
|
||||
<value>wayland</value>
|
||||
<key>env_var:LD_LIBRARY_PATH</key>
|
||||
<value/>
|
||||
<key>env_var:ALSA_CONFIG_DIR</key>
|
||||
<value>/usr/share/alsa</value>
|
||||
<key>env_var:KDEDIRS</key>
|
||||
<value>/usr</value>
|
||||
<key>env_var:JOURNAL_STREAM</key>
|
||||
<value>9:35499</value>
|
||||
<key>env_var:QT_IM_MODULES</key>
|
||||
<value>wayland;ibus</value>
|
||||
<key>env_var:XMODIFIERS</key>
|
||||
<value>@im=ibus</value>
|
||||
<key>env_var:DEBUGINFOD_IMA_CERT_PATH</key>
|
||||
<value>/etc/keys/ima:</value>
|
||||
<key>DJANGO_MANAGE_LOCATION</key>
|
||||
<value>manage.py</value>
|
||||
<key>env_var:MAIL</key>
|
||||
<value>/var/spool/mail/maarten</value>
|
||||
<key>env_var:HISTCONTROL</key>
|
||||
<value>ignoredups</value>
|
||||
<key>env_var:LESSOPEN</key>
|
||||
<value>||/usr/bin/lesspipe.sh %s</value>
|
||||
<key>env_var:MEMORY_PRESSURE_WRITE</key>
|
||||
<value>c29tZSAyMDAwMDAgMjAwMDAwMAA=</value>
|
||||
<key>env_var:GDMSESSION</key>
|
||||
<value>gnome</value>
|
||||
<key>env_var:MANAGERPIDFDID</key>
|
||||
<value>1278</value>
|
||||
<key>env_var:GJS_DEBUG_TOPICS</key>
|
||||
<value>JS ERROR;JS LOG</value>
|
||||
<key>env_var:XDG_RUNTIME_DIR</key>
|
||||
<value>/run/user/1000</value>
|
||||
<key>env_var:XDG_STATE_HOME</key>
|
||||
<value>/home/maarten/.var/app/org.eclipse.Java/.local/state</value>
|
||||
<key>env_var:LIBOVERLAY_SCROLLBAR</key>
|
||||
<value>0</value>
|
||||
<key>env_var:container</key>
|
||||
<value>flatpak</value>
|
||||
<key>env_var:SSH_ASKPASS</key>
|
||||
<value>/usr/bin/ksshaskpass</value>
|
||||
<key>env_var:SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS</key>
|
||||
<value>0</value>
|
||||
<key>env_var:XDG_SESSION_DESKTOP</key>
|
||||
<value>gnome</value>
|
||||
<key>env_var:USER</key>
|
||||
<value>maarten</value>
|
||||
<key>env_var:ALSA_CONFIG_PATH</key>
|
||||
<value>/usr/share/alsa/alsa-flatpak.conf</value>
|
||||
<key>env_var:DEBUGINFOD_URLS</key>
|
||||
<value>ima:enforcing https://debuginfod.fedoraproject.org/ ima:ignore </value>
|
||||
<key>env_var:SSH_AUTH_SOCK</key>
|
||||
<value>/run/flatpak/ssh-auth</value>
|
||||
<key>env_var:EDITOR</key>
|
||||
<value>/usr/bin/nano</value>
|
||||
<key>env_var:OXYGEN_DISABLE_INNER_SHADOWS_HACK</key>
|
||||
<value>1</value>
|
||||
<key>env_var:XAUTHORITY</key>
|
||||
<value>/run/user/1000/.mutter-Xwaylandauth.IT3MH3</value>
|
||||
<key>env_var:MEMORY_PRESSURE_WATCH</key>
|
||||
<value>/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/session.slice/org.gnome.Shell@wayland.service/memory.pressure</value>
|
||||
<key>env_var:GIO_LAUNCHED_DESKTOP_FILE_PID</key>
|
||||
<value>21757</value>
|
||||
<key>env_var:GNOME_SETUP_DISPLAY</key>
|
||||
<value>:1</value>
|
||||
<key>env_var:PIPENV_VENV_IN_PROJECT</key>
|
||||
<value>1</value>
|
||||
<key>env_var:GST_PLUGIN_SYSTEM_PATH</key>
|
||||
<value>/app/lib/gstreamer-1.0:/usr/lib/extensions/gstreamer-1.0:/usr/lib/x86_64-linux-gnu/codecs-extra/lib/gstreamer-1.0:/usr/lib/x86_64-linux-gnu/gstreamer-1.0</value>
|
||||
<key>env_var:XDG_CACHE_HOME</key>
|
||||
<value>/home/maarten/.var/app/org.eclipse.Java/cache</value>
|
||||
<key>env_var:PYTHONUSERBASE</key>
|
||||
<value>/var/data/python</value>
|
||||
<key>env_var:USERNAME</key>
|
||||
<value>maarten</value>
|
||||
<key>env_var:QT_IM_MODULE</key>
|
||||
<value>ibus</value>
|
||||
<key>env_var:SHELL</key>
|
||||
<value>/bin/sh</value>
|
||||
<key>env_var:SHLVL</key>
|
||||
<value>0</value>
|
||||
<key>env_var:FLATPAK_SANDBOX_DIR</key>
|
||||
<value>/home/maarten/.var/app/org.eclipse.Java/sandbox</value>
|
||||
<key>DJANGO_SETTINGS_MODULE</key>
|
||||
<value>jacksbastards.settings</value>
|
||||
<key>env_var:STEAM_FRAME_FORCE_CLOSE</key>
|
||||
<value>1</value>
|
||||
<key>env_var:AT_SPI_BUS_ADDRESS</key>
|
||||
<value>unix:path=/run/flatpak/at-spi-bus</value>
|
||||
<key>env_var:HISTSIZE</key>
|
||||
<value>1000</value>
|
||||
<key>env_var:GJS_DEBUG_OUTPUT</key>
|
||||
<value>stderr</value>
|
||||
<key>env_var:GIO_LAUNCHED_DESKTOP_FILE</key>
|
||||
<value>/var/lib/flatpak/exports/share/applications/org.eclipse.Java.desktop</value>
|
||||
<key>env_var:XDG_CONFIG_HOME</key>
|
||||
<value>/home/maarten/.var/app/org.eclipse.Java/config</value>
|
||||
<key>env_var:__EGL_EXTERNAL_PLATFORM_CONFIG_DIRS</key>
|
||||
<value>/etc/egl/egl_external_platform.d:/usr/lib/x86_64-linux-gnu/GL/egl/egl_external_platform.d:/usr/share/egl/egl_external_platform.d</value>
|
||||
<key>env_var:MOZ_GMP_PATH</key>
|
||||
<value>/usr/lib64/mozilla/plugins/gmp-gmpopenh264/system-installed</value>
|
||||
<key>env_var:DESKTOP_SESSION</key>
|
||||
<value>gnome</value>
|
||||
</pydev_variables_property>
|
||||
</pydev_project>
|
||||
60
Dockerfile
Normal file
@ -0,0 +1,60 @@
|
||||
# Use an official Python runtime based on Debian 12 "bookworm" as a parent image.
|
||||
FROM python:3.12-slim-bookworm
|
||||
|
||||
# Add user that will be used in the container.
|
||||
RUN useradd wagtail
|
||||
|
||||
# Port used by this container to serve HTTP.
|
||||
EXPOSE 8000
|
||||
|
||||
# Set environment variables.
|
||||
# 1. Force Python stdout and stderr streams to be unbuffered.
|
||||
# 2. Set PORT variable that is used by Gunicorn. This should match "EXPOSE"
|
||||
# command.
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PORT=8000
|
||||
|
||||
# Install system packages required by Wagtail and Django.
|
||||
RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \
|
||||
build-essential \
|
||||
libpq-dev \
|
||||
libmariadb-dev \
|
||||
libjpeg62-turbo-dev \
|
||||
zlib1g-dev \
|
||||
libwebp-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install the application server.
|
||||
RUN pip install "gunicorn==20.0.4"
|
||||
|
||||
# Install the project requirements.
|
||||
COPY requirements.txt /
|
||||
RUN pip install -r /requirements.txt
|
||||
|
||||
# Use /app folder as a directory where the source code is stored.
|
||||
WORKDIR /app
|
||||
|
||||
# Set this directory to be owned by the "wagtail" user. This Wagtail project
|
||||
# uses SQLite, the folder needs to be owned by the user that
|
||||
# will be writing to the database file.
|
||||
RUN chown wagtail:wagtail /app
|
||||
|
||||
# Copy the source code of the project into the container.
|
||||
COPY --chown=wagtail:wagtail . .
|
||||
|
||||
# Use user "wagtail" to run the build commands below and the server itself.
|
||||
USER wagtail
|
||||
|
||||
# Collect static files.
|
||||
RUN python manage.py collectstatic --noinput --clear
|
||||
|
||||
# Runtime command that executes when "docker run" is called, it does the
|
||||
# following:
|
||||
# 1. Migrate the database.
|
||||
# 2. Start the application server.
|
||||
# WARNING:
|
||||
# Migrating database at the same time as starting the server IS NOT THE BEST
|
||||
# PRACTICE. The database should be migrated manually or using the release
|
||||
# phase facilities of your hosting platform. This is used only so the
|
||||
# Wagtail instance can be started with a simple "docker run" command.
|
||||
CMD set -xe; python manage.py migrate --noinput; gunicorn jacksbastards.wsgi:application
|
||||
0
base/__init__.py
Normal file
BIN
base/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
base/__pycache__/admin.cpython-314.pyc
Normal file
BIN
base/__pycache__/apps.cpython-314.pyc
Normal file
BIN
base/__pycache__/models.cpython-314.pyc
Normal file
3
base/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
base/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BaseConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'base'
|
||||
26
base/migrations/0001_initial.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 20:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='NavigationSettings',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('linkedin_url', models.URLField(blank=True, verbose_name='LinkedIn URL')),
|
||||
('github_url', models.URLField(blank=True, verbose_name='GitHub URL')),
|
||||
('mastodon_url', models.URLField(blank=True, verbose_name='Mastodon URL')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,45 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 21:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='navigationsettings',
|
||||
name='github_url',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='navigationsettings',
|
||||
name='linkedin_url',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='navigationsettings',
|
||||
name='mastodon_url',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='navigationsettings',
|
||||
name='facebook_url',
|
||||
field=models.URLField(blank=True, verbose_name='Facebook URL'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='navigationsettings',
|
||||
name='gitea_url',
|
||||
field=models.URLField(blank=True, verbose_name='Gitea URL'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='navigationsettings',
|
||||
name='instagram_url',
|
||||
field=models.URLField(blank=True, verbose_name='Instagram URL'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='navigationsettings',
|
||||
name='youtube_url',
|
||||
field=models.URLField(blank=True, verbose_name='Youtube URL'),
|
||||
),
|
||||
]
|
||||
0
base/migrations/__init__.py
Normal file
BIN
base/migrations/__pycache__/0001_initial.cpython-314.pyc
Normal file
BIN
base/migrations/__pycache__/__init__.cpython-314.pyc
Normal file
29
base/models.py
Normal file
@ -0,0 +1,29 @@
|
||||
from django.db import models
|
||||
from wagtail.admin.panels import (
|
||||
FieldPanel,
|
||||
MultiFieldPanel,
|
||||
)
|
||||
from wagtail.contrib.settings.models import (
|
||||
BaseGenericSetting,
|
||||
register_setting,
|
||||
)
|
||||
|
||||
@register_setting
|
||||
class NavigationSettings(BaseGenericSetting):
|
||||
youtube_url = models.URLField(verbose_name="Youtube URL", blank=True)
|
||||
facebook_url = models.URLField(verbose_name="Facebook URL", blank=True)
|
||||
instagram_url = models.URLField(verbose_name="Instagram URL", blank=True)
|
||||
gitea_url = models.URLField(verbose_name="Gitea URL", blank=True)
|
||||
|
||||
|
||||
panels = [
|
||||
MultiFieldPanel(
|
||||
[
|
||||
FieldPanel("youtube_url"),
|
||||
FieldPanel("facebook_url"),
|
||||
FieldPanel("instagram_url"),
|
||||
FieldPanel("gitea_url"),
|
||||
],
|
||||
"Social settings",
|
||||
)
|
||||
]
|
||||
3
base/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
base/views.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
blog/__init__.py
Normal file
BIN
blog/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
blog/__pycache__/admin.cpython-314.pyc
Normal file
BIN
blog/__pycache__/apps.cpython-314.pyc
Normal file
BIN
blog/__pycache__/models.cpython-314.pyc
Normal file
3
blog/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
blog/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BlogConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'blog'
|
||||
28
blog/migrations/0001_initial.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 16:42
|
||||
|
||||
import django.db.models.deletion
|
||||
import wagtail.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('wagtailcore', '0096_referenceindex_referenceindex_source_object_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BlogIndexPage',
|
||||
fields=[
|
||||
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
|
||||
('intro', wagtail.fields.RichTextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('wagtailcore.page',),
|
||||
),
|
||||
]
|
||||
29
blog/migrations/0002_blogpage.py
Normal file
@ -0,0 +1,29 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 16:59
|
||||
|
||||
import django.db.models.deletion
|
||||
import wagtail.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0001_initial'),
|
||||
('wagtailcore', '0096_referenceindex_referenceindex_source_object_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BlogPage',
|
||||
fields=[
|
||||
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
|
||||
('date', models.DateField(verbose_name='Post date')),
|
||||
('intro', models.CharField(max_length=250)),
|
||||
('body', wagtail.fields.RichTextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('wagtailcore.page',),
|
||||
),
|
||||
]
|
||||
30
blog/migrations/0003_blogpagegalleryimage.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 17:37
|
||||
|
||||
import django.db.models.deletion
|
||||
import modelcluster.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0002_blogpage'),
|
||||
('wagtailimages', '0027_image_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BlogPageGalleryImage',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
|
||||
('caption', models.CharField(blank=True, max_length=250)),
|
||||
('image', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='wagtailimages.image')),
|
||||
('page', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='gallery_images', to='blog.blogpage')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['sort_order'],
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
26
blog/migrations/0004_author.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 18:54
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0003_blogpagegalleryimage'),
|
||||
('wagtailimages', '0027_image_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Author',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('author_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Authors',
|
||||
},
|
||||
),
|
||||
]
|
||||
19
blog/migrations/0005_blogpage_authors.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 19:04
|
||||
|
||||
import modelcluster.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0004_author'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='blogpage',
|
||||
name='authors',
|
||||
field=modelcluster.fields.ParentalManyToManyField(blank=True, to='blog.author'),
|
||||
),
|
||||
]
|
||||
33
blog/migrations/0006_blogpagetag_blogpage_tags.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 19:26
|
||||
|
||||
import django.db.models.deletion
|
||||
import modelcluster.contrib.taggit
|
||||
import modelcluster.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0005_blogpage_authors'),
|
||||
('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BlogPageTag',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('content_object', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='tagged_items', to='blog.blogpage')),
|
||||
('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_items', to='taggit.tag')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='blogpage',
|
||||
name='tags',
|
||||
field=modelcluster.contrib.taggit.ClusterTaggableManager(blank=True, help_text='A comma-separated list of tags.', through='blog.BlogPageTag', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
]
|
||||
25
blog/migrations/0007_blogtagindexpage.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 19:36
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0006_blogpagetag_blogpage_tags'),
|
||||
('wagtailcore', '0096_referenceindex_referenceindex_source_object_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BlogTagIndexPage',
|
||||
fields=[
|
||||
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('wagtailcore.page',),
|
||||
),
|
||||
]
|
||||
0
blog/migrations/__init__.py
Normal file
BIN
blog/migrations/__pycache__/0001_initial.cpython-314.pyc
Normal file
BIN
blog/migrations/__pycache__/0002_blogpage.cpython-314.pyc
Normal file
BIN
blog/migrations/__pycache__/0004_author.cpython-314.pyc
Normal file
BIN
blog/migrations/__pycache__/__init__.cpython-314.pyc
Normal file
86
blog/models.py
Normal file
@ -0,0 +1,86 @@
|
||||
from django import forms
|
||||
from django.db import models
|
||||
from wagtail.models import Page, Orderable
|
||||
from wagtail.fields import RichTextField
|
||||
from wagtail.snippets.models import register_snippet
|
||||
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
|
||||
from modelcluster.fields import ParentalKey, ParentalManyToManyField
|
||||
from modelcluster.contrib.taggit import ClusterTaggableManager
|
||||
from taggit.models import TaggedItemBase
|
||||
|
||||
|
||||
|
||||
class BlogIndexPage(Page):
|
||||
intro = RichTextField(blank=True)
|
||||
# add the get_context method:
|
||||
def get_context(self, request):
|
||||
# Update context to include only published posts, ordered by reverse-chron
|
||||
context = super().get_context(request)
|
||||
blogpages = self.get_children().live().order_by('-first_published_at')
|
||||
context['blogpages'] = blogpages
|
||||
return context
|
||||
|
||||
class BlogPageTag(TaggedItemBase):
|
||||
content_object = ParentalKey(
|
||||
'BlogPage',
|
||||
related_name='tagged_items',
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
class BlogPage(Page):
|
||||
date = models.DateField("Post date")
|
||||
intro = models.CharField(max_length=250)
|
||||
body = RichTextField(blank=True)
|
||||
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
|
||||
|
||||
def main_image(self):
|
||||
gallery_item = self.gallery_images.first()
|
||||
if gallery_item:
|
||||
return gallery_item.image
|
||||
else:
|
||||
return None
|
||||
|
||||
authors = ParentalManyToManyField('blog.Author', blank=True)
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
MultiFieldPanel(["date", FieldPanel("authors", widget=forms.CheckboxSelectMultiple), "tags",
|
||||
], heading="Blog information"),
|
||||
"intro", "body", "gallery_images"
|
||||
]
|
||||
class BlogPageGalleryImage(Orderable):
|
||||
page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='gallery_images')
|
||||
image = models.ForeignKey(
|
||||
'wagtailimages.Image', on_delete=models.CASCADE, related_name='+'
|
||||
)
|
||||
caption = models.CharField(blank=True, max_length=250)
|
||||
|
||||
panels = ["image", "caption"]
|
||||
|
||||
class BlogTagIndexPage(Page):
|
||||
|
||||
def get_context(self, request):
|
||||
|
||||
# Filter by tag
|
||||
tag = request.GET.get('tag')
|
||||
blogpages = BlogPage.objects.filter(tags__name=tag)
|
||||
|
||||
# Update template context
|
||||
context = super().get_context(request)
|
||||
context['blogpages'] = blogpages
|
||||
return context
|
||||
|
||||
@register_snippet
|
||||
class Author(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
author_image = models.ForeignKey(
|
||||
'wagtailimages.Image', null=True, blank=True,
|
||||
on_delete=models.SET_NULL, related_name='+'
|
||||
)
|
||||
|
||||
panels = ["name", "author_image"]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'Authors'
|
||||
28
blog/templates/blog/blog_index_page.html
Normal file
@ -0,0 +1,28 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load wagtailcore_tags %}
|
||||
|
||||
<!-- Load wagtailimages_tags: -->
|
||||
{% load wagtailcore_tags wagtailimages_tags %}
|
||||
|
||||
{% block body_class %}template-blogindexpage{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ page.title }}</h1>
|
||||
|
||||
<div class="intro">{{ page.intro|richtext }}</div>
|
||||
|
||||
{% for post in blogpages %}
|
||||
{% with post=post.specific %}
|
||||
<h2><a href="{% pageurl post %}">{{ post.title }}</a></h2>
|
||||
|
||||
{% with post.main_image as main_image %}
|
||||
{% if main_image %}{% image main_image fill-160x100 %}{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<p>{{ post.intro }}</p>
|
||||
{{ post.body|richtext }}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
47
blog/templates/blog/blog_page.html
Normal file
@ -0,0 +1,47 @@
|
||||
{% load wagtailcore_tags wagtailimages_tags %}
|
||||
|
||||
{% block body_class %}template-blogpage{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ page.title }}</h1>
|
||||
<p class="meta">{{ page.date }}</p>
|
||||
|
||||
{% with authors=page.authors.all %}
|
||||
{% if authors %}
|
||||
<h3>Posted by:</h3>
|
||||
<ul>
|
||||
{% for author in authors %}
|
||||
<li style="display: inline">
|
||||
{% image author.author_image fill-40x60 style="vertical-align: middle" %}
|
||||
{{ author.name }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="intro">{{ page.intro }}</div>
|
||||
|
||||
{{ page.body|richtext }}
|
||||
|
||||
{% for item in page.gallery_images.all %}
|
||||
<div style="float: inline-start; margin: 10px">
|
||||
{% image item.image fill-320x240 %}
|
||||
<p>{{ item.caption }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<p><a href="{{ page.get_parent.url }}">Return to blog</a></p>
|
||||
|
||||
{% with tags=page.tags.all %}
|
||||
{% if tags %}
|
||||
<div class="tags">
|
||||
<h3>Tags</h3>
|
||||
{% for tag in tags %}
|
||||
<a href="{% slugurl 'tags' %}?tag={{ tag }}"><button type="button">{{ tag }}</button></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
{% endblock %}
|
||||
21
blog/templates/blog/blog_tag_index_page.html
Normal file
@ -0,0 +1,21 @@
|
||||
{% extends "base.html" %}
|
||||
{% load wagtailcore_tags %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if request.GET.tag %}
|
||||
<h4>Showing pages tagged "{{ request.GET.tag }}"</h4>
|
||||
{% endif %}
|
||||
|
||||
{% for blogpage in blogpages %}
|
||||
|
||||
<p>
|
||||
<strong><a href="{% pageurl blogpage %}">{{ blogpage.title }}</a></strong><br />
|
||||
<small>Revised: {{ blogpage.latest_revision_created_at }}</small><br />
|
||||
</p>
|
||||
|
||||
{% empty %}
|
||||
No pages found with that tag.
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
3
blog/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
blog/views.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
BIN
db.sqlite3
Normal file
0
home/__init__.py
Normal file
BIN
home/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
home/__pycache__/apps.cpython-314.pyc
Normal file
BIN
home/__pycache__/models.cpython-314.pyc
Normal file
6
home/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class HomeConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "home"
|
||||
31
home/migrations/0001_initial.py
Normal file
@ -0,0 +1,31 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("wagtailcore", "0040_page_draft_title"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="HomePage",
|
||||
fields=[
|
||||
(
|
||||
"page_ptr",
|
||||
models.OneToOneField(
|
||||
on_delete=models.CASCADE,
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="wagtailcore.Page",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=("wagtailcore.page",),
|
||||
),
|
||||
]
|
||||
66
home/migrations/0002_create_homepage.py
Normal file
@ -0,0 +1,66 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_homepage(apps, schema_editor):
|
||||
# Get models
|
||||
ContentType = apps.get_model("contenttypes.ContentType")
|
||||
Page = apps.get_model("wagtailcore.Page")
|
||||
Site = apps.get_model("wagtailcore.Site")
|
||||
HomePage = apps.get_model("home.HomePage")
|
||||
|
||||
# Delete the default homepage (of type Page) as created by wagtailcore.0002_initial_data,
|
||||
# if it exists
|
||||
page_content_type = ContentType.objects.get(
|
||||
model="page", app_label="wagtailcore"
|
||||
)
|
||||
Page.objects.filter(
|
||||
content_type=page_content_type, slug="home", depth=2
|
||||
).delete()
|
||||
|
||||
# Create content type for homepage model
|
||||
homepage_content_type, __ = ContentType.objects.get_or_create(
|
||||
model="homepage", app_label="home"
|
||||
)
|
||||
|
||||
# Create a new homepage
|
||||
homepage = HomePage.objects.create(
|
||||
title="Home",
|
||||
draft_title="Home",
|
||||
slug="home",
|
||||
content_type=homepage_content_type,
|
||||
path="00010001",
|
||||
depth=2,
|
||||
numchild=0,
|
||||
url_path="/home/",
|
||||
)
|
||||
|
||||
# Create a site with the new homepage set as the root
|
||||
Site.objects.create(hostname="localhost", root_page=homepage, is_default_site=True)
|
||||
|
||||
|
||||
def remove_homepage(apps, schema_editor):
|
||||
# Get models
|
||||
ContentType = apps.get_model("contenttypes.ContentType")
|
||||
HomePage = apps.get_model("home.HomePage")
|
||||
|
||||
# Delete the default homepage
|
||||
# Page and Site objects CASCADE
|
||||
HomePage.objects.filter(slug="home", depth=2).delete()
|
||||
|
||||
# Delete content type for homepage model
|
||||
ContentType.objects.filter(model="homepage", app_label="home").delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
run_before = [
|
||||
("wagtailcore", "0053_locale_model"),
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
("home", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_homepage, remove_homepage),
|
||||
]
|
||||
19
home/migrations/0003_homepage_body.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 16:25
|
||||
|
||||
import wagtail.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0002_create_homepage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='homepage',
|
||||
name='body',
|
||||
field=wagtail.fields.RichTextField(blank=True),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,36 @@
|
||||
# Generated by Django 5.2.9 on 2025-12-19 20:13
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0003_homepage_body'),
|
||||
('wagtailcore', '0096_referenceindex_referenceindex_source_object_and_more'),
|
||||
('wagtailimages', '0027_image_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='homepage',
|
||||
name='hero_cta',
|
||||
field=models.CharField(blank=True, help_text='Text to display on Call to Action', max_length=255, verbose_name='Hero CTA'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='homepage',
|
||||
name='hero_cta_link',
|
||||
field=models.ForeignKey(blank=True, help_text='Choose a page to link to for the Call to Action', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailcore.page', verbose_name='Hero CTA link'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='homepage',
|
||||
name='hero_text',
|
||||
field=models.CharField(blank=True, help_text='Write an introduction for the site', max_length=255),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='homepage',
|
||||
name='image',
|
||||
field=models.ForeignKey(blank=True, help_text='Homepage image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'),
|
||||
),
|
||||
]
|
||||
0
home/migrations/__init__.py
Normal file
BIN
home/migrations/__pycache__/0001_initial.cpython-314.pyc
Normal file
BIN
home/migrations/__pycache__/0002_create_homepage.cpython-314.pyc
Normal file
BIN
home/migrations/__pycache__/0003_homepage_body.cpython-314.pyc
Normal file
BIN
home/migrations/__pycache__/__init__.cpython-314.pyc
Normal file
50
home/models.py
Normal file
@ -0,0 +1,50 @@
|
||||
from django.db import models
|
||||
from wagtail.models import Page
|
||||
from wagtail.fields import RichTextField
|
||||
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
|
||||
|
||||
|
||||
class HomePage(Page):
|
||||
# add the Hero section of HomePage:
|
||||
image = models.ForeignKey(
|
||||
"wagtailimages.Image",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="+",
|
||||
help_text="Homepage image",
|
||||
)
|
||||
hero_text = models.CharField(
|
||||
blank=True,
|
||||
max_length=255, help_text="Write an introduction for the site"
|
||||
)
|
||||
hero_cta = models.CharField(
|
||||
blank=True,
|
||||
verbose_name="Hero CTA",
|
||||
max_length=255,
|
||||
help_text="Text to display on Call to Action",
|
||||
)
|
||||
hero_cta_link = models.ForeignKey(
|
||||
"wagtailcore.Page",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="+",
|
||||
verbose_name="Hero CTA link",
|
||||
help_text="Choose a page to link to for the Call to Action",
|
||||
)
|
||||
|
||||
body = RichTextField(blank=True)
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
MultiFieldPanel(
|
||||
[
|
||||
FieldPanel("image"),
|
||||
FieldPanel("hero_text"),
|
||||
FieldPanel("hero_cta"),
|
||||
FieldPanel("hero_cta_link"),
|
||||
],
|
||||
heading="Hero section",
|
||||
),
|
||||
FieldPanel('body'),
|
||||
]
|
||||
184
home/static/css/welcome_page.css
Normal file
@ -0,0 +1,184 @@
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 960px;
|
||||
min-height: 100vh;
|
||||
margin: 0 auto;
|
||||
padding: 0 15px;
|
||||
color: #231f20;
|
||||
font-family: 'Helvetica Neue', 'Segoe UI', Arial, sans-serif;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
color: #308282;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #ea1b10;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
p,
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 150px;
|
||||
margin-inline-end: 20px;
|
||||
}
|
||||
|
||||
.logo a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.figure-logo {
|
||||
max-width: 150px;
|
||||
max-height: 55.1px;
|
||||
}
|
||||
|
||||
.release-notes {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 40px 0;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.figure-space {
|
||||
max-width: 265px;
|
||||
}
|
||||
|
||||
@keyframes pos {
|
||||
0%, 100% {
|
||||
transform: rotate(-6deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(6deg);
|
||||
}
|
||||
}
|
||||
|
||||
.egg {
|
||||
fill: #43b1b0;
|
||||
animation: pos 3s ease infinite;
|
||||
transform: translateY(50px);
|
||||
transform-origin: 50% 80%;
|
||||
}
|
||||
|
||||
.main-text {
|
||||
max-width: 400px;
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
.main-text h1 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.main-text p {
|
||||
margin: 15px auto 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid #e6e6e6;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.option {
|
||||
display: block;
|
||||
padding: 10px 10px 10px 34px;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.option svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: gray;
|
||||
border: 1px solid #d9d9d9;
|
||||
padding: 5px;
|
||||
border-radius: 100%;
|
||||
top: 10px;
|
||||
inset-inline-start: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.option h2 {
|
||||
font-size: 19px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.option p {
|
||||
padding-top: 3px;
|
||||
color: #231f20;
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
body {
|
||||
max-width: 780px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.option {
|
||||
flex: 0 0 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 599px) {
|
||||
.main {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.figure-space {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: block;
|
||||
width: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.header-link {
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
19
home/templates/home/home_page.html
Normal file
@ -0,0 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
{% load wagtailcore_tags wagtailimages_tags %}
|
||||
|
||||
{% block body_class %}template-homepage{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<h1>{{ page.title }}</h1>
|
||||
{% image page.image fill-480x320 %}
|
||||
<p>{{ page.hero_text }}</p>
|
||||
{% if page.hero_cta_link %}
|
||||
<a href="{% pageurl page.hero_cta_link %}">
|
||||
{% firstof page.hero_cta page.hero_cta_link.title %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{{ page.body|richtext }}
|
||||
{% endblock content %}
|
||||
52
home/templates/home/welcome_page.html
Normal file
@ -0,0 +1,52 @@
|
||||
{% load i18n wagtailcore_tags %}
|
||||
|
||||
<header class="header">
|
||||
<div class="logo">
|
||||
<a href="https://wagtail.org/">
|
||||
<svg class="figure-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 342.5 126.2"><title>{% trans "Visit the Wagtail website" %}</title><path fill="#FFF" d="M84 1.9v5.7s-10.2-3.8-16.8 3.1c-4.8 5-5.2 10.6-3 18.1 21.6 0 25 12.1 25 12.1L87 27l6.8-8.3c0-9.8-8.1-16.3-9.8-16.8z"/><circle cx="85.9" cy="15.9" r="2.6"/><path d="M89.2 40.9s-3.3-16.6-24.9-12.1c-2.2-7.5-1.8-13 3-18.1C73.8 3.8 84 7.6 84 7.6V1.9C80.4.3 77 0 73.2 0 59.3 0 51.6 10.4 48.3 17.4L9.2 89.3l11-2.1-20.2 39 14.1-2.5L24.9 93c30.6 0 69.8-11 64.3-52.1z"/><path d="M102.4 27l-8.6-8.3L87 27z"/><path fill="#FFF" d="M30 84.1s1-.2 2.8-.6c1.8-.4 4.3-1 7.3-1.8 1.5-.4 3.1-.9 4.8-1.5 1.7-.6 3.5-1.2 5.2-2 1.8-.7 3.6-1.6 5.4-2.6 1.8-1 3.5-2.1 5.1-3.4.4-.3.8-.6 1.2-1l1.2-1c.7-.7 1.5-1.4 2.2-2.2.7-.7 1.3-1.5 1.9-2.3l.9-1.2.4-.6.4-.6c.2-.4.5-.8.7-1.2.2-.4.4-.8.7-1.2l.3-.6.3-.6c.2-.4.4-.8.5-1.2l.9-2.4c.2-.8.5-1.6.7-2.3.2-.7.3-1.5.5-2.1.1-.7.2-1.3.3-2 .1-.6.2-1.2.2-1.7.1-.5.1-1 .2-1.5.1-1.8.1-2.8.1-2.8l1.6.1s-.1 1.1-.2 2.9c-.1.5-.1 1-.2 1.5-.1.6-.1 1.2-.3 1.8-.1.6-.3 1.3-.4 2-.2.7-.4 1.4-.6 2.2-.2.8-.5 1.5-.8 2.4-.3.8-.6 1.6-1 2.5l-.6 1.2-.3.6-.3.6c-.2.4-.5.8-.7 1.3-.3.4-.5.8-.8 1.2-.1.2-.3.4-.4.6l-.4.6-.9 1.2c-.7.8-1.3 1.6-2.1 2.3-.7.8-1.5 1.4-2.3 2.2l-1.2 1c-.4.3-.8.6-1.3.9-1.7 1.2-3.5 2.3-5.3 3.3-1.8.9-3.7 1.8-5.5 2.5-1.8.7-3.6 1.3-5.3 1.8-1.7.5-3.3 1-4.9 1.3-3 .7-5.6 1.3-7.4 1.6-1.6.6-2.6.8-2.6.8z"/><g fill="#231F20"><path d="M127 83.9h-8.8l-12.6-36.4h7.9l9 27.5 9-27.5h7.9l9 27.5 9-27.5h7.9L153 83.9h-8.8L135.6 59 127 83.9zM200.1 83.9h-7V79c-3 3.6-7 5.4-12.1 5.4-3.8 0-6.9-1.1-9.4-3.2s-3.7-5-3.7-8.6c0-3.6 1.3-6.3 4-8 2.6-1.8 6.2-2.7 10.7-2.7h9.9v-1.4c0-4.8-2.7-7.3-8.1-7.3-3.4 0-6.9 1.2-10.5 3.7l-3.4-4.8c4.4-3.5 9.4-5.3 15.1-5.3 4.3 0 7.8 1.1 10.5 3.2 2.7 2.2 4.1 5.6 4.1 10.2v23.7zm-7.7-13.6v-3.1h-8.6c-5.5 0-8.3 1.7-8.3 5.2 0 1.8.7 3.1 2.1 4.1 1.4.9 3.3 1.4 5.7 1.4 2.4 0 4.6-.7 6.4-2.1 1.8-1.3 2.7-3.1 2.7-5.5zM241.7 47.5v31.7c0 6.4-1.7 11.3-5.2 14.5-3.5 3.2-8 4.8-13.4 4.8-5.5 0-10.4-1.7-14.8-5.1l3.6-5.8c3.6 2.7 7.1 4 10.8 4 3.6 0 6.5-.9 8.6-2.8 2.1-1.9 3.2-4.9 3.2-9v-4.7c-1.1 2.1-2.8 3.9-4.9 5.1-2.1 1.3-4.5 1.9-7.1 1.9-4.8 0-8.8-1.7-11.9-5.1-3.1-3.4-4.7-7.6-4.7-12.6s1.6-9.2 4.7-12.6c3.1-3.4 7.1-5.1 11.9-5.1 4.8 0 8.7 2 11.7 6v-5.4h7.5zm-28.4 16.8c0 3 .9 5.6 2.8 7.7 1.8 2.2 4.3 3.2 7.5 3.2 3.1 0 5.7-1 7.6-3.1 1.9-2.1 2.9-4.7 2.9-7.8 0-3.1-1-5.8-2.9-7.9-2-2.2-4.5-3.2-7.6-3.2-3.1 0-5.6 1.1-7.4 3.4-2 2.1-2.9 4.7-2.9 7.7zM260.9 53.6v18.5c0 1.7.5 3.1 1.4 4.1.9 1 2.2 1.5 3.8 1.5 1.6 0 3.2-.8 4.7-2.4l3.1 5.4c-2.7 2.4-5.7 3.6-8.9 3.6-3.3 0-6-1.1-8.3-3.4-2.3-2.3-3.5-5.3-3.5-9.1V53.6h-4.6v-6.2h4.6V36.1h7.7v11.4h9.6v6.2h-9.6zM309.5 83.9h-7V79c-3 3.6-7 5.4-12.1 5.4-3.8 0-6.9-1.1-9.4-3.2s-3.7-5-3.7-8.6c0-3.6 1.3-6.3 4-8 2.6-1.8 6.2-2.7 10.7-2.7h9.9v-1.4c0-4.8-2.7-7.3-8.1-7.3-3.4 0-6.9 1.2-10.5 3.7l-3.4-4.8c4.4-3.5 9.4-5.3 15.1-5.3 4.3 0 7.8 1.1 10.5 3.2 2.7 2.2 4.1 5.6 4.1 10.2v23.7zm-7.7-13.6v-3.1h-8.6c-5.5 0-8.3 1.7-8.3 5.2 0 1.8.7 3.1 2.1 4.1 1.4.9 3.3 1.4 5.7 1.4 2.4 0 4.6-.7 6.4-2.1 1.8-1.3 2.7-3.1 2.7-5.5zM319.3 40.2c-1-1-1.4-2.1-1.4-3.4 0-1.3.5-2.5 1.4-3.4 1-1 2.1-1.4 3.4-1.4 1.3 0 2.5.5 3.4 1.4 1 1 1.4 2.1 1.4 3.4 0 1.3-.5 2.5-1.4 3.4s-2.1 1.4-3.4 1.4c-1.3.1-2.4-.4-3.4-1.4zm7.2 43.7h-7.7V47.5h7.7v36.4zM342.5 83.9h-7.7V33.1h7.7v50.8z"/></g></svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-link">
|
||||
{% comment %}
|
||||
This works for all cases but prerelease versions:
|
||||
{% endcomment %}
|
||||
<a href="{% wagtail_documentation_path %}/releases/{% wagtail_release_notes_path %}">
|
||||
{% trans "View the release notes" %}
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<main class="main">
|
||||
<div class="figure">
|
||||
<svg class="figure-space" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300" aria-hidden="true">
|
||||
<path class="egg" fill="currentColor" d="M150 250c-42.741 0-75-32.693-75-90s42.913-110 75-110c32.088 0 75 52.693 75 110s-32.258 90-75 90z"/>
|
||||
<ellipse fill="#ddd" cx="150" cy="270" rx="40" ry="7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="main-text">
|
||||
<h1>{% trans "Welcome to your new Wagtail site!" %}</h1>
|
||||
<p>{% trans 'Please feel free to <a href="https://github.com/wagtail/wagtail/wiki/Slack">join our community on Slack</a>, or get started with one of the links below.' %}</p>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="footer" role="contentinfo">
|
||||
<a class="option option-one" href="{% wagtail_documentation_path %}/">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 21c0 .5.4 1 1 1h4c.6 0 1-.5 1-1v-1H9v1zm3-19C8.1 2 5 5.1 5 9c0 2.4 1.2 4.5 3 5.7V17c0 .5.4 1 1 1h6c.6 0 1-.5 1-1v-2.3c1.8-1.3 3-3.4 3-5.7 0-3.9-3.1-7-7-7zm2.9 11.1l-.9.6V16h-4v-2.3l-.9-.6C7.8 12.2 7 10.6 7 9c0-2.8 2.2-5 5-5s5 2.2 5 5c0 1.6-.8 3.2-2.1 4.1z"/></svg>
|
||||
<div>
|
||||
<h2>{% trans "Wagtail Documentation" %}</h2>
|
||||
<p>{% trans "Topics, references, & how-tos" %}</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="option option-two" href="{% wagtail_documentation_path %}/getting_started/tutorial.html">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
|
||||
<div>
|
||||
<h2>{% trans "Tutorial" %}</h2>
|
||||
<p>{% trans "Build your first Wagtail site" %}</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="option option-three" href="{% url 'wagtailadmin_home' %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"/></svg>
|
||||
<div>
|
||||
<h2>{% trans "Admin Interface" %}</h2>
|
||||
<p>{% trans "Create your superuser first!" %}</p>
|
||||
</div>
|
||||
</a>
|
||||
</footer>
|
||||
42
home/tests.py
Normal file
@ -0,0 +1,42 @@
|
||||
from home.models import HomePage
|
||||
|
||||
from wagtail.models import Page, Site
|
||||
from wagtail.test.utils import WagtailPageTestCase
|
||||
|
||||
|
||||
class HomeSetUpTests(WagtailPageTestCase):
|
||||
"""
|
||||
Tests for basic page structure setup and HomePage creation.
|
||||
"""
|
||||
|
||||
def test_root_create(self):
|
||||
root_page = Page.objects.get(pk=1)
|
||||
self.assertIsNotNone(root_page)
|
||||
|
||||
def test_homepage_create(self):
|
||||
root_page = Page.objects.get(pk=1)
|
||||
homepage = HomePage(title="Home")
|
||||
root_page.add_child(instance=homepage)
|
||||
self.assertTrue(HomePage.objects.filter(title="Home").exists())
|
||||
|
||||
|
||||
class HomeTests(WagtailPageTestCase):
|
||||
"""
|
||||
Tests for homepage functionality and rendering.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create a homepage instance for testing.
|
||||
"""
|
||||
root_page = Page.get_first_root_node()
|
||||
Site.objects.create(hostname="testsite", root_page=root_page, is_default_site=True)
|
||||
self.homepage = HomePage(title="Home")
|
||||
root_page.add_child(instance=self.homepage)
|
||||
|
||||
def test_homepage_is_renderable(self):
|
||||
self.assertPageIsRenderable(self.homepage)
|
||||
|
||||
def test_homepage_template_used(self):
|
||||
response = self.client.get(self.homepage.url)
|
||||
self.assertTemplateUsed(response, "home/home_page.html")
|
||||
0
jacksbastards/__init__.py
Normal file
BIN
jacksbastards/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
jacksbastards/__pycache__/urls.cpython-314.pyc
Normal file
0
jacksbastards/settings/__init__.py
Normal file
BIN
jacksbastards/settings/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
jacksbastards/settings/__pycache__/base.cpython-314.pyc
Normal file
BIN
jacksbastards/settings/__pycache__/dev.cpython-314.pyc
Normal file
185
jacksbastards/settings/base.py
Normal file
@ -0,0 +1,185 @@
|
||||
"""
|
||||
Django settings for jacksbastards project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 6.0.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/6.0/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT_DIR = Path(__file__).resolve().parent.parent
|
||||
BASE_DIR = PROJECT_DIR.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"home",
|
||||
"blog",
|
||||
"base",
|
||||
"search",
|
||||
"wagtail.contrib.forms",
|
||||
"wagtail.contrib.redirects",
|
||||
"wagtail.contrib.settings",
|
||||
"wagtail.embeds",
|
||||
"wagtail.sites",
|
||||
"wagtail.users",
|
||||
"wagtail.snippets",
|
||||
"wagtail.documents",
|
||||
"wagtail.images",
|
||||
"wagtail.search",
|
||||
"wagtail.admin",
|
||||
"wagtail",
|
||||
"modelcluster",
|
||||
"taggit",
|
||||
"django_filters",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "jacksbastards.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [
|
||||
PROJECT_DIR / "templates",
|
||||
],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"wagtail.contrib.settings.context_processors.settings",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = "jacksbastards.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/6.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/6.0/howto/static-files/
|
||||
|
||||
STATICFILES_FINDERS = [
|
||||
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
]
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
PROJECT_DIR / "static",
|
||||
]
|
||||
|
||||
STATIC_ROOT = BASE_DIR / "static"
|
||||
STATIC_URL = "/static/"
|
||||
|
||||
MEDIA_ROOT = BASE_DIR / "media"
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
# Default storage settings
|
||||
# See https://docs.djangoproject.com/en/6.0/ref/settings/#std-setting-STORAGES
|
||||
STORAGES = {
|
||||
"default": {
|
||||
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||
},
|
||||
"staticfiles": {
|
||||
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
|
||||
},
|
||||
}
|
||||
|
||||
# Django sets a maximum of 1000 fields per form by default, but particularly complex page models
|
||||
# can exceed this limit within Wagtail's page editor.
|
||||
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10_000
|
||||
|
||||
|
||||
# Wagtail settings
|
||||
|
||||
WAGTAIL_SITE_NAME = "jacksbastards"
|
||||
|
||||
# Search
|
||||
# https://docs.wagtail.org/en/stable/topics/search/backends.html
|
||||
WAGTAILSEARCH_BACKENDS = {
|
||||
"default": {
|
||||
"BACKEND": "wagtail.search.backends.database",
|
||||
}
|
||||
}
|
||||
|
||||
# Base URL to use when referring to full URLs within the Wagtail admin backend -
|
||||
# e.g. in notification emails. Don't include '/admin' or a trailing slash
|
||||
WAGTAILADMIN_BASE_URL = "http://example.com"
|
||||
|
||||
# Allowed file extensions for documents in the document library.
|
||||
# This can be omitted to allow all files, but note that this may present a security risk
|
||||
# if untrusted users are allowed to upload files -
|
||||
# see https://docs.wagtail.org/en/stable/advanced_topics/deploying.html#user-uploaded-files
|
||||
WAGTAILDOCS_EXTENSIONS = ['csv', 'docx', 'key', 'odt', 'pdf', 'pptx', 'rtf', 'txt', 'xlsx', 'zip']
|
||||
18
jacksbastards/settings/dev.py
Normal file
@ -0,0 +1,18 @@
|
||||
from .base import *
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = "django-insecure-c@#kgredf)k#l1m@ebn&koyuva$f^1^7u_2g4ohc9imf4=ue^f"
|
||||
|
||||
# SECURITY WARNING: define the correct hosts in production!
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
|
||||
try:
|
||||
from .local import *
|
||||
except ImportError:
|
||||
pass
|
||||
14
jacksbastards/settings/production.py
Normal file
@ -0,0 +1,14 @@
|
||||
from .base import *
|
||||
|
||||
DEBUG = False
|
||||
|
||||
# ManifestStaticFilesStorage is recommended in production, to prevent
|
||||
# outdated JavaScript / CSS assets being served from cache
|
||||
# (e.g. after a Wagtail upgrade).
|
||||
# See https://docs.djangoproject.com/en/6.0/ref/contrib/staticfiles/#manifeststaticfilesstorage
|
||||
STORAGES["staticfiles"]["BACKEND"] = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
|
||||
|
||||
try:
|
||||
from .local import *
|
||||
except ImportError:
|
||||
pass
|
||||
0
jacksbastards/static/css/jacksbastards.css
Normal file
0
jacksbastards/static/js/jacksbastards.js
Normal file
11
jacksbastards/templates/404.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Page not found{% endblock %}
|
||||
|
||||
{% block body_class %}template-404{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Page not found</h1>
|
||||
|
||||
<h2>Sorry, this page could not be found.</h2>
|
||||
{% endblock %}
|
||||
13
jacksbastards/templates/500.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Internal server error</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Internal server error</h1>
|
||||
|
||||
<h2>Sorry, there seems to be an error. Please try again soon.</h2>
|
||||
</body>
|
||||
</html>
|
||||
48
jacksbastards/templates/base.html
Normal file
@ -0,0 +1,48 @@
|
||||
{% load static wagtailcore_tags wagtailuserbar %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>
|
||||
{% block title %}
|
||||
{% if page.seo_title %}{{ page.seo_title }}{% else %}{{ page.title }}{% endif %}
|
||||
{% endblock %}
|
||||
{% block title_suffix %}
|
||||
{% wagtail_site as current_site %}
|
||||
{% if current_site and current_site.site_name %}- {{ current_site.site_name }}{% endif %}
|
||||
{% endblock %}
|
||||
</title>
|
||||
{% if page.search_description %}
|
||||
<meta name="description" content="{{ page.search_description }}" />
|
||||
{% endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
{# Force all links in the live preview panel to be opened in a new tab #}
|
||||
{% if request.in_preview_panel %}
|
||||
<base target="_blank">
|
||||
{% endif %}
|
||||
|
||||
{# Global stylesheets #}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/jacksbastards.css' %}">
|
||||
|
||||
{% block extra_css %}
|
||||
{# Override this in templates to add extra stylesheets #}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body class="{% block body_class %}{% endblock %}">
|
||||
{% wagtailuserbar %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
{% include "includes/footer.html" %}
|
||||
|
||||
{# Global javascript #}
|
||||
<script type="text/javascript" src="{% static 'js/jacksbastards.js' %}"></script>
|
||||
|
||||
{% block extra_js %}
|
||||
{# Override this in templates to add extra javascript #}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
23
jacksbastards/templates/includes/footer.html
Normal file
@ -0,0 +1,23 @@
|
||||
<footer>
|
||||
<p>Built with Wagtail</p>
|
||||
|
||||
{% with youtube_url=settings.base.NavigationSettings.youtube_url facebook_url=settings.base.NavigationSettings.facebook_url instagram_url=settings.base.NavigationSettings.instagram_url gitea_url=settings.base.NavigationSettings.gitea_url %}
|
||||
{% if youtube_url or facebook_url or instagram_url or gitea_url %}
|
||||
<p>
|
||||
Follow me on:
|
||||
{% if youtube_url %}
|
||||
<a href="{{ youtube_url }}">Youtube</a>
|
||||
{% endif %}
|
||||
{% if facebook_url %}
|
||||
<a href="{{ facebook_url }}">Facebook</a>
|
||||
{% endif %}
|
||||
{% if instagram_url %}
|
||||
<a href="{{ instagram_url }}">Instagram</a>
|
||||
{% endif %}
|
||||
{% if gitea_url %}
|
||||
<a href="{{ gitea_url }}">Gitea</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</footer>
|
||||
35
jacksbastards/urls.py
Normal file
@ -0,0 +1,35 @@
|
||||
from django.conf import settings
|
||||
from django.urls import include, path
|
||||
from django.contrib import admin
|
||||
|
||||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
from wagtail import urls as wagtail_urls
|
||||
from wagtail.documents import urls as wagtaildocs_urls
|
||||
|
||||
from search import views as search_views
|
||||
|
||||
urlpatterns = [
|
||||
path("django-admin/", admin.site.urls),
|
||||
path("admin/", include(wagtailadmin_urls)),
|
||||
path("documents/", include(wagtaildocs_urls)),
|
||||
path("search/", search_views.search, name="search"),
|
||||
]
|
||||
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
# Serve static and media files from development server
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
urlpatterns = urlpatterns + [
|
||||
# For anything not caught by a more specific rule above, hand over to
|
||||
# Wagtail's page serving mechanism. This should be the last pattern in
|
||||
# the list:
|
||||
path("", include(wagtail_urls)),
|
||||
# Alternatively, if you want Wagtail pages to be served from a subpath
|
||||
# of your site, rather than the site root:
|
||||
# path("pages/", include(wagtail_urls)),
|
||||
]
|
||||
16
jacksbastards/wsgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for jacksbastards project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jacksbastards.settings.dev")
|
||||
|
||||
application = get_wsgi_application()
|
||||
22
manage.py
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jacksbastards.settings.dev")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 116 KiB |
BIN
media/images/43b8e519-7d2d-4d23-9c01-25593dd85e2d.original.jpg
Normal file
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 47 KiB |