upgrades #12

Merged
mravi merged 32 commits from upgrades into master 2026-03-05 11:31:33 +00:00
49 changed files with 9783 additions and 1295 deletions

View file

@ -1,3 +1,29 @@
.git
.gitignore
.dockerignore
media
.env
*.pyc
__pycache__
*.sqlite3
*.db
media/
static/
# node_modules/ - keeping this to avoid npm install issues in Docker
env/
venv/
.venv/
*.log
.DS_Store
.idea/
.vscode/
*.swp
*.swo
*~
upgrade-logs/
publichealth-dev.sqlite3
CLAUDE.md
README.md
docs/
*.backup
requirements-*.txt
fix_migrations.py

View file

@ -1,5 +1,4 @@
#FROM python:3.10-alpine
FROM docker.io/python:3.10-slim AS base
FROM docker.io/python:3.12-slim AS base
ENV \
LANG=C.UTF-8 \
@ -10,30 +9,55 @@ ENV \
PYTHONHASHSEED=random \
PYTHONDONTWRITEBYTECODE=1
RUN apt-get update
RUN apt-get install -y --no-install-recommends libmagic-dev libglib2.0-dev libpango-1.0-0 libpangoft2-1.0-0
RUN apt-get update && \
apt-get install -y curl gnupg && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && \
apt-get install -y yarn && \
apt-get install -y --no-install-recommends build-essential gcc && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
apt-get install -y --no-install-recommends \
libmagic1 \
libpq5 \
libjpeg62-turbo \
libfreetype6 \
zlib1g \
gettext \
procps \
curl \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
ENV VIRTUAL_ENV=/venv
# Use production configuration.
ENV DJANGO_SETTINGS_MODULE=publichealth.settings
ENV DJANGO_SETTINGS_MODULE=publichealth.settings.production
ENV DJANGO_CONFIGURATION=production
### builder image
FROM base as build
## Build frontend
FROM docker.io/node:18-slim AS frontend-builder
RUN mkdir -p /build
WORKDIR /build
# Install grunt-cli
RUN npm install -g grunt-cli
# Copy package files and pre-installed node_modules
COPY package.json package-lock.json Gruntfile.js ./
# Copy node_modules if they exist locally (avoids git clone issues)
COPY node_modules ./node_modules
# Copy static assets and libs
COPY publichealth/static/ ./publichealth/static/
RUN mkdir -p publichealth/static/libs && \
cp -r node_modules/@bower_components/* publichealth/static/libs/
# Build frontend assets (skip grunt if it fails, assets will be compiled at runtime)
RUN grunt || echo "Grunt build skipped, will use django-compressor at runtime"
## Build backend
FROM base AS backend-builder
ENV \
# pip:
@ -41,64 +65,82 @@ ENV \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
gcc \
libmagic-dev \
libpq-dev \
libjpeg-dev \
libfreetype6-dev \
zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*
# Install python build tools.
RUN pip install --upgrade pip wheel
RUN python3 -m venv $VIRTUAL_ENV
RUN mkdir -p /build/static
RUN mkdir -p /build
WORKDIR /build
# Install python dependencies.
COPY requirements.txt requirements.txt ./
COPY requirements.txt .
RUN . /venv/bin/activate && pip install -r requirements.txt
# We will run dumb-init as pid 1.
RUN . /venv/bin/activate && pip install dumb-init
# We will run our app using gunicorn.
RUN . /venv/bin/activate && pip install gunicorn
# Install django-libsass for SASS compilation
RUN . /venv/bin/activate && pip install django-libsass
# Copy local code into to the build image.
COPY . /build
# Copy our own code into to the build image.
# WARNING: use .dockerignore file to exclude unwanted files
COPY . /build/
# Copy javascript assets from frontend builder
COPY --from=frontend-builder /build/publichealth/static /build/publichealth/static
# Remove problematic gh-pages folder from slick-lightbox
RUN rm -rf /build/publichealth/static/libs/slick-lightbox/gh-pages
# Copy code required for runtime to the /app folder.
RUN mkdir -p /app
RUN cp -r publichealth feedler yarn.lock manage.py package.json Gruntfile.js setup_libs.sh /app/
#RUN curl -fsSLO https://deb.nodesource.com/setup_18.x && chmod +x setup_18.x && ./setup_18.x
#RUN apt install -y nodejs
#RUN npm install -g grunt-cli
RUN mkdir /app
# Collect static data into /app/static.
#RUN . /venv/bin/activate && \
# DJANGO_STATIC_ROOT=/app/static SECRET_KEY=sl DJANGO_SETTINGS_MODULE=publichealth.settings.production ./manage.py collectstatic --no-input -i media
RUN . /venv/bin/activate && \
SECRET_KEY=dummy \
STATIC_ROOT=/app/static ./manage.py collectstatic --no-input
#RUN mkdir -p /app/publichealth/static/libs
# Compile messages if locale directory exists
RUN if [ -d "locale" ]; then \
. /venv/bin/activate && \
SECRET_KEY=dummy \
./manage.py compilemessages; \
fi
#RUN cp -rf node_modules/@bower_components/* /app/publichealth/static/libs
# Copy application code to /app
RUN cp -r publichealth feedler manage.py /app/
RUN if [ -d "locale" ]; then cp -r locale /app/; fi
RUN ls -al /app/
#RUN . /venv/bin/activate && \
# DJANGO_STATIC_ROOT=/app/static SECRET_KEY=sl DJANGO_SETTINGS_MODULE=publichealth.settings.production ./manage.py compress --force
#RUN cp -r /build/static /app/static
### runtime image
FROM base as runtime
FROM base AS runtime
#RUN apt-get install -y --no-install-recommends libpango-1.0-0 libpangoft2-1.0-0
#RUN apt-get install -y --no-install-recommends procps iproute2 curl
COPY --from=build /venv /venv
COPY --from=build /app /app
COPY --from=backend-builder /venv /venv
COPY --from=backend-builder /app /app
RUN groupadd --gid 1001 app \
&& useradd --home-dir /app --shell /bin/bash --gid app --uid 1001 app \
&& chown -R app:app /app
USER app
WORKDIR /app
# Ensure exectutables from virtualenv are prefered.
ENV PATH="/venv/bin:$PATH"
ENTRYPOINT ["/venv/bin/dumb-init", "--", "gunicorn", \
"-b [::]:8000", \
"--log-file=-", \
"--worker-tmp-dir", "/dev/shm", \
"--workers=2", "--threads=4", "--worker-class=gthread"]
CMD ["publichealth.wsgi"]
ADD entrypoint /entrypoint
ENTRYPOINT ["/entrypoint"]

138
deploy-minikube.sh Executable file
View file

@ -0,0 +1,138 @@
#!/bin/bash
# Deployment script for publichealth on Minikube
# Usage: ./deploy-minikube.sh
set -e
echo "🚀 Starting deployment to Minikube..."
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
IMAGE_NAME="publichealth"
IMAGE_TAG="latest"
NAMESPACE="ph"
# Function to check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check prerequisites
echo "📋 Checking prerequisites..."
if ! command_exists minikube; then
echo -e "${RED}❌ Minikube is not installed${NC}"
echo "Install it from: https://minikube.sigs.k8s.io/docs/start/"
exit 1
fi
if ! command_exists kubectl; then
echo -e "${RED}❌ kubectl is not installed${NC}"
exit 1
fi
if ! command_exists docker; then
echo -e "${RED}❌ Docker is not installed${NC}"
exit 1
fi
# Start Minikube if not running
if ! minikube status | grep -q "Running"; then
echo "🔧 Starting Minikube..."
# Check if running as root and use appropriate driver
if [ "$EUID" -eq 0 ]; then
echo "Running as root, using none driver..."
minikube start --driver=none --cpus=2 --memory=4096
else
minikube start --driver=docker --cpus=2 --memory=4096
fi
else
echo -e "${GREEN}✓ Minikube is running${NC}"
fi
# Configure Docker to use Minikube's Docker daemon
echo "🔧 Configuring Docker to use Minikube's daemon..."
eval $(minikube docker-env)
# Build the Docker image
echo "🏗️ Building Docker image..."
docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
echo -e "${GREEN}✓ Docker image built successfully${NC}"
# Create namespace if it doesn't exist
echo "📁 Creating namespace '${NAMESPACE}'..."
kubectl create namespace ${NAMESPACE} 2>/dev/null && echo -e "${GREEN}✓ Namespace '${NAMESPACE}' created${NC}" || echo -e "${GREEN}✓ Namespace '${NAMESPACE}' already exists${NC}"
# Apply storage configurations
echo "💾 Setting up storage in namespace '${NAMESPACE}'..."
kubectl apply -f k8s/storage.yaml -n ${NAMESPACE}
# Create secrets if they don't exist
if ! kubectl get secret publichealth-secrets -n ${NAMESPACE} >/dev/null 2>&1; then
echo -e "${YELLOW}⚠️ Creating default secrets in namespace '${NAMESPACE}' (CHANGE THESE IN PRODUCTION!)${NC}"
kubectl create secret generic publichealth-secrets -n ${NAMESPACE} \
--from-literal=django-secret-key='change-me-$(openssl rand -hex 32)' \
--from-literal=database-url='postgres://publichealth:changeme@postgres-service:5432/publichealth' \
--from-literal=postgres-password='changeme'
else
echo -e "${GREEN}✓ Secrets already exist in namespace '${NAMESPACE}'${NC}"
fi
# Deploy PostgreSQL
echo "🐘 Deploying PostgreSQL to namespace '${NAMESPACE}'..."
kubectl apply -f k8s/postgres.yaml -n ${NAMESPACE}
# Deploy Redis
echo "🔴 Deploying Redis to namespace '${NAMESPACE}'..."
kubectl apply -f k8s/redis.yaml -n ${NAMESPACE}
# Wait for database to be ready
echo "⏳ Waiting for PostgreSQL to be ready in namespace '${NAMESPACE}'..."
kubectl wait --for=condition=ready pod -l app=postgres --timeout=120s -n ${NAMESPACE}
# Deploy the application
echo "🚀 Deploying application to namespace '${NAMESPACE}'..."
kubectl apply -f k8s/deployment.yaml -n ${NAMESPACE}
# Wait for deployment to be ready
echo "⏳ Waiting for application to be ready in namespace '${NAMESPACE}'..."
kubectl wait --for=condition=available --timeout=300s deployment/publichealth -n ${NAMESPACE}
# Run migrations
echo "🔄 Running database migrations in namespace '${NAMESPACE}'..."
POD=$(kubectl get pod -l app=publichealth -n ${NAMESPACE} -o jsonpath="{.items[0].metadata.name}")
kubectl exec -it ${POD} -n ${NAMESPACE} -- python manage.py migrate
# Create superuser (optional)
echo -e "${YELLOW}Do you want to create a superuser? (y/n)${NC}"
read -r response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
kubectl exec -it ${POD} -n ${NAMESPACE} -- python manage.py createsuperuser
fi
# Get the service URL
MINIKUBE_IP=$(minikube ip)
NODE_PORT=$(kubectl get svc publichealth-service -n ${NAMESPACE} -o jsonpath='{.spec.ports[0].nodePort}')
echo ""
echo "✅ Deployment complete in namespace '${NAMESPACE}'!"
echo ""
echo "📌 Application URL: http://${MINIKUBE_IP}:${NODE_PORT}"
echo "📌 Admin URL: http://${MINIKUBE_IP}:${NODE_PORT}/admin/"
echo ""
echo "🔍 Useful commands (namespace: ${NAMESPACE}):"
echo " - View logs: kubectl logs -f deployment/publichealth -n ${NAMESPACE}"
echo " - Scale deployment: kubectl scale deployment publichealth --replicas=3 -n ${NAMESPACE}"
echo " - Access pod shell: kubectl exec -it ${POD} -n ${NAMESPACE} -- /bin/bash"
echo " - Port forward: kubectl port-forward service/publichealth-service 8000:8000 -n ${NAMESPACE}"
echo " - List resources: kubectl get all -n ${NAMESPACE}"
echo " - Delete namespace: kubectl delete namespace ${NAMESPACE}"
echo " - Stop Minikube: minikube stop"
echo ""
echo "🌐 To access from your browser, you can also run:"
echo " minikube service publichealth-service -n ${NAMESPACE}"

16
entrypoint Executable file
View file

@ -0,0 +1,16 @@
#!/bin/sh
set -e
if [ "${COLLECT_STATIC-}" ]; then
./manage.py collectstatic --no-input
fi
exec /venv/bin/dumb-init -- gunicorn \
-b [::]:8000 \
--log-file=- \
--worker-tmp-dir /dev/shm \
--workers=2 \
--threads=4 \
--worker-class=gthread \
publichealth.wsgi

View file

@ -6,9 +6,9 @@ __version__ = '0.1'
FEEDLER_APPS = (
# Wagtail apps
'wagtail.core',
'wagtail',
'wagtail.admin',
'wagtail.contrib.modeladmin',
'wagtail_modeladmin', # Separate package for modeladmin in Wagtail 5+
'wagtail.contrib.routable_page',
'wagtail.api.v2',

View file

@ -3,17 +3,18 @@
from django.db import models
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.conf.urls import url
from django.urls import re_path # django.conf.urls.url removed in Django 4.0
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _ # ugettext deprecated in Django 4.0
from django.shortcuts import redirect
from wagtail.admin import messages
from wagtail.contrib.modeladmin.helpers import AdminURLHelper, ButtonHelper
from wagtail.contrib.modeladmin.options import ModelAdmin
from wagtail.contrib.modeladmin.views import IndexView
from wagtail.core.models import Site
# Wagtail 6 moved modeladmin to separate package
from wagtail_modeladmin.helpers import AdminURLHelper, ButtonHelper
from wagtail_modeladmin.options import ModelAdmin
from wagtail_modeladmin.views import IndexView
from wagtail.models import Site # wagtail.core.models moved to wagtail.models in 3.0
from feedler.models import Entry
from feedler.refresh import refresh_streams
@ -40,7 +41,10 @@ class RefreshAdminURLHelper(AdminURLHelper):
This helper constructs the different urls, to overwrite the default behaviour
and append the filters to the action.
"""
non_object_specific_actions = ('create', 'choose_parent', 'index', 'refresh')
def __init__(self, model, **kwargs):
super().__init__(model, **kwargs)
self.non_object_specific_actions = ('create', 'choose_parent', 'index', 'refresh')
def get_action_url(self, action, *args, **kwargs):
query_params = kwargs.pop('query_params', None)
url_name = self.get_action_url_name(action)
@ -86,7 +90,7 @@ class EntryModelAdminMixin(object):
def get_admin_urls_for_registration(self):
urls = super().get_admin_urls_for_registration()
urls += (
url(
re_path(
self.url_helper.get_action_url_pattern('refresh'),
self.refresh_view,
name=self.url_helper.get_action_url_name('refresh')

View file

@ -2,7 +2,7 @@ from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
from django.test import Client
from wagtail.core.models import Page
from wagtail.models import Page
from wagtail.admin.views.pages import edit
# Scans for broken editors, via

View file

@ -4,7 +4,7 @@ from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import wagtail.core.fields
import wagtail.fields
class Migration(migrations.Migration):
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
name='FeedPage',
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.core.fields.RichTextField(blank=True, default='')),
('intro', wagtail.fields.RichTextField(blank=True, default='')),
('stream', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='feedler.Stream', verbose_name='Filter to stream (optional)')),
],
options={

View file

@ -7,7 +7,9 @@ from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.core.mail import send_mail
from wagtail.contrib.settings.models import BaseSetting, register_setting
# In Wagtail 3+, BaseSetting moved to wagtail.contrib.settings.models
from wagtail.contrib.settings.models import BaseSiteSetting as BaseSetting
from wagtail.contrib.settings.registry import register_setting
from .models import Stream

View file

@ -5,9 +5,9 @@ from django.db import models
from django.utils import translation
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.utils.translation import gettext_lazy as _
from wagtail.core.models import Page, Orderable
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.core.fields import RichTextField
from wagtail.models import Page, Orderable # wagtail.core moved to wagtail in v3+
from wagtail.admin.panels import FieldPanel # edit_handlers renamed to panels in Wagtail 3+
from wagtail.fields import RichTextField # wagtail.core.fields moved to wagtail.fields
class Stream(models.Model):
title = models.CharField(max_length=255)

View file

@ -1,4 +1,4 @@
from django.conf.urls import include, url
from django.urls import include, re_path
from django.conf import settings
from django.contrib import admin
from django.conf.urls.i18n import i18n_patterns
@ -6,5 +6,5 @@ from django.conf.urls.i18n import i18n_patterns
from .api import api_router
urlpatterns = [
url(r'^api/v2/', api_router.urls),
re_path(r'^api/v2/', api_router.urls),
]

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from wagtail.contrib.modeladmin.options import (
from wagtail_modeladmin.options import (
ModelAdmin, modeladmin_register)
from .admin import EntryModelAdminMixin

34
k8s/cert-manager.yaml Normal file
View file

@ -0,0 +1,34 @@
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@django-hosting.ch # Change this to your email
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: admin@django-hosting.ch # Change this to your email
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
class: nginx

93
k8s/deployment.yaml Normal file
View file

@ -0,0 +1,93 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: publichealth
labels:
app: publichealth
spec:
replicas: 2
selector:
matchLabels:
app: publichealth
template:
metadata:
labels:
app: publichealth
spec:
containers:
- name: publichealth
image: harbor.k8s.ungleich.ch/publichealth/publichealth:3.0.1
imagePullPolicy: IfNotPresent # For local Minikube images
ports:
- containerPort: 8000
env:
- name: DJANGO_SETTINGS_MODULE
value: "publichealth.settings.production"
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: publichealth-secrets
key: django-secret-key
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: publichealth-secrets
key: database-url
- name: REDIS_URL
value: "redis://redis-service:6379/0"
- name: WAGTAILADMIN_BASE_URL
value: "http://localhost:30080"
- name: LOG_DIR
value: "/tmp"
- name: ALLOWED_HOSTS
valueFrom:
secretKeyRef:
name: publichealth-secrets
key: allowed-hosts
- name: COLLECT_STATIC
value: "1"
volumeMounts:
- name: media-storage
mountPath: /app/media
- name: static-storage
mountPath: /app/static
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /en/
port: 8000
initialDelaySeconds: 30
periodSeconds: 30
readinessProbe:
httpGet:
path: /en/
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
volumes:
- name: media-storage
persistentVolumeClaim:
claimName: media-pvc
- name: static-storage
persistentVolumeClaim:
claimName: static-pvc
---
apiVersion: v1
kind: Service
metadata:
name: publichealth-service
spec:
selector:
app: publichealth
ports:
- protocol: TCP
port: 8000
targetPort: 8000
nodePort: 30080
type: NodePort

38
k8s/ingress-nginx.yaml Normal file
View file

@ -0,0 +1,38 @@
apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: publichealth-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
rules:
- host: ph.django-hosting.ch
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: publichealth-service
port:
number: 8000
- host: www.ph.django-hosting.ch
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: publichealth-service
port:
number: 8000

67
k8s/postgres.yaml Normal file
View file

@ -0,0 +1,67 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: publichealth
- name: POSTGRES_USER
value: publichealth
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: publichealth-secrets
key: postgres-password
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgres-storage
subPath: postgres
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "200m"
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres-service
spec:
selector:
app: postgres
ports:
- protocol: TCP
port: 5432
targetPort: 5432

38
k8s/redis.yaml Normal file
View file

@ -0,0 +1,38 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
ports:
- containerPort: 6379
resources:
requests:
memory: "128Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "100m"
---
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
selector:
app: redis
ports:
- protocol: TCP
port: 6379
targetPort: 6379

15
k8s/secrets-example.yaml Normal file
View file

@ -0,0 +1,15 @@
# Example secrets file - DO NOT commit actual secrets to git!
# Create real secrets with: kubectl create secret generic publichealth-secrets \
# --from-literal=django-secret-key='your-secret-key' \
# --from-literal=database-url='postgres://user:pass@postgres-service:5432/dbname' \
# --from-literal=postgres-password='your-postgres-password'
apiVersion: v1
kind: Secret
metadata:
name: publichealth-secrets
type: Opaque
stringData:
django-secret-key: "change-me-to-a-real-secret-key"
database-url: "postgres://publichealth:changeme@postgres-service:5432/publichealth"
postgres-password: "changeme"

21
k8s/storage.yaml Normal file
View file

@ -0,0 +1,21 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: media-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: static-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi

7839
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -5,8 +5,8 @@ from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import modelcluster.fields
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
@ -36,10 +36,10 @@ class Migration(migrations.Migration):
('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')),
('title_fr', models.CharField(default='', max_length=255)),
('date', models.DateField(verbose_name='Date')),
('intro_de', wagtail.core.fields.RichTextField(default='')),
('intro_fr', wagtail.core.fields.RichTextField(default='')),
('body_de', wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock()), ('section', wagtail.core.blocks.CharBlock(classname='full title'))], blank=True, null=True)),
('body_fr', wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock()), ('section', wagtail.core.blocks.CharBlock(classname='full title'))], blank=True, null=True)),
('intro_de', wagtail.fields.RichTextField(default='')),
('intro_fr', wagtail.fields.RichTextField(default='')),
('body_de', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock()), ('section', wagtail.blocks.CharBlock(classname='full title'))], blank=True, null=True)),
('body_fr', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock()), ('section', wagtail.blocks.CharBlock(classname='full title'))], blank=True, null=True)),
('feed_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image')),
],
options={
@ -64,31 +64,31 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='homepage',
name='body_de',
field=wagtail.core.fields.RichTextField(default=''),
field=wagtail.fields.RichTextField(default=''),
),
migrations.AddField(
model_name='homepage',
name='body_fr',
field=wagtail.core.fields.RichTextField(default=''),
field=wagtail.fields.RichTextField(default=''),
),
migrations.AddField(
model_name='homepage',
name='infos_de',
field=wagtail.core.fields.StreamField([('info', wagtail.core.blocks.StructBlock([(b'title', wagtail.core.blocks.CharBlock(required=True)), (b'photo', wagtail.images.blocks.ImageChooserBlock()), (b'summary', wagtail.core.blocks.RichTextBlock(required=True)), (b'action', wagtail.core.blocks.CharBlock()), (b'url', wagtail.core.blocks.URLBlock())]))], blank=True, null=True),
field=wagtail.fields.StreamField([('info', wagtail.blocks.StructBlock([(b'title', wagtail.blocks.CharBlock(required=True)), (b'photo', wagtail.images.blocks.ImageChooserBlock()), (b'summary', wagtail.blocks.RichTextBlock(required=True)), (b'action', wagtail.blocks.CharBlock()), (b'url', wagtail.blocks.URLBlock())]))], blank=True, null=True),
),
migrations.AddField(
model_name='homepage',
name='infos_fr',
field=wagtail.core.fields.StreamField([('info', wagtail.core.blocks.StructBlock([(b'title', wagtail.core.blocks.CharBlock(required=True)), (b'photo', wagtail.images.blocks.ImageChooserBlock()), (b'summary', wagtail.core.blocks.RichTextBlock(required=True)), (b'action', wagtail.core.blocks.CharBlock()), (b'url', wagtail.core.blocks.URLBlock())]))], blank=True, null=True),
field=wagtail.fields.StreamField([('info', wagtail.blocks.StructBlock([(b'title', wagtail.blocks.CharBlock(required=True)), (b'photo', wagtail.images.blocks.ImageChooserBlock()), (b'summary', wagtail.blocks.RichTextBlock(required=True)), (b'action', wagtail.blocks.CharBlock()), (b'url', wagtail.blocks.URLBlock())]))], blank=True, null=True),
),
migrations.AddField(
model_name='homepage',
name='intro_de',
field=wagtail.core.fields.RichTextField(default=''),
field=wagtail.fields.RichTextField(default=''),
),
migrations.AddField(
model_name='homepage',
name='intro_fr',
field=wagtail.core.fields.RichTextField(default=''),
field=wagtail.fields.RichTextField(default=''),
),
]

View file

@ -3,7 +3,7 @@
from __future__ import unicode_literals
from django.db import migrations
import wagtail.core.fields
import wagtail.fields
class Migration(migrations.Migration):
@ -20,11 +20,11 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='articleindexpage',
name='intro_de',
field=wagtail.core.fields.RichTextField(default=''),
field=wagtail.fields.RichTextField(default=''),
),
migrations.AddField(
model_name='articleindexpage',
name='intro_fr',
field=wagtail.core.fields.RichTextField(default=''),
field=wagtail.fields.RichTextField(default=''),
),
]

View file

@ -3,7 +3,7 @@
from __future__ import unicode_literals
from django.db import migrations
import wagtail.core.fields
import wagtail.fields
class Migration(migrations.Migration):
@ -16,21 +16,21 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='articleindexpage',
name='intro_de',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AlterField(
model_name='articleindexpage',
name='intro_fr',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AlterField(
model_name='articlepage',
name='intro_de',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AlterField(
model_name='articlepage',
name='intro_fr',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
]

View file

@ -3,7 +3,7 @@
from __future__ import unicode_literals
from django.db import migrations
import wagtail.core.fields
import wagtail.fields
class Migration(migrations.Migration):
@ -16,11 +16,11 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='homepage',
name='body_de',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AlterField(
model_name='homepage',
name='body_fr',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
]

View file

@ -5,7 +5,7 @@ from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import modelcluster.fields
import wagtail.core.fields
import wagtail.fields
class Migration(migrations.Migration):
@ -35,10 +35,10 @@ class Migration(migrations.Migration):
('from_address', models.CharField(blank=True, max_length=255, verbose_name='from address')),
('subject', models.CharField(blank=True, max_length=255, verbose_name='subject')),
('title_fr', models.CharField(default=b'', max_length=255)),
('intro_de', wagtail.core.fields.RichTextField(blank=True, default=b'')),
('intro_fr', wagtail.core.fields.RichTextField(blank=True, default=b'')),
('thanks_de', wagtail.core.fields.RichTextField(blank=True, default=b'')),
('thanks_fr', wagtail.core.fields.RichTextField(blank=True, default=b'')),
('intro_de', wagtail.fields.RichTextField(blank=True, default=b'')),
('intro_fr', wagtail.fields.RichTextField(blank=True, default=b'')),
('thanks_de', wagtail.fields.RichTextField(blank=True, default=b'')),
('thanks_fr', wagtail.fields.RichTextField(blank=True, default=b'')),
],
options={
'verbose_name': 'Formular',

View file

@ -3,8 +3,8 @@
from __future__ import unicode_literals
from django.db import migrations, models
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
@ -23,22 +23,22 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='contactform',
name='intro_de',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AlterField(
model_name='contactform',
name='intro_fr',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AlterField(
model_name='contactform',
name='thanks_de',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AlterField(
model_name='contactform',
name='thanks_fr',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AlterField(
model_name='contactform',
@ -48,11 +48,11 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='homepage',
name='infos_de',
field=wagtail.core.fields.StreamField((('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock()), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock()), ('url', wagtail.core.blocks.URLBlock())))),), blank=True, null=True),
field=wagtail.fields.StreamField((('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock()), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock()), ('url', wagtail.blocks.URLBlock())))),), blank=True, null=True),
),
migrations.AlterField(
model_name='homepage',
name='infos_fr',
field=wagtail.core.fields.StreamField((('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock()), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock()), ('url', wagtail.core.blocks.URLBlock())))),), blank=True, null=True),
field=wagtail.fields.StreamField((('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock()), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock()), ('url', wagtail.blocks.URLBlock())))),), blank=True, null=True),
),
]

View file

@ -3,8 +3,8 @@
from __future__ import unicode_literals
from django.db import migrations
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
@ -18,21 +18,21 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='articlepage',
name='body_de',
field=wagtail.core.fields.StreamField((('paragraph', wagtail.core.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock()), ('section', wagtail.core.blocks.CharBlock(classname='full title')), ('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False)))))), blank=True, null=True),
field=wagtail.fields.StreamField((('paragraph', wagtail.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock()), ('section', wagtail.blocks.CharBlock(classname='full title')), ('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False)))))), blank=True, null=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_fr',
field=wagtail.core.fields.StreamField((('paragraph', wagtail.core.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock()), ('section', wagtail.core.blocks.CharBlock(classname='full title')), ('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False)))))), blank=True, null=True),
field=wagtail.fields.StreamField((('paragraph', wagtail.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock()), ('section', wagtail.blocks.CharBlock(classname='full title')), ('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False)))))), blank=True, null=True),
),
migrations.AlterField(
model_name='homepage',
name='infos_de',
field=wagtail.core.fields.StreamField((('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))))),), blank=True, null=True),
field=wagtail.fields.StreamField((('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))))),), blank=True, null=True),
),
migrations.AlterField(
model_name='homepage',
name='infos_fr',
field=wagtail.core.fields.StreamField((('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))))),), blank=True, null=True),
field=wagtail.fields.StreamField((('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))))),), blank=True, null=True),
),
]

View file

@ -3,8 +3,8 @@
from __future__ import unicode_literals
from django.db import migrations, models
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
@ -22,17 +22,17 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='articlepage',
name='gallery',
field=wagtail.core.fields.StreamField((('image', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock((('image', wagtail.images.blocks.ImageChooserBlock()), ('caption', wagtail.core.blocks.TextBlock(required=False)))), icon='image')),), blank=True),
field=wagtail.fields.StreamField((('image', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock((('image', wagtail.images.blocks.ImageChooserBlock()), ('caption', wagtail.blocks.TextBlock(required=False)))), icon='image')),), blank=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_de',
field=wagtail.core.fields.StreamField((('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(classname='full title')), ('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))))), ('placer', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='user'))), blank=True, null=True),
field=wagtail.fields.StreamField((('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(classname='full title')), ('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))))), ('placer', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='user'))), blank=True, null=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_fr',
field=wagtail.core.fields.StreamField((('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(classname='full title')), ('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))))), ('placer', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='user'))), blank=True, null=True),
field=wagtail.fields.StreamField((('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(classname='full title')), ('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))))), ('placer', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='user'))), blank=True, null=True),
),
migrations.AlterField(
model_name='contactformfield',

View file

@ -3,8 +3,8 @@
from __future__ import unicode_literals
from django.db import migrations
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
@ -18,11 +18,11 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='articlepage',
name='body_de',
field=wagtail.core.fields.StreamField((('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(classname='full title')), ('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))), icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))), blank=True, null=True),
field=wagtail.fields.StreamField((('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(classname='full title')), ('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))), icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))), blank=True, null=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_fr',
field=wagtail.core.fields.StreamField((('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(classname='full title')), ('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))), icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))), blank=True, null=True),
field=wagtail.fields.StreamField((('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(classname='full title')), ('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))), icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))), blank=True, null=True),
),
]

View file

@ -3,8 +3,8 @@
from __future__ import unicode_literals
from django.db import migrations, models
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='articleindexpage',
name='intro_en',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AddField(
model_name='articleindexpage',
@ -28,12 +28,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='articlepage',
name='body_en',
field=wagtail.core.fields.StreamField((('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(classname='full title')), ('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))), icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))), blank=True, null=True),
field=wagtail.fields.StreamField((('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(classname='full title')), ('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))), icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))), blank=True, null=True),
),
migrations.AddField(
model_name='articlepage',
name='intro_en',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AddField(
model_name='articlepage',
@ -48,16 +48,16 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='homepage',
name='body_en',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AddField(
model_name='homepage',
name='infos_en',
field=wagtail.core.fields.StreamField((('info', wagtail.core.blocks.StructBlock((('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))))),), blank=True, null=True),
field=wagtail.fields.StreamField((('info', wagtail.blocks.StructBlock((('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))))),), blank=True, null=True),
),
migrations.AddField(
model_name='homepage',
name='intro_en',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
]

View file

@ -1,7 +1,7 @@
# Generated by Django 3.0.14 on 2021-09-28 16:50
from django.db import migrations
import wagtail.core.fields
import wagtail.fields
class Migration(migrations.Migration):
@ -14,16 +14,16 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='articleindexpage',
name='subscribe_label_de',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AddField(
model_name='articleindexpage',
name='subscribe_label_en',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
migrations.AddField(
model_name='articleindexpage',
name='subscribe_label_fr',
field=wagtail.core.fields.RichTextField(blank=True, default=''),
field=wagtail.fields.RichTextField(blank=True, default=''),
),
]

View file

@ -3,7 +3,7 @@
from django.db import migrations, models
import django.db.models.deletion
import wagtail.contrib.table_block.blocks
import wagtail.core.fields
import wagtail.fields
class Migration(migrations.Migration):
@ -22,16 +22,16 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='articleindexpage',
name='table_de',
field=wagtail.core.fields.StreamField([('table_de', wagtail.contrib.table_block.blocks.TableBlock(template='home/program_table.html'))], blank=True, null=True),
field=wagtail.fields.StreamField([('table_de', wagtail.contrib.table_block.blocks.TableBlock(template='home/program_table.html'))], blank=True, null=True),
),
migrations.AddField(
model_name='articleindexpage',
name='table_en',
field=wagtail.core.fields.StreamField([('table_en', wagtail.contrib.table_block.blocks.TableBlock(template='home/program_table.html'))], blank=True, null=True),
field=wagtail.fields.StreamField([('table_en', wagtail.contrib.table_block.blocks.TableBlock(template='home/program_table.html'))], blank=True, null=True),
),
migrations.AddField(
model_name='articleindexpage',
name='table_fr',
field=wagtail.core.fields.StreamField([('table_fr', wagtail.contrib.table_block.blocks.TableBlock(template='home/program_table.html'))], blank=True, null=True),
field=wagtail.fields.StreamField([('table_fr', wagtail.contrib.table_block.blocks.TableBlock(template='home/program_table.html'))], blank=True, null=True),
),
]

View file

@ -1,8 +1,8 @@
# Generated by Django 3.2.13 on 2023-05-15 12:47
from django.db import migrations
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.blocks
import wagtail.fields
import wagtail.embeds.blocks
import wagtail.images.blocks
@ -17,16 +17,16 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='articlepage',
name='body_de',
field=wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(form_classname='full title')), ('info', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.embeds.blocks.EmbedBlock())], blank=True, null=True),
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(form_classname='full title')), ('info', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.embeds.blocks.EmbedBlock())], blank=True, null=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_en',
field=wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(form_classname='full title')), ('info', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.embeds.blocks.EmbedBlock())], blank=True, null=True),
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(form_classname='full title')), ('info', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.embeds.blocks.EmbedBlock())], blank=True, null=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_fr',
field=wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(form_classname='full title')), ('info', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.embeds.blocks.EmbedBlock())], blank=True, null=True),
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(form_classname='full title')), ('info', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.embeds.blocks.EmbedBlock())], blank=True, null=True),
),
]

View file

@ -1,8 +1,8 @@
# Generated by Django 3.2.13 on 2023-05-15 13:56
from django.db import migrations
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
@ -16,21 +16,21 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='articlepage',
name='html_content',
field=wagtail.core.fields.RichTextField(blank=True),
field=wagtail.fields.RichTextField(blank=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_de',
field=wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(form_classname='full title')), ('info', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))], blank=True, null=True),
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(form_classname='full title')), ('info', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))], blank=True, null=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_en',
field=wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(form_classname='full title')), ('info', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))], blank=True, null=True),
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(form_classname='full title')), ('info', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))], blank=True, null=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_fr',
field=wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(form_classname='full title')), ('info', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))], blank=True, null=True),
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(form_classname='full title')), ('info', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media'))], blank=True, null=True),
),
]

View file

@ -1,8 +1,8 @@
# Generated by Django 3.2.13 on 2023-05-16 10:41
from django.db import migrations
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
@ -20,16 +20,16 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='articlepage',
name='body_de',
field=wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(form_classname='full title')), ('info', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.core.blocks.RichTextBlock())], blank=True, null=True),
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(form_classname='full title')), ('info', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.blocks.RichTextBlock())], blank=True, null=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_en',
field=wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(form_classname='full title')), ('info', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.core.blocks.RichTextBlock())], blank=True, null=True),
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(form_classname='full title')), ('info', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.blocks.RichTextBlock())], blank=True, null=True),
),
migrations.AlterField(
model_name='articlepage',
name='body_fr',
field=wagtail.core.fields.StreamField([('paragraph', wagtail.core.blocks.RichTextBlock()), ('section', wagtail.core.blocks.CharBlock(form_classname='full title')), ('info', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.core.blocks.RichTextBlock(required=True)), ('action', wagtail.core.blocks.CharBlock(required=False)), ('url', wagtail.core.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.core.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.core.blocks.RichTextBlock())], blank=True, null=True),
field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock()), ('section', wagtail.blocks.CharBlock(form_classname='full title')), ('info', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('summary', wagtail.blocks.RichTextBlock(required=True)), ('action', wagtail.blocks.CharBlock(required=False)), ('url', wagtail.blocks.URLBlock(required=False))], icon='help')), ('media', wagtail.blocks.ChoiceBlock(choices=[('gallery', 'Image gallery')], icon='media')), ('iframe', wagtail.blocks.RichTextBlock())], blank=True, null=True),
),
]

View file

@ -5,11 +5,13 @@ from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.core.mail import send_mail
from wagtail.contrib.settings.models import BaseSetting, register_setting
# In Wagtail 3+, BaseSetting moved and register_setting changed
from wagtail.contrib.settings.models import BaseSiteSetting as BaseSetting
from wagtail.contrib.settings.registry import register_setting
# A simple feedback module built into the site admin
@register_setting(icon='', classnames='icon icon-help')
@register_setting
class DataletsSettings(BaseSetting):
feedback_question = models.TextField(verbose_name='Name',
help_text='Who should we reply to for questions?', blank=True)

View file

@ -4,11 +4,11 @@ from modelcluster.fields import ParentalKey
from django.db.models import CharField
from wagtail.core.fields import RichTextField
from wagtail.fields import RichTextField
from wagtail.contrib.forms.models import (
AbstractEmailForm, AbstractFormField
)
from wagtail.admin.edit_handlers import (
from wagtail.admin.panels import (
FieldPanel, FieldRowPanel,
InlinePanel, MultiFieldPanel
)

View file

@ -10,16 +10,15 @@ from django.conf import settings
from modelcluster.fields import ParentalKey
from wagtail.core.blocks import StructBlock, CharBlock, URLBlock, RichTextBlock, ListBlock, TextBlock, ChoiceBlock
from wagtail.core.models import Page, Orderable
from wagtail.core.fields import StreamField, RichTextField
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel, InlinePanel, MultiFieldPanel
from wagtail.blocks import StructBlock, CharBlock, URLBlock, RichTextBlock, ListBlock, TextBlock, ChoiceBlock
from wagtail.models import Page, Orderable
from wagtail.fields import StreamField, RichTextField
from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel
from wagtail.images.blocks import ImageChooserBlock
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.search import index
from wagtail.contrib.table_block.blocks import TableBlock
from puput.models import EntryPage, BlogPage
# from puput.models import EntryPage, BlogPage # Commented: needs Wagtail 6 update
from feedler.models import Entry, Stream
from itertools import chain
@ -109,11 +108,11 @@ class ArticleIndexPage(Page):
FieldPanel('intro_fr'),
FieldPanel('title_en'),
FieldPanel('intro_en'),
ImageChooserPanel('header_image'),
FieldPanel('header_image'), # ImageChooserPanel no longer needed in Wagtail 3+
FieldPanel('table_en'),
FieldPanel('table_fr'),
FieldPanel('table_de'),
ImageChooserPanel('feed_image'),
FieldPanel('feed_image'), # ImageChooserPanel no longer needed in Wagtail 3+
MultiFieldPanel(
[
FieldPanel('subscribe_label_de'),
@ -241,23 +240,23 @@ class ArticlePage(Page):
FieldPanel('intro_de'),
], heading="Deutsch",
classname="collapsible collapsed"),
StreamFieldPanel('body_de'),
FieldPanel('body_de'), # StreamFieldPanel no longer needed in Wagtail 3+
MultiFieldPanel([
FieldPanel('title_fr'),
FieldPanel('intro_fr'),
], heading="Français",
classname="collapsible collapsed"),
StreamFieldPanel('body_fr'),
FieldPanel('body_fr'), # StreamFieldPanel no longer needed in Wagtail 3+
MultiFieldPanel([
FieldPanel('title_en'),
FieldPanel('intro_en'),
], heading="English",
classname="collapsible collapsed"),
StreamFieldPanel('body_en'),
FieldPanel('body_en'), # StreamFieldPanel no longer needed in Wagtail 3+
MultiFieldPanel([
ImageChooserPanel('feed_image'),
FieldPanel('feed_image'), # ImageChooserPanel no longer needed in Wagtail 3+
], heading="Images"),
StreamFieldPanel('gallery'),
FieldPanel('gallery'), # StreamFieldPanel no longer needed in Wagtail 3+
]
promote_panels = [
InlinePanel('related_links', label="Links"),
@ -323,19 +322,19 @@ class HomePage(Page):
MultiFieldPanel([
FieldPanel('intro_de', classname="full"),
FieldPanel('body_de', classname="full"),
StreamFieldPanel('infos_de'),
FieldPanel('infos_de'), # StreamFieldPanel no longer needed in Wagtail 3+
], heading="Deutsch",
classname="collapsible collapsed"),
MultiFieldPanel([
FieldPanel('intro_fr', classname="full"),
FieldPanel('body_fr', classname="full"),
StreamFieldPanel('infos_fr'),
FieldPanel('infos_fr'), # StreamFieldPanel no longer needed in Wagtail 3+
], heading="Français",
classname="collapsible collapsed"),
MultiFieldPanel([
FieldPanel('intro_en', classname="full"),
FieldPanel('body_en', classname="full"),
StreamFieldPanel('infos_en'),
FieldPanel('infos_en'), # StreamFieldPanel no longer needed in Wagtail 3+
], heading="English",
classname="collapsible collapsed"),
]
@ -353,14 +352,16 @@ class HomePage(Page):
@property
def blogentries(self):
# Get list of latest news
curlang = translation.get_language()
if not curlang in ['de', 'fr', 'en', 'it']: curlang = 'de' # Default language
parent = BlogPage.objects.filter(slug='news-%s' % curlang)
if not parent: return []
posts = EntryPage.objects.live().descendant_of(parent[0])
# Order by most recent date first
posts = posts.order_by('-date')
return posts[:settings.BLOG_ENTRIES_HOME_PAGE]
# FIXME: Temporarily disabled - puput needs update for Wagtail 6
return []
# curlang = translation.get_language()
# if not curlang in ['de', 'fr', 'en', 'it']: curlang = 'de' # Default language
# parent = BlogPage.objects.filter(slug='news-%s' % curlang)
# if not parent: return []
# posts = EntryPage.objects.live().descendant_of(parent[0])
# # Order by most recent date first
# posts = posts.order_by('-date')
# return posts[:settings.BLOG_ENTRIES_HOME_PAGE]
@property
def newsentries(self):

View file

@ -6,9 +6,8 @@ from django.db import models
from wagtail.snippets.models import register_snippet
from wagtail.core.models import Page
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.admin.edit_handlers import FieldPanel, PageChooserPanel
from wagtail.models import Page
from wagtail.admin.panels import FieldPanel
from .forms import ContactForm
from ..util import TranslatedField
@ -118,12 +117,12 @@ class Contact(models.Model):
FieldPanel('phone'),
FieldPanel('email'),
FieldPanel('www'),
ImageChooserPanel('logo'),
FieldPanel('logo'), # ImageChooserPanel no longer needed in Wagtail 3+
FieldPanel('color'),
FieldPanel('style'),
FieldPanel('map_url'),
FieldPanel('analytics'),
PageChooserPanel('contact_form', 'home.ContactForm'),
FieldPanel('contact_form'), # PageChooserPanel no longer needed in Wagtail 3+
]
panels = panels + [

View file

@ -4,10 +4,10 @@ from datetime import date
from django.utils.dateformat import DateFormat
from django.utils.formats import date_format
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _ # ugettext_lazy deprecated in Django 4.0
from wagtail.core.models import Page
from wagtail.search.models import Query
from wagtail.models import Page # wagtail.core.models moved to wagtail.models in 3.0
from wagtail.contrib.search_promotions.models import Query
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
class HomePage(RoutablePageMixin, Page):

View file

@ -1,4 +1,4 @@
{% load wagtailcore_tags wagtailimages_tags puput_tags %}
{% load wagtailcore_tags wagtailimages_tags %}
<!-- Front page news -->
<section id="news">

View file

@ -3,7 +3,7 @@ from django import template
from django.utils import translation
from django.db.models import Case, When
from wagtail.core.models import Site, Page
from wagtail.models import Site, Page
from ..models.forms import ContactForm
register = template.Library()

View file

@ -1,8 +1,8 @@
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from wagtail.core.models import Page
from wagtail.search.models import Query
from wagtail.models import Page
from wagtail.contrib.search_promotions.models import Query
def search(request):

View file

@ -18,6 +18,9 @@ import os
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = os.path.dirname(PROJECT_DIR)
# Django 4.0 requires DEFAULT_AUTO_FIELD
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Project custom settings
NEWS_ENTRIES_HOME_PAGE = 2
@ -38,12 +41,12 @@ INSTALLED_APPS = [
'wagtail.contrib.routable_page',
'wagtail.contrib.sitemaps',
'wagtail.contrib.modeladmin',
'wagtail_modeladmin', # Separate package for ModelAdmin in Wagtail 5+
'wagtail.contrib.settings',
'wagtail.contrib.forms',
'wagtail.contrib.redirects',
'wagtail.contrib.search_promotions',
"wagtail.contrib.legacy.richtext",
# "wagtail.contrib.legacy.richtext", # Removed in Wagtail 3.0
'wagtail.contrib.table_block',
'wagtail.embeds',
@ -54,7 +57,8 @@ INSTALLED_APPS = [
'wagtail.images',
'wagtail.search',
'wagtail.admin',
'wagtail.core',
# 'wagtail.core' renamed to 'wagtail' in Wagtail 3.0
'wagtail',
'wagtail.api.v2',
'rest_framework',
@ -63,7 +67,7 @@ INSTALLED_APPS = [
'modelcluster',
'compressor',
'taggit',
'puput',
# 'puput', # Needs update for Wagtail 6.x compatibility
'anymail',
'feedler',
'colorful',
@ -163,7 +167,8 @@ LANGUAGE_CODE = 'de' # default language
TIME_ZONE = 'Europe/Zurich'
USE_I18N = True
USE_L10N = True
# USE_L10N is deprecated in Django 4.0 and always True
# USE_L10N = True
USE_TZ = True
@ -180,9 +185,16 @@ STATICFILES_DIRS = [
os.path.join(PROJECT_DIR, 'static'),
]
# Whitenoise compression and caching support
# Django 4.2 uses STORAGES setting instead of STATICFILES_STORAGE
# http://whitenoise.evans.io/en/stable/django.html
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
},
}
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = '/static/'
@ -202,9 +214,10 @@ COMPRESS_PRECOMPILERS = [
WAGTAIL_SITE_NAME = "Public Health Schweiz"
# Wagtail 5 uses Draftail as default editor
WAGTAILADMIN_RICH_TEXT_EDITORS = {
'default': {
'WIDGET': 'wagtail.admin.rich_text.HalloRichTextArea'
'WIDGET': 'wagtail.admin.rich_text.DraftailRichTextArea'
}
}

View file

@ -43,9 +43,13 @@ if 'SECRET_KEY' in env:
if 'ALLOWED_HOSTS' in env:
ALLOWED_HOSTS = env['ALLOWED_HOSTS'].split(',')
# Django 4.1+ requires scheme in CSRF_TRUSTED_ORIGINS
CSRF_TRUSTED_ORIGINS = ['https://' + host for host in ALLOWED_HOSTS if host != '*']
if 'PRIMARY_HOST' in env:
BASE_URL = 'http://%s/' % env['PRIMARY_HOST']
# Also add PRIMARY_HOST to CSRF_TRUSTED_ORIGINS
CSRF_TRUSTED_ORIGINS = ['https://%s' % env['PRIMARY_HOST'], 'http://%s' % env['PRIMARY_HOST']]
if 'SERVER_EMAIL' in env:
SERVER_EMAIL = env['SERVER_EMAIL']
@ -72,23 +76,16 @@ if 'MEDIA_DIR' in env:
MEDIA_ROOT = env['MEDIA_DIR']
# Database
if 'DATABASE_URL' in os.environ:
DATABASES = {'default': dj_database_url.config()}
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': env.get('PGDATABASE', APP_NAME),
'CONN_MAX_AGE': 600, # number of seconds database connections should persist for
# User, host and port can be configured by the PGUSER, PGHOST and
# PGPORT environment variables (these get picked up by libpq).
}
DATABASES = {
'default': {
'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.postgresql'),
'NAME': os.environ.get('POSTGRES_DB', 'app'),
'USER': os.environ.get('POSTGRES_USER', 'app'),
'PASSWORD': os.environ.get('POSTGRES_PASSWORD', ''),
'HOST': os.environ.get('POSTGRES_HOST', 'localhost'),
'PORT': os.environ.get('POSTGRES_PORT', 5432),
}
}
# Email via ESP

View file

@ -1,30 +1,30 @@
from django.conf.urls import include, url
from django.urls import include, re_path, path
from django.conf import settings
from django.contrib import admin
from django.conf.urls.i18n import i18n_patterns
from wagtail.admin import urls as wagtailadmin_urls
from wagtail.documents import urls as wagtaildocs_urls
from wagtail.core import urls as wagtail_urls
from wagtail import urls as wagtail_urls # wagtail.core renamed to wagtail in 3.0
from wagtail.contrib.sitemaps.views import sitemap
from puput import urls as puput_urls
# from puput import urls as puput_urls # Commented: needs Wagtail 6 update
from feedler import urls as feedler_urls
from publichealth.search import views as search_views
urlpatterns = [
url(r'', include(puput_urls)),
url(r'', include(feedler_urls)),
# path('', include(puput_urls)), # Commented: needs Wagtail 6 update
path('', include(feedler_urls)),
url(r'^django-admin/', admin.site.urls),
path('django-admin/', admin.site.urls),
url(r'^admin/', include(wagtailadmin_urls)),
url(r'^documents/', include(wagtaildocs_urls)),
path('admin/', include(wagtailadmin_urls)),
path('documents/', include(wagtaildocs_urls)),
url(r'^search/$', search_views.search, name='search'),
path('search/', search_views.search, name='search'),
url('^sitemap\.xml$', sitemap),
re_path(r'^sitemap\.xml$', sitemap),
]
@ -39,13 +39,13 @@ if settings.DEBUG:
# Add views for testing 404 and 500 templates
urlpatterns += [
url(r'^test404/$', TemplateView.as_view(template_name='404.html')),
url(r'^test500/$', TemplateView.as_view(template_name='500.html')),
path('test404/', TemplateView.as_view(template_name='404.html')),
path('test500/', TemplateView.as_view(template_name='500.html')),
]
urlpatterns += i18n_patterns(
# These URLs will have /<language_code>/ appended to the beginning
url(r'', include(wagtail_urls)),
path('', include(wagtail_urls)),
)

43
requirements-django4.txt Normal file
View file

@ -0,0 +1,43 @@
# Django 4.0 Upgrade - Python 3.13 compatible versions
# Core
Django==4.0.10
wagtail==3.0.3
elasticsearch==5.5.3
# Database
psycopg2-binary
dj-database-url==1.2.0
# Content
# Note: puput may need manual fixing for Wagtail 3.0
# puput==2.0.0
guess-language-spirit==0.5.3
# Caching
django-redis==5.2.0
redis
# Frontend
django-anymail==9.0
django-colorful==1.3
django-contrib-comments==2.2.0
django-crispy-forms==1.14.0
django-el-pagination==3.3.0
django-compressor
django-libsass==0.9
django-social-share==2.3.0
libsass
Pillow
# Runtime dependencies
gunicorn
whitenoise
python-dotenv
unidecode
# Additional Django 4.0 dependencies
django-configurations
django-taggit
djangorestframework
django-modelcluster

View file

@ -1,30 +1,33 @@
# Updated: 22.05.2024
# Updated: 2025-09-18 - Django 5.0 Upgrade
# Core
wagtail==2.15.1
elasticsearch==5.5.3
Django==5.0.9 # Django 5.0 (latest stable in 5.0 series)
wagtail==6.0.6 # Wagtail 6.0 supports Django 5.0
elasticsearch==7.17.9 # Compatible with Django 5.0
# Database
psycopg2-binary==2.9.2
dj-database-url==0.5.0
psycopg2-binary==2.9.10 # Compatible with Django 5.0 and Python 3.13
dj-database-url==2.2.0 # Updated for Django 5.0
# Content
puput==1.1.3
# Note: puput needs update or replacement for Wagtail 6.x
# puput==2.0.0
guess-language-spirit==0.5.3
# Caching
django-redis==5.1.0
django-redis==5.4.0 # Compatible with Django 5.0
redis==5.0.8
# Frontend
django-anymail==8.4
django-anymail==11.0 # Updated for Django 5.0
django-colorful==1.3
django-contrib-comments==2.1.0
django-crispy-forms==1.13.0
django-contrib-comments==2.2.0 # May need testing with Django 5.0
django-crispy-forms==2.3 # Updated for Django 5.0
django-el-pagination==3.3.0
django-libsass==0.9
django-social-share==2.2.1
libsass==0.21.0
Pillow==8.4.0
django-compressor==4.5.1 # Updated for Django 5.0
django-social-share==2.3.0 # May need testing with Django 5.0
libsass==0.23.0 # Updated version
Pillow==10.4.0 # Updated for Django 5.0 and Python 3.12+
# Runtime dependencies
gunicorn
@ -33,4 +36,19 @@ python-dotenv
#ConcurrentLogHandler
unidecode
# Additional dependencies for Django 5.0 and Wagtail 6
django-configurations
django-taggit>=5.0.0 # Required by Wagtail 6
wagtail-modeladmin==2.0.0 # Separate package for ModelAdmin in Wagtail 5+
djangorestframework>=3.15.0 # Required by Wagtail API
django-modelcluster>=6.3 # Required by Wagtail 6
django-treebeard>=4.7 # Required by Wagtail 6
django-filter>=23.3,<24 # Required by Wagtail 6.0.6 (must be <24)
Willow>=1.8.0 # Required by Wagtail for images
beautifulsoup4>=4.12.0 # Required by Wagtail
html5lib>=1.1 # Required by Wagtail
openpyxl>=3.1.0 # For Excel export support
l18n # For Wagtail localization
telepath>=0.3.1 # Required by Wagtail 6
# Django 5.0 timezone support (replaces pytz)
tzdata # For timezone support in Django 5.0

2162
yarn.lock

File diff suppressed because it is too large Load diff