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 diff --git a/content/om-oss/historie.md b/content/om-oss/historie.md index 56673e4..05cbef5 100644 --- a/content/om-oss/historie.md +++ b/content/om-oss/historie.md @@ -26,30 +26,12 @@ De la grunnlaget. Nå fortsetter vi kampen, sammen – for dem, for oss, og for **Britt-Inger Solheim** har lang erfaring som prosjektleder i flere internasjonale selskaper. Hun har lenge vært aktiv innen foreningsarbeid og har et stort nettverk innen pasientorganisasjoner og politikk. Hun deltar aktivt i møter, webinarer og debatter, og er et viktig bindeledd mellom pasienter og beslutningstakere. -**Geir Roger Moen** er en av Norges mest synlige stemmer for medisinsk cannabis. Han har deltatt i debatter og medieopptredener, og hans pasienthistorie har inspirert mange. Geir Roger er en viktig pådriver i vårt arbeid for å endre lovgivningen. [NRK artikkel.](https://www.nrk.no/tromsogfinnmark/sykehuset-ahus-vil-ikke-lenger-gi-geir-roger-moen-cannabismedisin-1.16258980) +**Geir Roger Moen** er en av Norges mest synlige stemmer for medisinsk cannabis. Han har deltatt i debatter og [medieopptredener](https://www.nrk.no/tromsogfinnmark/sykehuset-ahus-vil-ikke-lenger-gi-geir-roger-moen-cannabismedisin-1.16258980), og hans pasienthistorie har inspirert mange. Geir Roger er en viktig pådriver i vårt arbeid for å endre lovgivningen. -**Ruben Solvang** er pårørende og har sett hvor stor forskjell cannabismedisin kan utgjøre for pasienter som ikke får lindring av regulære medisiner. Han er teknisk altmuligmann med ansvar for nettsiden og jobber til daglig som IT-konsulent. [NRK Trygdekontoret.](https://tv.nrk.no/serie/trygdekontoret-dokumentar/sesong/2019/episode/MUHU10000619) +**Ruben Solvang** er pårørende og har sett hvor stor forskjell cannabismedisin kan utgjøre for pasienter som ikke får lindring av regulære medisiner. Han er teknisk altmuligmann med ansvar for nettsiden og jobber til daglig som IT-konsulent. Han ble også intervjuet av [NRK Trygdekontoret](https://tv.nrk.no/serie/trygdekontoret-dokumentar/sesong/2019/episode/MUHU10000619) i 2019. -Kort oppsummert setter vi pasientene i fokus og skaper forandring gjennom kunnskap og samarbeid! +Kort oppsummert setter vi pasientene i fokus og skaper forandring gjennom kunnskap, opplysning og samarbeid! ![](./cover.jpg) -## Vi står på skuldrene til kjemper - -Vi vil samtidig rette en stor takk til alle som har gått foran oss i denne kampen. De første pasientene og pårørende som stod frem i Norge, stod i utfordringer som gjør dagens kamp lettere å bære. Uten deres mot og offervilje hadde vi ikke vært der vi er i dag. - -De la grunnlaget. Nå fortsetter vi kampen, sammen – for dem, for oss, og for alle som kommer etter. - -## Møt styret - -**Sofie Haughom** er utdannet fysioterapeut med master i samfunnssikkerhet. Hun tar nå master i helsevitenskap. Sofie har stor interesse for fagfeltet og har skrevet fagsammendrag, kronikker, deltatt i debatter og holdt foredrag. Sofie har også personlig erfaring fra kampen for tilgang til cannabismedisin, og har stått frem med sin pasienthistorie i [NRK Ekko](https://radio.nrk.no/podkast/ekko_-_et_aktuelt_samfunnsprogram/sesong/202004/l_d183ff97-c98d-4015-83ff-97c98d4015fc) / [NRK Dokumentar](https://www.nrk.no/dokumentar/xl/sofie-sprayer-cannabis-under-tungen-fire-ganger-om-dagen-1.13458156). - -**Britt-Inger Solheim** har lang erfaring som prosjektleder i flere internasjonale selskaper. Hun har lenge vært aktiv innen foreningsarbeid og har et stort nettverk innen pasientorganisasjoner og politikk. Hun deltar aktivt i møter, webinarer og debatter, og er et viktig bindeledd mellom pasienter og beslutningstakere. - -**Geir Roger Moen** er en av Norges mest synlige stemmer for medisinsk cannabis. Han har deltatt i debatter og medieopptredener, og hans pasienthistorie har inspirert mange. Geir Roger er en viktig pådriver i vårt arbeid for å endre lovgivningen. - -**Ruben Solvang** er pårørende og har sett hvor stor forskjell cannabismedisin kan utgjøre for pasienter som ikke får lindring av regulære medisiner. Han er teknisk altmuligmann med ansvar for nettsiden og jobber til daglig som IT-konsulent. - -Kort oppsummert setter vi pasientene i fokus og skaper forandring gjennom kunnskap og samarbeid! - Les våre vedtekter 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'], + + + 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. | `