4.2 KiB
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 inapp/.- 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
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. Comments only for major sections.
Security: Path traversal protection, document root restriction, strict MIME types, escape all UGC, CSV injection prevention.
Code Style
PHP: Arrow functions, null coalescing, match. Type hints where practical. Single-purpose functions.
CSS: Variables, native nesting, grid. clamp() over @media. Relative units. Margin-top only (reset: * { margin-bottom: 0 }).
Templates: <?= htmlspecialchars($var) ?> for UGC. <?= $content ?> for pre-rendered HTML.
Project Structure
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
Key Features
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 <head> 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.
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