Sync local state with remote after untracked local evolution

Local site-ai/ had diverged significantly from the last commit pushed
to Gitea (new remote-env-setup/remove targets, SITE_CONFIG_DIR/MAIN_REPO
split, credential cleanup in server-install.sh, migration docs) without
ever being committed. This catches the repo up to what's actually on disk.

Also untracks the legacy user/ subtree left over from before content was
split into its own standalone repo (natascha-rieter.nl-user) — user/ is
gitignored here and stays a separate git repo, unaffected by this commit.
This commit is contained in:
2026-08-30 14:48:54 +02:00
parent 9f798daaa3
commit 8c93dfd7c9
106 changed files with 1056 additions and 1081 deletions
+35 -8
View File
@@ -1,9 +1,36 @@
REMOTE_USER=root
REMOTE_HOST=example.com
REMOTE_HOME=/home/example.com
REPO=https://gitea.example.com/user/natascha-rieter.nl-user.git
GRAV_VERSION=1.7.49.5
# Copy to .env and fill in real values. .env is gitignored — never commit it.
#
# cp .env.example .env
GITEA_HOST=gitea.example.com
GITEA_USER=natascha-deploy
GITEA_TOKEN=your-token-here
# ── SSH access to the production server ─────────────────────────────────────
REMOTE_USER=
REMOTE_HOST=
REMOTE_PORT=22
# Home directory of REMOTE_USER on the server. Used to derive WEBROOT and
# SITE_CONFIG_DIR below unless you override those directly.
REMOTE_HOME=
# Override only if the site doesn't live at $REMOTE_HOME/public_html
# WEBROOT=
# Override only if the config repo shouldn't clone to $REMOTE_HOME/site-config
# SITE_CONFIG_DIR=
# Hostname production identifies itself as to Grav (used by
# `make remote-configure-env` to pin git-sync's direction for this env —
# see user/env/<host>/config/... in CLAUDE.md). Defaults to
# nieuw.natascha-rieter.nl; override only if that changes.
# REMOTE_ENV_HOST=
# ── Gitea (content + config repos) ──────────────────────────────────────────
GITEA_HOST=
GITEA_USER=
GITEA_TOKEN=
# Git URLs for the two repos (used by `make remote-install` to clone them
# onto a fresh server)
USER_REPO=
MAIN_REPO=
# ── Grav ─────────────────────────────────────────────────────────────────────
# e.g. 1.7.49.5
GRAV_VERSION=
+5 -5
View File
@@ -1,11 +1,11 @@
# Environment
.env
# Grav CMS
user/accounts/
user/data/
user/cache/
user/plugins/
# Grav CMS content (standalone repo, see README)
user/
# Local scratch / migration testing
migration-test/
# Claude
.claude/
Regular → Executable
+27
View File
@@ -8,3 +8,30 @@
- **_resources/**: Contains html, css files and fonts
- **./**: Grav CMS for natascha-rieter.nl
### Environment
**Never read `.env`** — it contains sensitive credentials. You may pass it to commands (e.g. `docker compose`, `make`) but never read its contents directly. Ask the user if you need environment-specific information.
### Remote operations
Always use `make` commands for anything on the production server (`make remote-install-plugins`, `make remote-clean`, etc.) — never SSH directly since credentials live in `.env`. If a remote operation isn't covered by an existing `make` command, either ask the user to run it manually or suggest adding a new `make` command if it seems reusable.
### Content sync
- `make content-push` — commit and push `user/` to Gitea (triggers production pull via webhook)
- `make content-pull` — pull latest from Gitea to local
- `plugins.txt` is manually maintained — installing a plugin via Admin does NOT update it
### git-sync direction (per environment)
`sync.direction` in `user/config/plugins/git-sync.yaml` is shared/synced, so setting it there fights between environments (this caused a production outage: `direction: pull` synced everywhere and silently disabled production's pushes for days). The fix is Grav's `user/env/<hostname>/config/...` override path — it's outside git-sync's tracked folders and gitignored (`user/.gitignore` only allow-lists `pages/`, `themes/`, `config/`, `accounts/`), so it never syncs and is safe to pin per machine.
- `make configure-env-local` — pins this local checkout to `direction: pull` (never pushes)
- `make remote-configure-env` — pins production (`REMOTE_ENV_HOST`, default `nieuw.natascha-rieter.nl`) to `direction: both`
Re-run `configure-env-local` after a fresh clone or a wiped local checkout — the override files are intentionally untracked, so they don't survive a re-clone on their own.
### User repo gitignore
Only these folders are tracked in the `user/` Git repo: `pages/`, `config/`, `accounts/`, `themes/`. The `plugins/` and `data/` folders are excluded.
Regular → Executable
+67 -13
View File
@@ -1,10 +1,13 @@
-include .env
export
SSH := $(REMOTE_USER)@$(REMOTE_HOST)
WEBROOT := $(REMOTE_HOME)/public_html
REMOTE_PORT ?= 22
SSH := ssh -p $(REMOTE_PORT) $(REMOTE_USER)@$(REMOTE_HOST)
WEBROOT ?= $(REMOTE_HOME)/public_html
SITE_CONFIG_DIR ?= $(REMOTE_HOME)/site-config
# ── Local dev ──────────────────────────────────────────────────────────────────
# Local dev
start:
docker compose up -d
@@ -16,25 +19,76 @@ setup: start install-plugins
install-plugins:
docker exec natascha_grav php /app/www/public/bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y
# Content (user repo)
# ── Environment-specific config (git-sync direction) ───────────────────────────
# git-sync's `direction` lives in a shared, synced config file, so setting it
# there fights between environments. These targets instead write it into Grav's
# env/<hostname>/config/... override path, which is gitignored on purpose and
# never syncs. Re-run after a fresh clone/install or a wiped local checkout.
REMOTE_ENV_HOST ?= nieuw.natascha-rieter.nl
configure-env-local:
mkdir -p user/env/localhost/config/plugins user/env/cli/config/plugins
printf 'sync:\n direction: pull\n' | tee user/env/localhost/config/plugins/git-sync.yaml user/env/cli/config/plugins/git-sync.yaml > /dev/null
@echo "Local git-sync pinned to pull-only (user/env/{localhost,cli}/config/plugins/git-sync.yaml)."
# ── Content sync (user repo ↔ Gitea) ──────────────────────────────────────────
content-push:
git subtree push --prefix=user user-deploy main
git -C user push origin main
content-pull:
git subtree pull --prefix=user user-deploy main --squash
git -C user pull origin main
# ── Remote credentials ─────────────────────────────────────────────────────────
remote-env-setup:
@$(SSH) "printf 'GITEA_HOST=%s\nGITEA_USER=%s\nGITEA_TOKEN=%s\n' \
'$(GITEA_HOST)' '$(GITEA_USER)' '$(GITEA_TOKEN)' > ~/.env-natascha && chmod 600 ~/.env-natascha"
@echo "Credentials written to server. Run 'make remote-env-remove' when done."
remote-env-remove:
@$(SSH) "rm -f ~/.env-natascha"
@echo "Credentials removed from server."
# ── Remote: initial install ────────────────────────────────────────────────────
remote-wipe:
$(SSH) "cd $(WEBROOT) && rm -rf assets backup bin cache images logs system tmp vendor webserver-configs index.php .htaccess CHANGELOG.md LICENSE.txt README.md"
# Remote
remote-install:
ssh $(SSH) "WEBROOT=$(WEBROOT) REPO=$(REPO) GRAV_VERSION=$(GRAV_VERSION) PLUGINS='$(shell cat plugins.txt | tr '\n' ' ')' GITEA_HOST=$(GITEA_HOST) GITEA_USER=$(GITEA_USER) GITEA_TOKEN=$(GITEA_TOKEN) bash -s" < scripts/server-install.sh
$(SSH) "WEBROOT=$(WEBROOT) \
SITE_CONFIG_DIR=$(SITE_CONFIG_DIR) \
USER_REPO=$(USER_REPO) \
MAIN_REPO=$(MAIN_REPO) \
GRAV_VERSION=$(GRAV_VERSION) \
PLUGINS='$(shell cat plugins.txt | tr '\n' ' ')' \
GITEA_HOST=$(GITEA_HOST) \
GITEA_USER=$(GITEA_USER) \
GITEA_TOKEN=$(GITEA_TOKEN) \
bash -s" < scripts/server-install.sh
remote-deploy:
ssh $(SSH) "cd $(WEBROOT)/user && git pull && cd $(WEBROOT) && php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y"
# ── Remote: ongoing maintenance ────────────────────────────────────────────────
remote-fetch:
$(SSH) "git -C $(SITE_CONFIG_DIR) pull"
remote-install-plugins:
ssh $(SSH) "cd $(WEBROOT) && php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y"
$(SSH) "cd $(WEBROOT) && php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y"
remote-upgrade-grav:
ssh $(SSH) "cd $(WEBROOT) && php bin/grav upgrade"
$(SSH) "cd $(WEBROOT) && php bin/grav upgrade"
remote-clean:
ssh $(SSH) "cd $(WEBROOT) && php bin/grav clearcache"
$(SSH) "cd $(WEBROOT) && php bin/grav clearcache"
remote-maintenance-on:
$(SSH) "bash -s on $(WEBROOT)" < scripts/server-maintenance.sh
remote-maintenance-off:
$(SSH) "bash -s off $(WEBROOT)" < scripts/server-maintenance.sh
remote-configure-env:
$(SSH) "mkdir -p $(WEBROOT)/user/env/$(REMOTE_ENV_HOST)/config/plugins $(WEBROOT)/user/env/cli/config/plugins && \
printf 'sync:\n direction: both\n' | tee $(WEBROOT)/user/env/$(REMOTE_ENV_HOST)/config/plugins/git-sync.yaml $(WEBROOT)/user/env/cli/config/plugins/git-sync.yaml > /dev/null"
@echo "Production git-sync pinned to push+pull for $(REMOTE_ENV_HOST) and cli."
Regular → Executable
+111 -62
View File
@@ -1,94 +1,143 @@
# natascha-rieter.nl — Grav CMS
## Local development
Grav CMS site for natascha-rieter.nl. Local dev via Docker; production on a VPS managed entirely through `make`.
Requires Docker. To set up from scratch:
---
```
make setup
```
## Repository structure
This starts the container and installs all plugins listed in `plugins.txt` via GPM.
Two git repos:
Other commands:
| Repo | Contents | Location |
|------|----------|----------|
| `natascha-rieter-nl` (this repo) | Docker setup, Makefile, scripts, plugins.txt | `site-ai/` |
| `natascha-rieter.nl-user` | Site config, pages, theme | `user/` (standalone git repo) |
```
make start # start the container
make stop # stop the container
make install-plugins # (re)install plugins from plugins.txt
The `user/` directory is a standalone git repo — its changes are pushed/pulled independently to Gitea. The Grav Sync plugin on the server automatically pulls from Gitea when content is pushed.
---
## Prerequisites
- Docker (for local dev)
- SSH access to the production server
- Both Gitea repos created and accessible
- A Gitea personal access token with repo read/write access
---
## Local development setup
```bash
cp .env.example .env # fill in your values — never commit this file
make setup # start Docker container and install plugins
```
Site runs at http://localhost:8080.
## Repository structure
Clone the user content repo into `user/` if not already present:
This project uses two git repositories:
| Repo | Contents | Purpose |
|------|----------|---------|
| `natascha-rieter-nl` (this repo) | Docker setup, Makefile, scripts | Local dev and server management |
| `natascha-rieter.nl-user` | Contents of `user/` | Deployed to production, synced with Git Sync |
The `user/` folder in this repo is linked to the user repo via git subtree.
### What goes where
| Path | Description | In git |
|------|-------------|--------|
| `user/config/` | Site and plugin configuration | user repo |
| `user/pages/` | Page content | user repo |
| `user/themes/natascha/` | Custom theme | user repo |
| `user/plugins/` | Plugins (see plugins.txt) | no |
| `user/accounts/` | Admin credentials | no |
| `user/data/` | Runtime data | no |
| `user/cache/` | Generated cache | no |
## Deployment
Production runs on a VPS with Apache. The user repo is cloned into the Grav `user/` folder on the server and kept in sync via the Git Sync plugin.
### Remote config
Copy `.env.example` to `.env` and fill in your values (gitignored):
```
SERVER=user@example.com
WEBROOT=/path/to/public_html
USER_REPO=ssh://git@gitea.example.com/user/repo.git
```bash
git clone $USER_REPO user/
```
### First-time install
---
```
## First-time server setup
**1. Fill in `.env`** — copy `.env.example` and set all values including `REMOTE_USER`, `REMOTE_HOST`, `USER_REPO`, `MAIN_REPO`, and Gitea credentials.
**2. Run the install:**
```bash
make remote-install
```
This SSHes into the server and runs `scripts/server-install.sh`, which installs Grav and clones the user repo.
This SSHes into the server, downloads Grav, clones both repos (user content + this config repo), installs plugins, and prints the server's SSH public key.
### Deploying content changes
**3. Add the SSH key to Gitea** — copy the printed public key and add it as a read-only deploy key to both Gitea repos. After this, `make remote-fetch` works without credentials.
```
make content-push # push local user/ changes to the user repo on Gitea
make content-pull # pull editor's content changes back locally
---
## Content sync workflow
Editors push content via the Grav Admin panel or by editing files in `user/`. The Grav Sync plugin on the server syncs changes automatically to Gitea.
To pull those editor changes locally:
```bash
make content-pull # pull latest user/ content from Gitea → local
```
After `make content-push`, trigger a deploy on the server:
To push local changes to Gitea (triggers server sync):
```
make remote-deploy # pull latest user repo changes and install plugins on server
```bash
git -C user add -A && git -C user commit -m "content: describe change"
make content-push # push local user/ commits → Gitea
```
### Remote maintenance
---
## All commands
### Local
| Command | Description |
|---------|-------------|
| `make start` | Start the local Docker container |
| `make stop` | Stop the local Docker container |
| `make setup` | Start container and install all plugins from plugins.txt |
| `make install-plugins` | (Re)install plugins from plugins.txt in the local container |
| `make content-push` | Push local `user/` commits to Gitea |
| `make content-pull` | Pull latest `user/` content from Gitea |
### Remote credentials
| Command | Description |
|---------|-------------|
| `make remote-env-setup` | Write Gitea credentials to `~/.env-natascha` on the server |
| `make remote-env-remove` | Delete `~/.env-natascha` from the server |
Always run `make remote-env-remove` when done. Credentials must not persist on the server.
### Remote server management
| Command | Description |
|---------|-------------|
| `make remote-install` | First-time install: download Grav, clone both repos, install plugins |
| `make remote-fetch` | Pull latest config repo (Makefile, scripts, plugins.txt) on the server |
| `make remote-install-plugins` | Install/update plugins from local plugins.txt on the server |
| `make remote-upgrade-grav` | Upgrade Grav core on the server |
| `make remote-clean` | Clear Grav cache on the server |
| `make remote-maintenance-on` | Enable maintenance mode (visitors see offline page) |
| `make remote-maintenance-off` | Disable maintenance mode |
### Typical upgrade workflow
```bash
make remote-maintenance-on
make remote-upgrade-grav
make remote-install-plugins
make remote-clean
make remote-maintenance-off
```
make remote-install-plugins # install/update plugins from plugins.txt on server
make remote-upgrade-grav # upgrade Grav core on server
make remote-clean # clear Grav cache on server
```
---
## Plugins
Plugins are not committed to git. The full list is in `plugins.txt`.
Plugins are not committed to git. The full list is in `plugins.txt` — one plugin name per line.
- Locally: `make install-plugins`
- Remotely: `make remote-install-plugins`
- On server: `make remote-install-plugins`
---
## Security
- `.env` is gitignored. Never commit it — it contains your server credentials and Gitea token.
- `GITEA_TOKEN` exists only in `.env` locally, and in `~/.env-natascha` on the server only during active sessions. Always run `make remote-env-remove` after use.
- `~/.env-natascha` has `chmod 600` — readable only by the SSH user.
- The server pulls from Gitea using its SSH deploy key (read-only). No long-lived token is stored on the server after initial install.
- `scripts/server-install.sh` writes `~/.netrc` for the initial clone only, and deletes it immediately after via a `trap` handler — even if the script fails.
- Credentials are never passed as command-line arguments (they would appear in server process listings). They are passed as environment variables within the SSH session.
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 6.3 MiB

After

Width:  |  Height:  |  Size: 6.3 MiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 263 KiB

After

Width:  |  Height:  |  Size: 263 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
+12
View File
@@ -0,0 +1,12 @@
services:
grav2:
image: lscr.io/linuxserver/grav:2.0.21-ls271
container_name: natascha_grav2_test
environment:
- PUID=1000
- PGID=1000
ports:
- "8081:80"
volumes:
- ./migration-test/user:/config/www/user
restart: "no"
Regular → Executable
View File
+171
View File
@@ -0,0 +1,171 @@
# Grav 1.7 → 2.0 Migration Simulation — Handover
**Status as of 2026-08-25:** Investigation in progress, not concluded. Nothing has
touched production. This document is for a future session to pick up where this
one left off.
## What this is
The user asked (advisory, then "let's look into it — zero impact on production")
what the best strategy is to upgrade local Grav from 1.7.49.5 to the latest 2.x,
and to try installing a 2.x admin UI. Per explicit instruction, instead of
upgrading in place, a **second, fully isolated Docker container** running Grav
2.0.21 was stood up next to the existing 1.7 dev container, using a **copy** of
the real site content, to see what breaks before committing to a real migration.
**Nothing about the live dev container, the `user/` git content repo, or
production has been changed.** All `make remote-*` targets remain untouched.
## Current environment state
| | Container | Image | Port | Data | State |
|---|---|---|---|---|---|
| existing dev | `natascha_grav` | `lscr.io/linuxserver/grav:1.7.49.5-ls244` | 8080 | `./user` (live mount) | **Up**, compose project `site-ai` (`docker-compose.yml`) |
| migration sandbox | `natascha_grav2_test` | `lscr.io/linuxserver/grav:2.0.21-ls271` | 8081 | `./migration-test/user` (copy, no `.git`) | **Stopped** (exited cleanly, `restart: "no"` so it doesn't auto-start) |
Docker runs via **Colima** (no Docker Desktop). Colima must be running before any
of this works: check with `colima status`, start with `colima start`.
### To resume the sandbox container
```bash
cd /Users/mischa/Nextcloud/Projects/Natascha/natascha-rieter.nl/site-ai
docker compose -f docker-compose.grav2.yml -p natascha-grav2-test up -d
```
Then it's reachable at **http://localhost:8081** (front-end) and
**http://localhost:8081/admin** (admin login).
### To tear it down completely (once done investigating)
```bash
cd /Users/mischa/Nextcloud/Projects/Natascha/natascha-rieter.nl/site-ai
docker compose -f docker-compose.grav2.yml -p natascha-grav2-test down
rm -rf migration-test
```
`docker-compose.grav2.yml` itself can also be deleted once the simulation is
finished and a real decision is made — it was created purely as a scratch
sandbox definition and is separate from the production-mirroring
`docker-compose.yml`.
## Files created this investigation (all under `site-ai/`)
- `docker-compose.grav2.yml` — the sandbox compose file (image `2.0.21-ls271`,
port 8081, mounts `./migration-test/user`, `restart: "no"`).
- `migration-test/user/``rsync -a --exclude='.git'` copy of the live `user/`
folder (~50M, `.git` excluded on purpose since `user/` is itself a separate
git content-sync repo — copying `.git` would have created a confusing nested
repo). **This copy is stale** as of whenever it was taken; re-sync before
further testing if `user/` content has changed since:
```bash
rsync -a --exclude='.git' ./user/ ./migration-test/user/
```
- `~/.docker/cli-plugins/docker-compose` (outside repo, machine-level) —
symlink to the Homebrew `docker-compose` binary, registering it as the
`docker compose` CLI plugin. This was a durable fix for `make start` (which
calls `docker compose up -d` and previously failed with
`unknown shorthand flag: 'd'` because no compose plugin was registered).
Chosen explicitly by the user over editing the Makefile. Not something a
future session needs to redo, but worth knowing about if `make start` ever
breaks again on a different machine.
## Findings so far
1. **Grav 2.0 is not an in-place upgrade.** Official docs say so explicitly.
Also, in this project's setup, Grav core lives inside the Docker image, not
in the `./user` volume mount — so an in-place `gpm selfupgrade` wouldn't
persist across container recreation anyway. The real upgrade path is an
**image tag swap** (`1.7.49.5-ls244` → `2.0.21-ls271` or whatever is current)
combined with a content/plugin compatibility pass.
2. **Front-end renders correctly** on Grav 2.0.21 core against the unmodified
1.7-era `user/` content and theme — verified via curl against
`http://localhost:8081/`, including the `langswitcher` redirect (`/` → `/nl`).
3. **Classic `admin` plugin (v1.10.55) login page renders correctly** on Grav
2.0.21 — confirmed via `curl http://localhost:8081/admin`: full CSS/JS asset
loading, Dutch-localized labels, working nonce, no visible PHP errors. This
is a **better result than official Grav 2.0 docs suggest** — the docs imply
classic admin is incompatible with 2.0 and that migration should go through
`admin2` (the alpha SvelteKit rewrite) instead.
4. **No errors, exceptions, fatals, deprecation notices, or warnings** appeared
in `docker logs natascha_grav2_test` for either the `/` or `/admin` requests.
5. `bin/grav`/`bin/gpm` CLI layout is the same path as 1.7
(`/app/www/public/bin/`, must `cd` there first — `FATAL: Must be run from
ROOT directory of Grav!` otherwise), but the **CLI command set differs**
between 1.7 and 2.0 — e.g. `php bin/grav plugins` doesn't exist in 2.0
(confirm via `php bin/grav list`).
6. Grav's built-in `page-system-validator` tool (meant for before/after
render-diffing across an upgrade) **cannot be used from the CLI on this
setup** — it throws a PHP fatal
(`Call to a member function param() on null in .../admin/admin.php:354`)
because the `admin` plugin's `onPageInitialized()` handler expects an HTTP
request object that doesn't exist under CLI invocation. This happened on the
**old 1.7 container**, so it's an artifact of the admin plugin's CLI
handling generally, not a 2.0-specific regression. Don't waste time trying
to make this tool work as a validation method — it needs a different
approach (see "Next steps").
## What has NOT been tested yet
- **The actual admin dashboard post-login** — page editing, media management,
anything behind auth. This requires real admin credentials, which were
deliberately not obtained/guessed. If the user provides a throwaway
password (or creates one via `bin/grav user create` in the sandbox
container, which is safe since it's isolated test data), this is the
obvious next concrete step.
- **Individual plugin compatibility**, specifically the ones most likely to
break because they hook deep into core APIs that changed in 2.0:
- `git-sync`
- `flex-objects`
- `admin-media-move`, `admin-media-replace`, `admin-media-actions`
- `automagic-images`
- `social-meta-tags`
- `langswitcher` (front-end redirect already confirmed working, but not
deeper admin-side behavior)
- `draft-preview`
Grav 2.0's plugin blueprints can declare a `compatibility:` key; check each
plugin's `blueprints.yaml` under `migration-test/user/plugins/<name>/` for
this key, and/or check each plugin's upstream repo/changelog for 2.0
support statements. `bin/gpm info <plugin>` may also surface something once
run from inside the sandbox container (untested).
- **`admin2`** (the alpha SvelteKit/Vite/Tailwind admin rewrite) has not been
installed or tested in the sandbox at all. It's not GPM-installable — it
requires manually git-cloning `grav-plugin-api` and `grav-plugin-admin2`
into `user/plugins/`. Given it's explicitly alpha/non-production-ready
upstream, and classic admin already renders fine on 2.0 in this test, it's
worth explicitly asking the user whether they still want this installed
before spending time on it, rather than assuming yes from the original
advisory question.
## Suggested next steps (pick up here)
1. Bring the sandbox back up (command above), re-sync `migration-test/user`
from `./user` if content has changed since.
2. Either:
- Ask the user for a throwaway admin password to log in and click through
the real dashboard/page-editing/media flows, **or**
- Create a fresh test admin user inside the sandbox container only
(`docker exec -it natascha_grav2_test sh -c "cd /app/www/public && php bin/grav user create"`)
— safe since it only affects the isolated `migration-test/user` copy.
3. Go through the plugin list above one by one: check blueprint
`compatibility:` declarations, and exercise each plugin's actual feature
(git-sync a commit, upload/replace media, edit a flex-objects-backed page)
inside the sandbox to see what actually breaks vs. what merely lacks a
compatibility flag.
4. Once a real compatibility picture exists, come back to the original
question — decide whether the production upgrade path should be:
(a) image-tag swap + fix whatever plugins broke, or
(b) use Grav's official `migrate-grav` GPM plugin (side-by-side staged
migration with Reset/Restart/Promote controls) instead of doing it by hand.
This official tool was identified during research but deliberately not
used yet, since the user asked specifically for a separate-container
simulation first.
5. Revisit whether `admin2` is still wanted given classic admin's
better-than-expected 2.0 compatibility.
+561
View File
@@ -0,0 +1,561 @@
# Deployment Toolchain Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build a complete Make-based deployment toolchain for the natascha-rieter.nl Grav CMS site, covering initial server setup, content sync via Gitea, remote Grav management, temporary credential handling, and maintenance mode.
**Architecture:** Two git repos (root config repo + user content repo) are cloned on both local machine and server. The Makefile is the single interface for all operations, building SSH commands from local `.env`. Gitea credentials exist on the server only when explicitly written by `remote-env-setup` and are always removed with `remote-env-remove`. Content sync goes through Gitea; the Grav Sync plugin handles automatic server-side pulls. The `user/` directory is a standalone nested git repo (replacing git subtree).
**Tech Stack:** GNU Make, Bash (scripts only where Make is insufficient), SSH, Grav CMS 1.7, Gitea, Git, Docker (local only)
---
## File Map
| Action | File |
|--------|------|
| Modify | `.env.example` |
| Modify | `Makefile` |
| Modify | `scripts/server-install.sh` |
| Create | `scripts/server-maintenance.sh` |
| Modify | `README.md` |
---
### Task 1: Update `.env.example`
Add `USER_REPO`, `MAIN_REPO`, `SITE_CONFIG_DIR`. Rename `REPO``USER_REPO`. Add comments.
**Files:**
- Modify: `.env.example`
- [ ] **Step 1: Replace `.env.example` content**
```
# SSH connection
REMOTE_USER=root
REMOTE_HOST=example.com
REMOTE_HOME=/home/example.com
# Server paths (derived from REMOTE_HOME in Makefile; override here if needed)
WEBROOT=/home/example.com/public_html
SITE_CONFIG_DIR=/home/example.com/site-config
# Grav
GRAV_VERSION=1.7.49.5
# Repos
USER_REPO=https://gitea.example.com/org/natascha-rieter.nl-user.git
MAIN_REPO=https://gitea.example.com/org/natascha-rieter-nl.git
# Gitea credentials — never commit these; only ever in .env (local) or ~/.env-natascha (server, temporary)
GITEA_HOST=gitea.example.com
GITEA_USER=natascha-deploy
GITEA_TOKEN=your-token-here
```
- [ ] **Step 2: Verify `.env` (your local copy) has matching keys**
Open `.env` and add any keys missing compared to `.env.example`. Fill in real values. Do not change `.env.example` values — they stay as placeholders.
- [ ] **Step 3: Commit `.env.example`**
```bash
git add .env.example
git commit -m "config: update env.example with USER_REPO, MAIN_REPO, SITE_CONFIG_DIR"
```
---
### Task 2: Migrate `user/` from git subtree to standalone nested repo
The `user/` directory is currently tracked by the main repo via git subtree. We convert it to a standalone git repo so `content-push`/`content-pull` are simple `git push`/`git pull` operations.
**Files:**
- Modify: `.gitignore` (root of `site-ai/`)
- No code files changed
- [ ] **Step 1: Push current user/ state via old method (safety sync)**
```bash
cd /path/to/site-ai
git subtree push --prefix=user user-deploy main
```
Expected: pushes current user/ state to Gitea. If this fails because nothing changed, that's fine.
- [ ] **Step 2: Remove user/ from main repo tracking**
```bash
git rm -r --cached user/
```
Expected: output like `rm 'user/config/site.yaml'` for each file. The files stay on disk.
- [ ] **Step 3: Add user/ to .gitignore**
Create or edit `.gitignore` in the `site-ai/` root and add:
```
/user/
```
- [ ] **Step 4: Remove the old user-deploy git remote**
```bash
git remote remove user-deploy
```
Expected: no output, no error.
- [ ] **Step 5: Commit the removal**
```bash
git add .gitignore
git commit -m "chore: remove user/ from main repo tracking (now standalone git repo)"
```
- [ ] **Step 6: Clone user repo fresh into user/**
Replace `$USER_REPO` with the value from your `.env`.
```bash
rm -rf user/
git clone $USER_REPO user/
```
Expected: `user/` is now a proper git repo with its own `.git/` directory, tracking Gitea.
- [ ] **Step 7: Verify**
```bash
git -C user status
git -C user remote -v
```
Expected: clean working tree, remote `origin` pointing to Gitea user repo.
---
### Task 3: Update Makefile — content sync and variable defaults
Replace subtree-based targets with direct git push/pull. Allow `.env` to override `WEBROOT` and `SITE_CONFIG_DIR`.
**Files:**
- Modify: `Makefile`
- [ ] **Step 1: Replace the full Makefile**
```makefile
-include .env
export
SSH := $(REMOTE_USER)@$(REMOTE_HOST)
WEBROOT ?= $(REMOTE_HOME)/public_html
SITE_CONFIG_DIR ?= $(REMOTE_HOME)/site-config
# ── Local dev ──────────────────────────────────────────────────────────────────
start:
docker compose up -d
stop:
docker compose down
setup: start install-plugins
install-plugins:
docker exec natascha_grav php /app/www/public/bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y
# ── Content sync (user repo ↔ Gitea) ──────────────────────────────────────────
content-push:
git -C user push origin main
content-pull:
git -C user pull origin main
# ── Remote credentials ─────────────────────────────────────────────────────────
remote-env-setup:
@ssh $(SSH) "printf 'GITEA_HOST=%s\nGITEA_USER=%s\nGITEA_TOKEN=%s\n' \
'$(GITEA_HOST)' '$(GITEA_USER)' '$(GITEA_TOKEN)' > ~/.env-natascha && chmod 600 ~/.env-natascha"
@echo "Credentials written to server. Run 'make remote-env-remove' when done."
remote-env-remove:
@ssh $(SSH) "rm -f ~/.env-natascha"
@echo "Credentials removed from server."
# ── Remote: initial install ────────────────────────────────────────────────────
remote-install:
ssh $(SSH) "WEBROOT=$(WEBROOT) \
SITE_CONFIG_DIR=$(SITE_CONFIG_DIR) \
USER_REPO=$(USER_REPO) \
MAIN_REPO=$(MAIN_REPO) \
GRAV_VERSION=$(GRAV_VERSION) \
PLUGINS='$(shell cat plugins.txt | tr '\n' ' ')' \
GITEA_HOST=$(GITEA_HOST) \
GITEA_USER=$(GITEA_USER) \
GITEA_TOKEN=$(GITEA_TOKEN) \
bash -s" < scripts/server-install.sh
# ── Remote: ongoing maintenance ────────────────────────────────────────────────
remote-fetch:
ssh $(SSH) "git -C $(SITE_CONFIG_DIR) pull"
remote-install-plugins:
ssh $(SSH) "cd $(WEBROOT) && php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y"
remote-upgrade-grav:
ssh $(SSH) "cd $(WEBROOT) && php bin/grav upgrade"
remote-clean:
ssh $(SSH) "cd $(WEBROOT) && php bin/grav clearcache"
remote-maintenance-on:
ssh $(SSH) "bash -s on $(WEBROOT)" < scripts/server-maintenance.sh
remote-maintenance-off:
ssh $(SSH) "bash -s off $(WEBROOT)" < scripts/server-maintenance.sh
```
> Note: indentation in Makefiles must be tabs, not spaces.
- [ ] **Step 2: Verify Make parses without errors**
```bash
make -n start
```
Expected: prints `docker compose up -d`, no errors.
- [ ] **Step 3: Commit**
```bash
git add Makefile
git commit -m "feat: overhaul Makefile with full deployment toolchain"
```
---
### Task 4: Update `scripts/server-install.sh`
Add `SITE_CONFIG_DIR` and `MAIN_REPO` variables, clone the main repo to `SITE_CONFIG_DIR`, rename `REPO``USER_REPO`, print SSH public key at the end for Gitea deploy key setup.
**Files:**
- Modify: `scripts/server-install.sh`
- [ ] **Step 1: Replace the full script**
```bash
#!/bin/bash
set -e
: "${WEBROOT:?WEBROOT is not set}"
: "${SITE_CONFIG_DIR:?SITE_CONFIG_DIR is not set}"
: "${USER_REPO:?USER_REPO is not set}"
: "${MAIN_REPO:?MAIN_REPO is not set}"
: "${GRAV_VERSION:?GRAV_VERSION is not set}"
: "${PLUGINS:?PLUGINS is not set}"
: "${GITEA_HOST:?GITEA_HOST is not set}"
: "${GITEA_USER:?GITEA_USER is not set}"
: "${GITEA_TOKEN:?GITEA_TOKEN is not set}"
echo "==> Setting up credentials (temporary)"
printf 'machine %s\nlogin %s\npassword %s\n' "$GITEA_HOST" "$GITEA_USER" "$GITEA_TOKEN" > ~/.netrc
chmod 600 ~/.netrc
echo "==> Downloading Grav $GRAV_VERSION"
cd "$WEBROOT"
wget -q "https://getgrav.org/download/core/grav-admin/$GRAV_VERSION" -O grav-admin.zip
unzip -q grav-admin.zip
mv grav-admin/* grav-admin/.htaccess .
rm -rf grav-admin grav-admin.zip
echo "==> Cloning user repo"
rm -rf user
git clone "$USER_REPO" user
echo "==> Cloning main config repo to $SITE_CONFIG_DIR"
mkdir -p "$SITE_CONFIG_DIR"
git clone "$MAIN_REPO" "$SITE_CONFIG_DIR"
echo "==> Installing plugins"
php bin/gpm install $PLUGINS -y
echo "==> Setting permissions"
find "$WEBROOT" -type f -exec chmod 664 {} \;
find "$WEBROOT" -type d -exec chmod 775 {} \;
echo "==> Removing temporary credentials"
rm -f ~/.netrc
echo ""
echo "==> Done."
echo ""
echo "NEXT STEP — add this server's SSH public key to both Gitea repos as a deploy key"
echo "so that 'make remote-fetch' and future git pulls work without credentials:"
echo ""
cat ~/.ssh/id_rsa.pub 2>/dev/null || cat ~/.ssh/id_ed25519.pub 2>/dev/null || \
echo " No SSH key found. Generate one on the server: ssh-keygen -t ed25519 -C 'server-deploy'"
echo ""
echo "Visit your domain to complete Grav setup."
```
- [ ] **Step 2: Commit**
```bash
git add scripts/server-install.sh
git commit -m "feat: server-install clones both repos, removes credentials after use"
```
---
### Task 5: Create `scripts/server-maintenance.sh`
Toggle Grav's built-in maintenance mode by setting `pages.offline` in `user/config/system.yaml`. Grav serves its built-in offline page when this is `true`.
**Files:**
- Create: `scripts/server-maintenance.sh`
- [ ] **Step 1: Create the script**
```bash
#!/bin/bash
set -e
MODE="$1"
WEBROOT="$2"
CONFIG="$WEBROOT/user/config/system.yaml"
if [ "$MODE" != "on" ] && [ "$MODE" != "off" ]; then
echo "Usage: server-maintenance.sh on|off <webroot>"
exit 1
fi
[ -f "$CONFIG" ] || { echo "Not found: $CONFIG"; exit 1; }
VALUE="false"
[ "$MODE" = "on" ] && VALUE="true"
if grep -q "^\s*offline:" "$CONFIG"; then
sed -i "s/^\(\s*\)offline: .*/\1offline: $VALUE/" "$CONFIG"
else
printf '\npages:\n offline: %s\n' "$VALUE" >> "$CONFIG"
fi
echo "Maintenance mode: $MODE (offline: $VALUE)"
```
- [ ] **Step 2: Make executable**
```bash
chmod +x scripts/server-maintenance.sh
```
- [ ] **Step 3: Verify the script parses cleanly**
```bash
bash -n scripts/server-maintenance.sh
```
Expected: no output, exit 0.
- [ ] **Step 4: Commit**
```bash
git add scripts/server-maintenance.sh
git commit -m "feat: add server-maintenance.sh to toggle Grav offline mode"
```
---
### Task 6: Update `README.md`
Full rewrite. Covers every Make command (one-line description each), setup guide, content sync workflow, and security notes.
**Files:**
- Modify: `README.md`
- [ ] **Step 1: Replace README.md**
```markdown
# natascha-rieter.nl — Grav CMS
Grav CMS site for natascha-rieter.nl. Local dev via Docker; production on a VPS managed entirely through `make`.
---
## Repository structure
Two git repos:
| Repo | Contents | Location |
|------|----------|----------|
| `natascha-rieter-nl` (this repo) | Docker setup, Makefile, scripts, plugins.txt | `site-ai/` |
| `natascha-rieter.nl-user` | Site config, pages, theme | `user/` (nested git repo) |
The `user/` directory is a standalone git repo — its changes are pushed/pulled independently to Gitea. The Grav Sync plugin on the server automatically pulls from Gitea when content is pushed.
---
## Prerequisites
- Docker (for local dev)
- SSH access to the server
- Both Gitea repos created
- A Gitea personal access token with repo read/write access
---
## Local development setup
```bash
cp .env.example .env # fill in your values
make setup # start Docker, install plugins
```
Site runs at http://localhost:8080.
Clone the user content repo into `user/` if not already present:
```bash
git clone $USER_REPO user/
```
---
## First-time server setup
1. **Fill in `.env`** — copy `.env.example`, set all values.
2. **Run the install:**
```bash
make remote-install
```
This SSHes into the server, downloads Grav, clones both repos, installs plugins, and prints the server's SSH public key.
3. **Add the SSH key to Gitea** — copy the printed public key and add it as a deploy key to both Gitea repos (read access is enough for `remote-fetch`; the user repo also needs write if Git Sync pushes back).
After this, `make remote-fetch` works without credentials.
---
## Content sync workflow
Editors push content via the Grav Admin panel (or directly edit files in `user/`). The Grav Sync plugin on the server syncs automatically to Gitea.
To pull those changes locally:
```bash
make content-pull # pull latest user content from Gitea → local user/
```
To push local changes back to Gitea (and trigger server sync):
```bash
git -C user add -A && git -C user commit -m "content: ..."
make content-push # push local user/ changes → Gitea
```
---
## All commands
### Local
| Command | Description |
|---------|-------------|
| `make start` | Start the local Docker container |
| `make stop` | Stop the local Docker container |
| `make setup` | Start container and install all plugins |
| `make install-plugins` | (Re)install plugins from `plugins.txt` in the local container |
| `make content-push` | Push local `user/` commits to Gitea |
| `make content-pull` | Pull latest `user/` content from Gitea |
### Remote credentials
| Command | Description |
|---------|-------------|
| `make remote-env-setup` | Write Gitea credentials to `~/.env-natascha` on the server |
| `make remote-env-remove` | Delete `~/.env-natascha` from the server |
Always run `make remote-env-remove` when done with operations that required it.
### Remote server management
| Command | Description |
|---------|-------------|
| `make remote-install` | First-time install: download Grav, clone both repos, install plugins |
| `make remote-fetch` | Pull latest main repo (Makefile, scripts, plugins.txt) on the server |
| `make remote-install-plugins` | Install/update plugins from local `plugins.txt` on the server |
| `make remote-upgrade-grav` | Upgrade Grav core on the server |
| `make remote-clean` | Clear Grav cache on the server |
| `make remote-maintenance-on` | Enable Grav maintenance mode (site shows offline page) |
| `make remote-maintenance-off` | Disable Grav maintenance mode |
### Typical upgrade workflow
```bash
make remote-maintenance-on
make remote-env-setup
make remote-fetch
make remote-upgrade-grav
make remote-install-plugins
make remote-env-remove
make remote-maintenance-off
make remote-clean
```
---
## Plugins
Plugins are not committed to git. The full list is in `plugins.txt` — one plugin name per line.
- Locally: `make install-plugins`
- On server: `make remote-install-plugins`
---
## Security
- `.env` is gitignored. Never commit it.
- `GITEA_TOKEN` exists only in `.env` locally and in `~/.env-natascha` on the server during active sessions. Always run `make remote-env-remove` after use.
- `~/.env-natascha` has `chmod 600` — readable only by the SSH user.
- The server pulls from Gitea using its SSH key (deploy key, read-only). No long-lived token is stored on the server.
- `scripts/server-install.sh` writes `~/.netrc` for the initial clone and deletes it immediately after.
- Credentials are never passed as command-line arguments (they would appear in `ps` output). They are passed as environment variables in the SSH session.
```
- [ ] **Step 2: Commit**
```bash
git add README.md
git commit -m "docs: rewrite README with full command reference, setup guide, security notes"
```
---
## Self-Review
**Spec coverage:**
- ✅ Update initial install to set up both repos
- ✅ content-push/pull interact with Gitea via git (Grav Sync handles server side)
- ✅ remote-fetch (pull main repo changes on server)
- ✅ remote-upgrade-grav
- ✅ remote-install-plugins
- ✅ remote-maintenance-on / remote-maintenance-off
- ✅ remote-env-setup / remote-env-remove
- ✅ No persistent env files on server
- ✅ Make-first, bash only in scripts
- ✅ SSH built from env vars
- ✅ README updated with every command
**Placeholder scan:** None found.
**Type consistency:** N/A (shell/make, no typed interfaces).
Regular → Executable
+1
View File
@@ -13,3 +13,4 @@ automagic-images
admin-media-move
admin-media-replace
admin-media-actions
git-sync
+37 -9
View File
@@ -2,26 +2,42 @@
set -e
: "${WEBROOT:?WEBROOT is not set}"
: "${REPO:?REPO is not set}"
: "${SITE_CONFIG_DIR:?SITE_CONFIG_DIR is not set}"
: "${USER_REPO:?USER_REPO is not set}"
: "${MAIN_REPO:?MAIN_REPO is not set}"
: "${GRAV_VERSION:?GRAV_VERSION is not set}"
: "${PLUGINS:?PLUGINS is not set}"
: "${GITEA_HOST:?GITEA_HOST is not set}"
: "${GITEA_USER:?GITEA_USER is not set}"
: "${GITEA_TOKEN:?GITEA_TOKEN is not set}"
trap 'rm -f ~/.netrc' EXIT
echo "==> Setting up credentials (temporary)"
printf 'machine %s\nlogin %s\npassword %s\n' "$GITEA_HOST" "$GITEA_USER" "$GITEA_TOKEN" > ~/.netrc
chmod 600 ~/.netrc
echo "==> Downloading Grav $GRAV_VERSION"
cd "$WEBROOT"
wget -q "https://getgrav.org/download/core/grav-admin/$GRAV_VERSION" -O grav-admin.zip
unzip -q grav-admin.zip
mv grav-admin/* grav-admin/.htaccess .
wget --no-verbose "https://getgrav.org/download/core/grav-admin/$GRAV_VERSION" -O grav-admin.zip
unzip -oq grav-admin.zip
cp -rf grav-admin/. .
rm -rf grav-admin grav-admin.zip
echo "==> Cloning user repo"
printf 'machine %s\nlogin %s\npassword %s\n' "$GITEA_HOST" "$GITEA_USER" "$GITEA_TOKEN" > ~/.netrc
chmod 600 ~/.netrc
rm -rf user
git clone "$REPO" user
rm ~/.netrc
git clone "$USER_REPO" user
echo "==> Cloning main config repo to $SITE_CONFIG_DIR"
if [ -d "$SITE_CONFIG_DIR/.git" ]; then
git -C "$SITE_CONFIG_DIR" pull
else
rm -rf "$SITE_CONFIG_DIR"
git clone "$MAIN_REPO" "$SITE_CONFIG_DIR"
fi
echo "==> Creating required directories"
mkdir -p user/plugins user/accounts user/data
echo "==> Installing plugins"
php bin/gpm install $PLUGINS -y
@@ -30,4 +46,16 @@ echo "==> Setting permissions"
find "$WEBROOT" -type f -exec chmod 664 {} \;
find "$WEBROOT" -type d -exec chmod 775 {} \;
echo "==> Done. Visit your domain to complete setup."
echo "==> Removing temporary credentials"
rm -f ~/.netrc
echo ""
echo "==> Done."
echo ""
echo "NEXT STEP — add this server's SSH public key to both Gitea repos as a deploy key"
echo "so that 'make remote-fetch' and future git pulls work without credentials:"
echo ""
cat ~/.ssh/id_rsa.pub 2>/dev/null || cat ~/.ssh/id_ed25519.pub 2>/dev/null || \
echo " No SSH key found. Generate one on the server: ssh-keygen -t ed25519 -C 'server-deploy'"
echo ""
echo "Visit your domain to complete Grav setup."
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
set -e
if [ "$#" -ne 2 ]; then
echo "Usage: server-maintenance.sh on|off <webroot>"
exit 1
fi
MODE="$1"
WEBROOT="$2"
CONFIG="$WEBROOT/user/config/system.yaml"
if [ "$MODE" != "on" ] && [ "$MODE" != "off" ]; then
echo "Usage: server-maintenance.sh on|off <webroot>"
exit 1
fi
[ -f "$CONFIG" ] || { echo "Not found: $CONFIG"; exit 1; }
VALUE="false"
[ "$MODE" = "on" ] && VALUE="true"
if grep -q "^[[:space:]]*offline:" "$CONFIG"; then
sed -i "s/^\([[:space:]]*\)offline: .*/\1offline: $VALUE/" "$CONFIG"
else
printf '\npages:\n offline: %s\n' "$VALUE" >> "$CONFIG"
fi
echo "Maintenance mode: $MODE (offline: $VALUE)"
View File
View File
-101
View File
@@ -1,101 +0,0 @@
enabled: true
route: /admin
cache_enabled: true
theme: grav
logo_text: null
body_classes: null
content_padding: true
twofa_enabled: true
sidebar:
activate: tab
hover_delay: 100
size: auto
dashboard:
days_of_stats: 7
widgets_display:
dashboard-maintenance: 'true'
dashboard-statistics: 'true'
dashboard-notifications: 'false'
dashboard-feed: 'false'
dashboard-pages: 'true'
pages:
show_parents: both
show_modular: true
parents_levels: null
session:
timeout: 1800
edit_mode: normal
frontend_preview_target: inline
show_github_msg: false
admin_icons: line-awesome
enable_auto_updates_check: false
notifications:
feed: true
dashboard: true
plugins: true
themes: true
popularity:
enabled: true
ignore:
- '/test*'
- /modular
history:
daily: '30'
monthly: '12'
visitors: '20'
whitelabel:
quicktray_recompile: false
codemirror_theme: paper
codemirror_fontsize: md
codemirror_md_font: sans
logo_custom: { }
logo_login: { }
color_scheme:
accents:
primary-accent: button
secondary-accent: notice
tertiary-accent: critical
colors:
logo-bg: '#323640'
logo-link: '#FFFFFF'
nav-bg: '#3D424E'
nav-text: '#B7B9BD'
nav-link: '#ffffff'
nav-selected-bg: '#323640'
nav-selected-link: '#ffffff'
nav-hover-bg: '#434753'
nav-hover-link: '#ffffff'
toolbar-bg: '#ffffff'
toolbar-text: '#3D424E'
page-bg: '#F6F6F6'
page-text: '#6f7b8a'
page-link: '#0090D9'
content-bg: '#ffffff'
content-text: '#6f7b8a'
content-link: '#0090D9'
content-link2: '#da4b46'
content-header: '#414147'
content-tabs-bg: '#e6e6e6'
content-tabs-text: '#808080'
button-bg: '#0090D9'
button-text: '#ffffff'
notice-bg: '#06A599'
notice-text: '#ffffff'
update-bg: '#77559D'
update-text: '#ffffff'
critical-bg: '#F45857'
critical-text: '#ffffff'
content-highlight: '#ffffd7'
name: null
custom_footer: null
custom_css: null
custom_presets: null
show_beta_msg: null
pagemedia:
resize_width: 0
resize_height: 0
res_min_width: 0
res_min_height: 0
res_max_width: 0
res_max_height: 0
resize_quality: 0.8
@@ -1 +0,0 @@
enabled: false
-5
View File
@@ -1,5 +0,0 @@
enabled: true
built_in_css: true
translated_urls: true
untranslated_pages_behavior: none
language_display: short
-1
View File
@@ -1 +0,0 @@
salt: PD0Borzfn1Ss5U
-19
View File
@@ -1,19 +0,0 @@
title: 'Kunstgalerie Natascha Rieter'
default_lang: nl
author:
name: 'Natascha Rieter'
email: info@natascha-rieter.nl
taxonomies:
- category
- tag
metadata:
description: 'My Grav Site'
summary:
enabled: true
format: short
size: 300
delimiter: '==='
redirects: null
routes: null
blog:
route: /blog
-240
View File
@@ -1,240 +0,0 @@
absolute_urls: false
timezone: null
param_sep: ':'
wrapped_site: false
reverse_proxy_setup: false
force_ssl: false
force_lowercase_urls: true
custom_base_url: null
username_regex: '^[a-z0-9_-]{3,16}$'
pwd_regex: '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}'
intl_enabled: true
http_x_forwarded:
protocol: true
host: false
port: true
ip: true
languages:
supported:
- nl
- en
default_lang: null
include_default_lang: true
include_default_lang_file_extension: true
translations: true
translations_fallback: true
session_store_active: false
http_accept_language: true
override_locale: false
pages_fallback_only: false
debug: false
home:
alias: /home
hide_in_urls: false
pages:
type: regular
dirs:
- 'page://'
theme: natascha
order:
by: default
dir: asc
list:
count: 20
dateformat:
default: null
short: 'jS M Y'
long: 'F jS \a\t g:ia'
publish_dates: true
process:
markdown: true
twig: false
twig_first: false
never_cache_twig: false
events:
page: true
twig: true
markdown:
extra: false
auto_line_breaks: false
auto_url_links: false
escape_markup: false
special_chars:
'>': gt
'<': lt
valid_link_attributes:
- rel
- target
- id
- class
- classes
types:
- html
- htm
- xml
- txt
- json
- rss
- atom
append_url_extension: null
expires: 604800
cache_control: null
last_modified: false
etag: true
vary_accept_encoding: false
redirect_default_code: '302'
redirect_trailing_slash: 1
redirect_default_route: 0
ignore_files:
- .DS_Store
ignore_folders:
- .git
- .idea
ignore_hidden: true
hide_empty_folders: false
url_taxonomy_filters: true
frontmatter:
process_twig: false
ignore_fields:
- form
- forms
cache:
enabled: false
check:
method: file
driver: auto
prefix: g
purge_at: '0 4 * * *'
clear_at: '0 3 * * *'
clear_job_type: standard
clear_images_by_default: false
cli_compatibility: false
lifetime: 604800
purge_max_age_days: 30
gzip: false
allow_webserver_gzip: false
redis:
socket: '0'
password: null
database: null
server: null
port: null
memcache:
server: null
port: null
memcached:
server: null
port: null
twig:
cache: true
debug: true
auto_reload: true
autoescape: true
undefined_functions: true
undefined_filters: true
safe_functions: { }
safe_filters: { }
umask_fix: false
assets:
css_pipeline: false
css_pipeline_include_externals: true
css_pipeline_before_excludes: true
css_minify: true
css_minify_windows: false
css_rewrite: true
js_pipeline: false
js_pipeline_include_externals: true
js_pipeline_before_excludes: true
js_module_pipeline: false
js_module_pipeline_include_externals: true
js_module_pipeline_before_excludes: true
js_minify: true
enable_asset_timestamp: false
enable_asset_sri: false
collections:
jquery: 'system://assets/jquery/jquery-3.x.min.js'
errors:
display: 0
log: true
log:
handler: file
syslog:
facility: local6
tag: grav
debugger:
enabled: true
provider: clockwork
censored: false
shutdown:
close_connection: true
images:
adapter: imagick
default_image_quality: 85
cache_all: false
cache_perms: '0755'
debug: false
auto_fix_orientation: true
seofriendly: false
cls:
auto_sizes: false
aspect_ratio: false
retina_scale: '1'
defaults:
loading: auto
decoding: auto
fetchpriority: auto
watermark:
image: 'system://images/watermark.png'
position_y: center
position_x: center
scale: 33
watermark_all: false
media:
enable_media_timestamp: false
unsupported_inline_types: null
allowed_fallback_types: null
auto_metadata_exif: false
upload_limit: 2097152
session:
enabled: true
initialize: true
timeout: 1800
name: grav-site
uniqueness: path
secure: false
secure_https: true
httponly: true
samesite: Lax
split: true
domain: null
path: null
gpm:
releases: stable
official_gpm_only: true
http:
method: auto
enable_proxy: true
proxy_url: null
proxy_cert_path: null
concurrent_connections: 5
verify_peer: true
verify_host: true
accounts:
type: regular
storage: file
avatar: gravatar
flex:
cache:
index:
enabled: true
lifetime: 60
object:
enabled: true
lifetime: 600
render:
enabled: true
lifetime: 600
strict_mode:
yaml_compat: false
twig_compat: false
blueprint_compat: false
-4
View File
@@ -1,4 +0,0 @@
core:
grav:
version: 1.7.49.5
schema: 1.7.0_2020-11-20_1
-15
View File
@@ -1,15 +0,0 @@
---
title: 'Natascha Rieter'
menu: Home
published: true
sitemap:
lastmod: '19-04-2026 00:00'
portret: portret-2.jpg
extra_1: portret-1.jpg
logo: logo-blauw.png
extra_2: ''
---
I create small sculptures, reliefs, clay paintings, modelled figures and wheel-thrown work. My work is emotional and poetic with a monumental character. The outdoor objects are frost-resistant. In my clay paintings I combine ceramic and painting techniques. Working with clay is for me the same as writing a poem. In recent years I have been almost exclusively occupied with monumental commissions. I work on commission and give courses and workshops.
Side activities: Ceramics teacher, Kumulus in Maastricht; gallery owner of ceramic gallery "Groot Welsden"; owner of B&B de Kunstkamer.
-15
View File
@@ -1,15 +0,0 @@
---
title: 'Natascha Rieter'
menu: Home
published: true
sitemap:
lastmod: '18-04-2026 21:48'
portret: portret-2.jpg
extra_1: portret-1.jpg
logo: logo-blauw.png
extra_2: ''
---
Ik maak kleinplastieken, reliëfs, kleischilderijen, geboetseerde beelden en draaiwerk. Mijn werk is emotioneel en poëtisch met een monumentaal karakter. De objecten voor buiten zijn winterhard. In mijn kleischilderijen worden de keramische- en schilderstechniek met elkaar gecombineerd. Het werken met klei is voor mij het zelfde als het schrijven van een gedicht. De laatste jaren ben ik bijna uitsluitend met monumentale opdrachten bezig geweest. Ik werk in opdracht en geef cursussen en workshops.
Nevenactiviteiten: Docente keramiek, Kumulus te Maastricht, galeriehoudster keramiek galerie "Groot Welsden" en eigenaresse B&B de Kunstkamer.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

-18
View File
@@ -1,18 +0,0 @@
---
title: Natascha Rieter Curriculum Vitae
menu: CV
portret: portret-1.jpg
extra_1: ''
logo: logo-blauw.png
extra_2: ''
---
Natascha Rieter, born in Roermond (1948), has lived since 1988 in Margraten, in the hamlet of Groot Welsden, where she and her husband Siegfried Gorinskat (also a ceramist, who passed away in 2006) established their studios in a typical half-timbered farmhouse. Both artists enjoy great renown in the art world for their ceramic works. In 1989 they opened Ceramic Gallery "Groot Welsden" — with great success. Natascha Rieter is an emotional and poetic artist for whom ceramics is the mirror of the soul.
Natascha studied monumental design, majoring in painting, at the City Academy in Maastricht (19671971). After her studies she worked for two years in a ceramic studio in Switzerland (19711972). From 1972 to 1975 she studied ceramics at the Ceramic Hochschule in Höhr-Grenzhausen, Germany. She then moved to Nijswiller (South Limburg), where she established herself as a ceramist (19761988). She has exhibited in many galleries in the Netherlands, Germany, Belgium, France and Japan. The themes in her work are nature and humanity: LIFE. She translates her feelings into ceramics — "Working with clay is for me the same as writing a poem."
Natascha Rieter makes functional ceramics, reliefs, small sculptures, modelled figures and clay paintings. She uses stoneware clay from Germany and porcelain from France. In her clay paintings she combines ceramics with painting techniques on wooden panels.
In recent years Natascha has been almost exclusively occupied with monumental commissions in applied art — wall reliefs in building lobbies, monumental sculpture groups, and private and corporate commissions.
Side activities: Ceramics teacher at Kumulus, Centre for the Arts, Maastricht since 1977. Gallery owner of Ceramic Gallery "Groot Welsden" since 1989.
-18
View File
@@ -1,18 +0,0 @@
---
title: Natascha Rieter Curriculum Vitae
menu: CV
portret: portret-1.jpg
extra_1: ''
logo: logo-blauw.png
extra_2: ''
---
Natascha Rieter, geboren te Roermond (1948), woont sinds 1988 te Margraten, in het gehucht Groot Welsden, waar zij met haar man Siegfried Gorinskat (ook keramist) (2006 gestorven) in een typische vakwerkboerderij hun ateliers hebben gevestigd. Beide kunstenaars genieten vanwege hun keramische werken grote bekendheid in de kunstwereld. In 1989 openden zij Keramiek Galerie "Groot Welsden" en niet zonder succes. Natascha Rieter is een emotioneel en poëtisch kunstenaar voor wie keramiek de spiegel van de ziel is.
Natascha volgde een opleiding monumentale vormgeving, met als hoofdvak schilderen aan de stadsacademie in Maastricht (19671971). Na deze opleiding werkte zij 2 jaar in een keramisch atelier in Zwitserland (19711972). Van 1972 tot 1975 studeerde zij keramiek aan de keramische Hochschule in Höhr-Grenzhausen in Duitsland. Hierna kwam zij naar Nijswiller (Zuid-Limburg) waar zij zich als keramiste vestigde (19761988). Zij exposeerde in vele galerieën o.a. in Nederland, Duitsland, België, Frankrijk en Japan. De thema's die in haar werk een rol spelen zijn de natuur en de mens: HET LEVEN. Zij vertaalt haar gevoelens in keramiek, "Het werken met klei is voor mij hetzelfde als het schrijven van een gedicht."
Natascha Rieter maakt gebruikskeramiek, reliëfs, kleinplastieken en geboetseerde beelden en klei-schilderijen. Zij gebruikt steengoedklei uit Duitsland en porselein uit Frankrijk. Bij het vervaardigen van haar klei-schilderijen combineert zij keramiek met de schildertechniek en houten paneel.
Sinds vier jaar komt Natascha bijna niet meer toe tot haar vrije werk daar zij bezet is door monumentale opdrachten in de toegepaste kunst. Zoals muurreliëfs in de hal van gebouwen, b.v. een groot reliëf in de Residentie "De Heerlijkheid" te Deventer. Monumentale beeldengroepen heeft zij gemaakt, b.v. voor de Lückerheide kliniek te Kerkrade buiten in de tuin, beeldengroep bij de Hamboskliniek te Kerkrade, twee grafmonumenten op het kerkhof te Roermond, grote beeldengroep bij het restaurant de Leuf in Ubachsberg "tien jaar topgastronomie in beeld" enz. Vele opdrachten voor particulier en bedrijf.
Nevenfuncties: Docente keramiek bij Kumulus, centrum voor de Kunst, te Maastricht sinds 1977. Galeriehoudster van Keramiekgalerie "Groot Welsden" sinds 1989.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

-6
View File
@@ -1,6 +0,0 @@
---
title: Gallery
menu: Gallery
---
A selection of ceramic works by Natascha Rieter — from small sculptures to monumental commissions. Click an image for an enlarged view.
-6
View File
@@ -1,6 +0,0 @@
---
title: Galerie
menu: Galerie
---
Een selectie van keramische werken van Natascha Rieter van kleinplastieken tot monumentale opdrachten. Klik op een afbeelding voor een vergrote weergave.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

@@ -1,7 +0,0 @@
---
title: 'Large objects'
sitemap:
lastmod: '13-06-2026 13:29'
---
A series of larges objects made in the last years
@@ -1,8 +0,0 @@
---
title: 'Grote objecten'
sitemap:
lastmod: '13-06-2026 13:29'
media_order: '10298359_1112080642156692_2004541195504715933_o_1112080642156692.jpg,1936306_1105443762820380_8216889586053322199_n_1105443762820380.jpg,10683502_1112079375490152_4488529684021326036_o_1112079375490152.jpg,11053920_1112081192156637_6689568670232240087_o_1112081192156637.jpg,12029743_1107926249238798_5277465818662466467_o_1107926249238798.jpg,12418905_1112080445490045_6622839067204183466_o_1112080445490045.jpg,12418905_1112080975489992_3344234153253759058_o_1112080975489992.jpg,12418926_1112081552156601_1127226366914273536_o_1112081552156601.jpg,12493616_1112080162156740_7951360208797715360_o_1112080162156740.jpg,12495917_1112079482156808_5584179686913736172_o_1112079482156808.jpg,12496352_1112081338823289_3609436053778783843_o_1112081338823289.jpg,12593704_1112080822156674_1315022773915505238_o_1112080822156674.jpg'
---
Een serie van grote objecte gemaakt in de afgelopen jaren
@@ -1,4 +0,0 @@
---
title: Work in detail
menu: Work in detail
---
@@ -1,4 +0,0 @@
---
title: Werk in detail
menu: Werk in detail
---
-15
View File
@@ -1,15 +0,0 @@
---
title: Contact & Address
menu: Contact
---
**Opening hours**
Every Friday and Saturday
The first Sunday of the month
From 11:00 to 17:00
Also by appointment
Sint Agnesplein 4
NL-6241 CA Bunde
[+31 (0)43 458 27 51](tel:+31434582751)
[info@natascha-rieter.nl](mailto:info@natascha-rieter.nl)
-15
View File
@@ -1,15 +0,0 @@
---
title: Contact & Adres
menu: Contact
---
**Openingstijden**
Iedere vrijdag en zaterdag
De eerste zondag van de maand
Van 11:00 tot 17:00
Ook op afspraak
Sint Agnesplein 4
NL-6241 CA Bunde
[+31 (0)43 458 27 51](tel:+31434582751)
[info@natascha-rieter.nl](mailto:info@natascha-rieter.nl)
-7
View File
@@ -1,7 +0,0 @@
name: Natascha's Theme
version: 1.0.0
description: Custom theme for Natascha Rieter art gallery website
type: theme
thumbnail: /user/themes/natascha/images/logo-blauw.png
author:
name: Claude
@@ -1,4 +0,0 @@
title: Album
'@extends':
type: default
context: blueprints://pages
-31
View File
@@ -1,31 +0,0 @@
title: CV
'@extends':
type: default
context: blueprints://pages
form:
fields:
tabs:
type: tabs
active: 1
fields:
content:
type: tab
title: Inhoud
fields:
header.portret:
type: pagemediaselect
label: 'Portret (zichtbaar op mobiel)'
preview_images: true
header.extra_1:
type: pagemediaselect
label: 'Extra foto (verborgen op mobiel)'
preview_images: true
header.logo:
type: pagemediaselect
label: 'Logo (zichtbaar op mobiel)'
preview_images: true
header.extra_2:
type: pagemediaselect
label: 'Extra foto rechts (verborgen op mobiel)'
preview_images: true
-31
View File
@@ -1,31 +0,0 @@
title: Home
'@extends':
type: default
context: blueprints://pages
form:
fields:
tabs:
type: tabs
active: 1
fields:
content:
type: tab
title: Inhoud
fields:
header.portret:
type: pagemediaselect
label: 'Portret (zichtbaar op mobiel)'
preview_images: true
header.extra_1:
type: pagemediaselect
label: 'Extra foto (verborgen op mobiel)'
preview_images: true
header.logo:
type: pagemediaselect
label: 'Logo (zichtbaar op mobiel)'
preview_images: true
header.extra_2:
type: pagemediaselect
label: 'Extra foto rechts (verborgen op mobiel)'
preview_images: true
-181
View File
@@ -1,181 +0,0 @@
@font-face {
font-family: 'Hobo Std';
src: url('../fonts/HoboStd.eot');
src: url('../fonts/HoboStd.eot?#iefix') format('embedded-opentype'),
url('../fonts/HoboStd.woff2') format('woff2'),
url('../fonts/HoboStd.woff') format('woff'),
url('../fonts/HoboStd.ttf') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
:root {
--color-brand: #0081c4;
--color-text: #293236;
--color-text-light: #909090;
}
.funky-font {
font-family: 'Hobo Std', Helvetica, Verdana, Arial;
}
.text-color-blue { color: var(--color-brand); }
.text-color-white { color: #ffffff; }
.text-color-dark-grey { color: var(--color-text); }
.text-color-light-grey { color: var(--color-text-light); }
html, body {
font-family: Helvetica, Verdana, Arial;
color: var(--color-text);
min-height: 100%;
}
body {
background-image: url('../images/achtergrond-3.jpg');
background-repeat: no-repeat;
background-position: center;
background-size: cover;
background-attachment: fixed;
animation: pageIn 0.3s ease-in;
}
@keyframes pageIn {
from { opacity: 0; }
to { opacity: 1; }
}
#main-menu {
background-color: var(--color-brand);
padding-left: 8px;
padding-right: 8px;
}
@media (min-width: 768px) {
#main-menu {
padding-left: 15px;
padding-right: 15px;
}
}
@media (min-width: 1200px) {
#main-menu {
padding-left: 30px;
padding-right: 30px;
}
}
#main-menu a {
color: #fff;
}
#main-menu a.active,
#main-menu a:hover {
color: var(--color-text);
}
#main-menu .navbar-toggler {
border-color: rgba(255, 255, 255, 0.5);
}
#main-menu .navbar-toggler-icon {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255%2c 255%2c 255%2c 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
#main-menu .navbar-toggler:focus {
box-shadow: 0 0 0 0.25rem rgba(255, 255, 255, 0.25);
}
#main-menu .nav-item.dropdown .nav-link {
line-height: 25px;
padding-top: 0;
padding-bottom: 0;
}
#main-menu .dropdown-menu {
background-color: var(--color-brand);
border-color: #fff;
min-width: 0;
}
#main-menu .dropdown-menu .dropdown-item {
color: #fff;
}
#main-menu .dropdown-menu .dropdown-item:hover {
background-color: var(--color-brand);
color: var(--color-text);
}
img.social-icon {
filter: invert(1);
}
a.active img.social-icon,
a:hover img.social-icon {
filter: invert(0);
}
#main-outline {
border: 8px solid var(--color-brand);
border-top: none;
min-height: 50vh;
background-color: rgba(255, 255, 255, 0.88);
margin-left: 0;
margin-right: 0;
}
@media (min-width: 768px) {
#main-outline {
border-width: 15px;
}
}
@media (min-width: 1200px) {
#main-outline {
border-width: 30px;
}
}
.main-header {
font-size: 1.8em;
color: var(--color-brand);
}
@media (min-width: 768px) {
.main-header {
font-size: 2.4em;
}
}
@media (min-width: 1200px) {
.main-header {
font-size: 3em;
}
}
footer {
text-align: center;
text-shadow: 1px 1px var(--color-text);
}
.gallery-item {
display: block;
overflow: hidden;
}
.gallery-thumb {
aspect-ratio: 1;
overflow: hidden;
}
.gallery-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.gallery-item:hover .gallery-thumb img {
transform: scale(1.05);
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

-5
View File
@@ -1,5 +0,0 @@
document.addEventListener('DOMContentLoaded', function () {
if (typeof GLightbox !== 'undefined' && document.querySelector('.glightbox')) {
GLightbox({ selector: '.glightbox', touchNavigation: true });
}
});
-1
View File
@@ -1 +0,0 @@
enabled: true
@@ -1,26 +0,0 @@
{% extends 'partials/base.html.twig' %}
{% block extra_css %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css">
{% endblock %}
{% block extra_js %}
<script src="https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js" defer></script>
{% endblock %}
{% block content %}
<div class="row mb-3">
<div class="col-12 col-lg-8 offset-lg-2">
<h1 class="funky-font main-header text-center">{{ page.title }}</h1>
{{ content|raw }}
</div>
</div>
<div class="row g-3">
{% for name, img in page.media.images %}
<div class="col-6 col-md-4 col-lg-3">
<a href="{{ img.url }}" class="glightbox gallery-item" data-gallery="{{ page.slug }}">
<div class="gallery-thumb">
<img src="{{ img.url }}" loading="lazy" alt="{{ page.title }}">
</div>
</a>
</div>
{% endfor %}
</div>
{% endblock %}
@@ -1,8 +0,0 @@
{% extends 'partials/base.html.twig' %}
{% set page_class = 'row p-3' %}
{% block content %}
<div class="col-12 col-md-8 col-lg-6 p-3 text-start">
<h1 class="funky-font main-header">{{ page.title }}</h1>
{{ content|raw }}
</div>
{% endblock %}
@@ -1,22 +0,0 @@
{% extends 'partials/base.html.twig' %}
{% set page_class = 'row p-3' %}
{% block content %}
<div class="col-12 col-md-3 p-3">
{% set portret = page.header.portret ? page.media[page.header.portret].url : url('theme://images/portret-1.jpg') %}
<img src="{{ portret }}" class="img-fluid mb-3" alt="Portret Natascha Rieter">
{% if page.header.extra_1 %}
<img src="{{ page.media[page.header.extra_1].url }}" class="img-fluid mb-3 d-none d-md-block" loading="lazy" alt="Portret Natascha Rieter">
{% endif %}
</div>
<div class="col-12 col-md-6 p-3 text-start">
<h1 class="funky-font main-header text-center">{{ page.title }}</h1>
{{ content|raw }}
</div>
<div class="col-12 col-md-3 p-3 text-center">
{% set logo = page.header.logo ? page.media[page.header.logo].url : url('theme://images/logo-blauw.png') %}
<img src="{{ logo }}" class="img-fluid mb-3" loading="lazy" alt="Logo Galerie Groot Welsden">
{% if page.header.extra_2 %}
<img src="{{ page.media[page.header.extra_2].url }}" class="img-fluid d-none d-md-block" loading="lazy" alt="Portret Natascha Rieter">
{% endif %}
</div>
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More