From 631c784efc807c1571f75c34559bcbf6c1d5af50 Mon Sep 17 00:00:00 2001 From: Ruben Date: Fri, 6 Feb 2026 19:03:13 +0100 Subject: [PATCH 1/4] Add feed link to head section when available --- custom/templates/base.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/custom/templates/base.php b/custom/templates/base.php index e473327..7e1409e 100644 --- a/custom/templates/base.php +++ b/custom/templates/base.php @@ -74,6 +74,9 @@ function getActiveClass($href) { return rtrim(parse_url($_SERVER['REQUEST_URI'], + + + From 2f024e28be7945848e32e6c0a4621746423c9a64 Mon Sep 17 00:00:00 2001 From: Ruben Date: Fri, 6 Feb 2026 19:15:21 +0100 Subject: [PATCH 2/4] Update AGENT.md with expanded documentation structure --- AGENT.md | 84 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/AGENT.md b/AGENT.md index 9cef900..6f33123 100644 --- a/AGENT.md +++ b/AGENT.md @@ -1,37 +1,77 @@ -## Philosophy -Minimal PHP for modern conveniences. Prioritize longevity (decade-scale maintainability) by avoiding volatile dependencies. Strictly add only what's essential—readable, simple, and future-proof. +# Stopp lidelsen - Site built on FolderWeb + +Advocacy site for medical cannabis patients in Norway. Built on FolderWeb (minimal file-based CMS). ## Edit instructions All edits to this AGENT.md file should be done in a compressed shortform for LLM consumption. +## Two Repos, One Site + +- `app/` is a **symlink** to the FolderWeb framework (`folderweb/app`). **NEVER modify files in `app/`.** +- Only modify files in `custom/`, `content/`, and project root. +- Framework docs: read `app/docs/` when needed for understanding routing, hooks, templates, etc. +- Site-specific docs: read `docs/` in this repo for guidance on this site's features and customizations. + +## Philosophy +Minimal PHP for modern conveniences. Decade-scale maintainability. No volatile dependencies. Only essential code. + ## Core Constraints -**Minimalism:** Only essential tech (HTML, PHP 8.4+, CSS). No JS, frameworks, build tools, or package managers. Comments only for major sections. +**Stack:** HTML5, PHP 8.4+, CSS. no frameworks, no build tools, no package managers. -**Frontend:** -- Classless, semantic HTML5 -- Modern CSS: nesting, `oklch()`, grid, `clamp()`, logical props -- Responsive via fluid typography + flexible layouts +**Frontend:** Classless semantic HTML5. Modern CSS (nesting, `oklch()`, grid, `clamp()`, logical props). Responsive via fluid typography + flexible layouts. Comments only for major sections. -**Security:** -- Path validation blocks traversal -- Files restricted to document root -- Strict MIME types + no direct user-input execution +**Security:** Path traversal protection, document root restriction, strict MIME types, escape all UGC, CSV injection prevention. ## Code Style -**PHP:** Modern syntax (arrow functions, null coalescing, match). Type hints where practical. Ternary for simple conditionals. Single-purpose functions. +**PHP:** Arrow functions, null coalescing, match. Type hints where practical. Single-purpose functions. +**CSS:** Variables, native nesting, grid. `clamp()` over `@media`. Relative units. +**Templates:** `` for UGC. `` for pre-rendered HTML. -**CSS:** Variables, native nesting, grid layouts. `clamp()` over `@media`. Relative units > pixels. +## Project Structure -**Templates:** Escape output (`htmlspecialchars()` for UGC). Short echo tags (``). +``` +content/ # Site content (folders = URLs) +custom/ + config.ini # Site config (languages, plugins) + templates/ # Template overrides (base, list, page, list-card-grid, list-faq, list-grid) + styles/ # CSS overrides + plugins/page/ # Page plugins (petition-form, newsletter-signup) + languages/ # Translation files (no.ini, en.ini) + data/ # Runtime data (petition CSVs, rate limits, SMTP logs) + vendor/ # PHPMailer.Lite +app/ -> symlink # FolderWeb framework (DO NOT EDIT) +docs/ # Site-specific LLM documentation +``` -## Development Environment and workflow +## Key Features -The host does not have PHP, always use `podman compose` with the `compose.yaml` configuration when interacting with the app through CLI. Check localhost:4040 when you need to look at the development site to check your changes. +**Content:** Folders = URLs. `metadata.ini` controls titles, slugs, menu order, templates, feeds. +**Languages:** Norwegian (default), English available. Language plugin via `[en]` sections in metadata. +**Templates:** base.php wraps all pages. List templates: list.php, list-card-grid.php, list-faq.php, list-grid.php. Selected via `page_template` in metadata. +**Atom feeds:** Enabled per-section via `feed = true` in metadata. URL: `/{section}/feed.xml`. Feed link auto-added to `` when `$feedUrl` is set. +**Petition system:** GDPR-compliant, CSV-based, double opt-in email confirmation, file locking, rate limiting. See `docs/petition-system.md`. +**Newsletter:** Listmonk integration via public API. Hero and small themes. See `docs/newsletter-plugin.md`. -**LLM Agent Testing Protocol:** -**Container isolation:** ALL test data (CSV, temp files, generated content) MUST remain inside container filesystem (not mounted volumes) -*Testing website:** `curl localhost:4040/path` to fetch pages, test forms with `curl -X POST -d "field=value" localhost:4040/path` -- **Test data paths:** Use `/tmp/` or `/var/test/` inside container—never write to `/var/www/html` or mounted dirs during tests +## Knowledge Base + +Read these docs on-demand when working on related areas: + +| Topic | File | Read When | +|---|---|---| +| Templates & variables | `docs/templates.md` | Modifying templates, understanding available variables | +| Content & metadata | `docs/content-system.md` | Adding/modifying content, metadata fields, feeds | +| Petition system | `docs/petition-system.md` | Modifying petition form, CSV format, email flow | +| Newsletter plugin | `docs/newsletter-plugin.md` | Modifying newsletter signup, Listmonk integration | +| Framework internals | `app/docs/` | Understanding routing, hooks, plugins, rendering pipeline | + +## Development Environment + +Host has no PHP. Always use `podman compose` with `compose.yaml`. + +**Dev server:** `localhost:4040` +**Testing:** `curl localhost:4040/path` to fetch pages; `curl -X POST -d "field=value" localhost:4040/path` for forms +**Container isolation:** ALL test data (CSV, temp files) MUST stay inside container filesystem, never in mounted volumes +**Test data paths:** Use `/tmp/` or `/var/test/` inside container **Running tests:** `podman exec stopplidelsen.no php /path/to/test.php` or `podman-compose run --rm custom php /tmp/test-script.php` - **Syntax checks:** `podman exec stopplidelsen.no php -l /var/www/custom/file.php` - **Cleanup:** `podman-compose down && podman-compose up -d` between test runs +**Syntax checks:** `podman exec stopplidelsen.no php -l /var/www/custom/file.php` +**Cleanup:** `podman-compose down && podman-compose up -d` between test runs From a142b0562f501124a47370bcf7f7f824ee436aac Mon Sep 17 00:00:00 2001 From: Ruben Date: Fri, 6 Feb 2026 19:15:29 +0100 Subject: [PATCH 3/4] Add documentation for content system, newsletter plugin, petition system, and templates Add content system documentation Add newsletter plugin documentation Add petition system documentation Add templates documentation --- docs/content-system.md | 163 ++++++++++++++++++++++++++++++++++++++ docs/newsletter-plugin.md | 94 ++++++++++++++++++++++ docs/petition-system.md | 139 ++++++++++++++++++++++++++++++++ docs/templates.md | 125 +++++++++++++++++++++++++++++ 4 files changed, 521 insertions(+) create mode 100644 docs/content-system.md create mode 100644 docs/newsletter-plugin.md create mode 100644 docs/petition-system.md create mode 100644 docs/templates.md diff --git a/docs/content-system.md b/docs/content-system.md new file mode 100644 index 0000000..dcf032a --- /dev/null +++ b/docs/content-system.md @@ -0,0 +1,163 @@ +# Content System Reference + +For LLM agents working with site content. Read when adding/modifying content or metadata. + +## Folder = URL + +Every directory under `content/` becomes a URL path. The folder name is the default slug. + +``` +content/nyheter/2025-09-26-banebrytende-studie/ -> /nyheter/banebrytende-studie/ +content/faq/Hva-er-MC/ -> /faq/hva-er-mc/ +content/kontakt/ -> /kontakt/ +``` + +Date prefixes (`YYYY-MM-DD-`) are stripped from URLs and used as the item date. +Numeric prefixes (`NN-`) control ordering and are stripped from URLs. + +## Content Files + +Place these inside a content directory: + +| File | Purpose | +|---|---| +| `article.md` or `index.md` | Main content (Markdown) | +| `article.en.md` | English translation | +| `index.html` | HTML content alternative | +| `index.php` | PHP content (has access to `$GLOBALS['ctx']`) | +| `metadata.ini` | Configuration for this directory | +| `cover.png/jpg/webp` | Cover image for list display | +| `styles.css` | Page-specific CSS | +| `script.js` | Page-specific JS | +| `*.pdf` | Downloadable PDF | + +Multiple content files in one directory are rendered in sequence (alphabetical order). + +## Page vs List Detection + +- Directory with **no subdirectories** (or `hide_list = true`) -> renders as **page** using `page.php` +- Directory with **subdirectories** -> renders as **list** using `list.php` or `page_template` override + +## metadata.ini Reference + +### Core Fields + +```ini +title = "Page Title" # Display title +summary = "Short description" # Shown in lists and meta description +date = "2026-01-15" # Explicit date (overrides folder date prefix) +slug = "custom-url" # Custom URL segment (overrides folder name) +``` + +### Menu & Ordering + +```ini +menu = true # Show in main navigation +menu_order = 1 # Navigation sort order (lower = first) +order = 5 # Sort order within parent list +``` + +### Display & Templates + +```ini +page_template = "list-faq" # Override list template (list-card-grid, list-faq, list-grid) +show_date = false # Hide dates in list display +hide_list = true # Treat directory-with-subdirs as a page instead of list +``` + +### Feeds + +```ini +feed = true # Enable Atom feed at /{section}/feed.xml +author = "Stopp lidelsen" # Feed author name +``` + +When `feed = true` is set: +1. `/{section}/feed.xml` serves Atom XML with full content of each item +2. `$feedUrl` is set in template context, enabling `` in `` +3. Feed includes all list items with rendered HTML content + +### Plugins + +```ini +plugins = "petition-form" # Comma-separated page plugins to load +petition_id = "my-petition" # Plugin-specific config +thank_you_page = "takk" # Plugin-specific config +``` + +### Language Overrides + +```ini +[en] +title = "English Title" +summary = "English description" +slug = "english-url" +``` + +Language sections are merged into base metadata when that language is active. Any field can be overridden per language. + +### Metadata Priority + +Language-specific `[lang]` section > root fields > auto-extracted (date from folder name) > defaults + +### Categories & Tags + +```ini +tags = "nyhetsbrev, oppsummering" +categories = "Nyhetsbrev" + +[en] +tags = "newsletter, summary" +categories = "Newsletters" +``` + +Displayed by `page.php` template if present. + +## Current Site Sections + +| Section | Folder | Template | Menu | Feed | Notes | +|---|---|---|---|---|---| +| Home | `content/` | page | - | - | Has newsletter-signup plugin | +| News | `content/nyheter/` | list | Yes (1) | Eligible | Date-prefixed articles | +| Articles | `content/artikler/` | list | Yes (2) | - | Subsections with articles | +| Brochures | `content/brosjyrer/` | list-card-grid | No | - | PDFs + redirects to articles | +| FAQ | `content/faq/` | list-faq | Yes | - | Accordion layout | +| Docs | `content/docs/` | list | No | - | Internal documentation | +| Petitions | `content/underskriftskampanje/` | list | No | - | Has petition-form plugin | +| Contact | `content/kontakt/` | page | Yes (10) | - | | +| About | `content/om-oss/` | page | No | - | `hide_list=true` | +| Privacy | `content/personvern/` | page | No | - | `hide_list=true` | + +## Adding Content + +### New Article in Existing Section + +``` +content/nyheter/YYYY-MM-DD-slug-name/ + article.md # Norwegian content + article.en.md # English content (optional) + metadata.ini # tags, categories, [en] overrides + cover.png # Optional cover image +``` + +### New Section + +``` +content/new-section/ + metadata.ini # menu=true, menu_order=N, page_template=... + article.md # Intro content (shown above list) + subsection-1/ + article.md + metadata.ini + subsection-2/ + ... +``` + +## Language System + +- Default language: Norwegian (`no`) +- Available: `no`, `en` (configured in `custom/config.ini`) +- Norwegian URLs: `/nyheter/`, `/faq/`, etc. +- English URLs: `/en/news/`, `/en/faq/`, etc. (with `slug` from `[en]` section) +- Content files: `article.md` (Norwegian), `article.en.md` (English) +- Translation strings: `custom/languages/no.ini`, `custom/languages/en.ini` diff --git a/docs/newsletter-plugin.md b/docs/newsletter-plugin.md new file mode 100644 index 0000000..a9fa898 --- /dev/null +++ b/docs/newsletter-plugin.md @@ -0,0 +1,94 @@ +# Newsletter Plugin Reference + +For LLM agents working on the newsletter signup. Read when modifying newsletter functionality. + +## Overview + +Modular newsletter signup that can be embedded on any page. Uses Listmonk's public subscription API for double opt-in. Located in `custom/plugins/page/newsletter-signup.php`. + +## How It Activates + +Add to a page's `metadata.ini`: +```ini +plugins = "newsletter-signup" +``` + +Then call from PHP content files: +```php + + +``` + +## Themes + +| Theme | Description | HTML Element | +|---|---|---| +| `hero` | Full-width gradient background section (green->blue). Centered form with pill-shaped inputs. | `
` | +| `small` | Compact inline box with green border. Fits between paragraphs. | `