Update AGENT.md to reflect current project structure and philosophy Add comprehensive architecture documentation covering: - Directory layout - Stable contracts - Request flow - Module dependencies - Page vs list detection - Deployment models - Demo fallback Remove outdated plugin system documentation Add new content system documentation Add configuration documentation Add context API documentation Add hooks and plugins documentation Add templates documentation Add rendering documentation Add development environment documentation
137 lines
6.9 KiB
Markdown
137 lines
6.9 KiB
Markdown
# Architecture
|
|
|
|
## Directory Layout
|
|
|
|
```
|
|
app/ # Framework core — stable API surface
|
|
router.php # Entry point: all requests route here
|
|
constants.php # CONTENT_EXTENSIONS, COVER_IMAGE_EXTENSIONS
|
|
hooks.php # Hook enum + Hooks class
|
|
context.php # Context + Templates classes
|
|
config.php # createContext(): config merge + bootstrap
|
|
helpers.php # Utility functions (template resolution, extraction)
|
|
content.php # Content discovery, slug resolution, navigation
|
|
rendering.php # Markdown/HTML/PHP rendering, template wrapping
|
|
cache.php # Markdown render cache (/tmp/folderweb_cache)
|
|
plugins.php # PluginManager class
|
|
static.php # /app/* asset serving with traversal protection
|
|
vendor/ # Parsedown + ParsedownExtra (Markdown→HTML)
|
|
plugins/global/ # Built-in plugins (languages.php)
|
|
default/ # Demo/fallback theme (NOT for production use)
|
|
config.ini # Default configuration
|
|
templates/ # base.php, page.php, list.php, list-compact.php, list-grid.php
|
|
styles/ # Default stylesheet
|
|
languages/ # en.ini, no.ini
|
|
content/ # Demo content shown when custom/ has no content
|
|
custom/ # User layer — all site-specific work goes here
|
|
config.ini # Config overrides (merged over app/default/config.ini)
|
|
templates/ # Override any template by matching filename
|
|
styles/ # Stylesheets served via /app/styles/*
|
|
fonts/ # Fonts served via /app/fonts/*
|
|
assets/ # Static files served at document root (favicon, robots.txt)
|
|
languages/ # Translation overrides/additions (*.ini)
|
|
plugins/global/ # Custom global plugins
|
|
plugins/page/ # Custom page plugins (reserved, not yet active)
|
|
content/ # Website content (= document root in production)
|
|
devel/ # Dev environment (Containerfile, compose, Apache, perf tools)
|
|
docs/ # Documentation (01-03 human-facing, 04 machine-facing)
|
|
```
|
|
|
|
## Stable Contracts
|
|
|
|
**`app/` is the framework.** When developing `custom/` sites, never modify `app/`. When developing the framework itself, preserve these contracts:
|
|
|
|
| Contract | Guaranteed Behavior |
|
|
|---|---|
|
|
| Override chain | `custom/*` always takes priority over `app/default/*` for templates, styles, languages, config |
|
|
| Template names | `base.php`, `page.php`, `list.php` are the three core template names |
|
|
| Hook enum | `Hook::CONTEXT_READY`, `Hook::PROCESS_CONTENT`, `Hook::TEMPLATE_VARS` — signatures documented in `05-hooks-plugins.md` |
|
|
| Context API | `$ctx->set()`, `$ctx->get()`, `$ctx->has()` — stable key/value store |
|
|
| Content extensions | `md`, `html`, `php` — defined in `CONTENT_EXTENSIONS` |
|
|
| Config format | INI with sections, merged via `array_replace_recursive` |
|
|
| Metadata format | INI file named `metadata.ini` in content directories |
|
|
| URL structure | Folder path = URL path, with slug overrides via metadata |
|
|
| Plugin locations | `app/plugins/{scope}/` and `custom/plugins/{scope}/` |
|
|
| Asset routes | `/app/styles/*` → `custom/styles/`, `/app/fonts/*` → `custom/fonts/`, `/app/default-styles/*` → `app/default/styles/` |
|
|
| Trailing slash | Pages and lists enforce trailing slash via 301 redirect |
|
|
|
|
## Request Flow
|
|
|
|
```
|
|
Browser request
|
|
│
|
|
├─ Apache rewrite: all non-/app/ requests → app/router.php
|
|
│
|
|
▼
|
|
router.php
|
|
│
|
|
├─ 1. Load modules (constants, hooks, context, helpers, plugins, config, content, rendering)
|
|
├─ 2. createContext()
|
|
│ ├─ Parse + merge config (default ← custom)
|
|
│ ├─ loadGlobalPlugins() → fires Hook::CONTEXT_READY
|
|
│ ├─ Determine contentDir (custom content or demo fallback)
|
|
│ ├─ Parse REQUEST_URI → requestPath
|
|
│ └─ Resolve template paths (custom fallback to default)
|
|
│
|
|
├─ 3. Check custom/assets/{path} → serve static file + exit
|
|
├─ 4. Check content/{path} for static asset (css/img/pdf/font) → serve + exit
|
|
│
|
|
├─ 5. Empty path? → frontpage: findAllContentFiles + renderMultipleFiles
|
|
│
|
|
└─ 6. parseRequestPath() → {type, path}
|
|
│
|
|
├─ "page": trailing slash redirect → findAllContentFiles → renderMultipleFiles
|
|
│ (fires Hook::PROCESS_CONTENT for file filtering)
|
|
│ (fires Hook::TEMPLATE_VARS before template render)
|
|
│ Template chain: content → page.php → base.php
|
|
│
|
|
├─ "list": trailing slash redirect → build items array from subdirectories
|
|
│ ├─ Check hide_list metadata → treat as page if true
|
|
│ ├─ Select list template from metadata page_template
|
|
│ ├─ For each subdir: loadMetadata, extractTitle, extractDateFromFolder, findCoverImage
|
|
│ ├─ Sort items by date (metadata `order` = ascending|descending)
|
|
│ ├─ Fire Hook::TEMPLATE_VARS
|
|
│ └─ Template chain: items → list-*.php → base.php
|
|
│
|
|
└─ "not_found": 404 response
|
|
```
|
|
|
|
## Module Dependency Order
|
|
|
|
Loaded sequentially in `router.php`:
|
|
|
|
```
|
|
constants.php → hooks.php → context.php → helpers.php → plugins.php → config.php → content.php → rendering.php
|
|
```
|
|
|
|
`config.php` calls `createContext()` which triggers plugin loading, so `hooks.php` and `plugins.php` must be loaded before it.
|
|
|
|
## Page vs List Detection
|
|
|
|
A resolved directory becomes a **list** if it contains subdirectories, otherwise a **page**. Override with `hide_list = true` in metadata to force page rendering on directories with children.
|
|
|
|
## Deployment Models
|
|
|
|
### Framework development (this repo)
|
|
|
|
Both `app/` and `custom/` live in the same repository. `custom/` holds demo/test overrides.
|
|
|
|
### Site development (separate repo)
|
|
|
|
The site is its own git repository containing `custom/`, content, and deployment config. `app/` is included as a symlink, git submodule, or copied directory pointing to a specific framework version. The site repo never modifies `app/`.
|
|
|
|
Typical site repo layout:
|
|
|
|
```
|
|
my-site/ # Site git repo
|
|
app/ → ../folderweb/app # Symlink to framework (or submodule, or copy)
|
|
custom/ # Site-specific templates, styles, plugins, config
|
|
content/ # Website content (often the document root)
|
|
devel/ # Site's own dev environment config (optional)
|
|
```
|
|
|
|
Either direction works: `app/` symlinked into a site repo, or `custom/` symlinked into the framework repo during development.
|
|
|
|
## Demo Fallback
|
|
|
|
When `content/` (document root) has no files, `app/default/content/` is used automatically. This is **demo mode only** — production sites always provide their own content via the document root.
|