folderweb/docs/04-development/03-configuration.md
Ruben b03511f99b Update AGENT.md and add architecture documentation
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
2026-02-05 23:30:44 +01:00

114 lines
3.9 KiB
Markdown

# Configuration
## Config Loading
**`createContext()`** in `app/config.php` handles the full bootstrap:
1. Parse `app/default/config.ini` (framework defaults)
2. If `custom/config.ini` exists, merge via `array_replace_recursive` (custom wins)
3. Load global plugins using merged config
4. Determine content directory
5. Create `Context` object
6. Fire `Hook::CONTEXT_READY` — plugins receive the merged `$config` array
## INI Format
Standard PHP `parse_ini_file` with sections enabled:
```ini
[languages]
default = "en"
available = "no,en"
[plugins]
enabled = "languages"
[custom_section]
key = "value"
```
## Built-in Config Keys
| Section | Key | Type | Default | Purpose |
|---|---|---|---|---|
| `languages` | `default` | string | `"en"` | Default language (no URL prefix) |
| `languages` | `available` | string | `"no,en"` | Comma-separated ISO 639-1 codes |
| `plugins` | `enabled` | string | `"languages"` | Comma-separated plugin names (without `.php`) |
All other sections are custom — define whatever your plugins need.
## The custom/ Override System
This is the primary mechanism for using FolderWeb. The `custom/` directory is the user layer:
```
custom/
config.ini # Merged over app/default/config.ini
templates/ # Override by matching filename (e.g., base.php, page.php, list.php)
styles/ # Served at /app/styles/*
fonts/ # Served at /app/fonts/*
assets/ # Served at document root (favicon.ico, robots.txt, etc.)
languages/ # Merged over app/default/languages/*.ini
plugins/
global/ # Loaded alongside app/plugins/global/
page/ # Reserved for future use
```
### Override Resolution Order
| Resource | Lookup | Fallback |
|---|---|---|
| Templates | `custom/templates/{name}.php` | `app/default/templates/{name}.php` |
| Styles | `custom/styles/*` via `/app/styles/*` | `app/default/styles/*` via `/app/default-styles/*` |
| Languages | `custom/languages/{lang}.ini` merged over | `app/default/languages/{lang}.ini` |
| Config | `custom/config.ini` merged over | `app/default/config.ini` |
| Plugins | `custom/plugins/{scope}/{name}.php` | `app/plugins/{scope}/{name}.php` |
For plugins, custom takes priority: if both `custom/plugins/global/foo.php` and `app/plugins/global/foo.php` exist, the custom version loads.
For full template resolution details, see `06-templates.md` "Template Resolution (canonical reference)".
### Static Asset Routing
The router checks these locations before content routing:
1. `custom/assets/{requestPath}` — served with `mime_content_type()`
2. `{contentDir}/{requestPath}` — served with explicit MIME map for: `css`, `jpg`, `jpeg`, `png`, `gif`, `webp`, `svg`, `pdf`, `woff`, `woff2`, `ttf`, `otf`
`/app/*` requests are handled by `static.php`:
- `/app/styles/*``custom/styles/*`
- `/app/fonts/*``custom/fonts/*`
- `/app/assets/*``custom/assets/*`
- `/app/default-styles/*``app/default/styles/*`
## Accessing Config in Plugins
The merged config array is passed to `Hook::CONTEXT_READY`:
```php
Hooks::add(Hook::CONTEXT_READY, function(Context $ctx, array $config) {
$val = $config['my_section']['my_key'] ?? 'default';
$ctx->set('my_val', $val);
return $ctx;
});
```
Config is **not** directly available in templates. Expose values via `Hook::TEMPLATE_VARS`:
```php
Hooks::add(Hook::TEMPLATE_VARS, function(array $vars, Context $ctx) {
global $config;
$vars['siteTitle'] = $config['site']['title'] ?? 'My Site';
return $vars;
});
```
## Content Directory Resolution
In `createContext()`:
1. `$_SERVER['DOCUMENT_ROOT']` is checked for content (>2 entries in scandir)
2. If content exists → use document root as `contentDir`
3. If empty → fall back to `app/default/content/` (demo mode)
Production deployments set Apache's `DocumentRoot` to the content directory. The `app/` and `custom/` directories live outside the document root, accessed via Apache aliases.